@threadlabs/looma 0.28.17 → 0.28.18
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 +50 -16
- package/editor/extensions/markdown.d.ts +1 -1
- package/markdown/fixtures.js +16 -2
- package/markdown/index.d.ts +15 -2
- package/markdown/index.js +35 -4
- package/markdown/markdown-it.d.ts +10 -2
- package/markdown/markdown-it.js +15 -12
- package/package.json +2 -1
|
@@ -309,15 +309,46 @@ function parseLoomaFenceInfo(info) {
|
|
|
309
309
|
...numbers ? { lineNumbers: numbers[1] === void 0 } : {}
|
|
310
310
|
};
|
|
311
311
|
}
|
|
312
|
+
var MAPPING_KEY = /^(?:[^\s#:'"\-?,[\]{}&*!|>%@`][^:\n]*?|"[^"\n]*"|'[^'\n]*')[ \t]*:(?:[ \t]|$)/;
|
|
313
|
+
var NOTATION = /[ \t]*(?:\/\/|#|--|<!--|\{?\/\*)[ \t]*\[!code highlight(?::(\d+))?\][ \t]*(?:-->|\*\/\}?)?[ \t]*$/;
|
|
314
|
+
/**
|
|
315
|
+
* Applies code notation: a line ending in a `[!code highlight]` comment is highlighted and loses
|
|
316
|
+
* the comment, `[!code highlight:3]` covers three lines, and a marker alone on its line is removed
|
|
317
|
+
* with the line and applies to the lines after it.
|
|
318
|
+
* @contract Returns the code a reader sees and its highlighted line numbers, from 1, ascending.
|
|
319
|
+
* ponytail: matched on source lines, so a string that ends in the marker counts too.
|
|
320
|
+
*/
|
|
321
|
+
function applyLoomaCodeNotation(code) {
|
|
322
|
+
const kept = [];
|
|
323
|
+
const lines = /* @__PURE__ */ new Set();
|
|
324
|
+
for (const line of code.split("\n")) {
|
|
325
|
+
const match = NOTATION.exec(line);
|
|
326
|
+
if (!match) {
|
|
327
|
+
kept.push(line);
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
330
|
+
const rest = line.slice(0, match.index);
|
|
331
|
+
const own = rest.trim() !== "";
|
|
332
|
+
if (own) kept.push(rest);
|
|
333
|
+
for (let count = 0; count < Number(match[1] ?? 1); count++) lines.add(kept.length + (own ? 0 : 1) + count);
|
|
334
|
+
}
|
|
335
|
+
return {
|
|
336
|
+
code: kept.join("\n"),
|
|
337
|
+
lines: [...lines].sort((a, b) => a - b)
|
|
338
|
+
};
|
|
339
|
+
}
|
|
312
340
|
/**
|
|
313
341
|
* Splits YAML front matter from the start of a document.
|
|
314
|
-
* @contract Front matter opens with a `---` line at the very start
|
|
315
|
-
* line, and
|
|
342
|
+
* @contract Front matter opens with a `---` line at the very start and closes at a `---` or `...`
|
|
343
|
+
* line, and what lies between is a YAML mapping: its first line that is not blank or a comment
|
|
344
|
+
* starts with a key, whose value may be a scalar, a list or a mapping. `source` is the block as
|
|
345
|
+
* written, delimiters included.
|
|
316
346
|
* @failure Returns null when the document has none.
|
|
317
347
|
*/
|
|
318
348
|
function splitLoomaFrontmatter(document) {
|
|
319
349
|
const match = /^---[ \t]*\r?\n([\s\S]*?)\r?\n(?:---|\.\.\.)[ \t]*(?:\r?\n|$)/.exec(document.slice(0, 8192));
|
|
320
|
-
|
|
350
|
+
const first = match?.[1].split(/\r?\n/).find((line) => line.trim() && !line.trimStart().startsWith("#"));
|
|
351
|
+
if (!match || !first || !MAPPING_KEY.test(first)) return null;
|
|
321
352
|
return {
|
|
322
353
|
source: match[0].trimEnd(),
|
|
323
354
|
yaml: match[1],
|
|
@@ -9695,7 +9726,10 @@ function taskLists(state) {
|
|
|
9695
9726
|
inline.content = inline.content.replace(TASK, "");
|
|
9696
9727
|
item.attrSet("data-type", "taskItem");
|
|
9697
9728
|
item.attrSet("data-checked", String(checked));
|
|
9698
|
-
|
|
9729
|
+
inline.meta = {
|
|
9730
|
+
...inline.meta,
|
|
9731
|
+
task: checked
|
|
9732
|
+
};
|
|
9699
9733
|
}
|
|
9700
9734
|
}
|
|
9701
9735
|
}
|
|
@@ -9737,6 +9771,11 @@ function attributes(state) {
|
|
|
9737
9771
|
const token = tokens[index];
|
|
9738
9772
|
if (token.type !== "inline" || !token.children) continue;
|
|
9739
9773
|
const children = token.children;
|
|
9774
|
+
if (typeof token.meta?.task === "boolean") {
|
|
9775
|
+
const html = (content) => Object.assign(new state.Token("html_inline", "", 0), { content });
|
|
9776
|
+
children.unshift(html(`<label><input type="checkbox" disabled=""${token.meta.task ? " checked=\"\"" : ""}> `));
|
|
9777
|
+
children.push(html("</label>"));
|
|
9778
|
+
}
|
|
9740
9779
|
for (let child = children.length - 3; child >= 0; child--) {
|
|
9741
9780
|
const text = children[child + 1];
|
|
9742
9781
|
if (children[child].type !== "link_open" || children[child].markup !== "linkify" || /^(?:[a-z][\w+.-]*:|www\.)/i.test(text.content)) continue;
|
|
@@ -9813,7 +9852,7 @@ function tableOfContents(tokens, format, depth, escape) {
|
|
|
9813
9852
|
* @contract Renders the canonical HTML, which is the HTML LoomaEditor saves. It is not sanitized.
|
|
9814
9853
|
* Linkify and raw HTML are instance options; `createLoomaMarkdownIt` turns both on.
|
|
9815
9854
|
*/
|
|
9816
|
-
function loomaMarkdown$1(md) {
|
|
9855
|
+
function loomaMarkdown$1(md, { codeNotation = "apply" } = {}) {
|
|
9817
9856
|
const escape = md.utils.escapeHtml;
|
|
9818
9857
|
md.block.ruler.before("table", "looma_frontmatter", frontmatter);
|
|
9819
9858
|
md.block.ruler.before("fence", "looma_container", container, { alt: [
|
|
@@ -9864,15 +9903,10 @@ function loomaMarkdown$1(md) {
|
|
|
9864
9903
|
const id = String(attributes.id);
|
|
9865
9904
|
return `<span data-type="mention" class="looma-mention" data-id="${escape(id)}" data-label="${escape(label || id)}">@${escape(label || id)}</span>`;
|
|
9866
9905
|
};
|
|
9867
|
-
const listItem = rules.list_item_open ?? ((tokens, index, options, _env, self) => self.renderToken(tokens, index, options));
|
|
9868
|
-
rules.list_item_open = (tokens, index, options, env, self) => {
|
|
9869
|
-
const html = listItem(tokens, index, options, env, self);
|
|
9870
|
-
const task = tokens[index].meta;
|
|
9871
|
-
return task && typeof task.checked === "boolean" ? `${html}<input type="checkbox" disabled=""${task.checked ? " checked=\"\"" : ""}>` : html;
|
|
9872
|
-
};
|
|
9873
9906
|
const code = (content, info) => {
|
|
9874
9907
|
const { language, meta } = parseLoomaFenceInfo(info);
|
|
9875
|
-
|
|
9908
|
+
const source = content.replace(/\n$/, "");
|
|
9909
|
+
return `<pre><code${language ? ` class="language-${escape(language)}"` : ""}${meta ? ` data-meta="${escape(meta)}"` : ""}>${escape(codeNotation === "keep" ? source : applyLoomaCodeNotation(source).code)}</code></pre>\n`;
|
|
9876
9910
|
};
|
|
9877
9911
|
rules.fence = (tokens, index) => code(tokens[index].content, tokens[index].info);
|
|
9878
9912
|
rules.code_block = (tokens, index) => code(tokens[index].content, "");
|
|
@@ -9881,17 +9915,17 @@ function loomaMarkdown$1(md) {
|
|
|
9881
9915
|
* A markdown-it instance for Looma Markdown: raw HTML and GFM autolinks on, with `loomaMarkdown`.
|
|
9882
9916
|
* @contract Each call returns a new instance that callers may extend.
|
|
9883
9917
|
*/
|
|
9884
|
-
function createLoomaMarkdownIt() {
|
|
9918
|
+
function createLoomaMarkdownIt(options = {}) {
|
|
9885
9919
|
return new MarkdownIt({
|
|
9886
9920
|
html: true,
|
|
9887
9921
|
linkify: true,
|
|
9888
9922
|
typographer: false
|
|
9889
|
-
}).use(loomaMarkdown$1);
|
|
9923
|
+
}).use(loomaMarkdown$1, options);
|
|
9890
9924
|
}
|
|
9891
9925
|
//#endregion
|
|
9892
9926
|
//#region src/editor/extensions/markdown.ts
|
|
9893
|
-
/** The markdown-it instance LoomaEditor reads Looma Markdown with. */
|
|
9894
|
-
var loomaMarkdown = createLoomaMarkdownIt();
|
|
9927
|
+
/** The markdown-it instance LoomaEditor reads Looma Markdown with; code keeps its notation as source. */
|
|
9928
|
+
var loomaMarkdown = createLoomaMarkdownIt({ codeNotation: "keep" });
|
|
9895
9929
|
/**
|
|
9896
9930
|
* Renders Looma Markdown as the HTML the editor parses.
|
|
9897
9931
|
* @contract The result is not sanitized; pass it to the editor's `setContent`, whose schema
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type Node as ProseMirrorNode } from "@tiptap/pm/model";
|
|
2
|
-
/** The markdown-it instance LoomaEditor reads Looma Markdown with. */
|
|
2
|
+
/** The markdown-it instance LoomaEditor reads Looma Markdown with; code keeps its notation as source. */
|
|
3
3
|
export declare const loomaMarkdown: import("markdown-it").default;
|
|
4
4
|
/**
|
|
5
5
|
* Renders Looma Markdown as the HTML the editor parses.
|
package/markdown/fixtures.js
CHANGED
|
@@ -23,7 +23,9 @@ var files = /* #__PURE__ */ Object.assign({
|
|
|
23
23
|
"../../markdown-fixtures/code-blocks/fences.md": "```ts\nconst answer = 42;\n```\n\n```\nplain text\n```\n\n indented\n",
|
|
24
24
|
"../../markdown-fixtures/code-blocks/long-fence.html": "<pre><code class=\"language-md\">```ts\nconst a = 1;\n```</code></pre>\n",
|
|
25
25
|
"../../markdown-fixtures/code-blocks/long-fence.md": "````md\n```ts\nconst a = 1;\n```\n````\n",
|
|
26
|
-
"../../markdown-fixtures/code-meta/notation.html": "<pre><code class=\"language-ts\">const
|
|
26
|
+
"../../markdown-fixtures/code-meta/notation-alone.html": "<pre><code class=\"language-ts\">const host = "localhost";\nconst port = 8080;</code></pre>\n",
|
|
27
|
+
"../../markdown-fixtures/code-meta/notation-alone.md": "```ts\n// [!code highlight:2]\nconst host = \"localhost\";\nconst port = 8080;\n```\n",
|
|
28
|
+
"../../markdown-fixtures/code-meta/notation.html": "<pre><code class=\"language-ts\">const port = 8080;</code></pre>\n",
|
|
27
29
|
"../../markdown-fixtures/code-meta/notation.md": "```ts\nconst port = 8080; // [!code highlight]\n```\n",
|
|
28
30
|
"../../markdown-fixtures/code-meta/title-lines.html": "<pre><code class=\"language-ts\" data-meta=\"title="config.ts" {1,3-4} :line-numbers\">export default {\n name: "docs",\n base: "/docs/",\n};</code></pre>\n",
|
|
29
31
|
"../../markdown-fixtures/code-meta/title-lines.md": "```ts title=\"config.ts\" {1,3-4} :line-numbers\nexport default {\n name: \"docs\",\n base: \"/docs/\",\n};\n```\n",
|
|
@@ -47,9 +49,18 @@ var files = /* #__PURE__ */ Object.assign({
|
|
|
47
49
|
"../../markdown-fixtures/details/open.md": "::: details Show the full log {open}\nThe build finished in 42 seconds.\n:::\n",
|
|
48
50
|
"../../markdown-fixtures/figures/caption.html": "<figure>\n<img src=\"./pipeline.svg\" alt=\"The build pipeline\">\n<figcaption>The build, from <em>source</em> to release</figcaption>\n</figure>\n",
|
|
49
51
|
"../../markdown-fixtures/figures/caption.md": "::: figure The build, from *source* to release\n\n:::\n",
|
|
52
|
+
"../../markdown-fixtures/front-matter/list-only.canonical.md": "# Notes\n",
|
|
53
|
+
"../../markdown-fixtures/front-matter/list-only.html": "<h1 id=\"notes\">Notes</h1>\n",
|
|
54
|
+
"../../markdown-fixtures/front-matter/list-only.md": "---\naliases:\n - /old/\n - /older/\n---\n\n# Notes\n",
|
|
55
|
+
"../../markdown-fixtures/front-matter/mapping-only.canonical.md": "# Notes\n",
|
|
56
|
+
"../../markdown-fixtures/front-matter/mapping-only.html": "<h1 id=\"notes\">Notes</h1>\n",
|
|
57
|
+
"../../markdown-fixtures/front-matter/mapping-only.md": "---\nhead:\n meta:\n robots: noindex\n---\n\n# Notes\n",
|
|
50
58
|
"../../markdown-fixtures/front-matter/metadata.canonical.md": "# Notes\n",
|
|
51
59
|
"../../markdown-fixtures/front-matter/metadata.html": "<h1 id=\"notes\">Notes</h1>\n",
|
|
52
60
|
"../../markdown-fixtures/front-matter/metadata.md": "---\ntitle: Release notes\n---\n\n# Notes\n",
|
|
61
|
+
"../../markdown-fixtures/front-matter/nested.canonical.md": "# Notes\n",
|
|
62
|
+
"../../markdown-fixtures/front-matter/nested.html": "<h1 id=\"notes\">Notes</h1>\n",
|
|
63
|
+
"../../markdown-fixtures/front-matter/nested.md": "---\n# Page settings\nsidebar:\n position: 2\n items:\n - label: Intro\n href: /intro/\ntitle: Notes\n---\n\n# Notes\n",
|
|
53
64
|
"../../markdown-fixtures/heading-ids/accented.html": "<h2 id=\"über-uns\">Über uns</h2>\n<h2 id=\"café-crème\">Café crème</h2>\n",
|
|
54
65
|
"../../markdown-fixtures/heading-ids/accented.md": "## Über uns\n\n## Café crème\n",
|
|
55
66
|
"../../markdown-fixtures/heading-ids/cjk.html": "<h2 id=\"はじめに\">はじめに</h2>\n<h2 id=\"設定-と-使い方\">設定 と 使い方</h2>\n",
|
|
@@ -85,8 +96,11 @@ var files = /* #__PURE__ */ Object.assign({
|
|
|
85
96
|
"../../markdown-fixtures/tabs/code-group.md": "::: code-group\n```js title=\"looma.config.js\"\nexport default { theme: \"light\" };\n```\n```ts\nexport default { theme: \"light\" } satisfies Config;\n```\n:::\n",
|
|
86
97
|
"../../markdown-fixtures/tabs/labelled.html": "<ui-tabs group=\"install\">\n<section aria-label=\"npm\">\n<pre><code class=\"language-sh\">npm install @threadlabs/looma</code></pre>\n</section>\n<section aria-label=\"pnpm\">\n<pre><code class=\"language-sh\">pnpm add @threadlabs/looma</code></pre>\n</section>\n</ui-tabs>\n",
|
|
87
98
|
"../../markdown-fixtures/tabs/labelled.md": "::: tabs install\n== npm\n```sh\nnpm install @threadlabs/looma\n```\n== pnpm\n```sh\npnpm add @threadlabs/looma\n```\n:::\n",
|
|
88
|
-
"../../markdown-fixtures/task-lists/checked.html": "<ul data-type=\"taskList\">\n<li data-type=\"taskItem\" data-checked=\"true\"><input type=\"checkbox\" disabled checked>Read the changelog</li>\n<li data-type=\"taskItem\" data-checked=\"false\"><input type=\"checkbox\" disabled>Run the migration</li>\n</ul>\n",
|
|
99
|
+
"../../markdown-fixtures/task-lists/checked.html": "<ul data-type=\"taskList\">\n<li data-type=\"taskItem\" data-checked=\"true\"><label><input type=\"checkbox\" disabled checked> Read the changelog</label></li>\n<li data-type=\"taskItem\" data-checked=\"false\"><label><input type=\"checkbox\" disabled> Run the migration</label></li>\n</ul>\n",
|
|
89
100
|
"../../markdown-fixtures/task-lists/checked.md": "- [x] Read the changelog\n- [ ] Run the migration\n",
|
|
101
|
+
"../../markdown-fixtures/task-lists/loose.canonical.md": "- [x] Read the changelog\n\n Then tell the team.\n- [ ] Run the migration\n",
|
|
102
|
+
"../../markdown-fixtures/task-lists/loose.html": "<ul data-type=\"taskList\">\n<li data-type=\"taskItem\" data-checked=\"true\">\n<p><label><input type=\"checkbox\" disabled checked> Read the changelog</label></p>\n<p>Then tell the team.</p>\n</li>\n<li data-type=\"taskItem\" data-checked=\"false\">\n<p><label><input type=\"checkbox\" disabled> Run the migration</label></p>\n</li>\n</ul>\n",
|
|
103
|
+
"../../markdown-fixtures/task-lists/loose.md": "- [x] Read the changelog\n\n Then tell the team.\n\n- [ ] Run the migration\n",
|
|
90
104
|
"../../markdown-fixtures/task-lists/mixed.canonical.md": "- \\[x\\] Done\n- Not a task\n",
|
|
91
105
|
"../../markdown-fixtures/task-lists/mixed.html": "<ul>\n<li>[x] Done</li>\n<li>Not a task</li>\n</ul>\n",
|
|
92
106
|
"../../markdown-fixtures/task-lists/mixed.md": "- [x] Done\n- Not a task\n",
|
package/markdown/index.d.ts
CHANGED
|
@@ -92,10 +92,23 @@ export interface LoomaFenceInfo {
|
|
|
92
92
|
}
|
|
93
93
|
/** Reads a fence's info string: ```` ```ts title="a.ts" {1,3-5} :line-numbers ````. */
|
|
94
94
|
export declare function parseLoomaFenceInfo(info: string): LoomaFenceInfo;
|
|
95
|
+
/**
|
|
96
|
+
* Applies code notation: a line ending in a `[!code highlight]` comment is highlighted and loses
|
|
97
|
+
* the comment, `[!code highlight:3]` covers three lines, and a marker alone on its line is removed
|
|
98
|
+
* with the line and applies to the lines after it.
|
|
99
|
+
* @contract Returns the code a reader sees and its highlighted line numbers, from 1, ascending.
|
|
100
|
+
* ponytail: matched on source lines, so a string that ends in the marker counts too.
|
|
101
|
+
*/
|
|
102
|
+
export declare function applyLoomaCodeNotation(code: string): {
|
|
103
|
+
code: string;
|
|
104
|
+
lines: number[];
|
|
105
|
+
};
|
|
95
106
|
/**
|
|
96
107
|
* Splits YAML front matter from the start of a document.
|
|
97
|
-
* @contract Front matter opens with a `---` line at the very start
|
|
98
|
-
* line, and
|
|
108
|
+
* @contract Front matter opens with a `---` line at the very start and closes at a `---` or `...`
|
|
109
|
+
* line, and what lies between is a YAML mapping: its first line that is not blank or a comment
|
|
110
|
+
* starts with a key, whose value may be a scalar, a list or a mapping. `source` is the block as
|
|
111
|
+
* written, delimiters included.
|
|
99
112
|
* @failure Returns null when the document has none.
|
|
100
113
|
*/
|
|
101
114
|
export declare function splitLoomaFrontmatter(document: string): {
|
package/markdown/index.js
CHANGED
|
@@ -204,15 +204,46 @@ function parseLoomaFenceInfo(info) {
|
|
|
204
204
|
...numbers ? { lineNumbers: numbers[1] === void 0 } : {}
|
|
205
205
|
};
|
|
206
206
|
}
|
|
207
|
+
var MAPPING_KEY = /^(?:[^\s#:'"\-?,[\]{}&*!|>%@`][^:\n]*?|"[^"\n]*"|'[^'\n]*')[ \t]*:(?:[ \t]|$)/;
|
|
208
|
+
var NOTATION = /[ \t]*(?:\/\/|#|--|<!--|\{?\/\*)[ \t]*\[!code highlight(?::(\d+))?\][ \t]*(?:-->|\*\/\}?)?[ \t]*$/;
|
|
209
|
+
/**
|
|
210
|
+
* Applies code notation: a line ending in a `[!code highlight]` comment is highlighted and loses
|
|
211
|
+
* the comment, `[!code highlight:3]` covers three lines, and a marker alone on its line is removed
|
|
212
|
+
* with the line and applies to the lines after it.
|
|
213
|
+
* @contract Returns the code a reader sees and its highlighted line numbers, from 1, ascending.
|
|
214
|
+
* ponytail: matched on source lines, so a string that ends in the marker counts too.
|
|
215
|
+
*/
|
|
216
|
+
function applyLoomaCodeNotation(code) {
|
|
217
|
+
const kept = [];
|
|
218
|
+
const lines = /* @__PURE__ */ new Set();
|
|
219
|
+
for (const line of code.split("\n")) {
|
|
220
|
+
const match = NOTATION.exec(line);
|
|
221
|
+
if (!match) {
|
|
222
|
+
kept.push(line);
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
const rest = line.slice(0, match.index);
|
|
226
|
+
const own = rest.trim() !== "";
|
|
227
|
+
if (own) kept.push(rest);
|
|
228
|
+
for (let count = 0; count < Number(match[1] ?? 1); count++) lines.add(kept.length + (own ? 0 : 1) + count);
|
|
229
|
+
}
|
|
230
|
+
return {
|
|
231
|
+
code: kept.join("\n"),
|
|
232
|
+
lines: [...lines].sort((a, b) => a - b)
|
|
233
|
+
};
|
|
234
|
+
}
|
|
207
235
|
/**
|
|
208
236
|
* Splits YAML front matter from the start of a document.
|
|
209
|
-
* @contract Front matter opens with a `---` line at the very start
|
|
210
|
-
* line, and
|
|
237
|
+
* @contract Front matter opens with a `---` line at the very start and closes at a `---` or `...`
|
|
238
|
+
* line, and what lies between is a YAML mapping: its first line that is not blank or a comment
|
|
239
|
+
* starts with a key, whose value may be a scalar, a list or a mapping. `source` is the block as
|
|
240
|
+
* written, delimiters included.
|
|
211
241
|
* @failure Returns null when the document has none.
|
|
212
242
|
*/
|
|
213
243
|
function splitLoomaFrontmatter(document) {
|
|
214
244
|
const match = /^---[ \t]*\r?\n([\s\S]*?)\r?\n(?:---|\.\.\.)[ \t]*(?:\r?\n|$)/.exec(document.slice(0, 8192));
|
|
215
|
-
|
|
245
|
+
const first = match?.[1].split(/\r?\n/).find((line) => line.trim() && !line.trimStart().startsWith("#"));
|
|
246
|
+
if (!match || !first || !MAPPING_KEY.test(first)) return null;
|
|
216
247
|
return {
|
|
217
248
|
source: match[0].trimEnd(),
|
|
218
249
|
yaml: match[1],
|
|
@@ -229,4 +260,4 @@ function loomaCodeFence(code) {
|
|
|
229
260
|
return "`".repeat(Math.max(3, longest + 1));
|
|
230
261
|
}
|
|
231
262
|
//#endregion
|
|
232
|
-
export { LOOMA_BADGE_TONES, LOOMA_CALLOUT_TONES, LOOMA_CALLOUT_TONE_ALIASES, LOOMA_CHIP_TONES, LOOMA_CONTAINER_NAMES, LOOMA_MEANING_TONES, assignLoomaHeadingIds, formatLoomaAttributes, loomaCodeFence, loomaContainerMarker, loomaHeadingSlug, normalizeLoomaCalloutTone, normalizeLoomaChipTone, parseLoomaAttributes, parseLoomaFenceInfo, readLoomaAttributes, splitLoomaFrontmatter };
|
|
263
|
+
export { LOOMA_BADGE_TONES, LOOMA_CALLOUT_TONES, LOOMA_CALLOUT_TONE_ALIASES, LOOMA_CHIP_TONES, LOOMA_CONTAINER_NAMES, LOOMA_MEANING_TONES, applyLoomaCodeNotation, assignLoomaHeadingIds, formatLoomaAttributes, loomaCodeFence, loomaContainerMarker, loomaHeadingSlug, normalizeLoomaCalloutTone, normalizeLoomaChipTone, parseLoomaAttributes, parseLoomaFenceInfo, readLoomaAttributes, splitLoomaFrontmatter };
|
|
@@ -1,13 +1,21 @@
|
|
|
1
1
|
import MarkdownIt from "markdown-it";
|
|
2
|
+
/** How a Looma Markdown instance renders. */
|
|
3
|
+
export interface LoomaMarkdownOptions {
|
|
4
|
+
/**
|
|
5
|
+
* `apply` (default) renders code as a reader sees it, without its `[!code highlight]` comments;
|
|
6
|
+
* `keep` keeps them in the code, for an editor that edits the source.
|
|
7
|
+
*/
|
|
8
|
+
codeNotation?: "apply" | "keep";
|
|
9
|
+
}
|
|
2
10
|
/**
|
|
3
11
|
* Adds Looma Markdown to a markdown-it instance: containers, alerts, task lists, the table of
|
|
4
12
|
* contents, chips, mentions, brace attributes, heading anchors, code meta and front matter.
|
|
5
13
|
* @contract Renders the canonical HTML, which is the HTML LoomaEditor saves. It is not sanitized.
|
|
6
14
|
* Linkify and raw HTML are instance options; `createLoomaMarkdownIt` turns both on.
|
|
7
15
|
*/
|
|
8
|
-
export declare function loomaMarkdown(md: MarkdownIt): void;
|
|
16
|
+
export declare function loomaMarkdown(md: MarkdownIt, { codeNotation }?: LoomaMarkdownOptions): void;
|
|
9
17
|
/**
|
|
10
18
|
* A markdown-it instance for Looma Markdown: raw HTML and GFM autolinks on, with `loomaMarkdown`.
|
|
11
19
|
* @contract Each call returns a new instance that callers may extend.
|
|
12
20
|
*/
|
|
13
|
-
export declare function createLoomaMarkdownIt(): MarkdownIt;
|
|
21
|
+
export declare function createLoomaMarkdownIt(options?: LoomaMarkdownOptions): MarkdownIt;
|
package/markdown/markdown-it.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { LOOMA_CONTAINER_NAMES, assignLoomaHeadingIds, normalizeLoomaCalloutTone, normalizeLoomaChipTone, parseLoomaAttributes, parseLoomaFenceInfo, readLoomaAttributes, splitLoomaFrontmatter } from "./index.js";
|
|
1
|
+
import { LOOMA_CONTAINER_NAMES, applyLoomaCodeNotation, assignLoomaHeadingIds, normalizeLoomaCalloutTone, normalizeLoomaChipTone, parseLoomaAttributes, parseLoomaFenceInfo, readLoomaAttributes, splitLoomaFrontmatter } from "./index.js";
|
|
2
2
|
import MarkdownIt from "markdown-it";
|
|
3
3
|
//#region src/markdown/markdown-it.ts
|
|
4
4
|
var CONTAINER_OPEN = /^(:{3,})[ \t]*([A-Za-z][\w-]*)(?:[ \t]+(.*?))?[ \t]*$/;
|
|
@@ -200,7 +200,10 @@ function taskLists(state) {
|
|
|
200
200
|
inline.content = inline.content.replace(TASK, "");
|
|
201
201
|
item.attrSet("data-type", "taskItem");
|
|
202
202
|
item.attrSet("data-checked", String(checked));
|
|
203
|
-
|
|
203
|
+
inline.meta = {
|
|
204
|
+
...inline.meta,
|
|
205
|
+
task: checked
|
|
206
|
+
};
|
|
204
207
|
}
|
|
205
208
|
}
|
|
206
209
|
}
|
|
@@ -242,6 +245,11 @@ function attributes(state) {
|
|
|
242
245
|
const token = tokens[index];
|
|
243
246
|
if (token.type !== "inline" || !token.children) continue;
|
|
244
247
|
const children = token.children;
|
|
248
|
+
if (typeof token.meta?.task === "boolean") {
|
|
249
|
+
const html = (content) => Object.assign(new state.Token("html_inline", "", 0), { content });
|
|
250
|
+
children.unshift(html(`<label><input type="checkbox" disabled=""${token.meta.task ? " checked=\"\"" : ""}> `));
|
|
251
|
+
children.push(html("</label>"));
|
|
252
|
+
}
|
|
245
253
|
for (let child = children.length - 3; child >= 0; child--) {
|
|
246
254
|
const text = children[child + 1];
|
|
247
255
|
if (children[child].type !== "link_open" || children[child].markup !== "linkify" || /^(?:[a-z][\w+.-]*:|www\.)/i.test(text.content)) continue;
|
|
@@ -318,7 +326,7 @@ function tableOfContents(tokens, format, depth, escape) {
|
|
|
318
326
|
* @contract Renders the canonical HTML, which is the HTML LoomaEditor saves. It is not sanitized.
|
|
319
327
|
* Linkify and raw HTML are instance options; `createLoomaMarkdownIt` turns both on.
|
|
320
328
|
*/
|
|
321
|
-
function loomaMarkdown(md) {
|
|
329
|
+
function loomaMarkdown(md, { codeNotation = "apply" } = {}) {
|
|
322
330
|
const escape = md.utils.escapeHtml;
|
|
323
331
|
md.block.ruler.before("table", "looma_frontmatter", frontmatter);
|
|
324
332
|
md.block.ruler.before("fence", "looma_container", container, { alt: [
|
|
@@ -369,15 +377,10 @@ function loomaMarkdown(md) {
|
|
|
369
377
|
const id = String(attributes.id);
|
|
370
378
|
return `<span data-type="mention" class="looma-mention" data-id="${escape(id)}" data-label="${escape(label || id)}">@${escape(label || id)}</span>`;
|
|
371
379
|
};
|
|
372
|
-
const listItem = rules.list_item_open ?? ((tokens, index, options, _env, self) => self.renderToken(tokens, index, options));
|
|
373
|
-
rules.list_item_open = (tokens, index, options, env, self) => {
|
|
374
|
-
const html = listItem(tokens, index, options, env, self);
|
|
375
|
-
const task = tokens[index].meta;
|
|
376
|
-
return task && typeof task.checked === "boolean" ? `${html}<input type="checkbox" disabled=""${task.checked ? " checked=\"\"" : ""}>` : html;
|
|
377
|
-
};
|
|
378
380
|
const code = (content, info) => {
|
|
379
381
|
const { language, meta } = parseLoomaFenceInfo(info);
|
|
380
|
-
|
|
382
|
+
const source = content.replace(/\n$/, "");
|
|
383
|
+
return `<pre><code${language ? ` class="language-${escape(language)}"` : ""}${meta ? ` data-meta="${escape(meta)}"` : ""}>${escape(codeNotation === "keep" ? source : applyLoomaCodeNotation(source).code)}</code></pre>\n`;
|
|
381
384
|
};
|
|
382
385
|
rules.fence = (tokens, index) => code(tokens[index].content, tokens[index].info);
|
|
383
386
|
rules.code_block = (tokens, index) => code(tokens[index].content, "");
|
|
@@ -386,12 +389,12 @@ function loomaMarkdown(md) {
|
|
|
386
389
|
* A markdown-it instance for Looma Markdown: raw HTML and GFM autolinks on, with `loomaMarkdown`.
|
|
387
390
|
* @contract Each call returns a new instance that callers may extend.
|
|
388
391
|
*/
|
|
389
|
-
function createLoomaMarkdownIt() {
|
|
392
|
+
function createLoomaMarkdownIt(options = {}) {
|
|
390
393
|
return new MarkdownIt({
|
|
391
394
|
html: true,
|
|
392
395
|
linkify: true,
|
|
393
396
|
typographer: false
|
|
394
|
-
}).use(loomaMarkdown);
|
|
397
|
+
}).use(loomaMarkdown, options);
|
|
395
398
|
}
|
|
396
399
|
//#endregion
|
|
397
400
|
export { createLoomaMarkdownIt, loomaMarkdown };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@threadlabs/looma",
|
|
3
|
-
"version": "0.28.
|
|
3
|
+
"version": "0.28.18",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Looma components as HTML, Vue, and plain DOM, with design tokens.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -171,6 +171,7 @@
|
|
|
171
171
|
"@tiptap/vue-3": "^2.11.5",
|
|
172
172
|
"@types/markdown-it": "14.2.0",
|
|
173
173
|
"@vitest/browser": "~3.2.6",
|
|
174
|
+
"axe-core": "^4.10.3",
|
|
174
175
|
"jsdom": "^27.0.0"
|
|
175
176
|
},
|
|
176
177
|
"engines": {
|