@pixerate/editor 0.13.0 → 0.14.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/dist/index.d.cts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { T as Token } from './tokens-7kRTEtPb.cjs';
2
2
  export { C as ColorGradient, I as INSTRUCTION_REGEX, a as TEMPLATE_REGEX, b as Template, c as TemplateVersion, d as TokenType, V as VARIABLE_REGEX, i as instructionRegex, t as templateRegex, v as variableRegex } from './tokens-7kRTEtPb.cjs';
3
- export { ResolveTemplatesOptions, SourceMapSegment, extractInstructionNames, extractTemplateNames, extractVariableNames, getLatestVersion, mapRawOffsetToResolved, mapResolvedOffsetToRaw, resolveTemplates, resolveTemplatesWithMapping, resolveVariables, tokenizePrompt } from './grammar/index.cjs';
3
+ export { ResolveTemplatesOptions, SourceMapSegment, TEMPLATE_DEPTH_ERROR, TEMPLATE_LOOP_ERROR, extractInstructionNames, extractTemplateNames, extractVariableNames, getLatestVersion, getTemplateBody, mapRawOffsetToResolved, mapResolvedOffsetToRaw, resolveTemplates, resolveTemplatesWithMapping, resolveVariables, tokenizePrompt } from './grammar/index.cjs';
4
4
  export { ColorHighlighter, FontSize, GradientText, GradientTextOptions, Image, ImageOptions, LoadingNode, Markdown, RichTextPresetOptions, SlashCommandMatchData, SlashCommands, SlashCommandsOptions, SmilieReplacer, TemplateSuggestions, TemplateSuggestionsOptions, createRichTextPreset, findHexColors, handleImageInsertion, imagePastePluginKey, insertImageNode, removeImageNode, updateImageSrc } from './extensions/index.cjs';
5
5
  export { M as Mention, a as MentionEntity, b as MentionOptions, c as createMentionExtension } from './mention-Dcevh3ff.cjs';
6
6
  export { ExtractedMention, MARKDOWN_MENTION_SPLIT_REGEX, MENTION_REGEX, MarkdownToHtmlOptions, MentionSegment, PreprocessMarkdownMentionsOptions, escapeHtml, extractMentions, extractMentionsFromDoc, getEditorMarkdown, getEditorText, htmlToPlainText, markdownToTipTapHtml, parseMentionSegments, plainTextToTipTapHtml, preprocessMarkdownFileLinks, preprocessMarkdownMentions, stripMarkdownCode } from './serializers/index.cjs';
