@threadlabs/looma 0.28.15 → 0.28.16
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/editor/chunks/extensions.js +521 -75
- package/editor/extensions/callout.d.ts +2 -9
- package/editor/extensions/chip.d.ts +3 -2
- package/editor/extensions/index.js +1 -1
- package/editor/extensions/markdown.d.ts +6 -7
- package/editor/index.js +1 -1
- package/markdown/fixtures.d.ts +27 -0
- package/markdown/fixtures.js +214 -0
- package/markdown/index.d.ts +106 -0
- package/markdown/index.js +230 -0
- package/markdown/markdown-it.d.ts +13 -0
- package/markdown/markdown-it.js +397 -0
- package/package.json +17 -1
|
@@ -8,8 +8,8 @@ import { dropCursor } from "@tiptap/pm/dropcursor";
|
|
|
8
8
|
import { gapCursor } from "@tiptap/pm/gapcursor";
|
|
9
9
|
import { createLowlight } from "lowlight";
|
|
10
10
|
import { Mapping } from "@tiptap/pm/transform";
|
|
11
|
-
import MarkdownIt from "markdown-it";
|
|
12
11
|
import { MarkdownSerializer, defaultMarkdownSerializer } from "@tiptap/pm/markdown";
|
|
12
|
+
import MarkdownIt from "markdown-it";
|
|
13
13
|
import { CellSelection, TableMap, addColumn, addColumnAfter, addColumnBefore, addRow, addRowAfter, addRowBefore, cellAround, columnResizing, deleteCellSelection, deleteColumn, deleteRow, deleteTable, fixTables, goToNextCell, mergeCells, moveTableColumn, moveTableRow, setCellAttr, splitCell, tableEditing, toggleHeader, toggleHeaderCell } from "@tiptap/pm/tables";
|
|
14
14
|
//#region src/editor/extensions/active-block.ts
|
|
15
15
|
/**
|
|
@@ -127,10 +127,10 @@ var LoomaActiveBlock = Extension.create({
|
|
|
127
127
|
}
|
|
128
128
|
});
|
|
129
129
|
//#endregion
|
|
130
|
-
//#region src/
|
|
130
|
+
//#region src/markdown/index.ts
|
|
131
131
|
/**
|
|
132
|
-
*
|
|
133
|
-
*
|
|
132
|
+
* Callout tones a document stores, in the order the editor offers them. The Callout component also
|
|
133
|
+
* takes `neutral` and `success`, which Markdown and the editor store as `note`.
|
|
134
134
|
*/
|
|
135
135
|
var LOOMA_CALLOUT_TONES = [
|
|
136
136
|
"note",
|
|
@@ -140,16 +140,182 @@ var LOOMA_CALLOUT_TONES = [
|
|
|
140
140
|
"warning",
|
|
141
141
|
"danger"
|
|
142
142
|
];
|
|
143
|
-
|
|
143
|
+
/** Names that read as a stored tone: `caution` is `danger`, and `neutral` is the same muted aside as `note`. */
|
|
144
|
+
var LOOMA_CALLOUT_TONE_ALIASES = {
|
|
144
145
|
caution: "danger",
|
|
145
146
|
neutral: "note"
|
|
146
147
|
};
|
|
147
|
-
/**
|
|
148
|
+
/**
|
|
149
|
+
* Maps a tone name or alias, in any case, onto a stored tone.
|
|
150
|
+
* @failure Unknown and non-string values become `note`.
|
|
151
|
+
*/
|
|
148
152
|
function normalizeLoomaCalloutTone(value) {
|
|
149
153
|
if (typeof value !== "string") return "note";
|
|
150
154
|
const tone = value.toLowerCase();
|
|
151
|
-
return LOOMA_CALLOUT_TONES.includes(tone) ? tone :
|
|
155
|
+
return LOOMA_CALLOUT_TONES.includes(tone) ? tone : LOOMA_CALLOUT_TONE_ALIASES[tone] ?? "note";
|
|
156
|
+
}
|
|
157
|
+
/** The hues chips and swatches take as their tone, written `:chip[Label]{tone=green}`. */
|
|
158
|
+
var LOOMA_CHIP_TONES = [
|
|
159
|
+
"neutral",
|
|
160
|
+
"blue",
|
|
161
|
+
"green",
|
|
162
|
+
"yellow",
|
|
163
|
+
"red",
|
|
164
|
+
"purple"
|
|
165
|
+
];
|
|
166
|
+
/**
|
|
167
|
+
* Keeps a chip tone within the palette.
|
|
168
|
+
* @failure Unknown values become `neutral`.
|
|
169
|
+
*/
|
|
170
|
+
function normalizeLoomaChipTone(value) {
|
|
171
|
+
return LOOMA_CHIP_TONES.includes(value) ? value : "neutral";
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Names that open a `:::` container, matched without regard to case. A container with any other
|
|
175
|
+
* name is ordinary text.
|
|
176
|
+
*/
|
|
177
|
+
var LOOMA_CONTAINER_NAMES = /* @__PURE__ */ new Set([
|
|
178
|
+
...LOOMA_CALLOUT_TONES,
|
|
179
|
+
"caution",
|
|
180
|
+
"details",
|
|
181
|
+
"figure",
|
|
182
|
+
"tabs",
|
|
183
|
+
"code-group"
|
|
184
|
+
]);
|
|
185
|
+
var ATTRIBUTE_KEY = /[A-Za-z][\w-]*/y;
|
|
186
|
+
var ATTRIBUTE_ID = /#([\w-]+)/y;
|
|
187
|
+
var ATTRIBUTE_VALUE = /[^\s"'=<>`{}]+/y;
|
|
188
|
+
function sticky(pattern, source, index) {
|
|
189
|
+
pattern.lastIndex = index;
|
|
190
|
+
return pattern.exec(source);
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Reads a brace attribute list starting at `source[start]`, which must be `{`.
|
|
194
|
+
* @contract Returns the attributes and the index just past the closing brace.
|
|
195
|
+
* @failure Returns null for anything that is not a complete, well-formed list, so the text stays literal.
|
|
196
|
+
*/
|
|
197
|
+
function readLoomaAttributes(source, start = 0) {
|
|
198
|
+
if (source[start] !== "{") return null;
|
|
199
|
+
const attributes = {};
|
|
200
|
+
let index = start + 1;
|
|
201
|
+
for (;;) {
|
|
202
|
+
while (source[index] === " " || source[index] === " ") index++;
|
|
203
|
+
if (source[index] === "}") return {
|
|
204
|
+
attributes,
|
|
205
|
+
end: index + 1
|
|
206
|
+
};
|
|
207
|
+
if (index > start + 1 && !/\s/.test(source[index - 1])) return null;
|
|
208
|
+
const id = sticky(ATTRIBUTE_ID, source, index);
|
|
209
|
+
if (id) {
|
|
210
|
+
attributes.id = id[1];
|
|
211
|
+
index += id[0].length;
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
const key = sticky(ATTRIBUTE_KEY, source, index);
|
|
215
|
+
if (!key) return null;
|
|
216
|
+
index += key[0].length;
|
|
217
|
+
if (source[index] !== "=") {
|
|
218
|
+
attributes[key[0]] = true;
|
|
219
|
+
continue;
|
|
220
|
+
}
|
|
221
|
+
index++;
|
|
222
|
+
const quote = source[index];
|
|
223
|
+
if (quote === "\"" || quote === "'") {
|
|
224
|
+
const close = source.indexOf(quote, index + 1);
|
|
225
|
+
if (close < 0) return null;
|
|
226
|
+
attributes[key[0]] = source.slice(index + 1, close);
|
|
227
|
+
index = close + 1;
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
const value = sticky(ATTRIBUTE_VALUE, source, index);
|
|
231
|
+
if (!value) return null;
|
|
232
|
+
attributes[key[0]] = value[0];
|
|
233
|
+
index += value[0].length;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Reads a whole string as one brace attribute list.
|
|
238
|
+
* @failure Returns null unless the string is exactly one well-formed list.
|
|
239
|
+
*/
|
|
240
|
+
function parseLoomaAttributes(source) {
|
|
241
|
+
const read = readLoomaAttributes(source);
|
|
242
|
+
return read && read.end === source.length ? read.attributes : null;
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* The anchor a heading's text derives: NFC, lowercase, keeping letters, numbers and combining
|
|
246
|
+
* marks, with every other run of characters one hyphen.
|
|
247
|
+
* @failure Text with no letters or numbers, such as only emoji, derives `heading`.
|
|
248
|
+
*/
|
|
249
|
+
function loomaHeadingSlug(text) {
|
|
250
|
+
return text.normalize("NFC").toLowerCase().replace(/[^\p{L}\p{N}\p{M}]+/gu, "-").replace(/^-+|-+$/g, "") || "heading";
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Gives every heading of a document its anchor, in document order.
|
|
254
|
+
* @contract A custom ID is kept unless an earlier heading already has it. Any other heading takes
|
|
255
|
+
* its slug, or the slug's first free suffix (`-1`, `-2`…), never one a later custom ID reserves.
|
|
256
|
+
*/
|
|
257
|
+
function assignLoomaHeadingIds(headings) {
|
|
258
|
+
const reserved = new Set(headings.map(({ id }) => id).filter((id) => Boolean(id)));
|
|
259
|
+
const used = /* @__PURE__ */ new Set();
|
|
260
|
+
return headings.map(({ text, id: custom }) => {
|
|
261
|
+
let id = custom || loomaHeadingSlug(text);
|
|
262
|
+
if (custom ? used.has(id) : used.has(id) || reserved.has(id)) {
|
|
263
|
+
const stem = id;
|
|
264
|
+
let suffix = 1;
|
|
265
|
+
while (used.has(`${stem}-${suffix}`) || reserved.has(`${stem}-${suffix}`)) suffix++;
|
|
266
|
+
id = `${stem}-${suffix}`;
|
|
267
|
+
}
|
|
268
|
+
used.add(id);
|
|
269
|
+
return id;
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
/** Reads a fence's info string: ```` ```ts title="a.ts" {1,3-5} :line-numbers ````. */
|
|
273
|
+
function parseLoomaFenceInfo(info) {
|
|
274
|
+
const source = info.trim();
|
|
275
|
+
const language = /^[^\s={:]+(?=[\s{:]|$)/.exec(source)?.[0];
|
|
276
|
+
const meta = (language ? source.slice(language.length) : source).trim();
|
|
277
|
+
const title = /(?:^|\s)title="([^"]*)"/.exec(meta)?.[1];
|
|
278
|
+
const rest = meta.replace(/(?:^|\s)title="[^"]*"/, "");
|
|
279
|
+
const lines = /* @__PURE__ */ new Set();
|
|
280
|
+
for (const range of /\{([\d\s,-]+)\}/.exec(rest)?.[1].split(",") ?? []) {
|
|
281
|
+
const [from = NaN, to = from] = range.split("-").map((part) => Number.parseInt(part, 10));
|
|
282
|
+
for (let line = from; line <= to; line++) lines.add(line);
|
|
283
|
+
}
|
|
284
|
+
const numbers = /:(no-)?line-numbers(?![\w-])/.exec(rest);
|
|
285
|
+
return {
|
|
286
|
+
...language ? { language } : {},
|
|
287
|
+
meta,
|
|
288
|
+
...title !== void 0 ? { title } : {},
|
|
289
|
+
lines: [...lines].sort((a, b) => a - b),
|
|
290
|
+
...numbers ? { lineNumbers: numbers[1] === void 0 } : {}
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Splits YAML front matter from the start of a document.
|
|
295
|
+
* @contract Front matter opens with a `---` line at the very start, holds at least one `key: value`
|
|
296
|
+
* line, and closes at a `---` or `...` line. `source` is the block as written, delimiters included.
|
|
297
|
+
* @failure Returns null when the document has none.
|
|
298
|
+
*/
|
|
299
|
+
function splitLoomaFrontmatter(document) {
|
|
300
|
+
const match = /^---[ \t]*\r?\n([\s\S]*?)\r?\n(?:---|\.\.\.)[ \t]*(?:\r?\n|$)/.exec(document.slice(0, 8192));
|
|
301
|
+
if (!match || !/^[A-Za-z][\w-]*:[ \t]*\S/m.test(match[1])) return null;
|
|
302
|
+
return {
|
|
303
|
+
source: match[0].trimEnd(),
|
|
304
|
+
yaml: match[1],
|
|
305
|
+
body: document.slice(match[0].length)
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
/** The colons a container opens and closes with, given how deeply containers nest inside it. */
|
|
309
|
+
function loomaContainerMarker(nestedDepth) {
|
|
310
|
+
return ":".repeat(3 + Math.max(0, nestedDepth));
|
|
311
|
+
}
|
|
312
|
+
/** A backtick fence longer than any run of backticks in the code, and at least three long. */
|
|
313
|
+
function loomaCodeFence(code) {
|
|
314
|
+
const longest = Math.max(0, ...(code.match(/`+/g) ?? []).map((run) => run.length));
|
|
315
|
+
return "`".repeat(Math.max(3, longest + 1));
|
|
152
316
|
}
|
|
317
|
+
//#endregion
|
|
318
|
+
//#region src/editor/extensions/callout.ts
|
|
153
319
|
/**
|
|
154
320
|
* A durable block container for informational editor content, saved as Looma's Callout.
|
|
155
321
|
*
|
|
@@ -196,19 +362,12 @@ var LoomaCallout = Node$1.create({
|
|
|
196
362
|
});
|
|
197
363
|
//#endregion
|
|
198
364
|
//#region src/editor/extensions/chip.ts
|
|
199
|
-
/** The six palette values a chip may persist; other values fall back to neutral. */
|
|
200
|
-
var LOOMA_CHIP_COLORS =
|
|
201
|
-
"neutral",
|
|
202
|
-
"blue",
|
|
203
|
-
"green",
|
|
204
|
-
"yellow",
|
|
205
|
-
"red",
|
|
206
|
-
"purple"
|
|
207
|
-
];
|
|
365
|
+
/** The six palette values a chip may persist, Looma Markdown's chip tones; other values fall back to neutral. */
|
|
366
|
+
var LOOMA_CHIP_COLORS = LOOMA_CHIP_TONES;
|
|
208
367
|
var CHIP_PLACEHOLDER = "Set a label";
|
|
209
368
|
/** Keep imported or programmatic colors within the supported palette. */
|
|
210
369
|
function normalizeLoomaChipColor(value) {
|
|
211
|
-
return
|
|
370
|
+
return normalizeLoomaChipTone(value);
|
|
212
371
|
}
|
|
213
372
|
/**
|
|
214
373
|
* An inline, atomic label. The editor's popover edits its attributes; its text
|
|
@@ -9165,47 +9324,96 @@ function createDOMPurify() {
|
|
|
9165
9324
|
}
|
|
9166
9325
|
var purify = createDOMPurify();
|
|
9167
9326
|
//#endregion
|
|
9168
|
-
//#region src/
|
|
9169
|
-
var
|
|
9170
|
-
"note",
|
|
9171
|
-
"tip",
|
|
9172
|
-
"info",
|
|
9173
|
-
"important",
|
|
9174
|
-
"warning",
|
|
9175
|
-
"danger",
|
|
9176
|
-
"caution",
|
|
9177
|
-
"details"
|
|
9178
|
-
]);
|
|
9179
|
-
var CONTAINER_OPEN = /^(:{3,})[ \t]*([A-Za-z]+)(?:[ \t]+(.*?))?[ \t]*$/;
|
|
9327
|
+
//#region src/markdown/markdown-it.ts
|
|
9328
|
+
var CONTAINER_OPEN = /^(:{3,})[ \t]*([A-Za-z][\w-]*)(?:[ \t]+(.*?))?[ \t]*$/;
|
|
9180
9329
|
var CONTAINER_CLOSE = /^:{3,}[ \t]*$/;
|
|
9330
|
+
var FENCE_OPEN = /^(`{3,}|~{3,})/;
|
|
9331
|
+
var TAB_LABEL = /^==[ \t]+(\S.*?)[ \t]*$/;
|
|
9181
9332
|
var OPEN_FLAG = /[ \t]*\{open\}$/;
|
|
9182
9333
|
var ALERT = /^\[!(NOTE|TIP|IMPORTANT|WARNING|CAUTION)\][ \t]*(?:\n|$)/i;
|
|
9334
|
+
var LEAF = /^::([A-Za-z][\w-]*)(\{.*\})?[ \t]*$/;
|
|
9335
|
+
var TASK = /^\[([ xX])\][ \t]+/;
|
|
9336
|
+
var TOC_FORMATS = /* @__PURE__ */ new Set([
|
|
9337
|
+
"plain",
|
|
9338
|
+
"bulleted",
|
|
9339
|
+
"numbered"
|
|
9340
|
+
]);
|
|
9341
|
+
var PLACEMENTS = /* @__PURE__ */ new Set([
|
|
9342
|
+
"block",
|
|
9343
|
+
"center",
|
|
9344
|
+
"wrap-left",
|
|
9345
|
+
"wrap-right"
|
|
9346
|
+
]);
|
|
9347
|
+
var opensContainer = (text) => {
|
|
9348
|
+
const match = CONTAINER_OPEN.exec(text);
|
|
9349
|
+
return match && LOOMA_CONTAINER_NAMES.has(match[2].toLowerCase()) ? match : null;
|
|
9350
|
+
};
|
|
9183
9351
|
function lineText(state, line) {
|
|
9184
9352
|
return state.src.slice(state.bMarks[line] + state.tShift[line], state.eMarks[line]);
|
|
9185
9353
|
}
|
|
9354
|
+
function frontmatter(state, startLine, _endLine, silent) {
|
|
9355
|
+
if (startLine !== 0 || state.blkIndent !== 0) return false;
|
|
9356
|
+
const split = splitLoomaFrontmatter(state.src);
|
|
9357
|
+
if (!split) return false;
|
|
9358
|
+
if (silent) return true;
|
|
9359
|
+
const lines = split.source.split("\n").length;
|
|
9360
|
+
const token = state.push("looma_frontmatter", "", 0);
|
|
9361
|
+
Object.assign(token, {
|
|
9362
|
+
content: split.yaml,
|
|
9363
|
+
block: true,
|
|
9364
|
+
hidden: true,
|
|
9365
|
+
map: [0, lines]
|
|
9366
|
+
});
|
|
9367
|
+
state.env.frontmatter = split.yaml;
|
|
9368
|
+
state.line = lines;
|
|
9369
|
+
return true;
|
|
9370
|
+
}
|
|
9186
9371
|
function container(state, startLine, endLine, silent) {
|
|
9187
9372
|
if (state.sCount[startLine] - state.blkIndent >= 4) return false;
|
|
9188
|
-
const match =
|
|
9189
|
-
if (!match
|
|
9373
|
+
const match = opensContainer(lineText(state, startLine));
|
|
9374
|
+
if (!match) return false;
|
|
9190
9375
|
if (silent) return true;
|
|
9376
|
+
const name = match[2].toLowerCase();
|
|
9377
|
+
const grouped = name === "tabs" || name === "code-group";
|
|
9378
|
+
const labels = [];
|
|
9191
9379
|
let depth = 1;
|
|
9380
|
+
let fence = null;
|
|
9192
9381
|
let next = startLine + 1;
|
|
9193
9382
|
for (; next < endLine; next++) {
|
|
9194
|
-
if (state.sCount[next] < state.blkIndent) break;
|
|
9195
9383
|
const text = lineText(state, next);
|
|
9384
|
+
if (text && state.sCount[next] < state.blkIndent) break;
|
|
9385
|
+
if (fence) {
|
|
9386
|
+
const close = FENCE_OPEN.exec(text);
|
|
9387
|
+
if (close && close[1][0] === fence.char && close[1].length >= fence.length && !text.slice(close[1].length).trim()) fence = null;
|
|
9388
|
+
continue;
|
|
9389
|
+
}
|
|
9390
|
+
const opener = FENCE_OPEN.exec(text);
|
|
9391
|
+
if (opener) {
|
|
9392
|
+
fence = {
|
|
9393
|
+
char: opener[1][0],
|
|
9394
|
+
length: opener[1].length
|
|
9395
|
+
};
|
|
9396
|
+
continue;
|
|
9397
|
+
}
|
|
9196
9398
|
if (CONTAINER_CLOSE.test(text)) {
|
|
9197
9399
|
if (--depth === 0) break;
|
|
9198
|
-
} else if (
|
|
9400
|
+
} else if (opensContainer(text)) depth++;
|
|
9401
|
+
else if (grouped && depth === 1) {
|
|
9402
|
+
const label = TAB_LABEL.exec(text);
|
|
9403
|
+
if (label) labels.push({
|
|
9404
|
+
line: next,
|
|
9405
|
+
label: label[1]
|
|
9406
|
+
});
|
|
9407
|
+
}
|
|
9199
9408
|
}
|
|
9200
|
-
const name = match[2].toLowerCase();
|
|
9201
9409
|
let title = (match[3] ?? "").trim();
|
|
9202
|
-
const open = OPEN_FLAG.test(title);
|
|
9203
|
-
title = title.replace(OPEN_FLAG, "");
|
|
9410
|
+
const open = name === "details" && OPEN_FLAG.test(title);
|
|
9411
|
+
if (name === "details") title = title.replace(OPEN_FLAG, "");
|
|
9204
9412
|
const parentType = state.parentType;
|
|
9205
9413
|
const lineMax = state.lineMax;
|
|
9206
9414
|
state.parentType = "container";
|
|
9207
9415
|
state.lineMax = next;
|
|
9208
|
-
const opening = state.push("looma_container_open", "", 1);
|
|
9416
|
+
const opening = state.push("looma_container_open", "div", 1);
|
|
9209
9417
|
Object.assign(opening, {
|
|
9210
9418
|
info: name,
|
|
9211
9419
|
meta: {
|
|
@@ -9216,10 +9424,30 @@ function container(state, startLine, endLine, silent) {
|
|
|
9216
9424
|
map: [startLine, next],
|
|
9217
9425
|
markup: match[1]
|
|
9218
9426
|
});
|
|
9219
|
-
|
|
9220
|
-
|
|
9427
|
+
const panel = (label, from, to) => {
|
|
9428
|
+
state.push("looma_tab_open", "section", 1).meta = { label };
|
|
9429
|
+
state.md.block.tokenize(state, from, to);
|
|
9430
|
+
state.push("looma_tab_close", "section", -1);
|
|
9431
|
+
};
|
|
9432
|
+
if (labels.length) {
|
|
9433
|
+
state.md.block.tokenize(state, startLine + 1, labels[0].line);
|
|
9434
|
+
labels.forEach(({ line, label }, index) => panel(label, line + 1, labels[index + 1]?.line ?? next));
|
|
9435
|
+
} else {
|
|
9436
|
+
const first = state.tokens.length;
|
|
9437
|
+
state.md.block.tokenize(state, startLine + 1, next);
|
|
9438
|
+
if (name === "code-group") for (let index = state.tokens.length - 1; index >= first; index--) {
|
|
9439
|
+
const token = state.tokens[index];
|
|
9440
|
+
if (token.type !== "fence" || token.level !== opening.level + 1) continue;
|
|
9441
|
+
const { title: fenceTitle, language } = parseLoomaFenceInfo(token.info);
|
|
9442
|
+
const tabOpen = new state.Token("looma_tab_open", "section", 1);
|
|
9443
|
+
tabOpen.meta = { label: fenceTitle ?? language ?? "Code" };
|
|
9444
|
+
state.tokens.splice(index, 1, tabOpen, token, new state.Token("looma_tab_close", "section", -1));
|
|
9445
|
+
}
|
|
9446
|
+
}
|
|
9447
|
+
const closing = state.push("looma_container_close", "div", -1);
|
|
9221
9448
|
Object.assign(closing, {
|
|
9222
9449
|
info: name,
|
|
9450
|
+
meta: { title },
|
|
9223
9451
|
block: true,
|
|
9224
9452
|
markup: match[1]
|
|
9225
9453
|
});
|
|
@@ -9228,6 +9456,27 @@ function container(state, startLine, endLine, silent) {
|
|
|
9228
9456
|
state.line = Math.min(next + 1, endLine);
|
|
9229
9457
|
return true;
|
|
9230
9458
|
}
|
|
9459
|
+
function leaf(state, startLine, _endLine, silent) {
|
|
9460
|
+
if (state.sCount[startLine] - state.blkIndent >= 4) return false;
|
|
9461
|
+
const match = LEAF.exec(lineText(state, startLine));
|
|
9462
|
+
if (!match || match[1].toLowerCase() !== "toc") return false;
|
|
9463
|
+
const attributes = match[2] ? parseLoomaAttributes(match[2]) : {};
|
|
9464
|
+
if (!attributes) return false;
|
|
9465
|
+
if (silent) return true;
|
|
9466
|
+
const format = typeof attributes.format === "string" && TOC_FORMATS.has(attributes.format) ? attributes.format : "plain";
|
|
9467
|
+
const depth = attributes.depth === "1" || attributes.depth === "2" ? Number(attributes.depth) : 3;
|
|
9468
|
+
const token = state.push("looma_toc", "nav", 0);
|
|
9469
|
+
Object.assign(token, {
|
|
9470
|
+
block: true,
|
|
9471
|
+
map: [startLine, startLine + 1],
|
|
9472
|
+
meta: {
|
|
9473
|
+
format,
|
|
9474
|
+
depth
|
|
9475
|
+
}
|
|
9476
|
+
});
|
|
9477
|
+
state.line = startLine + 1;
|
|
9478
|
+
return true;
|
|
9479
|
+
}
|
|
9231
9480
|
function alerts(state) {
|
|
9232
9481
|
const tokens = state.tokens;
|
|
9233
9482
|
for (let index = 0; index < tokens.length; index++) {
|
|
@@ -9246,36 +9495,239 @@ function alerts(state) {
|
|
|
9246
9495
|
}
|
|
9247
9496
|
}
|
|
9248
9497
|
}
|
|
9249
|
-
function
|
|
9498
|
+
function taskLists(state) {
|
|
9499
|
+
const tokens = state.tokens;
|
|
9500
|
+
for (let index = 0; index < tokens.length; index++) {
|
|
9501
|
+
if (tokens[index].type !== "bullet_list_open") continue;
|
|
9502
|
+
const level = tokens[index].level;
|
|
9503
|
+
const items = [];
|
|
9504
|
+
let isTaskList = true;
|
|
9505
|
+
for (let cursor = index + 1; cursor < tokens.length; cursor++) {
|
|
9506
|
+
const token = tokens[cursor];
|
|
9507
|
+
if (token.type === "bullet_list_close" && token.level === level) break;
|
|
9508
|
+
if (token.type !== "list_item_open" || token.level !== level + 1) continue;
|
|
9509
|
+
const inline = tokens[cursor + 2];
|
|
9510
|
+
const match = tokens[cursor + 1]?.type === "paragraph_open" && inline?.type === "inline" ? TASK.exec(inline.content) : null;
|
|
9511
|
+
if (!match) {
|
|
9512
|
+
isTaskList = false;
|
|
9513
|
+
break;
|
|
9514
|
+
}
|
|
9515
|
+
items.push({
|
|
9516
|
+
item: token,
|
|
9517
|
+
inline,
|
|
9518
|
+
checked: match[1] !== " "
|
|
9519
|
+
});
|
|
9520
|
+
}
|
|
9521
|
+
if (!isTaskList || !items.length) continue;
|
|
9522
|
+
tokens[index].attrSet("data-type", "taskList");
|
|
9523
|
+
for (const { item, inline, checked } of items) {
|
|
9524
|
+
inline.content = inline.content.replace(TASK, "");
|
|
9525
|
+
item.attrSet("data-type", "taskItem");
|
|
9526
|
+
item.attrSet("data-checked", String(checked));
|
|
9527
|
+
item.meta = { checked };
|
|
9528
|
+
}
|
|
9529
|
+
}
|
|
9530
|
+
}
|
|
9531
|
+
function directive(state, silent) {
|
|
9532
|
+
const { src, pos } = state;
|
|
9533
|
+
if (src.charCodeAt(pos) !== 58) return false;
|
|
9534
|
+
const name = /^:(chip|mention)\[/.exec(src.slice(pos, pos + 10))?.[1];
|
|
9535
|
+
if (!name) return false;
|
|
9536
|
+
let label = "";
|
|
9537
|
+
let index = pos + name.length + 2;
|
|
9538
|
+
for (; index < state.posMax && src[index] !== "]"; index++) {
|
|
9539
|
+
if (src[index] === "\\" && index + 1 < state.posMax) index++;
|
|
9540
|
+
label += src[index];
|
|
9541
|
+
}
|
|
9542
|
+
if (src[index] !== "]") return false;
|
|
9543
|
+
const read = src[index + 1] === "{" ? readLoomaAttributes(src, index + 1) : {
|
|
9544
|
+
attributes: {},
|
|
9545
|
+
end: index + 1
|
|
9546
|
+
};
|
|
9547
|
+
if (!read || read.end > state.posMax) return false;
|
|
9548
|
+
if (name === "mention" && typeof read.attributes.id !== "string") return false;
|
|
9549
|
+
if (!silent) {
|
|
9550
|
+
const token = state.push(`looma_${name}`, "span", 0);
|
|
9551
|
+
token.meta = {
|
|
9552
|
+
label,
|
|
9553
|
+
attributes: read.attributes
|
|
9554
|
+
};
|
|
9555
|
+
}
|
|
9556
|
+
state.pos = read.end;
|
|
9557
|
+
return true;
|
|
9558
|
+
}
|
|
9559
|
+
function inlineText(token) {
|
|
9560
|
+
return (token?.children ?? []).map((child) => child.type === "image" ? inlineText(child) : child.content).join("");
|
|
9561
|
+
}
|
|
9562
|
+
function attributes(state) {
|
|
9563
|
+
const headings = [];
|
|
9564
|
+
const tokens = state.tokens;
|
|
9565
|
+
for (let index = 0; index < tokens.length; index++) {
|
|
9566
|
+
const token = tokens[index];
|
|
9567
|
+
if (token.type !== "inline" || !token.children) continue;
|
|
9568
|
+
const children = token.children;
|
|
9569
|
+
for (let child = children.length - 3; child >= 0; child--) {
|
|
9570
|
+
const text = children[child + 1];
|
|
9571
|
+
if (children[child].type !== "link_open" || children[child].markup !== "linkify" || /^(?:[a-z][\w+.-]*:|www\.)/i.test(text.content)) continue;
|
|
9572
|
+
children.splice(child, 3, Object.assign(new state.Token("text", "", 0), { content: text.content }));
|
|
9573
|
+
}
|
|
9574
|
+
if (tokens[index - 1]?.type === "heading_open") {
|
|
9575
|
+
const last = children.at(-1);
|
|
9576
|
+
const marker = last?.type === "text" ? /[ \t]+(\{[^{}]*\})$/.exec(last.content) : null;
|
|
9577
|
+
const parsed = marker && parseLoomaAttributes(marker[1]);
|
|
9578
|
+
let id;
|
|
9579
|
+
if (last && marker && parsed && typeof parsed.id === "string") {
|
|
9580
|
+
id = parsed.id;
|
|
9581
|
+
last.content = last.content.slice(0, marker.index);
|
|
9582
|
+
}
|
|
9583
|
+
headings.push({
|
|
9584
|
+
open: tokens[index - 1],
|
|
9585
|
+
text: inlineText(token).trim(),
|
|
9586
|
+
id
|
|
9587
|
+
});
|
|
9588
|
+
}
|
|
9589
|
+
for (let child = 0; child < children.length; child++) {
|
|
9590
|
+
const next = children[child + 1];
|
|
9591
|
+
if (children[child].type !== "image" || next?.type !== "text" || !next.content.startsWith("{")) continue;
|
|
9592
|
+
const read = readLoomaAttributes(next.content);
|
|
9593
|
+
if (!read) continue;
|
|
9594
|
+
applyImageAttributes(children[child], read.attributes);
|
|
9595
|
+
next.content = next.content.slice(read.end);
|
|
9596
|
+
if (!next.content) children.splice(child + 1, 1);
|
|
9597
|
+
}
|
|
9598
|
+
if (tokens[index - 1]?.type === "paragraph_open" && children.length === 1 && children[0].type === "image") {
|
|
9599
|
+
tokens[index - 1].hidden = true;
|
|
9600
|
+
tokens[index + 1].hidden = true;
|
|
9601
|
+
}
|
|
9602
|
+
}
|
|
9603
|
+
const ids = assignLoomaHeadingIds(headings);
|
|
9604
|
+
headings.forEach(({ open }, index) => open.attrSet("id", ids[index]));
|
|
9605
|
+
}
|
|
9606
|
+
function applyImageAttributes(image, attributes) {
|
|
9607
|
+
for (const key of ["width", "height"]) {
|
|
9608
|
+
const value = Number(attributes[key]);
|
|
9609
|
+
if (Number.isSafeInteger(value) && value > 0) image.attrSet(key, String(value));
|
|
9610
|
+
}
|
|
9611
|
+
const placement = attributes.placement;
|
|
9612
|
+
if (typeof placement === "string" && PLACEMENTS.has(placement) && placement !== "block") image.attrSet("data-placement", placement);
|
|
9613
|
+
if (attributes.responsive === true) image.attrSet("data-looma-responsive", "");
|
|
9614
|
+
}
|
|
9615
|
+
function tableOfContents(tokens, format, depth, escape) {
|
|
9616
|
+
const roots = [];
|
|
9617
|
+
const ancestors = [];
|
|
9618
|
+
tokens.forEach((token, index) => {
|
|
9619
|
+
if (token.type !== "heading_open") return;
|
|
9620
|
+
const level = Number(token.tag.slice(1));
|
|
9621
|
+
const text = inlineText(tokens[index + 1]).trim();
|
|
9622
|
+
if (level > depth || !text) return;
|
|
9623
|
+
while (ancestors.length && ancestors[ancestors.length - 1].level >= level) ancestors.pop();
|
|
9624
|
+
const entry = {
|
|
9625
|
+
id: token.attrGet("id") ?? "",
|
|
9626
|
+
text,
|
|
9627
|
+
children: []
|
|
9628
|
+
};
|
|
9629
|
+
(ancestors.at(-1)?.entry.children ?? roots).push(entry);
|
|
9630
|
+
ancestors.push({
|
|
9631
|
+
level,
|
|
9632
|
+
entry
|
|
9633
|
+
});
|
|
9634
|
+
});
|
|
9635
|
+
const [list, item] = format === "plain" ? ["div", "div"] : [format === "numbered" ? "ol" : "ul", "li"];
|
|
9636
|
+
const render = (entries) => `<${list} role="list">${entries.map(({ id, text, children }) => `<${item} role="listitem"><a href="#${escape(id)}">${escape(text)}</a>${children.length ? render(children) : ""}</${item}>`).join("")}</${list}>`;
|
|
9637
|
+
return `<nav data-looma-toc="" data-format="${format}" data-depth="${depth}" aria-label="Table of contents"><strong>Table of contents</strong>${roots.length ? render(roots) : ""}</nav>\n`;
|
|
9638
|
+
}
|
|
9639
|
+
/**
|
|
9640
|
+
* Adds Looma Markdown to a markdown-it instance: containers, alerts, task lists, the table of
|
|
9641
|
+
* contents, chips, mentions, brace attributes, heading anchors, code meta and front matter.
|
|
9642
|
+
* @contract Renders the canonical HTML, which is the HTML LoomaEditor saves. It is not sanitized.
|
|
9643
|
+
* Linkify and raw HTML are instance options; `createLoomaMarkdownIt` turns both on.
|
|
9644
|
+
*/
|
|
9645
|
+
function loomaMarkdown$1(md) {
|
|
9250
9646
|
const escape = md.utils.escapeHtml;
|
|
9647
|
+
md.block.ruler.before("table", "looma_frontmatter", frontmatter);
|
|
9251
9648
|
md.block.ruler.before("fence", "looma_container", container, { alt: [
|
|
9252
9649
|
"paragraph",
|
|
9253
9650
|
"reference",
|
|
9254
9651
|
"blockquote",
|
|
9255
9652
|
"list"
|
|
9256
9653
|
] });
|
|
9654
|
+
md.block.ruler.before("paragraph", "looma_leaf", leaf, { alt: [
|
|
9655
|
+
"paragraph",
|
|
9656
|
+
"reference",
|
|
9657
|
+
"blockquote",
|
|
9658
|
+
"list"
|
|
9659
|
+
] });
|
|
9660
|
+
md.inline.ruler.before("link", "looma_directive", directive);
|
|
9661
|
+
md.core.ruler.after("block", "looma_task_lists", taskLists);
|
|
9257
9662
|
md.core.ruler.before("inline", "looma_alerts", alerts);
|
|
9258
|
-
md.
|
|
9663
|
+
md.core.ruler.before("text_join", "looma_attributes", attributes);
|
|
9664
|
+
const rules = md.renderer.rules;
|
|
9665
|
+
rules.looma_frontmatter = () => "";
|
|
9666
|
+
rules.looma_container_open = (tokens, index) => {
|
|
9259
9667
|
const { info, meta } = tokens[index];
|
|
9260
9668
|
if (info === "details") return `<details${meta.open ? " open" : ""}><summary>${escape(meta.title || "Details")}</summary>\n`;
|
|
9669
|
+
if (info === "figure") return "<figure>\n";
|
|
9670
|
+
if (info === "tabs" || info === "code-group") return `<ui-tabs${info === "code-group" ? " class=\"code-group\"" : ""} group="${escape(meta.title || info)}">\n`;
|
|
9261
9671
|
return `<ui-callout tone="${normalizeLoomaCalloutTone(info)}"${meta.title ? ` heading="${escape(meta.title)}"` : ""}>\n`;
|
|
9262
9672
|
};
|
|
9263
|
-
|
|
9264
|
-
|
|
9673
|
+
rules.looma_container_close = (tokens, index, _options, env) => {
|
|
9674
|
+
const { info } = tokens[index];
|
|
9675
|
+
if (info === "details") return "</details>\n";
|
|
9676
|
+
if (info === "figure") {
|
|
9677
|
+
const title = tokens[index].meta.title;
|
|
9678
|
+
return `${title ? `<figcaption>${md.renderInline(title, env)}</figcaption>\n` : ""}</figure>\n`;
|
|
9679
|
+
}
|
|
9680
|
+
if (info === "tabs" || info === "code-group") return "</ui-tabs>\n";
|
|
9681
|
+
return "</ui-callout>\n";
|
|
9682
|
+
};
|
|
9683
|
+
rules.looma_tab_open = (tokens, index) => `<section aria-label="${escape(tokens[index].meta.label)}">\n`;
|
|
9684
|
+
rules.looma_tab_close = () => "</section>\n";
|
|
9685
|
+
rules.looma_toc = (tokens, index) => tableOfContents(tokens, tokens[index].meta.format, tokens[index].meta.depth, escape);
|
|
9686
|
+
rules.looma_chip = (tokens, index) => {
|
|
9687
|
+
const { label, attributes } = tokens[index].meta;
|
|
9688
|
+
const tone = normalizeLoomaChipTone(attributes.tone);
|
|
9689
|
+
return `<span data-looma-chip="" data-label="${escape(label)}" data-color="${tone}"${label ? "" : " data-placeholder=\"\""}>${escape(label)}</span>`;
|
|
9690
|
+
};
|
|
9691
|
+
rules.looma_mention = (tokens, index) => {
|
|
9692
|
+
const { label, attributes } = tokens[index].meta;
|
|
9693
|
+
const id = String(attributes.id);
|
|
9694
|
+
return `<span data-type="mention" class="looma-mention" data-id="${escape(id)}" data-label="${escape(label || id)}">@${escape(label || id)}</span>`;
|
|
9695
|
+
};
|
|
9696
|
+
const listItem = rules.list_item_open ?? ((tokens, index, options, _env, self) => self.renderToken(tokens, index, options));
|
|
9697
|
+
rules.list_item_open = (tokens, index, options, env, self) => {
|
|
9698
|
+
const html = listItem(tokens, index, options, env, self);
|
|
9699
|
+
const task = tokens[index].meta;
|
|
9700
|
+
return task && typeof task.checked === "boolean" ? `${html}<input type="checkbox" disabled=""${task.checked ? " checked=\"\"" : ""}>` : html;
|
|
9701
|
+
};
|
|
9702
|
+
const code = (content, info) => {
|
|
9703
|
+
const { language, meta } = parseLoomaFenceInfo(info);
|
|
9704
|
+
return `<pre><code${language ? ` class="language-${escape(language)}"` : ""}${meta ? ` data-meta="${escape(meta)}"` : ""}>${escape(content.replace(/\n$/, ""))}</code></pre>\n`;
|
|
9705
|
+
};
|
|
9706
|
+
rules.fence = (tokens, index) => code(tokens[index].content, tokens[index].info);
|
|
9707
|
+
rules.code_block = (tokens, index) => code(tokens[index].content, "");
|
|
9265
9708
|
}
|
|
9266
|
-
/** The markdown-it instance LoomaEditor imports Markdown with, shared block vocabulary included. */
|
|
9267
|
-
var loomaMarkdown = withSharedBlocks(new MarkdownIt({
|
|
9268
|
-
html: true,
|
|
9269
|
-
linkify: false,
|
|
9270
|
-
typographer: false
|
|
9271
|
-
}));
|
|
9272
9709
|
/**
|
|
9273
|
-
*
|
|
9710
|
+
* A markdown-it instance for Looma Markdown: raw HTML and GFM autolinks on, with `loomaMarkdown`.
|
|
9711
|
+
* @contract Each call returns a new instance that callers may extend.
|
|
9712
|
+
*/
|
|
9713
|
+
function createLoomaMarkdownIt() {
|
|
9714
|
+
return new MarkdownIt({
|
|
9715
|
+
html: true,
|
|
9716
|
+
linkify: true,
|
|
9717
|
+
typographer: false
|
|
9718
|
+
}).use(loomaMarkdown$1);
|
|
9719
|
+
}
|
|
9720
|
+
//#endregion
|
|
9721
|
+
//#region src/editor/extensions/markdown.ts
|
|
9722
|
+
/** The markdown-it instance LoomaEditor reads Looma Markdown with. */
|
|
9723
|
+
var loomaMarkdown = createLoomaMarkdownIt();
|
|
9724
|
+
/**
|
|
9725
|
+
* Renders Looma Markdown as the HTML the editor parses.
|
|
9274
9726
|
* @contract The result is not sanitized; pass it to the editor's `setContent`, whose schema
|
|
9275
|
-
* keeps only content it knows.
|
|
9727
|
+
* keeps only content it knows. Front matter renders nothing.
|
|
9276
9728
|
*/
|
|
9277
9729
|
function loomaMarkdownToHtml(source) {
|
|
9278
|
-
return loomaMarkdown.render(source)
|
|
9730
|
+
return loomaMarkdown.render(source);
|
|
9279
9731
|
}
|
|
9280
9732
|
var containerDepth = (node) => {
|
|
9281
9733
|
let depth = 0;
|
|
@@ -9286,10 +9738,11 @@ var containerDepth = (node) => {
|
|
|
9286
9738
|
return depth;
|
|
9287
9739
|
};
|
|
9288
9740
|
function writeContainer(state, node, opening) {
|
|
9289
|
-
const marker =
|
|
9741
|
+
const marker = loomaContainerMarker(containerDepth(node));
|
|
9290
9742
|
state.write(`${marker} ${opening}`.trimEnd());
|
|
9291
9743
|
state.ensureNewLine();
|
|
9292
9744
|
state.renderContent(node);
|
|
9745
|
+
state.flushClose(1);
|
|
9293
9746
|
state.write(marker);
|
|
9294
9747
|
state.closeBlock(node);
|
|
9295
9748
|
}
|
|
@@ -9300,7 +9753,7 @@ var NODES = {
|
|
|
9300
9753
|
blockquote: defaults.blockquote,
|
|
9301
9754
|
heading: defaults.heading,
|
|
9302
9755
|
horizontalRule: defaults.horizontal_rule,
|
|
9303
|
-
bulletList:
|
|
9756
|
+
bulletList: (state, node) => state.renderList(node, " ", () => "- "),
|
|
9304
9757
|
orderedList: (state, node) => {
|
|
9305
9758
|
const start = Number(node.attrs.start ?? 1);
|
|
9306
9759
|
state.renderList(node, " ", (index) => `${start + index}. `);
|
|
@@ -9308,8 +9761,7 @@ var NODES = {
|
|
|
9308
9761
|
listItem: defaults.list_item,
|
|
9309
9762
|
hardBreak: defaults.hard_break,
|
|
9310
9763
|
codeBlock: (state, node) => {
|
|
9311
|
-
const
|
|
9312
|
-
const fence = "`".repeat(Math.max(3, longest + 1));
|
|
9764
|
+
const fence = loomaCodeFence(node.textContent);
|
|
9313
9765
|
state.write(`${fence}${node.attrs.language ?? ""}\n`);
|
|
9314
9766
|
state.text(node.textContent, false);
|
|
9315
9767
|
state.ensureNewLine();
|
|
@@ -9342,8 +9794,8 @@ var MARKS = {
|
|
|
9342
9794
|
}
|
|
9343
9795
|
};
|
|
9344
9796
|
/**
|
|
9345
|
-
* Writes an editor document as Markdown, with callouts and expand sections
|
|
9346
|
-
*
|
|
9797
|
+
* Writes an editor document as Looma Markdown, with callouts and expand sections as `:::`
|
|
9798
|
+
* containers that `loomaMarkdownToHtml` and Markupress read.
|
|
9347
9799
|
* @contract Content Markdown has no syntax for (tables, task lists, images, chips, mentions,
|
|
9348
9800
|
* the table of contents) is written as its saved HTML, which Markdown carries unchanged.
|
|
9349
9801
|
* Marks without a Markdown form other than underline and highlight are dropped.
|
|
@@ -9391,12 +9843,14 @@ function looksLikeMarkdown(value) {
|
|
|
9391
9843
|
"blockquote_open",
|
|
9392
9844
|
"fence",
|
|
9393
9845
|
"table_open",
|
|
9394
|
-
"looma_container_open"
|
|
9846
|
+
"looma_container_open",
|
|
9847
|
+
"looma_toc"
|
|
9395
9848
|
].includes(token.type)) return true;
|
|
9396
|
-
return token.type === "inline" && Boolean(token.children?.some((child) => [
|
|
9397
|
-
"link_open",
|
|
9849
|
+
return token.type === "inline" && Boolean(token.children?.some((child) => child.type === "link_open" && child.markup !== "linkify" || [
|
|
9398
9850
|
"image",
|
|
9399
|
-
"strong_open"
|
|
9851
|
+
"strong_open",
|
|
9852
|
+
"looma_chip",
|
|
9853
|
+
"looma_mention"
|
|
9400
9854
|
].includes(child.type)));
|
|
9401
9855
|
});
|
|
9402
9856
|
}
|
|
@@ -9411,7 +9865,7 @@ function hasRichClipboardStructure(value) {
|
|
|
9411
9865
|
if (body.querySelector("[data-pm-slice]") && !body.querySelector("pre")) return true;
|
|
9412
9866
|
return body.querySelectorAll("p").length > 1;
|
|
9413
9867
|
}
|
|
9414
|
-
function parseHtmlSlice(schema, value) {
|
|
9868
|
+
function parseHtmlSlice(schema, value, fromHtml = false) {
|
|
9415
9869
|
const parsed = new globalThis.DOMParser().parseFromString(value, "text/html");
|
|
9416
9870
|
if (parsed.body.style.display === "none") return Slice.empty;
|
|
9417
9871
|
for (const element of parsed.body.querySelectorAll("[style]")) if (element.style.display === "none") element.remove();
|
|
@@ -9422,7 +9876,7 @@ function parseHtmlSlice(schema, value) {
|
|
|
9422
9876
|
if (/\s$/.test(child.textContent) || /^\s/.test(next.textContent)) continue;
|
|
9423
9877
|
wrapper.insertBefore(parsed.createTextNode(" "), next);
|
|
9424
9878
|
}
|
|
9425
|
-
for (const pre of parsed.body.querySelectorAll("pre")) {
|
|
9879
|
+
for (const pre of fromHtml ? parsed.body.querySelectorAll("pre") : []) {
|
|
9426
9880
|
let last = pre;
|
|
9427
9881
|
while (last.lastChild) last = last.lastChild;
|
|
9428
9882
|
if (last.nodeType === Node.TEXT_NODE) last.textContent = last.textContent?.replace(/\r?\n$/, "") ?? "";
|
|
@@ -9446,21 +9900,13 @@ function parseHtmlSlice(schema, value) {
|
|
|
9446
9900
|
if (!container.textContent?.trim() && !container.querySelector("img, hr")) return Slice.empty;
|
|
9447
9901
|
return DOMParser.fromSchema(schema).parseSlice(container, { preserveWhitespace: false });
|
|
9448
9902
|
}
|
|
9449
|
-
function markdownFrontmatter(value) {
|
|
9450
|
-
const match = /^---[ \t]*\r?\n([\s\S]*?)\r?\n(?:---|\.\.\.)[ \t]*(?:\r?\n|$)/.exec(value.slice(0, 8192));
|
|
9451
|
-
if (!match || !/^[A-Za-z][\w-]*:[ \t]*\S/m.test(match[1])) return null;
|
|
9452
|
-
return {
|
|
9453
|
-
source: match[0].trimEnd(),
|
|
9454
|
-
body: value.slice(match[0].length)
|
|
9455
|
-
};
|
|
9456
|
-
}
|
|
9457
9903
|
function escapeHtmlText(value) {
|
|
9458
9904
|
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
9459
9905
|
}
|
|
9460
9906
|
function documentSlice(schema, plainText) {
|
|
9461
|
-
const frontmatter =
|
|
9907
|
+
const frontmatter = splitLoomaFrontmatter(plainText);
|
|
9462
9908
|
if (frontmatter || looksLikeMarkdown(plainText)) return parseHtmlSlice(schema, frontmatter ? `<pre><code class="language-yaml">${escapeHtmlText(frontmatter.source)}</code></pre>${loomaMarkdown.render(frontmatter.body)}` : loomaMarkdown.render(plainText));
|
|
9463
|
-
if (looksLikeDocumentHtml(plainText)) return parseHtmlSlice(schema, plainText);
|
|
9909
|
+
if (looksLikeDocumentHtml(plainText)) return parseHtmlSlice(schema, plainText, true);
|
|
9464
9910
|
return null;
|
|
9465
9911
|
}
|
|
9466
9912
|
/**
|
|
@@ -11076,4 +11522,4 @@ function createLoomaSlashCommandExtension(options = {}) {
|
|
|
11076
11522
|
});
|
|
11077
11523
|
}
|
|
11078
11524
|
//#endregion
|
|
11079
|
-
export { filterLoomaMentionItems as A, LOOMA_CHIP_COLORS as B, loomaMarkdownToHtml as C, createLoomaMentionExtension as D, LoomaMentionSuggestionPluginKey as E, LoomaTableOfContents as F, LOOMA_ACTIVE_BLOCK_BLUR_GRACE_MS as G, normalizeLoomaChipColor as H, getTableOfContentsEntries as I, LoomaActiveBlock as J, LOOMA_ACTIVE_BLOCK_CLASS as K, normalizeTableOfContentsConfig as L, LoomaImage as M, normalizeImageDimension as N, DEFAULT_MENTION_RESULT_LIMIT as O, normalizeImagePlacement as P, LoomaExpand as R, loomaMarkdownFromDoc as S, siteRelativeHref as T,
|
|
11525
|
+
export { filterLoomaMentionItems as A, LOOMA_CHIP_COLORS as B, loomaMarkdownToHtml as C, createLoomaMentionExtension as D, LoomaMentionSuggestionPluginKey as E, LoomaTableOfContents as F, LOOMA_ACTIVE_BLOCK_BLUR_GRACE_MS as G, normalizeLoomaChipColor as H, getTableOfContentsEntries as I, LoomaActiveBlock as J, LOOMA_ACTIVE_BLOCK_CLASS as K, normalizeTableOfContentsConfig as L, LoomaImage as M, normalizeImageDimension as N, DEFAULT_MENTION_RESULT_LIMIT as O, normalizeImagePlacement as P, LoomaExpand as R, loomaMarkdownFromDoc as S, siteRelativeHref as T, LoomaCallout as U, LoomaChip as V, LOOMA_CALLOUT_TONES as W, LoomaActiveBlockPluginKey as Y, getActiveTableCellAlignment as _, getActiveTableUiState as a, setActiveTableCellBackground as b, insertTableAtRange as c, LoomaTableKit as d, getDefaultEditorExtensions as f, LoomaTableHeader as g, LoomaTableCell as h, getDefaultSlashCommands as i, normalizeMentionResultLimit as j, MAX_MENTION_RESULT_LIMIT as k, normalizeActiveTableColumnWidths as l, LoomaTable as m, createLoomaSlashCommandExtension as n, handleTableAction as o, getLoomaTableExtensions as p, LOOMA_ACTIVE_BLOCK_TYPING_IDLE_MS as q, filterLoomaSlashCommands as r, handleTableOverlayAction as s, LoomaSlashCommand as t, shouldShowTextFormattingToolbar as u, getActiveTableCellBackground as v, createSiteRelativeLinks as w, LoomaSmartPaste as x, setActiveTableCellAlignment as y, revealLoomaExpandAt as z };
|