@threadlabs/looma 0.28.16 → 0.28.17
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 +381 -42
- package/editor/extensions/figure.d.ts +12 -0
- package/editor/extensions/index.d.ts +2 -0
- package/editor/extensions/index.js +2 -2
- package/editor/extensions/markdown.d.ts +4 -5
- package/editor/extensions/table-of-contents.d.ts +1 -1
- package/editor/extensions/tabs.d.ts +13 -0
- package/editor/index.js +2 -2
- package/markdown/fixtures.js +10 -0
- package/markdown/index.d.ts +5 -2
- package/markdown/index.js +6 -4
- package/markdown/markdown-it.js +1 -1
- package/package.json +1 -1
- package/vue/editor/index.js +61 -19
|
@@ -242,6 +242,25 @@ function parseLoomaAttributes(source) {
|
|
|
242
242
|
return read && read.end === source.length ? read.attributes : null;
|
|
243
243
|
}
|
|
244
244
|
/**
|
|
245
|
+
* Writes attributes as a brace list, `id` first as `#id`, then the rest in order.
|
|
246
|
+
* @contract `true` writes a bare key; values are quoted only when they need it. `false`, null and
|
|
247
|
+
* undefined values are left out, and an empty set writes an empty string. An ID that `#` cannot
|
|
248
|
+
* carry, or any ID when `hashId` is false, is written `id=…`.
|
|
249
|
+
*/
|
|
250
|
+
function formatLoomaAttributes(attributes, { hashId = true } = {}) {
|
|
251
|
+
const items = [];
|
|
252
|
+
const { id, ...rest } = attributes;
|
|
253
|
+
const hashed = hashId && typeof id === "string" && /^[\w-]+$/.test(id);
|
|
254
|
+
if (hashed) items.push(`#${id}`);
|
|
255
|
+
for (const [key, value] of Object.entries(hashed ? rest : attributes)) if (value === true) items.push(key);
|
|
256
|
+
else if (value === false || value === null || value === void 0) continue;
|
|
257
|
+
else {
|
|
258
|
+
const text = String(value);
|
|
259
|
+
items.push(/^[^\s"'=<>`{}]+$/.test(text) ? `${key}=${text}` : text.includes("\"") ? `${key}='${text}'` : `${key}="${text}"`);
|
|
260
|
+
}
|
|
261
|
+
return items.length ? `{${items.join(" ")}}` : "";
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
245
264
|
* The anchor a heading's text derives: NFC, lowercase, keeping letters, numbers and combining
|
|
246
265
|
* marks, with every other run of characters one hyphen.
|
|
247
266
|
* @failure Text with no letters or numbers, such as only emoji, derives `heading`.
|
|
@@ -625,6 +644,144 @@ var LoomaExpand = Node$1.create({
|
|
|
625
644
|
}
|
|
626
645
|
});
|
|
627
646
|
//#endregion
|
|
647
|
+
//#region src/editor/extensions/figure.ts
|
|
648
|
+
/**
|
|
649
|
+
* An image with a caption, saved as `<figure><img><figcaption>` and written in Looma Markdown as
|
|
650
|
+
* `::: figure Caption`. The caption is inline content the author edits in place.
|
|
651
|
+
* @invariant A figure holds exactly one block image and one caption; an empty caption stays empty.
|
|
652
|
+
*/
|
|
653
|
+
var LoomaFigure = Node$1.create({
|
|
654
|
+
name: "loomaFigure",
|
|
655
|
+
group: "block",
|
|
656
|
+
content: "image loomaFigcaption",
|
|
657
|
+
defining: true,
|
|
658
|
+
parseHTML() {
|
|
659
|
+
return [{
|
|
660
|
+
tag: "figure",
|
|
661
|
+
getAttrs: (element) => element.querySelector(":scope > img") ? null : false
|
|
662
|
+
}];
|
|
663
|
+
},
|
|
664
|
+
renderHTML() {
|
|
665
|
+
return ["figure", 0];
|
|
666
|
+
}
|
|
667
|
+
});
|
|
668
|
+
/**
|
|
669
|
+
* A figure's caption: inline content under its image.
|
|
670
|
+
* @invariant It exists only as the second child of `LoomaFigure`.
|
|
671
|
+
*/
|
|
672
|
+
var LoomaFigcaption = Node$1.create({
|
|
673
|
+
name: "loomaFigcaption",
|
|
674
|
+
content: "inline*",
|
|
675
|
+
defining: true,
|
|
676
|
+
parseHTML() {
|
|
677
|
+
return [{ tag: "figcaption" }];
|
|
678
|
+
},
|
|
679
|
+
renderHTML() {
|
|
680
|
+
return ["figcaption", 0];
|
|
681
|
+
}
|
|
682
|
+
});
|
|
683
|
+
//#endregion
|
|
684
|
+
//#region src/editor/extensions/tabs.ts
|
|
685
|
+
/**
|
|
686
|
+
* Tabs or a code group, kept whole so Looma Markdown's `::: tabs` and `::: code-group` survive
|
|
687
|
+
* editing. Saved as `<ui-tabs group>` with a `<section aria-label>` per panel.
|
|
688
|
+
* @contract While editing, panels show one after another under their labels; the Tabs component
|
|
689
|
+
* draws them only where the saved HTML renders.
|
|
690
|
+
*/
|
|
691
|
+
var LoomaTabs = Node$1.create({
|
|
692
|
+
name: "loomaTabs",
|
|
693
|
+
group: "block",
|
|
694
|
+
content: "loomaTab+",
|
|
695
|
+
defining: true,
|
|
696
|
+
isolating: true,
|
|
697
|
+
addAttributes() {
|
|
698
|
+
return {
|
|
699
|
+
group: {
|
|
700
|
+
default: "",
|
|
701
|
+
parseHTML: (element) => element.getAttribute("group") ?? "",
|
|
702
|
+
renderHTML: (attributes) => attributes.group ? { group: attributes.group } : {}
|
|
703
|
+
},
|
|
704
|
+
codeGroup: {
|
|
705
|
+
default: false,
|
|
706
|
+
parseHTML: (element) => element.classList.contains("code-group"),
|
|
707
|
+
renderHTML: (attributes) => attributes.codeGroup ? { class: "code-group" } : {}
|
|
708
|
+
}
|
|
709
|
+
};
|
|
710
|
+
},
|
|
711
|
+
parseHTML() {
|
|
712
|
+
return [{ tag: "ui-tabs" }];
|
|
713
|
+
},
|
|
714
|
+
renderHTML({ HTMLAttributes }) {
|
|
715
|
+
return [
|
|
716
|
+
"ui-tabs",
|
|
717
|
+
HTMLAttributes,
|
|
718
|
+
0
|
|
719
|
+
];
|
|
720
|
+
},
|
|
721
|
+
addNodeView() {
|
|
722
|
+
return () => {
|
|
723
|
+
const dom = document.createElement("div");
|
|
724
|
+
dom.dataset.loomaTabs = "";
|
|
725
|
+
return {
|
|
726
|
+
dom,
|
|
727
|
+
contentDOM: dom
|
|
728
|
+
};
|
|
729
|
+
};
|
|
730
|
+
}
|
|
731
|
+
});
|
|
732
|
+
/**
|
|
733
|
+
* One panel of `LoomaTabs`, labelled by `label`.
|
|
734
|
+
* @invariant It exists only inside `LoomaTabs`; its label is the tab Tabs shows and the `== Label` line Markdown writes.
|
|
735
|
+
*/
|
|
736
|
+
var LoomaTab = Node$1.create({
|
|
737
|
+
name: "loomaTab",
|
|
738
|
+
content: "block+",
|
|
739
|
+
defining: true,
|
|
740
|
+
isolating: true,
|
|
741
|
+
addAttributes() {
|
|
742
|
+
return { label: {
|
|
743
|
+
default: "",
|
|
744
|
+
parseHTML: (element) => element.getAttribute("aria-label") ?? "",
|
|
745
|
+
renderHTML: (attributes) => ({ "aria-label": attributes.label })
|
|
746
|
+
} };
|
|
747
|
+
},
|
|
748
|
+
parseHTML() {
|
|
749
|
+
return [{ tag: "ui-tabs > section" }];
|
|
750
|
+
},
|
|
751
|
+
renderHTML({ HTMLAttributes }) {
|
|
752
|
+
return [
|
|
753
|
+
"section",
|
|
754
|
+
HTMLAttributes,
|
|
755
|
+
0
|
|
756
|
+
];
|
|
757
|
+
},
|
|
758
|
+
addNodeView() {
|
|
759
|
+
return ({ node }) => {
|
|
760
|
+
let current = node;
|
|
761
|
+
const dom = document.createElement("section");
|
|
762
|
+
const label = document.createElement("div");
|
|
763
|
+
label.contentEditable = "false";
|
|
764
|
+
label.dataset.loomaTabLabel = "";
|
|
765
|
+
label.textContent = node.attrs.label;
|
|
766
|
+
const contentDOM = document.createElement("div");
|
|
767
|
+
dom.setAttribute("aria-label", node.attrs.label);
|
|
768
|
+
dom.append(label, contentDOM);
|
|
769
|
+
return {
|
|
770
|
+
dom,
|
|
771
|
+
contentDOM,
|
|
772
|
+
update(next) {
|
|
773
|
+
if (next.type !== current.type) return false;
|
|
774
|
+
current = next;
|
|
775
|
+
label.textContent = next.attrs.label;
|
|
776
|
+
dom.setAttribute("aria-label", next.attrs.label);
|
|
777
|
+
return true;
|
|
778
|
+
},
|
|
779
|
+
ignoreMutation: (mutation) => mutation.type !== "selection" && !contentDOM.contains(mutation.target)
|
|
780
|
+
};
|
|
781
|
+
};
|
|
782
|
+
}
|
|
783
|
+
});
|
|
784
|
+
//#endregion
|
|
628
785
|
//#region src/editor/extensions/table-of-contents.ts
|
|
629
786
|
/** Normalize loaded configuration without persisting unsupported presentation values. */
|
|
630
787
|
function normalizeTableOfContentsConfig(attributes) {
|
|
@@ -671,7 +828,17 @@ function tableOfContentsTree(entries) {
|
|
|
671
828
|
}
|
|
672
829
|
return roots;
|
|
673
830
|
}
|
|
674
|
-
|
|
831
|
+
var derivedFrom = (id, text) => {
|
|
832
|
+
const slug = loomaHeadingSlug(text);
|
|
833
|
+
return id === slug || id.startsWith(`${slug}-`) && /^\d+$/.test(id.slice(slug.length + 1));
|
|
834
|
+
};
|
|
835
|
+
/**
|
|
836
|
+
* Keeps heading anchors on Looma Markdown's rule. An anchor derived from its heading's text
|
|
837
|
+
* (as it read before this change) follows the text; any other, including `looma-heading-N` anchors
|
|
838
|
+
* saved before this rule, is a custom anchor and stays, so links to it keep working. With
|
|
839
|
+
* a table of contents every heading gets an anchor; without one, only headings that have one.
|
|
840
|
+
*/
|
|
841
|
+
function anchorTransaction(state, previous) {
|
|
675
842
|
let hasToc = false;
|
|
676
843
|
const headings = [];
|
|
677
844
|
state.doc.descendants((node, position) => {
|
|
@@ -681,24 +848,28 @@ function anchorTransaction(state) {
|
|
|
681
848
|
position
|
|
682
849
|
});
|
|
683
850
|
});
|
|
684
|
-
if (!
|
|
685
|
-
const
|
|
686
|
-
|
|
851
|
+
if (!headings.length) return null;
|
|
852
|
+
const before = /* @__PURE__ */ new Map();
|
|
853
|
+
previous?.descendants((node) => {
|
|
854
|
+
if (node.type.name === "heading" && typeof node.attrs.id === "string") before.set(node.attrs.id, node.textContent.trim());
|
|
855
|
+
});
|
|
856
|
+
const custom = (node) => {
|
|
857
|
+
const id = node.attrs.id;
|
|
858
|
+
if (typeof id !== "string" || !id || /\s/.test(id)) return void 0;
|
|
859
|
+
return derivedFrom(id, before.get(id) ?? node.textContent.trim()) ? void 0 : id;
|
|
860
|
+
};
|
|
861
|
+
const ids = assignLoomaHeadingIds(headings.map(({ node }) => ({
|
|
862
|
+
text: node.textContent.trim(),
|
|
863
|
+
id: custom(node)
|
|
864
|
+
})));
|
|
687
865
|
const transaction = state.tr;
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
transaction.setNodeMarkup(position, void 0, {
|
|
696
|
-
...node.attrs,
|
|
697
|
-
id
|
|
698
|
-
});
|
|
699
|
-
}
|
|
700
|
-
used.add(id);
|
|
701
|
-
}
|
|
866
|
+
headings.forEach(({ node, position }, index) => {
|
|
867
|
+
if (node.attrs.id === ids[index] || !hasToc && !node.attrs.id) return;
|
|
868
|
+
transaction.setNodeMarkup(position, void 0, {
|
|
869
|
+
...node.attrs,
|
|
870
|
+
id: ids[index]
|
|
871
|
+
});
|
|
872
|
+
});
|
|
702
873
|
return transaction.docChanged ? transaction : null;
|
|
703
874
|
}
|
|
704
875
|
var LoomaHeadingAnchors = Extension.create({
|
|
@@ -714,7 +885,7 @@ var LoomaHeadingAnchors = Extension.create({
|
|
|
714
885
|
if (transaction) this.editor.view.dispatch(transaction.setMeta("addToHistory", false));
|
|
715
886
|
},
|
|
716
887
|
addProseMirrorPlugins() {
|
|
717
|
-
return [new Plugin({ appendTransaction: (transactions,
|
|
888
|
+
return [new Plugin({ appendTransaction: (transactions, oldState, state) => transactions.some((transaction) => transaction.docChanged) ? anchorTransaction(state, oldState.doc) : null })];
|
|
718
889
|
}
|
|
719
890
|
});
|
|
720
891
|
function contentsHtml(doc, config) {
|
|
@@ -9332,7 +9503,7 @@ var TAB_LABEL = /^==[ \t]+(\S.*?)[ \t]*$/;
|
|
|
9332
9503
|
var OPEN_FLAG = /[ \t]*\{open\}$/;
|
|
9333
9504
|
var ALERT = /^\[!(NOTE|TIP|IMPORTANT|WARNING|CAUTION)\][ \t]*(?:\n|$)/i;
|
|
9334
9505
|
var LEAF = /^::([A-Za-z][\w-]*)(\{.*\})?[ \t]*$/;
|
|
9335
|
-
var TASK = /^\[([ xX])\][ \t]
|
|
9506
|
+
var TASK = /^\[([ xX])\](?:[ \t]+|$)/;
|
|
9336
9507
|
var TOC_FORMATS = /* @__PURE__ */ new Set([
|
|
9337
9508
|
"plain",
|
|
9338
9509
|
"bulleted",
|
|
@@ -9729,29 +9900,46 @@ var loomaMarkdown = createLoomaMarkdownIt();
|
|
|
9729
9900
|
function loomaMarkdownToHtml(source) {
|
|
9730
9901
|
return loomaMarkdown.render(source);
|
|
9731
9902
|
}
|
|
9903
|
+
var CONTAINERS = /* @__PURE__ */ new Set([
|
|
9904
|
+
"loomaCallout",
|
|
9905
|
+
"loomaExpand",
|
|
9906
|
+
"loomaFigure",
|
|
9907
|
+
"loomaTabs"
|
|
9908
|
+
]);
|
|
9732
9909
|
var containerDepth = (node) => {
|
|
9733
9910
|
let depth = 0;
|
|
9734
9911
|
node.descendants((child) => {
|
|
9735
|
-
if (
|
|
9912
|
+
if (!CONTAINERS.has(child.type.name)) return true;
|
|
9913
|
+
depth = Math.max(depth, 1 + containerDepth(child));
|
|
9736
9914
|
return false;
|
|
9737
9915
|
});
|
|
9738
9916
|
return depth;
|
|
9739
9917
|
};
|
|
9740
|
-
|
|
9918
|
+
var endLine = (state) => state.flushClose(1);
|
|
9919
|
+
function writeContainer(state, node, opening, content = () => state.renderContent(node)) {
|
|
9741
9920
|
const marker = loomaContainerMarker(containerDepth(node));
|
|
9742
9921
|
state.write(`${marker} ${opening}`.trimEnd());
|
|
9743
9922
|
state.ensureNewLine();
|
|
9744
|
-
|
|
9745
|
-
state
|
|
9923
|
+
content();
|
|
9924
|
+
endLine(state);
|
|
9746
9925
|
state.write(marker);
|
|
9747
9926
|
state.closeBlock(node);
|
|
9748
9927
|
}
|
|
9749
9928
|
var defaults = defaultMarkdownSerializer.nodes;
|
|
9929
|
+
var label = (text) => text.replace(/[\\\]]/g, "\\$&");
|
|
9930
|
+
var ALIGNMENT = {
|
|
9931
|
+
left: ":---",
|
|
9932
|
+
center: ":---:",
|
|
9933
|
+
right: "---:"
|
|
9934
|
+
};
|
|
9750
9935
|
var NODES = {
|
|
9751
9936
|
paragraph: defaults.paragraph,
|
|
9752
|
-
text:
|
|
9937
|
+
text: (state, node, parent, index) => {
|
|
9938
|
+
const marker = /^:{3,}[ \t]*([A-Za-z][\w-]*)?/.exec(node.text ?? "");
|
|
9939
|
+
if ((marker ? !marker[1] || LOOMA_CONTAINER_NAMES.has(marker[1].toLowerCase()) : /^::toc(?![\w-])/i.test(node.text ?? "")) && state.atBlank()) state.write("\\");
|
|
9940
|
+
defaults.text(state, node, parent, index);
|
|
9941
|
+
},
|
|
9753
9942
|
blockquote: defaults.blockquote,
|
|
9754
|
-
heading: defaults.heading,
|
|
9755
9943
|
horizontalRule: defaults.horizontal_rule,
|
|
9756
9944
|
bulletList: (state, node) => state.renderList(node, " ", () => "- "),
|
|
9757
9945
|
orderedList: (state, node) => {
|
|
@@ -9759,17 +9947,82 @@ var NODES = {
|
|
|
9759
9947
|
state.renderList(node, " ", (index) => `${start + index}. `);
|
|
9760
9948
|
},
|
|
9761
9949
|
listItem: defaults.list_item,
|
|
9950
|
+
taskList: (state, node) => state.renderList(node, " ", () => "- "),
|
|
9951
|
+
taskItem: (state, node) => {
|
|
9952
|
+
state.write(node.attrs.checked ? "[x] " : "[ ] ");
|
|
9953
|
+
state.renderContent(node);
|
|
9954
|
+
},
|
|
9762
9955
|
hardBreak: defaults.hard_break,
|
|
9763
9956
|
codeBlock: (state, node) => {
|
|
9764
9957
|
const fence = loomaCodeFence(node.textContent);
|
|
9765
|
-
state.write(`${fence}${node.attrs.language ?? ""}\n`);
|
|
9958
|
+
state.write(`${fence}${node.attrs.language ?? ""}${node.attrs.meta ? ` ${node.attrs.meta}` : ""}\n`);
|
|
9766
9959
|
state.text(node.textContent, false);
|
|
9767
9960
|
state.ensureNewLine();
|
|
9768
9961
|
state.write(fence);
|
|
9769
9962
|
state.closeBlock(node);
|
|
9770
9963
|
},
|
|
9964
|
+
image: (state, node, parent, index) => {
|
|
9965
|
+
defaults.image(state, node, parent, index);
|
|
9966
|
+
const { width, height, placement, responsive } = node.attrs;
|
|
9967
|
+
state.write(formatLoomaAttributes({
|
|
9968
|
+
width,
|
|
9969
|
+
height,
|
|
9970
|
+
placement: placement && placement !== "block" ? placement : null,
|
|
9971
|
+
responsive: responsive === true
|
|
9972
|
+
}));
|
|
9973
|
+
if (node.isBlock) state.closeBlock(node);
|
|
9974
|
+
},
|
|
9771
9975
|
loomaCallout: (state, node) => writeContainer(state, node, `${node.attrs.tone} ${node.attrs.heading ?? ""}`),
|
|
9772
|
-
loomaExpand: (state, node) => writeContainer(state, node, `details ${node.attrs.summary}${node.attrs.open ? " {open}" : ""}`)
|
|
9976
|
+
loomaExpand: (state, node) => writeContainer(state, node, `details ${node.attrs.summary}${node.attrs.open ? " {open}" : ""}`),
|
|
9977
|
+
loomaFigure: (state, node) => {
|
|
9978
|
+
const caption = node.lastChild;
|
|
9979
|
+
const marker = loomaContainerMarker(containerDepth(node));
|
|
9980
|
+
state.write(`${marker} figure`);
|
|
9981
|
+
if (caption.content.size) {
|
|
9982
|
+
state.write(" ");
|
|
9983
|
+
state.renderInline(caption, false);
|
|
9984
|
+
}
|
|
9985
|
+
state.ensureNewLine();
|
|
9986
|
+
state.render(node.firstChild, node, 0);
|
|
9987
|
+
endLine(state);
|
|
9988
|
+
state.write(marker);
|
|
9989
|
+
state.closeBlock(node);
|
|
9990
|
+
},
|
|
9991
|
+
loomaFigcaption: (state, node) => state.renderInline(node),
|
|
9992
|
+
loomaTabs: (state, node) => {
|
|
9993
|
+
const name = node.attrs.codeGroup ? "code-group" : "tabs";
|
|
9994
|
+
const bare = node.attrs.codeGroup && Array.from({ length: node.childCount }, (_, index) => node.child(index)).every((tab) => {
|
|
9995
|
+
const code = tab.firstChild;
|
|
9996
|
+
if (tab.childCount !== 1 || code?.type.name !== "codeBlock") return false;
|
|
9997
|
+
const { title, language } = parseLoomaFenceInfo(`${code.attrs.language ?? ""} ${code.attrs.meta ?? ""}`);
|
|
9998
|
+
return tab.attrs.label === (title ?? language ?? "Code");
|
|
9999
|
+
});
|
|
10000
|
+
writeContainer(state, node, `${name} ${node.attrs.group && node.attrs.group !== name ? node.attrs.group : ""}`, () => node.forEach((tab, _offset, index) => {
|
|
10001
|
+
if (index) endLine(state);
|
|
10002
|
+
if (!bare) {
|
|
10003
|
+
state.write(`== ${tab.attrs.label}`);
|
|
10004
|
+
state.ensureNewLine();
|
|
10005
|
+
}
|
|
10006
|
+
state.renderContent(tab);
|
|
10007
|
+
}));
|
|
10008
|
+
},
|
|
10009
|
+
loomaTab: (state, node) => state.renderContent(node),
|
|
10010
|
+
loomaTableOfContents: (state, node) => {
|
|
10011
|
+
const { format, depth } = node.attrs;
|
|
10012
|
+
state.write(`::toc${formatLoomaAttributes({
|
|
10013
|
+
depth: depth !== 3 ? depth : null,
|
|
10014
|
+
format: format !== "plain" ? format : null
|
|
10015
|
+
})}`);
|
|
10016
|
+
state.closeBlock(node);
|
|
10017
|
+
},
|
|
10018
|
+
loomaChip: (state, node) => {
|
|
10019
|
+
const tone = normalizeLoomaChipTone(node.attrs.color);
|
|
10020
|
+
state.write(`:chip[${label(String(node.attrs.label ?? ""))}]${formatLoomaAttributes({ tone: tone !== "neutral" ? tone : null })}`);
|
|
10021
|
+
},
|
|
10022
|
+
mention: (state, node) => {
|
|
10023
|
+
const id = String(node.attrs.id ?? "");
|
|
10024
|
+
state.write(`:mention[${label(String(node.attrs.label ?? id))}]${formatLoomaAttributes({ id }, { hashId: false })}`);
|
|
10025
|
+
}
|
|
9773
10026
|
};
|
|
9774
10027
|
var MARKS = {
|
|
9775
10028
|
bold: defaultMarkdownSerializer.marks.strong,
|
|
@@ -9793,31 +10046,96 @@ var MARKS = {
|
|
|
9793
10046
|
mixable: true
|
|
9794
10047
|
}
|
|
9795
10048
|
};
|
|
10049
|
+
function customHeadingIds(doc) {
|
|
10050
|
+
const headings = [];
|
|
10051
|
+
doc.descendants((node) => {
|
|
10052
|
+
if (node.type.name === "heading") headings.push({
|
|
10053
|
+
text: node.textContent.trim(),
|
|
10054
|
+
id: typeof node.attrs.id === "string" && node.attrs.id ? node.attrs.id : void 0
|
|
10055
|
+
});
|
|
10056
|
+
});
|
|
10057
|
+
const marked = /* @__PURE__ */ new Set();
|
|
10058
|
+
for (;;) {
|
|
10059
|
+
const derived = assignLoomaHeadingIds(headings.map(({ text, id }, index) => ({
|
|
10060
|
+
text,
|
|
10061
|
+
id: marked.has(index) ? id : void 0
|
|
10062
|
+
})));
|
|
10063
|
+
const next = headings.findIndex(({ id }, index) => id && !marked.has(index) && derived[index] !== id);
|
|
10064
|
+
if (next < 0) return marked;
|
|
10065
|
+
marked.add(next);
|
|
10066
|
+
}
|
|
10067
|
+
}
|
|
9796
10068
|
/**
|
|
9797
|
-
* Writes an editor document as Looma Markdown,
|
|
9798
|
-
*
|
|
9799
|
-
*
|
|
9800
|
-
*
|
|
9801
|
-
* Marks without a Markdown form other than underline and highlight are dropped.
|
|
10069
|
+
* Writes an editor document as Looma Markdown, the form `loomaMarkdownToHtml` and Markupress read.
|
|
10070
|
+
* @contract Every node in the default preset has a Markdown form. A table that fits GFM is a GFM
|
|
10071
|
+
* table and any other is HTML. Content from extensions outside the preset is written as its saved
|
|
10072
|
+
* HTML, and marks without a Markdown form other than underline and highlight are dropped.
|
|
9802
10073
|
*/
|
|
9803
10074
|
function loomaMarkdownFromDoc(doc) {
|
|
9804
|
-
const
|
|
10075
|
+
const schema = doc.type.schema;
|
|
10076
|
+
const dom = DOMSerializer.fromSchema(schema);
|
|
9805
10077
|
const html = (node) => {
|
|
9806
10078
|
const holder = document.createElement("div");
|
|
9807
10079
|
holder.append(dom.serializeNode(node));
|
|
9808
10080
|
return holder.innerHTML;
|
|
9809
10081
|
};
|
|
9810
|
-
const
|
|
9811
|
-
|
|
10082
|
+
const custom = customHeadingIds(doc);
|
|
10083
|
+
let heading = 0;
|
|
10084
|
+
const nodes = {
|
|
10085
|
+
heading: (state, node) => {
|
|
10086
|
+
state.write(`${"#".repeat(node.attrs.level)} `);
|
|
10087
|
+
state.renderInline(node, false);
|
|
10088
|
+
if (custom.has(heading++)) state.write(` {#${node.attrs.id}}`);
|
|
10089
|
+
else {
|
|
10090
|
+
const out = state;
|
|
10091
|
+
out.out = out.out.replace(/([ \t])\{([^{}]*\})$/, "$1\\{$2");
|
|
10092
|
+
}
|
|
10093
|
+
state.closeBlock(node);
|
|
10094
|
+
},
|
|
10095
|
+
table: (state, node) => {
|
|
10096
|
+
state.write(gfmTable(node) ?? `<table><tbody>${Array.from({ length: node.childCount }, (_, index) => html(node.child(index))).join("")}</tbody></table>`.replace(/ (?:colspan|rowspan)="1"/g, ""));
|
|
10097
|
+
state.closeBlock(node);
|
|
10098
|
+
}
|
|
10099
|
+
};
|
|
10100
|
+
for (const name of Object.keys(schema.nodes)) nodes[name] ??= NODES[name] ?? ((state, node) => {
|
|
9812
10101
|
state.write(html(node));
|
|
9813
10102
|
if (node.isBlock) state.closeBlock(node);
|
|
9814
10103
|
});
|
|
9815
10104
|
const marks = {};
|
|
9816
|
-
for (const name of Object.keys(
|
|
10105
|
+
for (const name of Object.keys(schema.marks)) marks[name] = MARKS[name] ?? {
|
|
9817
10106
|
open: "",
|
|
9818
10107
|
close: ""
|
|
9819
10108
|
};
|
|
9820
|
-
|
|
10109
|
+
const serializer = new MarkdownSerializer(nodes, marks);
|
|
10110
|
+
const inline = (paragraph) => serializer.serialize(schema.topNodeType.create(null, paragraph)).replace(/\|/g, "\\|");
|
|
10111
|
+
function gfmTable(table) {
|
|
10112
|
+
const rows = Array.from({ length: table.childCount }, (_, index) => table.child(index));
|
|
10113
|
+
const width = rows[0]?.childCount ?? 0;
|
|
10114
|
+
const alignments = [];
|
|
10115
|
+
const lines = [];
|
|
10116
|
+
for (const [rowIndex, row] of rows.entries()) {
|
|
10117
|
+
if (row.childCount !== width) return null;
|
|
10118
|
+
const cells = [];
|
|
10119
|
+
for (let column = 0; column < width; column++) {
|
|
10120
|
+
const cell = row.child(column);
|
|
10121
|
+
const { colspan, rowspan, colwidth, backgroundColor, textAlign } = cell.attrs;
|
|
10122
|
+
const paragraph = cell.firstChild;
|
|
10123
|
+
if (cell.type.name === "tableHeader" !== (rowIndex === 0) || colspan !== 1 || rowspan !== 1 || colwidth || backgroundColor || cell.childCount !== 1 || paragraph?.type.name !== "paragraph") return null;
|
|
10124
|
+
let hasBreak = false;
|
|
10125
|
+
paragraph.descendants((child) => {
|
|
10126
|
+
if (child.type.name === "hardBreak") hasBreak = true;
|
|
10127
|
+
});
|
|
10128
|
+
if (hasBreak) return null;
|
|
10129
|
+
if (rowIndex === 0) alignments.push(textAlign ?? null);
|
|
10130
|
+
else if ((textAlign ?? null) !== alignments[column]) return null;
|
|
10131
|
+
cells.push(inline(paragraph));
|
|
10132
|
+
}
|
|
10133
|
+
lines.push(`| ${cells.join(" | ")} |`);
|
|
10134
|
+
if (rowIndex === 0) lines.push(`| ${alignments.map((alignment) => ALIGNMENT[alignment] ?? "---").join(" | ")} |`);
|
|
10135
|
+
}
|
|
10136
|
+
return width ? lines.join("\n") : null;
|
|
10137
|
+
}
|
|
10138
|
+
return serializer.serialize(doc, { tightLists: true });
|
|
9821
10139
|
}
|
|
9822
10140
|
//#endregion
|
|
9823
10141
|
//#region src/editor/extensions/smart-paste.ts
|
|
@@ -9893,8 +10211,12 @@ function parseHtmlSlice(schema, value, fromHtml = false) {
|
|
|
9893
10211
|
"template"
|
|
9894
10212
|
],
|
|
9895
10213
|
FORBID_ATTR: ["style"],
|
|
9896
|
-
ADD_TAGS: ["ui-callout"],
|
|
9897
|
-
ADD_ATTR: [
|
|
10214
|
+
ADD_TAGS: ["ui-callout", "ui-tabs"],
|
|
10215
|
+
ADD_ATTR: [
|
|
10216
|
+
"tone",
|
|
10217
|
+
"heading",
|
|
10218
|
+
"group"
|
|
10219
|
+
],
|
|
9898
10220
|
RETURN_DOM_FRAGMENT: true
|
|
9899
10221
|
}));
|
|
9900
10222
|
if (!container.textContent?.trim() && !container.querySelector("img, hr")) return Slice.empty;
|
|
@@ -10438,6 +10760,16 @@ function setActiveTableCellBackground(editor, backgroundColor) {
|
|
|
10438
10760
|
* Uses the Vanilla JS Tiptap API; apps provide @tiptap/core and Looma ships the preset extensions.
|
|
10439
10761
|
*/
|
|
10440
10762
|
var LoomaCodeBlock = CodeBlockLowlight.extend({
|
|
10763
|
+
addAttributes() {
|
|
10764
|
+
return {
|
|
10765
|
+
...this.parent?.(),
|
|
10766
|
+
meta: {
|
|
10767
|
+
default: null,
|
|
10768
|
+
parseHTML: (element) => element.querySelector("code")?.getAttribute("data-meta") || element.getAttribute("data-meta") || null,
|
|
10769
|
+
renderHTML: () => ({})
|
|
10770
|
+
}
|
|
10771
|
+
};
|
|
10772
|
+
},
|
|
10441
10773
|
renderHTML({ node, HTMLAttributes }) {
|
|
10442
10774
|
return [
|
|
10443
10775
|
"pre",
|
|
@@ -10446,6 +10778,7 @@ var LoomaCodeBlock = CodeBlockLowlight.extend({
|
|
|
10446
10778
|
"code",
|
|
10447
10779
|
{
|
|
10448
10780
|
class: node.attrs.language ? `${this.options.languageClassPrefix}${node.attrs.language}` : null,
|
|
10781
|
+
"data-meta": node.attrs.meta || null,
|
|
10449
10782
|
spellcheck: "false"
|
|
10450
10783
|
},
|
|
10451
10784
|
0
|
|
@@ -10521,7 +10854,10 @@ function getDefaultEditorExtensions(options = {}) {
|
|
|
10521
10854
|
Heading.configure({ levels: [
|
|
10522
10855
|
1,
|
|
10523
10856
|
2,
|
|
10524
|
-
3
|
|
10857
|
+
3,
|
|
10858
|
+
4,
|
|
10859
|
+
5,
|
|
10860
|
+
6
|
|
10525
10861
|
] }),
|
|
10526
10862
|
BulletList,
|
|
10527
10863
|
OrderedList,
|
|
@@ -10531,6 +10867,9 @@ function getDefaultEditorExtensions(options = {}) {
|
|
|
10531
10867
|
Blockquote,
|
|
10532
10868
|
calloutNodeView ? LoomaCallout.extend({ addNodeView: () => calloutNodeView }) : LoomaCallout,
|
|
10533
10869
|
LoomaChip,
|
|
10870
|
+
LoomaTabs,
|
|
10871
|
+
LoomaTab,
|
|
10872
|
+
...imageInline ? [] : [LoomaFigure, LoomaFigcaption],
|
|
10534
10873
|
LoomaExpand.configure({ nodeView: expandNodeView ?? null }),
|
|
10535
10874
|
LoomaTableOfContents.configure({ nodeView: tableOfContentsNodeView ?? null }),
|
|
10536
10875
|
dividerNodeView ? HorizontalRule.extend({ addNodeView: () => dividerNodeView }) : HorizontalRule,
|
|
@@ -11522,4 +11861,4 @@ function createLoomaSlashCommandExtension(options = {}) {
|
|
|
11522
11861
|
});
|
|
11523
11862
|
}
|
|
11524
11863
|
//#endregion
|
|
11525
|
-
export { filterLoomaMentionItems as A,
|
|
11864
|
+
export { LoomaActiveBlockPluginKey as $, filterLoomaMentionItems as A, LoomaFigcaption as B, loomaMarkdownToHtml as C, createLoomaMentionExtension as D, LoomaMentionSuggestionPluginKey as E, LoomaTableOfContents as F, LoomaChip as G, LoomaExpand as H, getTableOfContentsEntries as I, LOOMA_CALLOUT_TONES as J, normalizeLoomaChipColor as K, normalizeTableOfContentsConfig as L, LoomaImage as M, normalizeImageDimension as N, DEFAULT_MENTION_RESULT_LIMIT as O, normalizeImagePlacement as P, LoomaActiveBlock as Q, LoomaTab as R, loomaMarkdownFromDoc as S, siteRelativeHref as T, revealLoomaExpandAt as U, LoomaFigure as V, LOOMA_CHIP_COLORS as W, LOOMA_ACTIVE_BLOCK_CLASS as X, LOOMA_ACTIVE_BLOCK_BLUR_GRACE_MS as Y, LOOMA_ACTIVE_BLOCK_TYPING_IDLE_MS as Z, 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, LoomaCallout 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, LoomaTabs as z };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Node } from "@tiptap/core";
|
|
2
|
+
/**
|
|
3
|
+
* An image with a caption, saved as `<figure><img><figcaption>` and written in Looma Markdown as
|
|
4
|
+
* `::: figure Caption`. The caption is inline content the author edits in place.
|
|
5
|
+
* @invariant A figure holds exactly one block image and one caption; an empty caption stays empty.
|
|
6
|
+
*/
|
|
7
|
+
export declare const LoomaFigure: Node<any, any>;
|
|
8
|
+
/**
|
|
9
|
+
* A figure's caption: inline content under its image.
|
|
10
|
+
* @invariant It exists only as the second child of `LoomaFigure`.
|
|
11
|
+
*/
|
|
12
|
+
export declare const LoomaFigcaption: Node<any, any>;
|
|
@@ -10,6 +10,8 @@ export { LOOMA_ACTIVE_BLOCK_BLUR_GRACE_MS, LOOMA_ACTIVE_BLOCK_CLASS, LOOMA_ACTIV
|
|
|
10
10
|
export { LoomaCallout, LOOMA_CALLOUT_TONES, type LoomaCalloutTone, } from "./callout.js";
|
|
11
11
|
export { LoomaChip, LOOMA_CHIP_COLORS, normalizeLoomaChipColor, type LoomaChipColor } from "./chip.js";
|
|
12
12
|
export { LoomaExpand, revealLoomaExpandAt, type LoomaExpandAttributes } from "./expand.js";
|
|
13
|
+
export { LoomaFigcaption, LoomaFigure } from "./figure.js";
|
|
14
|
+
export { LoomaTab, LoomaTabs } from "./tabs.js";
|
|
13
15
|
export { LoomaTableOfContents, getTableOfContentsEntries, normalizeTableOfContentsConfig, type LoomaTableOfContentsConfig, type LoomaTableOfContentsEntry, type LoomaTableOfContentsFormat, } from "./table-of-contents.js";
|
|
14
16
|
export { getDefaultEditorExtensions, getLoomaTableExtensions, LoomaTableKit, type DefaultEditorExtensionsOptions, type LoomaCodeLanguages, } from "./preset.js";
|
|
15
17
|
export { LoomaSmartPaste } from "./smart-paste.js";
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { n as TABLE_CELL_BACKGROUND_PRESETS } from "../chunks/table-backgrounds.js";
|
|
2
|
-
import { A as filterLoomaMentionItems, B as
|
|
3
|
-
export { DEFAULT_MENTION_RESULT_LIMIT, LOOMA_ACTIVE_BLOCK_BLUR_GRACE_MS, LOOMA_ACTIVE_BLOCK_CLASS, LOOMA_ACTIVE_BLOCK_TYPING_IDLE_MS, LOOMA_CALLOUT_TONES, LOOMA_CHIP_COLORS, LoomaActiveBlock, LoomaActiveBlockPluginKey, LoomaCallout, LoomaChip, LoomaExpand, LoomaImage, LoomaMentionSuggestionPluginKey, LoomaSlashCommand, LoomaSmartPaste, LoomaTable, LoomaTableCell, LoomaTableHeader, LoomaTableKit, LoomaTableOfContents, MAX_MENTION_RESULT_LIMIT, TABLE_CELL_BACKGROUND_PRESETS, createLoomaMentionExtension, createLoomaSlashCommandExtension, createSiteRelativeLinks, filterLoomaMentionItems, filterLoomaSlashCommands, getActiveTableCellAlignment, getActiveTableCellBackground, getActiveTableUiState, getDefaultEditorExtensions, getDefaultSlashCommands, getLoomaTableExtensions, getTableOfContentsEntries, handleTableAction, handleTableOverlayAction, insertTableAtRange, loomaMarkdownFromDoc, loomaMarkdownToHtml, normalizeActiveTableColumnWidths, normalizeImageDimension, normalizeImagePlacement, normalizeLoomaChipColor, normalizeMentionResultLimit, normalizeTableOfContentsConfig, revealLoomaExpandAt, setActiveTableCellAlignment, setActiveTableCellBackground, shouldShowTextFormattingToolbar, siteRelativeHref };
|
|
2
|
+
import { $ as LoomaActiveBlockPluginKey, A as filterLoomaMentionItems, B as LoomaFigcaption, C as loomaMarkdownToHtml, D as createLoomaMentionExtension, E as LoomaMentionSuggestionPluginKey, F as LoomaTableOfContents, G as LoomaChip, H as LoomaExpand, I as getTableOfContentsEntries, J as LOOMA_CALLOUT_TONES, K as normalizeLoomaChipColor, L as normalizeTableOfContentsConfig, M as LoomaImage, N as normalizeImageDimension, O as DEFAULT_MENTION_RESULT_LIMIT, P as normalizeImagePlacement, Q as LoomaActiveBlock, R as LoomaTab, S as loomaMarkdownFromDoc, T as siteRelativeHref, U as revealLoomaExpandAt, V as LoomaFigure, W as LOOMA_CHIP_COLORS, X as LOOMA_ACTIVE_BLOCK_CLASS, Y as LOOMA_ACTIVE_BLOCK_BLUR_GRACE_MS, Z as LOOMA_ACTIVE_BLOCK_TYPING_IDLE_MS, _ as getActiveTableCellAlignment, a as getActiveTableUiState, b as setActiveTableCellBackground, c as insertTableAtRange, d as LoomaTableKit, f as getDefaultEditorExtensions, g as LoomaTableHeader, h as LoomaTableCell, i as getDefaultSlashCommands, j as normalizeMentionResultLimit, k as MAX_MENTION_RESULT_LIMIT, l as normalizeActiveTableColumnWidths, m as LoomaTable, n as createLoomaSlashCommandExtension, o as handleTableAction, p as getLoomaTableExtensions, q as LoomaCallout, r as filterLoomaSlashCommands, s as handleTableOverlayAction, t as LoomaSlashCommand, u as shouldShowTextFormattingToolbar, v as getActiveTableCellBackground, w as createSiteRelativeLinks, x as LoomaSmartPaste, y as setActiveTableCellAlignment, z as LoomaTabs } from "../chunks/extensions.js";
|
|
3
|
+
export { DEFAULT_MENTION_RESULT_LIMIT, LOOMA_ACTIVE_BLOCK_BLUR_GRACE_MS, LOOMA_ACTIVE_BLOCK_CLASS, LOOMA_ACTIVE_BLOCK_TYPING_IDLE_MS, LOOMA_CALLOUT_TONES, LOOMA_CHIP_COLORS, LoomaActiveBlock, LoomaActiveBlockPluginKey, LoomaCallout, LoomaChip, LoomaExpand, LoomaFigcaption, LoomaFigure, LoomaImage, LoomaMentionSuggestionPluginKey, LoomaSlashCommand, LoomaSmartPaste, LoomaTab, LoomaTable, LoomaTableCell, LoomaTableHeader, LoomaTableKit, LoomaTableOfContents, LoomaTabs, MAX_MENTION_RESULT_LIMIT, TABLE_CELL_BACKGROUND_PRESETS, createLoomaMentionExtension, createLoomaSlashCommandExtension, createSiteRelativeLinks, filterLoomaMentionItems, filterLoomaSlashCommands, getActiveTableCellAlignment, getActiveTableCellBackground, getActiveTableUiState, getDefaultEditorExtensions, getDefaultSlashCommands, getLoomaTableExtensions, getTableOfContentsEntries, handleTableAction, handleTableOverlayAction, insertTableAtRange, loomaMarkdownFromDoc, loomaMarkdownToHtml, normalizeActiveTableColumnWidths, normalizeImageDimension, normalizeImagePlacement, normalizeLoomaChipColor, normalizeMentionResultLimit, normalizeTableOfContentsConfig, revealLoomaExpandAt, setActiveTableCellAlignment, setActiveTableCellBackground, shouldShowTextFormattingToolbar, siteRelativeHref };
|
|
@@ -8,10 +8,9 @@ export declare const loomaMarkdown: import("markdown-it").default;
|
|
|
8
8
|
*/
|
|
9
9
|
export declare function loomaMarkdownToHtml(source: string): string;
|
|
10
10
|
/**
|
|
11
|
-
* Writes an editor document as Looma Markdown,
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* Marks without a Markdown form other than underline and highlight are dropped.
|
|
11
|
+
* Writes an editor document as Looma Markdown, the form `loomaMarkdownToHtml` and Markupress read.
|
|
12
|
+
* @contract Every node in the default preset has a Markdown form. A table that fits GFM is a GFM
|
|
13
|
+
* table and any other is HTML. Content from extensions outside the preset is written as its saved
|
|
14
|
+
* HTML, and marks without a Markdown form other than underline and highlight are dropped.
|
|
16
15
|
*/
|
|
17
16
|
export declare function loomaMarkdownFromDoc(doc: ProseMirrorNode): string;
|
|
@@ -10,7 +10,7 @@ export interface LoomaTableOfContentsConfig {
|
|
|
10
10
|
/**
|
|
11
11
|
* One current heading for navigation or host projections.
|
|
12
12
|
* @lifecycle Positions belong to the supplied document; recompute after edits.
|
|
13
|
-
*
|
|
13
|
+
* An ID derived from a heading's text follows the text; a custom ID stays with its heading.
|
|
14
14
|
*/
|
|
15
15
|
export interface LoomaTableOfContentsEntry {
|
|
16
16
|
id: string;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Node } from "@tiptap/core";
|
|
2
|
+
/**
|
|
3
|
+
* Tabs or a code group, kept whole so Looma Markdown's `::: tabs` and `::: code-group` survive
|
|
4
|
+
* editing. Saved as `<ui-tabs group>` with a `<section aria-label>` per panel.
|
|
5
|
+
* @contract While editing, panels show one after another under their labels; the Tabs component
|
|
6
|
+
* draws them only where the saved HTML renders.
|
|
7
|
+
*/
|
|
8
|
+
export declare const LoomaTabs: Node<any, any>;
|
|
9
|
+
/**
|
|
10
|
+
* One panel of `LoomaTabs`, labelled by `label`.
|
|
11
|
+
* @invariant It exists only inside `LoomaTabs`; its label is the tab Tabs shows and the `== Label` line Markdown writes.
|
|
12
|
+
*/
|
|
13
|
+
export declare const LoomaTab: Node<any, any>;
|
package/editor/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { n as TABLE_CELL_BACKGROUND_PRESETS, t as TABLE_CELL_BACKGROUND_OPTIONS } from "./chunks/table-backgrounds.js";
|
|
2
2
|
import { n as resolveTableCellAt, t as measureTableOverlayGeometry } from "./chunks/ui.js";
|
|
3
|
-
import { A as filterLoomaMentionItems, B as
|
|
3
|
+
import { $ as LoomaActiveBlockPluginKey, A as filterLoomaMentionItems, B as LoomaFigcaption, C as loomaMarkdownToHtml, D as createLoomaMentionExtension, E as LoomaMentionSuggestionPluginKey, F as LoomaTableOfContents, G as LoomaChip, H as LoomaExpand, I as getTableOfContentsEntries, J as LOOMA_CALLOUT_TONES, K as normalizeLoomaChipColor, L as normalizeTableOfContentsConfig, M as LoomaImage, N as normalizeImageDimension, O as DEFAULT_MENTION_RESULT_LIMIT, P as normalizeImagePlacement, Q as LoomaActiveBlock, R as LoomaTab, S as loomaMarkdownFromDoc, T as siteRelativeHref, U as revealLoomaExpandAt, V as LoomaFigure, W as LOOMA_CHIP_COLORS, X as LOOMA_ACTIVE_BLOCK_CLASS, Y as LOOMA_ACTIVE_BLOCK_BLUR_GRACE_MS, Z as LOOMA_ACTIVE_BLOCK_TYPING_IDLE_MS, _ as getActiveTableCellAlignment, a as getActiveTableUiState, b as setActiveTableCellBackground, c as insertTableAtRange, d as LoomaTableKit, f as getDefaultEditorExtensions, g as LoomaTableHeader, h as LoomaTableCell, i as getDefaultSlashCommands, j as normalizeMentionResultLimit, k as MAX_MENTION_RESULT_LIMIT, l as normalizeActiveTableColumnWidths, m as LoomaTable, n as createLoomaSlashCommandExtension, o as handleTableAction, p as getLoomaTableExtensions, q as LoomaCallout, r as filterLoomaSlashCommands, s as handleTableOverlayAction, t as LoomaSlashCommand, u as shouldShowTextFormattingToolbar, v as getActiveTableCellBackground, w as createSiteRelativeLinks, x as LoomaSmartPaste, y as setActiveTableCellAlignment, z as LoomaTabs } from "./chunks/extensions.js";
|
|
4
4
|
import { AlignCenter, AlignLeft, AlignRight, ArrowLeft, Bell, Bold, BookUser, Braces, Calculator, Check, ChevronDown, ChevronLeft, ChevronRight, CircleCheck, CircleDot, CircleQuestionMark, CircleX, CodeXml, Columns3, Compass, CreditCard, Ellipsis, Eraser, ExternalLink, FileText, Folder, GripHorizontal, GripVertical, Heading1, Heading2, Heading3, Highlighter, History, Image, Info, Italic, LayoutDashboard, Lightbulb, Link, List, ListOrdered, ListTodo, LoaderCircle, Merge, MessageSquareWarning, Minus, NotebookPen, PaintBucket, PanelBottom, PanelLeft, PanelRight, PanelTop, Pencil, Pilcrow, Plus, Quote, Receipt, Redo2, Rows3, Search, Settings, Split, Strikethrough, Table2, Tag, Trash2, TriangleAlert, Truck, Underline, Undo2, Unlink, UserRound, Users, X } from "lucide";
|
|
5
5
|
//#region src/editor/icons.ts
|
|
6
6
|
/** The opinionated Lucide set used by Looma's shipped interaction surfaces. */
|
|
@@ -123,4 +123,4 @@ function getVisualViewportRect(owner = window) {
|
|
|
123
123
|
};
|
|
124
124
|
}
|
|
125
125
|
//#endregion
|
|
126
|
-
export { DEFAULT_MENTION_RESULT_LIMIT, LOOMA_ACTIVE_BLOCK_BLUR_GRACE_MS, LOOMA_ACTIVE_BLOCK_CLASS, LOOMA_ACTIVE_BLOCK_TYPING_IDLE_MS, LOOMA_CALLOUT_TONES, LOOMA_CHIP_COLORS, LOOMA_ICONS, LoomaActiveBlock, LoomaActiveBlockPluginKey, LoomaCallout, LoomaChip, LoomaExpand, LoomaImage, LoomaMentionSuggestionPluginKey, LoomaSlashCommand, LoomaSmartPaste, LoomaTable, LoomaTableCell, LoomaTableHeader, LoomaTableKit, LoomaTableOfContents, MAX_MENTION_RESULT_LIMIT, TABLE_CELL_BACKGROUND_OPTIONS, TABLE_CELL_BACKGROUND_PRESETS, createLoomaMentionExtension, createLoomaSlashCommandExtension, createSiteRelativeLinks, filterLoomaMentionItems, filterLoomaSlashCommands, getActiveTableCellAlignment, getActiveTableCellBackground, getActiveTableUiState, getDefaultEditorExtensions, getDefaultSlashCommands, getLoomaTableExtensions, getTableOfContentsEntries, getVisualViewportRect, handleTableAction, handleTableOverlayAction, insertTableAtRange, loomaIconMarkup, loomaMarkdownFromDoc, loomaMarkdownToHtml, measureTableOverlayGeometry, normalizeActiveTableColumnWidths, normalizeImageDimension, normalizeImagePlacement, normalizeLoomaChipColor, normalizeMentionResultLimit, normalizeTableOfContentsConfig, resolveTableCellAt, revealLoomaExpandAt, setActiveTableCellAlignment, setActiveTableCellBackground, shouldShowTextFormattingToolbar, siteRelativeHref };
|
|
126
|
+
export { DEFAULT_MENTION_RESULT_LIMIT, LOOMA_ACTIVE_BLOCK_BLUR_GRACE_MS, LOOMA_ACTIVE_BLOCK_CLASS, LOOMA_ACTIVE_BLOCK_TYPING_IDLE_MS, LOOMA_CALLOUT_TONES, LOOMA_CHIP_COLORS, LOOMA_ICONS, LoomaActiveBlock, LoomaActiveBlockPluginKey, LoomaCallout, LoomaChip, LoomaExpand, LoomaFigcaption, LoomaFigure, LoomaImage, LoomaMentionSuggestionPluginKey, LoomaSlashCommand, LoomaSmartPaste, LoomaTab, LoomaTable, LoomaTableCell, LoomaTableHeader, LoomaTableKit, LoomaTableOfContents, LoomaTabs, MAX_MENTION_RESULT_LIMIT, TABLE_CELL_BACKGROUND_OPTIONS, TABLE_CELL_BACKGROUND_PRESETS, createLoomaMentionExtension, createLoomaSlashCommandExtension, createSiteRelativeLinks, filterLoomaMentionItems, filterLoomaSlashCommands, getActiveTableCellAlignment, getActiveTableCellBackground, getActiveTableUiState, getDefaultEditorExtensions, getDefaultSlashCommands, getLoomaTableExtensions, getTableOfContentsEntries, getVisualViewportRect, handleTableAction, handleTableOverlayAction, insertTableAtRange, loomaIconMarkup, loomaMarkdownFromDoc, loomaMarkdownToHtml, measureTableOverlayGeometry, normalizeActiveTableColumnWidths, normalizeImageDimension, normalizeImagePlacement, normalizeLoomaChipColor, normalizeMentionResultLimit, normalizeTableOfContentsConfig, resolveTableCellAt, revealLoomaExpandAt, setActiveTableCellAlignment, setActiveTableCellBackground, shouldShowTextFormattingToolbar, siteRelativeHref };
|
package/markdown/fixtures.js
CHANGED
|
@@ -14,6 +14,8 @@ var files = /* #__PURE__ */ Object.assign({
|
|
|
14
14
|
"../../markdown-fixtures/callouts/open-in-title.md": "::: tip Keep {open}\nA callout title keeps its braces.\n:::\n",
|
|
15
15
|
"../../markdown-fixtures/callouts/titled.html": "<ui-callout tone=\"tip\" heading=\"Before you start\">\n<p>Install <strong>first</strong>.</p>\n</ui-callout>\n",
|
|
16
16
|
"../../markdown-fixtures/callouts/titled.md": "::: tip Before you start\nInstall **first**.\n:::\n",
|
|
17
|
+
"../../markdown-fixtures/chips/escaped-label.html": "<p><span data-looma-chip data-label=\"Draft ] v2\" data-color=\"red\">Draft ] v2</span></p>\n",
|
|
18
|
+
"../../markdown-fixtures/chips/escaped-label.md": ":chip[Draft \\] v2]{tone=red}\n",
|
|
17
19
|
"../../markdown-fixtures/chips/tones.html": "<p>Status <span data-looma-chip data-label=\"Ready\" data-color=\"green\">Ready</span> and <span data-looma-chip data-label=\"Draft\" data-color=\"neutral\">Draft</span>.</p>\n",
|
|
18
20
|
"../../markdown-fixtures/chips/tones.md": "Status :chip[Ready]{tone=green} and :chip[Draft].\n",
|
|
19
21
|
"../../markdown-fixtures/code-blocks/fences.canonical.md": "```ts\nconst answer = 42;\n```\n\n```\nplain text\n```\n\n```\nindented\n```\n",
|
|
@@ -25,6 +27,8 @@ var files = /* #__PURE__ */ Object.assign({
|
|
|
25
27
|
"../../markdown-fixtures/code-meta/notation.md": "```ts\nconst port = 8080; // [!code highlight]\n```\n",
|
|
26
28
|
"../../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",
|
|
27
29
|
"../../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",
|
|
30
|
+
"../../markdown-fixtures/containers/escaped-marker.html": "<p>::: tip is text here</p>\n",
|
|
31
|
+
"../../markdown-fixtures/containers/escaped-marker.md": "\\::: tip is text here\n",
|
|
28
32
|
"../../markdown-fixtures/containers/fence-inside.html": "<ui-callout tone=\"tip\">\n<pre><code class=\"language-md\">:::</code></pre>\n</ui-callout>\n",
|
|
29
33
|
"../../markdown-fixtures/containers/fence-inside.md": "::: tip\n```md\n:::\n```\n:::\n",
|
|
30
34
|
"../../markdown-fixtures/containers/nesting.canonical.md": ":::: details More\n::: warning\nNested.\n:::\n::::\n",
|
|
@@ -43,6 +47,7 @@ var files = /* #__PURE__ */ Object.assign({
|
|
|
43
47
|
"../../markdown-fixtures/details/open.md": "::: details Show the full log {open}\nThe build finished in 42 seconds.\n:::\n",
|
|
44
48
|
"../../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",
|
|
45
49
|
"../../markdown-fixtures/figures/caption.md": "::: figure The build, from *source* to release\n\n:::\n",
|
|
50
|
+
"../../markdown-fixtures/front-matter/metadata.canonical.md": "# Notes\n",
|
|
46
51
|
"../../markdown-fixtures/front-matter/metadata.html": "<h1 id=\"notes\">Notes</h1>\n",
|
|
47
52
|
"../../markdown-fixtures/front-matter/metadata.md": "---\ntitle: Release notes\n---\n\n# Notes\n",
|
|
48
53
|
"../../markdown-fixtures/heading-ids/accented.html": "<h2 id=\"über-uns\">Über uns</h2>\n<h2 id=\"café-crème\">Café crème</h2>\n",
|
|
@@ -57,6 +62,8 @@ var files = /* #__PURE__ */ Object.assign({
|
|
|
57
62
|
"../../markdown-fixtures/heading-ids/emoji-only.md": "## 🚀\n\n## 🎉 🎉\n",
|
|
58
63
|
"../../markdown-fixtures/heading-ids/generated.html": "<h2 id=\"getting-started\">Getting started</h2>\n<h2 id=\"getting-started-1\">Getting started</h2>\n<h2 id=\"über-code-more\">Über <code>code</code> & more</h2>\n",
|
|
59
64
|
"../../markdown-fixtures/heading-ids/generated.md": "## Getting started\n\n## Getting started\n\n## Über `code` & more\n",
|
|
65
|
+
"../../markdown-fixtures/heading-ids/literal-braces.html": "<h2 id=\"price-usd\">Price {#usd}</h2>\n",
|
|
66
|
+
"../../markdown-fixtures/heading-ids/literal-braces.md": "## Price \\{#usd}\n",
|
|
60
67
|
"../../markdown-fixtures/headings/levels.html": "<h1 id=\"one\">One</h1>\n<h2 id=\"two\">Two</h2>\n<h3 id=\"three\">Three</h3>\n<h4 id=\"four\">Four</h4>\n<h5 id=\"five\">Five</h5>\n<h6 id=\"six\">Six</h6>\n",
|
|
61
68
|
"../../markdown-fixtures/headings/levels.md": "# One\n\n## Two\n\n### Three\n\n#### Four\n\n##### Five\n\n###### Six\n",
|
|
62
69
|
"../../markdown-fixtures/image-attributes/placed.html": "<img src=\"./pipeline.svg\" alt=\"Pipeline\" width=\"640\" height=\"360\" data-placement=\"wrap-left\" data-looma-responsive>\n",
|
|
@@ -69,6 +76,9 @@ var files = /* #__PURE__ */ Object.assign({
|
|
|
69
76
|
"../../markdown-fixtures/mentions/person.md": "Thanks, :mention[Ada Lovelace]{id=u1}.\n",
|
|
70
77
|
"../../markdown-fixtures/tables/gfm.html": "<table>\n<thead>\n<tr>\n<th>Package</th>\n<th style=\"text-align:right\">Version</th>\n<th style=\"text-align:center\">Notes</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>looma</td>\n<td style=\"text-align:right\">0.28</td>\n<td style=\"text-align:center\">Components</td>\n</tr>\n</tbody>\n</table>\n",
|
|
71
78
|
"../../markdown-fixtures/tables/gfm.md": "| Package | Version | Notes |\n| --- | ---: | :---: |\n| looma | 0.28 | Components |\n",
|
|
79
|
+
"../../markdown-fixtures/tables/left-aligned.html": "<table>\n<thead>\n<tr>\n<th style=\"text-align:left\">Name</th>\n<th>Notes</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td style=\"text-align:left\">a | b</td>\n<td><code>x | y</code></td>\n</tr>\n</tbody>\n</table>\n",
|
|
80
|
+
"../../markdown-fixtures/tables/left-aligned.md": "| Name | Notes |\n| :--- | --- |\n| a \\| b | `x \\| y` |\n",
|
|
81
|
+
"../../markdown-fixtures/tables/rich.canonical.md": "<table><tbody><tr><th><p>Status</p></th><th><p>Owner</p></th></tr><tr><td style=\"background-color: rgb(220, 252, 231);\"><p>Done</p><ul><li><p>Shipped</p></li></ul></td><td style=\"text-align: center;\"><p>Ada</p></td></tr></tbody></table>\n",
|
|
72
82
|
"../../markdown-fixtures/tables/rich.html": "<table><tbody><tr><th><p>Status</p></th><th><p>Owner</p></th></tr><tr><td style=\"background-color: #dcfce7\"><p>Done</p><ul><li><p>Shipped</p></li></ul></td><td colspan=\"1\" rowspan=\"1\" style=\"text-align: center\"><p>Ada</p></td></tr></tbody></table>\n",
|
|
73
83
|
"../../markdown-fixtures/tables/rich.md": "<table><tbody><tr><th><p>Status</p></th><th><p>Owner</p></th></tr><tr><td style=\"background-color: #dcfce7\"><p>Done</p><ul><li><p>Shipped</p></li></ul></td><td colspan=\"1\" rowspan=\"1\" style=\"text-align: center\"><p>Ada</p></td></tr></tbody></table>\n",
|
|
74
84
|
"../../markdown-fixtures/tabs/code-group.html": "<ui-tabs class=\"code-group\" group=\"code-group\">\n<section aria-label=\"looma.config.js\">\n<pre><code class=\"language-js\" data-meta=\"title="looma.config.js"\">export default { theme: "light" };</code></pre>\n</section>\n<section aria-label=\"ts\">\n<pre><code class=\"language-ts\">export default { theme: "light" } satisfies Config;</code></pre>\n</section>\n</ui-tabs>\n",
|
package/markdown/index.d.ts
CHANGED
|
@@ -56,9 +56,12 @@ export declare function parseLoomaAttributes(source: string): LoomaAttributes |
|
|
|
56
56
|
/**
|
|
57
57
|
* Writes attributes as a brace list, `id` first as `#id`, then the rest in order.
|
|
58
58
|
* @contract `true` writes a bare key; values are quoted only when they need it. `false`, null and
|
|
59
|
-
* undefined values are left out, and an empty set writes an empty string.
|
|
59
|
+
* undefined values are left out, and an empty set writes an empty string. An ID that `#` cannot
|
|
60
|
+
* carry, or any ID when `hashId` is false, is written `id=…`.
|
|
60
61
|
*/
|
|
61
|
-
export declare function formatLoomaAttributes(attributes: Readonly<Record<string, string | number | boolean | null | undefined
|
|
62
|
+
export declare function formatLoomaAttributes(attributes: Readonly<Record<string, string | number | boolean | null | undefined>>, { hashId }?: {
|
|
63
|
+
hashId?: boolean | undefined;
|
|
64
|
+
}): string;
|
|
62
65
|
/**
|
|
63
66
|
* The anchor a heading's text derives: NFC, lowercase, keeping letters, numbers and combining
|
|
64
67
|
* marks, with every other run of characters one hyphen.
|
package/markdown/index.js
CHANGED
|
@@ -139,13 +139,15 @@ function parseLoomaAttributes(source) {
|
|
|
139
139
|
/**
|
|
140
140
|
* Writes attributes as a brace list, `id` first as `#id`, then the rest in order.
|
|
141
141
|
* @contract `true` writes a bare key; values are quoted only when they need it. `false`, null and
|
|
142
|
-
* undefined values are left out, and an empty set writes an empty string.
|
|
142
|
+
* undefined values are left out, and an empty set writes an empty string. An ID that `#` cannot
|
|
143
|
+
* carry, or any ID when `hashId` is false, is written `id=…`.
|
|
143
144
|
*/
|
|
144
|
-
function formatLoomaAttributes(attributes) {
|
|
145
|
+
function formatLoomaAttributes(attributes, { hashId = true } = {}) {
|
|
145
146
|
const items = [];
|
|
146
147
|
const { id, ...rest } = attributes;
|
|
147
|
-
|
|
148
|
-
|
|
148
|
+
const hashed = hashId && typeof id === "string" && /^[\w-]+$/.test(id);
|
|
149
|
+
if (hashed) items.push(`#${id}`);
|
|
150
|
+
for (const [key, value] of Object.entries(hashed ? rest : attributes)) if (value === true) items.push(key);
|
|
149
151
|
else if (value === false || value === null || value === void 0) continue;
|
|
150
152
|
else {
|
|
151
153
|
const text = String(value);
|
package/markdown/markdown-it.js
CHANGED
|
@@ -8,7 +8,7 @@ var TAB_LABEL = /^==[ \t]+(\S.*?)[ \t]*$/;
|
|
|
8
8
|
var OPEN_FLAG = /[ \t]*\{open\}$/;
|
|
9
9
|
var ALERT = /^\[!(NOTE|TIP|IMPORTANT|WARNING|CAUTION)\][ \t]*(?:\n|$)/i;
|
|
10
10
|
var LEAF = /^::([A-Za-z][\w-]*)(\{.*\})?[ \t]*$/;
|
|
11
|
-
var TASK = /^\[([ xX])\][ \t]
|
|
11
|
+
var TASK = /^\[([ xX])\](?:[ \t]+|$)/;
|
|
12
12
|
var TOC_FORMATS = /* @__PURE__ */ new Set([
|
|
13
13
|
"plain",
|
|
14
14
|
"bulleted",
|
package/package.json
CHANGED
package/vue/editor/index.js
CHANGED
|
@@ -1840,6 +1840,34 @@ function formatEditorShortcut(keys, apple) {
|
|
|
1840
1840
|
].filter(Boolean).join("+");
|
|
1841
1841
|
}
|
|
1842
1842
|
var isApplePlatform = () => typeof navigator !== "undefined" && /mac|iphone|ipad|ipod/i.test(navigator.userAgentData?.platform || navigator.platform || navigator.userAgent);
|
|
1843
|
+
/**
|
|
1844
|
+
* The anchor a heading's text derives: NFC, lowercase, keeping letters, numbers and combining
|
|
1845
|
+
* marks, with every other run of characters one hyphen.
|
|
1846
|
+
* @failure Text with no letters or numbers, such as only emoji, derives `heading`.
|
|
1847
|
+
*/
|
|
1848
|
+
function loomaHeadingSlug(text) {
|
|
1849
|
+
return text.normalize("NFC").toLowerCase().replace(/[^\p{L}\p{N}\p{M}]+/gu, "-").replace(/^-+|-+$/g, "") || "heading";
|
|
1850
|
+
}
|
|
1851
|
+
/**
|
|
1852
|
+
* Gives every heading of a document its anchor, in document order.
|
|
1853
|
+
* @contract A custom ID is kept unless an earlier heading already has it. Any other heading takes
|
|
1854
|
+
* its slug, or the slug's first free suffix (`-1`, `-2`…), never one a later custom ID reserves.
|
|
1855
|
+
*/
|
|
1856
|
+
function assignLoomaHeadingIds(headings) {
|
|
1857
|
+
const reserved = new Set(headings.map(({ id }) => id).filter((id) => Boolean(id)));
|
|
1858
|
+
const used = /* @__PURE__ */ new Set();
|
|
1859
|
+
return headings.map(({ text, id: custom }) => {
|
|
1860
|
+
let id = custom || loomaHeadingSlug(text);
|
|
1861
|
+
if (custom ? used.has(id) : used.has(id) || reserved.has(id)) {
|
|
1862
|
+
const stem = id;
|
|
1863
|
+
let suffix = 1;
|
|
1864
|
+
while (used.has(`${stem}-${suffix}`) || reserved.has(`${stem}-${suffix}`)) suffix++;
|
|
1865
|
+
id = `${stem}-${suffix}`;
|
|
1866
|
+
}
|
|
1867
|
+
used.add(id);
|
|
1868
|
+
return id;
|
|
1869
|
+
});
|
|
1870
|
+
}
|
|
1843
1871
|
//#endregion
|
|
1844
1872
|
//#region src/editor/extensions/expand.ts
|
|
1845
1873
|
/**
|
|
@@ -2049,7 +2077,17 @@ function tableOfContentsTree(entries) {
|
|
|
2049
2077
|
}
|
|
2050
2078
|
return roots;
|
|
2051
2079
|
}
|
|
2052
|
-
|
|
2080
|
+
var derivedFrom = (id, text) => {
|
|
2081
|
+
const slug = loomaHeadingSlug(text);
|
|
2082
|
+
return id === slug || id.startsWith(`${slug}-`) && /^\d+$/.test(id.slice(slug.length + 1));
|
|
2083
|
+
};
|
|
2084
|
+
/**
|
|
2085
|
+
* Keeps heading anchors on Looma Markdown's rule. An anchor derived from its heading's text
|
|
2086
|
+
* (as it read before this change) follows the text; any other, including `looma-heading-N` anchors
|
|
2087
|
+
* saved before this rule, is a custom anchor and stays, so links to it keep working. With
|
|
2088
|
+
* a table of contents every heading gets an anchor; without one, only headings that have one.
|
|
2089
|
+
*/
|
|
2090
|
+
function anchorTransaction(state, previous) {
|
|
2053
2091
|
let hasToc = false;
|
|
2054
2092
|
const headings = [];
|
|
2055
2093
|
state.doc.descendants((node, position) => {
|
|
@@ -2059,24 +2097,28 @@ function anchorTransaction(state) {
|
|
|
2059
2097
|
position
|
|
2060
2098
|
});
|
|
2061
2099
|
});
|
|
2062
|
-
if (!
|
|
2063
|
-
const
|
|
2064
|
-
|
|
2100
|
+
if (!headings.length) return null;
|
|
2101
|
+
const before = /* @__PURE__ */ new Map();
|
|
2102
|
+
previous?.descendants((node) => {
|
|
2103
|
+
if (node.type.name === "heading" && typeof node.attrs.id === "string") before.set(node.attrs.id, node.textContent.trim());
|
|
2104
|
+
});
|
|
2105
|
+
const custom = (node) => {
|
|
2106
|
+
const id = node.attrs.id;
|
|
2107
|
+
if (typeof id !== "string" || !id || /\s/.test(id)) return void 0;
|
|
2108
|
+
return derivedFrom(id, before.get(id) ?? node.textContent.trim()) ? void 0 : id;
|
|
2109
|
+
};
|
|
2110
|
+
const ids = assignLoomaHeadingIds(headings.map(({ node }) => ({
|
|
2111
|
+
text: node.textContent.trim(),
|
|
2112
|
+
id: custom(node)
|
|
2113
|
+
})));
|
|
2065
2114
|
const transaction = state.tr;
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
transaction.setNodeMarkup(position, void 0, {
|
|
2074
|
-
...node.attrs,
|
|
2075
|
-
id
|
|
2076
|
-
});
|
|
2077
|
-
}
|
|
2078
|
-
used.add(id);
|
|
2079
|
-
}
|
|
2115
|
+
headings.forEach(({ node, position }, index) => {
|
|
2116
|
+
if (node.attrs.id === ids[index] || !hasToc && !node.attrs.id) return;
|
|
2117
|
+
transaction.setNodeMarkup(position, void 0, {
|
|
2118
|
+
...node.attrs,
|
|
2119
|
+
id: ids[index]
|
|
2120
|
+
});
|
|
2121
|
+
});
|
|
2080
2122
|
return transaction.docChanged ? transaction : null;
|
|
2081
2123
|
}
|
|
2082
2124
|
var LoomaHeadingAnchors = Extension.create({
|
|
@@ -2092,7 +2134,7 @@ var LoomaHeadingAnchors = Extension.create({
|
|
|
2092
2134
|
if (transaction) this.editor.view.dispatch(transaction.setMeta("addToHistory", false));
|
|
2093
2135
|
},
|
|
2094
2136
|
addProseMirrorPlugins() {
|
|
2095
|
-
return [new Plugin({ appendTransaction: (transactions,
|
|
2137
|
+
return [new Plugin({ appendTransaction: (transactions, oldState, state) => transactions.some((transaction) => transaction.docChanged) ? anchorTransaction(state, oldState.doc) : null })];
|
|
2096
2138
|
}
|
|
2097
2139
|
});
|
|
2098
2140
|
function contentsHtml(doc, config) {
|