@@ -55,8 +55,16 @@ declare class EditorController {
55
55
  setPlainText(text: string, emitUpdate?: boolean): void;
56
56
  getMarkdown(): string;
57
57
  setMarkdown(markdown: string, emitUpdate?: boolean): void;
58
+ /**
59
+ * Replaces `from`..`to` with literal text: HTML in `text` is not parsed, and
60
+ * newlines split paragraphs.
61
+ */
62
+ private insertPlainText;
63
+ /** Inserts text literally at the selection; HTML in `text` is not parsed. */
58
64
  insertText(text: string): void;
65
+ /** Inserts text literally at `pos`; HTML in `text` is not parsed. */
59
66
  insertTextAt(pos: number, text: string): void;
67
+ /** Replaces a range with literal text; HTML in `text` is not parsed. */
60
68
  replaceRange(from: number, to: number, text: string): void;
61
69
  getCurrentContentString(): string;
62
70
  isDirty(): boolean;
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { T as Token } from './tokens-7kRTEtPb.js';
2
2
  export { C as ColorGradient, I as INSTRUCTION_REGEX, a as TEMPLATE_REGEX, b as Template, c as TemplateVersion, d as TokenType, V as VARIABLE_REGEX, i as instructionRegex, t as templateRegex, v as variableRegex } from './tokens-7kRTEtPb.js';
3
- export { ResolveTemplatesOptions, SourceMapSegment, extractInstructionNames, extractTemplateNames, extractVariableNames, getLatestVersion, mapRawOffsetToResolved, mapResolvedOffsetToRaw, resolveTemplates, resolveTemplatesWithMapping, resolveVariables, tokenizePrompt } from './grammar/index.js';
3
+ export { ResolveTemplatesOptions, SourceMapSegment, TEMPLATE_DEPTH_ERROR, TEMPLATE_LOOP_ERROR, extractInstructionNames, extractTemplateNames, extractVariableNames, getLatestVersion, getTemplateBody, mapRawOffsetToResolved, mapResolvedOffsetToRaw, resolveTemplates, resolveTemplatesWithMapping, resolveVariables, tokenizePrompt } from './grammar/index.js';
4
4
  export { ColorHighlighter, FontSize, GradientText, GradientTextOptions, Image, ImageOptions, LoadingNode, Markdown, RichTextPresetOptions, SlashCommandMatchData, SlashCommands, SlashCommandsOptions, SmilieReplacer, TemplateSuggestions, TemplateSuggestionsOptions, createRichTextPreset, findHexColors, handleImageInsertion, imagePastePluginKey, insertImageNode, removeImageNode, updateImageSrc } from './extensions/index.js';
5
5
  export { M as Mention, a as MentionEntity, b as MentionOptions, c as createMentionExtension } from './mention-Dcevh3ff.js';
6
6
  export { ExtractedMention, MARKDOWN_MENTION_SPLIT_REGEX, MENTION_REGEX, MarkdownToHtmlOptions, MentionSegment, PreprocessMarkdownMentionsOptions, escapeHtml, extractMentions, extractMentionsFromDoc, getEditorMarkdown, getEditorText, htmlToPlainText, markdownToTipTapHtml, parseMentionSegments, plainTextToTipTapHtml, preprocessMarkdownFileLinks, preprocessMarkdownMentions, stripMarkdownCode } from './serializers/index.js';
@@ -55,8 +55,16 @@ declare class EditorController {
55
55
  setPlainText(text: string, emitUpdate?: boolean): void;
56
56
  getMarkdown(): string;
57
57
  setMarkdown(markdown: string, emitUpdate?: boolean): void;
58
+ /**
59
+ * Replaces `from`..`to` with literal text: HTML in `text` is not parsed, and
60
+ * newlines split paragraphs.
61
+ */
62
+ private insertPlainText;
63
+ /** Inserts text literally at the selection; HTML in `text` is not parsed. */
58
64
  insertText(text: string): void;
65
+ /** Inserts text literally at `pos`; HTML in `text` is not parsed. */
59
66
  insertTextAt(pos: number, text: string): void;
67
+ /** Replaces a range with literal text; HTML in `text` is not parsed. */
60
68
  replaceRange(from: number, to: number, text: string): void;
61
69
  getCurrentContentString(): string;
62
70
  isDirty(): boolean;
package/dist/index.js CHANGED
@@ -116,6 +116,18 @@ function getLatestVersion(template) {
116
116
  }
117
117
  return template.versions[template.versions.length - 1];
118
118
  }
119
+ var TEMPLATE_LOOP_ERROR = "[Template loop detected]";
120
+ var TEMPLATE_DEPTH_ERROR = "[Template max depth exceeded]";
121
+ function getTemplateBody(template, bindings) {
122
+ const boundVersionId = bindings?.[template.name];
123
+ if (boundVersionId && template.versions) {
124
+ const boundVersion = template.versions.find(
125
+ (v) => v.id === boundVersionId || v.version === boundVersionId
126
+ );
127
+ return boundVersion?.body;
128
+ }
129
+ return getLatestVersion(template)?.body ?? template.body;
130
+ }
119
131
  function resolveTemplates(text, templates = [], options) {
120
132
  const templateMap = /* @__PURE__ */ new Map();
121
133
  for (const t of templates) {
@@ -123,50 +135,27 @@ function resolveTemplates(text, templates = [], options) {
123
135
  }
124
136
  const maxDepth = options?.maxDepth ?? 10;
125
137
  function resolveRecursive(currentText, visited, depth) {
126
- if (depth > maxDepth) {
127
- return "[Template max depth exceeded]";
128
- }
129
- let result = currentText;
130
138
  const regex = new RegExp(TEMPLATE_REGEX.source, TEMPLATE_REGEX.flags);
131
- let match;
132
- while ((match = regex.exec(result)) !== null) {
133
- const [fullMatch, rawTemplateName] = match;
139
+ return currentText.replace(regex, (fullMatch, rawTemplateName) => {
134
140
  const templateName = rawTemplateName.trim();
135
141
  if (visited.has(templateName)) {
136
- return "[Template loop detected]";
142
+ return TEMPLATE_LOOP_ERROR;
137
143
  }
138
144
  const template = templateMap.get(templateName);
139
145
  if (!template) {
140
- continue;
141
- }
142
- let versionBody;
143
- const boundVersionId = options?.bindings?.[templateName];
144
- if (boundVersionId && template.versions) {
145
- const boundVersion = template.versions.find(
146
- (v) => v.id === boundVersionId || v.version === boundVersionId
147
- );
148
- versionBody = boundVersion?.body;
149
- } else {
150
- const latest = getLatestVersion(template);
151
- versionBody = latest?.body ?? template.body;
146
+ return fullMatch;
152
147
  }
148
+ const versionBody = getTemplateBody(template, options?.bindings);
153
149
  if (versionBody === void 0) {
154
- continue;
150
+ return fullMatch;
151
+ }
152
+ if (depth + 1 > maxDepth) {
153
+ return TEMPLATE_DEPTH_ERROR;
155
154
  }
156
155
  const nextVisited = new Set(visited);
157
156
  nextVisited.add(templateName);
158
- const resolvedBody = resolveRecursive(
159
- versionBody,
160
- nextVisited,
161
- depth + 1
162
- );
163
- if (resolvedBody === "[Template loop detected]" || resolvedBody === "[Template max depth exceeded]") {
164
- return resolvedBody;
165
- }
166
- result = result.replace(fullMatch, resolvedBody);
167
- regex.lastIndex = 0;
168
- }
169
- return result;
157
+ return resolveRecursive(versionBody, nextVisited, depth + 1);
158
+ });
170
159
  }
171
160
  return resolveRecursive(text, /* @__PURE__ */ new Set(), 0);
172
161
  }
@@ -215,7 +204,7 @@ function extractInstructionNames(text) {
215
204
  }
216
205
 
217
206
  // src/grammar/sourcemap.ts
218
- function resolveTemplatesWithMapping(text, templates = [], templateBindings) {
207
+ function resolveTemplatesWithMapping(text, templates = [], templateBindings, options) {
219
208
  if (!templates || templates.length === 0 || !text.includes("{{")) {
220
209
  return {
221
210
  text,
@@ -231,7 +220,7 @@ function resolveTemplatesWithMapping(text, templates = [], templateBindings) {
231
220
  };
232
221
  }
233
222
  const templateMap = new Map(templates.map((t) => [t.name, t]));
234
- const regex = /{{\s*([^}]+?)\s*}}/g;
223
+ const regex = new RegExp(TEMPLATE_REGEX.source, TEMPLATE_REGEX.flags);
235
224
  let match;
236
225
  let lastIndex = 0;
237
226
  let resolvedText = "";
@@ -253,25 +242,13 @@ function resolveTemplatesWithMapping(text, templates = [], templateBindings) {
253
242
  }
254
243
  let resolvedBody = fullMatch;
255
244
  let isTemplate = false;
256
- if (templateMap.has(templateName)) {
257
- const template = templateMap.get(templateName);
258
- let versionBody;
259
- const boundVersionId = templateBindings?.[templateName];
260
- if (boundVersionId && template.versions) {
261
- const boundVersion = template.versions.find(
262
- (v) => v.id === boundVersionId || v.version === boundVersionId
263
- );
264
- versionBody = boundVersion?.body;
265
- } else {
266
- const latestVersion = getLatestVersion(template);
267
- versionBody = latestVersion?.body ?? template.body;
268
- }
269
- if (versionBody !== void 0) {
270
- resolvedBody = resolveTemplates(versionBody, templates, {
271
- bindings: templateBindings
272
- });
273
- isTemplate = true;
274
- }
245
+ const template = templateMap.get(templateName);
246
+ if (template && getTemplateBody(template, templateBindings) !== void 0) {
247
+ resolvedBody = resolveTemplates(fullMatch, templates, {
248
+ bindings: templateBindings,
249
+ maxDepth: options?.maxDepth
250
+ });
251
+ isTemplate = true;
275
252
  }
276
253
  if (isTemplate) {
277
254
  map.push({
@@ -316,9 +293,11 @@ function mapResolvedOffsetToRaw(resolvedOffset, map) {
316
293
  if (inRange) {
317
294
  if (seg.source === "raw") {
318
295
  return seg.rawStart + (resolvedOffset - seg.resolvedStart);
319
- } else {
320
- return seg.rawStart;
321
296
  }
297
+ if (resolvedOffset === seg.resolvedEnd) {
298
+ return seg.rawEnd;
299
+ }
300
+ return seg.rawStart;
322
301
  }
323
302
  }
324
303
  return resolvedOffset;
@@ -331,9 +310,11 @@ function mapRawOffsetToResolved(rawOffset, map) {
331
310
  if (inRange) {
332
311
  if (seg.source === "raw") {
333
312
  return seg.resolvedStart + (rawOffset - seg.rawStart);
334
- } else {
335
- return seg.resolvedStart;
336
313
  }
314
+ if (rawOffset === seg.rawEnd) {
315
+ return seg.resolvedEnd;
316
+ }
317
+ return seg.resolvedStart;
337
318
  }
338
319
  }
339
320
  return rawOffset;
@@ -2349,38 +2330,98 @@ function escapeHtml(text) {
2349
2330
  if (!text) return "";
2350
2331
  return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
2351
2332
  }
2333
+ var PARAGRAPH_HTML_REGEX = /^\s*<p[\s>][\s\S]*<\/p>\s*$/i;
2352
2334
  function plainTextToTipTapHtml(text) {
2353
2335
  if (!text) return "<p></p>";
2354
- if (/<p[\s>]/i.test(text)) {
2336
+ if (PARAGRAPH_HTML_REGEX.test(text)) {
2355
2337
  return text;
2356
2338
  }
2357
2339
  return text.replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n").map((line) => `<p>${escapeHtml(line)}</p>`).join("");
2358
2340
  }
2341
+ var BLOCK_TAGS = /* @__PURE__ */ new Set([
2342
+ "P",
2343
+ "DIV",
2344
+ "LI",
2345
+ "UL",
2346
+ "OL",
2347
+ "PRE",
2348
+ "BLOCKQUOTE",
2349
+ "TABLE",
2350
+ "TR",
2351
+ "H1",
2352
+ "H2",
2353
+ "H3",
2354
+ "H4",
2355
+ "H5",
2356
+ "H6",
2357
+ "HR"
2358
+ ]);
2359
+ var MENTION_SPAN_REGEX = /<span\b(?=[^>]*\bdata-type=["']mention["'])([^>]*)>[\s\S]*?<\/span>/gi;
2360
+ function getHtmlAttribute(attrs, name) {
2361
+ const match = new RegExp(`\\b${name}=(?:"([^"]*)"|'([^']*)')`, "i").exec(
2362
+ attrs
2363
+ );
2364
+ return match ? match[1] ?? match[2] : void 0;
2365
+ }
2366
+ function parseHtmlFragment(html) {
2367
+ const template = document.createElement("template");
2368
+ template.innerHTML = html;
2369
+ return template.content;
2370
+ }
2371
+ function decodeHtmlEntities(text) {
2372
+ return text.replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&#0*39;/g, "'").replace(/&nbsp;/g, "\xA0").replace(/&amp;/g, "&");
2373
+ }
2359
2374
  function htmlToPlainText(html) {
2360
2375
  if (!html) return "";
2361
- const mentionNormalized = html.replace(
2362
- /<span[^>]*data-type=["']mention["'][^>]*data-label=["']([^"']+)["'][^>]*>.*?<\/span>/gi,
2363
- "@$1"
2364
- ).replace(
2365
- /<span[^>]*data-type=["']mention["'][^>]*data-id=["']([^"']+)["'][^>]*>.*?<\/span>/gi,
2366
- "@$1"
2367
- );
2376
+ const mentionNormalized = html.replace(MENTION_SPAN_REGEX, (match, attrs) => {
2377
+ const label = getHtmlAttribute(attrs, "data-label") || getHtmlAttribute(attrs, "data-id");
2378
+ return label ? `@${label}` : match;
2379
+ });
2368
2380
  if (typeof document !== "undefined") {
2369
- const tempDiv = document.createElement("div");
2370
- tempDiv.innerHTML = mentionNormalized;
2371
- tempDiv.querySelectorAll("br.ProseMirror-trailingBreak").forEach((br) => br.remove());
2372
- tempDiv.querySelectorAll("br").forEach((br) => br.replaceWith("\n"));
2373
- const paragraphs = tempDiv.querySelectorAll("p, div, li, h1, h2, h3, h4, h5, h6");
2374
- if (paragraphs.length > 0) {
2375
- const textParts = [];
2376
- paragraphs.forEach((p) => {
2377
- textParts.push(p.textContent || "");
2378
- });
2379
- return textParts.join("\n");
2380
- }
2381
- return tempDiv.textContent || "";
2382
- }
2383
- return mentionNormalized.replace(/<br\s*\/?>/gi, "\n").replace(/<\/p>/gi, "\n").replace(/<\/div>/gi, "\n").replace(/<\/li>/gi, "\n").replace(/<[^>]+>/g, "").replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&#039;/g, "'").trim();
2381
+ const fragment = parseHtmlFragment(mentionNormalized);
2382
+ fragment.querySelectorAll("br.ProseMirror-trailingBreak").forEach((br) => br.remove());
2383
+ const lines = [];
2384
+ let current = null;
2385
+ const flush = () => {
2386
+ if (current !== null) {
2387
+ lines.push(current);
2388
+ current = null;
2389
+ }
2390
+ };
2391
+ const visit = (node) => {
2392
+ if (node.nodeType === 3) {
2393
+ const value = node.nodeValue ?? "";
2394
+ if (current === null && value.trim() === "") return;
2395
+ current = (current ?? "") + value;
2396
+ return;
2397
+ }
2398
+ if (node.nodeType !== 1) return;
2399
+ const tag = node.tagName.toUpperCase();
2400
+ if (tag === "BR") {
2401
+ current = (current ?? "") + "\n";
2402
+ return;
2403
+ }
2404
+ if (!BLOCK_TAGS.has(tag)) {
2405
+ node.childNodes.forEach(visit);
2406
+ return;
2407
+ }
2408
+ flush();
2409
+ const linesBefore = lines.length;
2410
+ node.childNodes.forEach(visit);
2411
+ flush();
2412
+ if (lines.length === linesBefore && tag !== "HR") {
2413
+ lines.push("");
2414
+ }
2415
+ };
2416
+ fragment.childNodes.forEach(visit);
2417
+ flush();
2418
+ return lines.join("\n");
2419
+ }
2420
+ const text = mentionNormalized.replace(/<br\s*\/?>/gi, "\n").replace(
2421
+ /<\/(?:p|div|li|pre|blockquote|h[1-6])>(?:\s*<\/(?:li|ul|ol|blockquote|div)>)*/gi,
2422
+ "\n"
2423
+ ).replace(/<[^>]+>/g, "");
2424
+ return decodeHtmlEntities(text).replace(/\n$/, "");
2384
2425
  }
2385
2426
  function getEditorText(editor) {
2386
2427
  if (!editor) return "";
@@ -2439,9 +2480,7 @@ function extractMentionsFromDoc(docOrEditor) {
2439
2480
  }
2440
2481
  if (typeof docOrEditor === "string") {
2441
2482
  if (typeof document !== "undefined") {
2442
- const div = document.createElement("div");
2443
- div.innerHTML = docOrEditor;
2444
- div.querySelectorAll('span[data-type="mention"]').forEach((el) => {
2483
+ parseHtmlFragment(docOrEditor).querySelectorAll('span[data-type="mention"]').forEach((el) => {
2445
2484
  mentions.push({
2446
2485
  id: el.getAttribute("data-id") || "",
2447
2486
  label: el.getAttribute("data-label") || "",
@@ -2533,7 +2572,25 @@ function getEditorMarkdown(editor) {
2533
2572
 
2534
2573
  // src/controller.ts
2535
2574
  import { Editor } from "@tiptap/core";
2536
- import { Slice, Fragment as Fragment2, Node as ProsemirrorNode } from "@tiptap/pm/model";
2575
+ import {
2576
+ Slice,
2577
+ Fragment as Fragment2,
2578
+ Node as ProsemirrorNode
2579
+ } from "@tiptap/pm/model";
2580
+ function plainTextToSlice(text, schema) {
2581
+ const nodes = [];
2582
+ text.replace(/\r\n?/g, "\n").split("\n").forEach((line) => {
2583
+ const nodeJson = { type: "paragraph" };
2584
+ if (line.length > 0) {
2585
+ nodeJson.content = [{ type: "text", text: line }];
2586
+ }
2587
+ try {
2588
+ nodes.push(ProsemirrorNode.fromJSON(schema, nodeJson));
2589
+ } catch {
2590
+ }
2591
+ });
2592
+ return Slice.maxOpen(Fragment2.fromArray(nodes));
2593
+ }
2537
2594
  var EditorController = class {
2538
2595
  editor;
2539
2596
  plainTextMode;
@@ -2589,20 +2646,7 @@ var EditorController = class {
2589
2646
  clipboardTextParser: (text, context, _plain, view) => {
2590
2647
  const schema = context?.doc?.type?.schema || context?.schema || view?.state?.schema || this.editor.schema;
2591
2648
  if (!schema) return Slice.empty;
2592
- const blocks = text.replace(/\r\n?/g, "\n").split("\n");
2593
- const nodes = [];
2594
- blocks.forEach((line) => {
2595
- const nodeJson = { type: "paragraph" };
2596
- if (line.length > 0) {
2597
- nodeJson.content = [{ type: "text", text: line }];
2598
- }
2599
- try {
2600
- const node = ProsemirrorNode.fromJSON(schema, nodeJson);
2601
- nodes.push(node);
2602
- } catch {
2603
- }
2604
- });
2605
- return Slice.maxOpen(Fragment2.fromArray(nodes));
2649
+ return plainTextToSlice(text, schema);
2606
2650
  }
2607
2651
  }
2608
2652
  });
@@ -2645,14 +2689,29 @@ var EditorController = class {
2645
2689
  this.editor.commands.setContent(html, emitUpdate);
2646
2690
  }
2647
2691
  }
2692
+ /**
2693
+ * Replaces `from`..`to` with literal text: HTML in `text` is not parsed, and
2694
+ * newlines split paragraphs.
2695
+ */
2696
+ insertPlainText(from, to, text) {
2697
+ const { state, view } = this.editor;
2698
+ const tr = text.includes("\n") || text.includes("\r") ? state.tr.replace(from, to, plainTextToSlice(text, state.schema)) : text ? state.tr.insertText(text, from, to) : state.tr.delete(from, to);
2699
+ view.dispatch(tr.scrollIntoView());
2700
+ }
2701
+ /** Inserts text literally at the selection; HTML in `text` is not parsed. */
2648
2702
  insertText(text) {
2649
- this.editor.commands.insertContent(text);
2703
+ const { from, to } = this.editor.state.selection;
2704
+ this.insertPlainText(from, to, text);
2650
2705
  }
2706
+ /** Inserts text literally at `pos`; HTML in `text` is not parsed. */
2651
2707
  insertTextAt(pos, text) {
2652
- this.editor.chain().focus().insertContentAt(pos, text).run();
2708
+ this.insertPlainText(pos, pos, text);
2709
+ this.editor.commands.focus();
2653
2710
  }
2711
+ /** Replaces a range with literal text; HTML in `text` is not parsed. */
2654
2712
  replaceRange(from, to, text) {
2655
- this.editor.chain().focus().deleteRange({ from, to }).insertContentAt(from, text).run();
2713
+ this.insertPlainText(from, to, text);
2714
+ this.editor.commands.focus();
2656
2715
  }
2657
2716
  getCurrentContentString() {
2658
2717
  if (this.plainTextMode) return this.getPlainText();
@@ -6865,6 +6924,8 @@ export {
6865
6924
  SmilieReplacer,
6866
6925
  SpreadsheetController,
6867
6926
  StreamingLayoutManager,
6927
+ TEMPLATE_DEPTH_ERROR,
6928
+ TEMPLATE_LOOP_ERROR,
6868
6929
  TEMPLATE_REGEX,
6869
6930
  TemplateSuggestions,
6870
6931
  VARIABLE_REGEX,
@@ -6910,6 +6971,7 @@ export {
6910
6971
  getEditorText,
6911
6972
  getLatestVersion,
6912
6973
  getLayoutedNodes,
6974
+ getTemplateBody,
6913
6975
  getViewportMetrics,
6914
6976
  globalImageCache,
6915
6977
  handleImageInsertion,