@kedataindo/docflow-plugins 0.0.85 → 0.0.87

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE CHANGED
@@ -1,44 +1,201 @@
1
- DocFlow — Proprietary EULA Reference
2
-
3
- Copyright © 2026 PT Kedata Indonesia (the "Licensor"). All rights reserved.
4
-
5
- ────────────────────────────────────────────────────────────────────────
6
- THIS IS A TEMPLATE / SCAFFOLD FILE.
7
- ────────────────────────────────────────────────────────────────────────
8
- This document is a placeholder summarising the licensing model. The
9
- binding text is the **DocFlow End-User License Agreement (EULA)** to
10
- be supplied separately to each customer, paired with a signed Order
11
- Form (per-deal price + tier + connection cap + server-instance cap).
12
- Until the EULA is reviewed by counsel and published, you may treat
13
- this file as the canonical statement that:
14
-
15
- 1. The DocFlow software is proprietary and NOT open-source.
16
- 2. No rights are granted by viewing the source code unless the
17
- customer has accepted the EULA in writing.
18
- 3. The bundled third-party components listed in `NOTICE` keep
19
- their original open-source licenses (mostly MIT).
20
-
21
- The "MIT" placeholder in `README.md` (carried over from before the
22
- proprietary decision) is **incorrect** and is updated by PR #90
23
- (replace "MIT" → "Proprietary — see LICENSE + NOTICE").
24
- ────────────────────────────────────────────────────────────────────────
25
-
26
- Licensing model (per PRD §5 — proprietary EULA + separate MSA):
27
-
28
- • DocFlow EULA (End-User License Agreement) — perpetual,
29
- non-exclusive, non-transferable, metered per simultaneous
30
- connection per server instance. Standard tiers: Community (free,
31
- no warranty), Professional, Enterprise, Government. Hosting
32
- DocFlow as a third-party SaaS is **prohibited** without a
33
- separate OEM/SaaS agreement.
34
- • Maintenance & Support Agreement (MSA) — annual, separate from
35
- the EULA. Required for Professional / Enterprise / Government
36
- for SLA-backed support; optional for Community.
37
- • Source-code escrow (Government tier) — source code is deposited
38
- with an escrow agent; released to the licensee if the Licensor
39
- ceases to operate or fails the maintenance SLA.
40
-
41
- Governing law: Republic of Indonesia (UU Hak Cipta No. 28/2014).
42
- Dispute venue: District Court of Central Jakarta.
43
-
44
- For licensing questions: legal@kedata.co
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "[]"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
188
+
189
+ Copyright 2026 PT Kedata Indonesia Digital
190
+
191
+ Licensed under the Apache License, Version 2.0 (the "License");
192
+ you may not use this file except in compliance with the License.
193
+ You may obtain a copy of the License at
194
+
195
+ http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+ Unless required by applicable law or agreed to in writing, software
198
+ distributed under the License is distributed on an "AS IS" BASIS,
199
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+ See the License for the specific language governing permissions and
201
+ limitations under the License.
package/README.md ADDED
@@ -0,0 +1,32 @@
1
+ # @kedataindo/docflow-plugins
2
+
3
+ Built-in plugins for [DocFlow](https://github.com/Kedata-Indonesia/docflow) — the open-source editor engine for document-style apps.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @kedataindo/docflow-plugins
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```ts
14
+ import { defaultPlugins } from '@kedataindo/docflow-plugins'
15
+ // pass to <DocsEditor :plugins="defaultPlugins">, createEditor(), or <docs-editor>
16
+ ```
17
+
18
+ `defaultPlugins` is the 21-plugin built-in set: underline, headings, lists (bullet, ordered, task), text alignment, link, image, tables, table page splitting, blockquote, code block, placeholder, page break, footnotes, table of contents, font size, text color, highlight, citations, AI, comments, and smart elements. Bold, italic, and strike come from the core StarterKit base schema, not from a plugin.
19
+
20
+ `slashMenuPlugin` (the `/` command menu) is exported separately and is **not** part of `defaultPlugins` — add it explicitly if you want it in the toolbar set.
21
+
22
+ A second entrypoint, `@kedataindo/docflow-plugins/citations`, provides the citations & references engine (DOI/CrossRef + BibTeX/RIS import, CSL styles).
23
+
24
+ Write your own with `definePlugin` from `@kedataindo/docflow-core` — see the [plugin system docs](https://github.com/Kedata-Indonesia/docflow#plugin-system).
25
+
26
+ ## Documentation
27
+
28
+ [github.com/Kedata-Indonesia/docflow](https://github.com/Kedata-Indonesia/docflow)
29
+
30
+ ## License
31
+
32
+ Apache-2.0 — free to embed, forever.
package/dist/index.cjs CHANGED
@@ -625,13 +625,6 @@ var footnotePlugin = (0, import_docflow_core12.definePlugin)({
625
625
  // src/toc.ts
626
626
  var import_core4 = require("@tiptap/core");
627
627
  var import_docflow_core13 = require("@kedataindo/docflow-core");
628
- var TOC_EXCLUDED_HEADING_NORMS = ["DAFTAR ISI"];
629
- function normHeadingText(text) {
630
- return text.toUpperCase().replace(/[—–]/g, "-").replace(/[^A-Z0-9]+/g, " ").trim();
631
- }
632
- function withoutExcludedHeadings(headings) {
633
- return headings.filter((h) => !TOC_EXCLUDED_HEADING_NORMS.includes(normHeadingText(h.text)));
634
- }
635
628
  function collectHeadingsFromDoc(doc) {
636
629
  const headings = [];
637
630
  doc.descendants((node, pos) => {
@@ -648,9 +641,6 @@ function collectHeadingsFromDoc(doc) {
648
641
  function collectHeadings(editor) {
649
642
  return collectHeadingsFromDoc(editor.state.doc);
650
643
  }
651
- function collectTocHeadings(doc) {
652
- return withoutExcludedHeadings(collectHeadingsFromDoc(doc));
653
- }
654
644
  function createPageResolver(editor) {
655
645
  let breakers = [];
656
646
  try {
@@ -691,7 +681,7 @@ function buildTocEntryNodes(schema, headings, pageFor) {
691
681
  });
692
682
  }
693
683
  function buildTocContentJSON(editor) {
694
- const headings = collectTocHeadings(editor.state.doc);
684
+ const headings = collectHeadings(editor);
695
685
  if (headings.length === 0) {
696
686
  return [{ type: "tocEntry", attrs: { level: 1, pos: 0 } }];
697
687
  }
@@ -720,7 +710,7 @@ function applyTocRegeneration(editor, tr) {
720
710
  return true;
721
711
  });
722
712
  if (targets.length === 0) return false;
723
- const entries = buildTocEntryNodes(editor.schema, collectTocHeadings(tr.doc), createPageResolver(editor));
713
+ const entries = buildTocEntryNodes(editor.schema, collectHeadingsFromDoc(tr.doc), createPageResolver(editor));
724
714
  for (const target of targets.reverse()) {
725
715
  tr.replaceWith(target.pos, target.pos + target.nodeSize, tocType.create(null, entries));
726
716
  }
@@ -865,21 +855,10 @@ var TocNode = import_core4.Node.create({
865
855
  var tocPlugin = (0, import_docflow_core13.definePlugin)({
866
856
  id: "toc",
867
857
  tiptapExtensions: [TocPageNumNode, TocEntryNode, TocNode, TocCommandsExtension],
868
- // `menu: 'insert'` puts it in the toolbar's "+" (Sisipkan) dropdown: the
869
- // action is plugin-own (not one of the built-in insert actions), so it has to
870
- // opt in explicitly. `generateToc` = regenerate, else insert → idempotent.
871
- toolbar: [
872
- { id: "insert-toc", label: "Generate Daftar Isi", action: "generateToc", iconComponent: "List", menu: "insert" }
873
- ],
874
858
  slashCommands: [{ name: "Table of contents", description: "Daftar isi", command: "insertToc" }],
875
859
  commands: {
876
860
  insertToc: (editor) => editor.commands.insertToc(),
877
- refreshToc: (editor) => regenerateToc(editor),
878
- // Toolbar-facing "Generate Daftar Isi": regenerate every existing toc, or
879
- // insert a prefilled one when the document has none. Implemented as a
880
- // plugin action (like refreshToc) so the manual `refreshToc` dispatch
881
- // doesn't nest inside a tiptap command transaction.
882
- generateToc: (editor) => regenerateToc(editor) || editor.commands.insertToc()
861
+ refreshToc: (editor) => regenerateToc(editor)
883
862
  }
884
863
  });
885
864
 
@@ -2046,32 +2025,12 @@ function getEngine(editor) {
2046
2025
  const storage = editor.storage.citationEngine;
2047
2026
  return storage?.engine ?? null;
2048
2027
  }
2049
- function isHeadingHidden(value) {
2050
- return value === false || value === "false";
2051
- }
2052
- function headingLabel(value) {
2053
- return typeof value === "string" && value !== "" ? value : "Bibliography";
2054
- }
2055
2028
  var BibliographyNode = import_core6.Node.create({
2056
2029
  name: "bibliography",
2057
2030
  group: "block",
2058
2031
  selectable: true,
2059
2032
  draggable: true,
2060
2033
  atom: true,
2061
- addAttributes() {
2062
- return {
2063
- showHeading: {
2064
- default: true,
2065
- parseHTML: (el) => !isHeadingHidden(el.getAttribute("data-show-heading")),
2066
- renderHTML: (attrs) => isHeadingHidden(attrs.showHeading) ? { "data-show-heading": "false" } : {}
2067
- },
2068
- headingText: {
2069
- default: "Bibliography",
2070
- parseHTML: (el) => headingLabel(el.getAttribute("data-heading-text")),
2071
- renderHTML: (attrs) => typeof attrs.headingText === "string" && attrs.headingText !== "Bibliography" ? { "data-heading-text": attrs.headingText } : {}
2072
- }
2073
- };
2074
- },
2075
2034
  parseHTML() {
2076
2035
  return [{ tag: 'div[data-node-type="bibliography"]' }];
2077
2036
  },
@@ -2093,27 +2052,21 @@ var BibliographyNode = import_core6.Node.create({
2093
2052
  const dom = document.createElement("div");
2094
2053
  dom.className = "docs-bibliography";
2095
2054
  dom.setAttribute("data-node-type", "bibliography");
2096
- let attrs = props.node?.attrs ?? {};
2097
2055
  const render = () => {
2098
- const engine = getEngine(editor);
2099
- const items = engine?.getBibliography() ?? [];
2100
- const showHeading = !isHeadingHidden(attrs.showHeading);
2101
- const headingText = headingLabel(attrs.headingText);
2056
+ const engine2 = getEngine(editor);
2057
+ const items = engine2?.getBibliography() ?? [];
2102
2058
  dom.innerHTML = "";
2103
2059
  if (items.length === 0) {
2104
- if (!showHeading) return;
2105
2060
  const placeholder = document.createElement("p");
2106
2061
  placeholder.className = "docs-bibliography__empty";
2107
- placeholder.textContent = headingText;
2062
+ placeholder.textContent = "Bibliography";
2108
2063
  dom.appendChild(placeholder);
2109
2064
  return;
2110
2065
  }
2111
- if (showHeading) {
2112
- const heading = document.createElement("h2");
2113
- heading.className = "docs-bibliography__heading";
2114
- heading.textContent = headingText;
2115
- dom.appendChild(heading);
2116
- }
2066
+ const heading = document.createElement("h2");
2067
+ heading.className = "docs-bibliography__heading";
2068
+ heading.textContent = "Bibliography";
2069
+ dom.appendChild(heading);
2117
2070
  const list = document.createElement("div");
2118
2071
  list.className = "docs-bibliography__entries";
2119
2072
  for (const item of items) {
@@ -2125,33 +2078,15 @@ var BibliographyNode = import_core6.Node.create({
2125
2078
  dom.appendChild(list);
2126
2079
  };
2127
2080
  render();
2128
- let unsubscribe = null;
2129
- let bound = null;
2130
- const syncEngine = () => {
2131
- const engine = getEngine(editor);
2132
- if (engine === bound) return;
2133
- unsubscribe?.();
2134
- bound = engine;
2135
- unsubscribe = engine ? engine.onChange(render) : null;
2136
- render();
2137
- };
2138
- syncEngine();
2139
- editor.on("create", syncEngine);
2140
- editor.on("citationEngineReady", syncEngine);
2081
+ const engine = getEngine(editor);
2082
+ const unsubscribe = engine?.onChange(render);
2141
2083
  return {
2142
2084
  dom,
2143
2085
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
2144
2086
  update(updatedNode) {
2145
- if (updatedNode?.type?.name !== "bibliography") return false;
2146
- const next = updatedNode.attrs ?? {};
2147
- const changed = isHeadingHidden(next.showHeading) !== isHeadingHidden(attrs.showHeading) || headingLabel(next.headingText) !== headingLabel(attrs.headingText);
2148
- attrs = next;
2149
- if (changed) render();
2150
- return true;
2087
+ return updatedNode?.type?.name === "bibliography";
2151
2088
  },
2152
2089
  destroy() {
2153
- editor.off("create", syncEngine);
2154
- editor.off("citationEngineReady", syncEngine);
2155
2090
  unsubscribe?.();
2156
2091
  }
2157
2092
  };
@@ -2192,91 +2127,83 @@ var CitationEngineExtension = import_core7.Extension.create({
2192
2127
  * recomputes only when the ordered signature changed, so plain typing
2193
2128
  * costs one cheap walk and no citeproc work.
2194
2129
  */
2195
- /**
2196
- * Keep the engine's cluster registry in sync with the document — see
2197
- * `syncCitationClusters`. The engine recomputes only when the ordered
2198
- * signature changed, so plain typing costs one cheap walk and no citeproc
2199
- * work.
2200
- */
2201
2130
  onUpdate() {
2202
- syncCitationClusters(this.editor);
2203
- }
2204
- });
2205
- function syncCitationClusters(editor) {
2206
- const engine = getCitationEngine(editor);
2207
- if (!engine) return;
2208
- {
2209
- const seen = /* @__PURE__ */ new Set();
2210
- const repairs = [];
2131
+ const editor = this.editor;
2132
+ const engine = getCitationEngine(editor);
2133
+ if (!engine) return;
2134
+ {
2135
+ const seen = /* @__PURE__ */ new Set();
2136
+ const repairs = [];
2137
+ editor.state.doc.descendants((node, pos) => {
2138
+ if (node.type.name === "citation" || node.type.name === "footnote" && node.attrs.sourceId) {
2139
+ const id = node.attrs.citationId;
2140
+ if (id) {
2141
+ if (seen.has(id)) {
2142
+ repairs.push({ pos, attrs: { ...node.attrs } });
2143
+ } else {
2144
+ seen.add(id);
2145
+ }
2146
+ }
2147
+ }
2148
+ return true;
2149
+ });
2150
+ if (repairs.length > 0) {
2151
+ const tr = editor.state.tr;
2152
+ for (const { pos, attrs } of repairs) {
2153
+ tr.setNodeMarkup(pos, void 0, { ...attrs, citationId: nextCitationId() });
2154
+ }
2155
+ editor.view.dispatch(tr);
2156
+ return;
2157
+ }
2158
+ }
2159
+ const ordered = [];
2211
2160
  editor.state.doc.descendants((node, pos) => {
2212
2161
  if (node.type.name === "citation" || node.type.name === "footnote" && node.attrs.sourceId) {
2213
- const id = node.attrs.citationId;
2214
- if (id) {
2215
- if (seen.has(id)) {
2216
- repairs.push({ pos, attrs: { ...node.attrs } });
2217
- } else {
2218
- seen.add(id);
2162
+ ordered.push({
2163
+ citationId: node.attrs.citationId ?? `anon-${pos}`,
2164
+ attrs: {
2165
+ sourceId: node.attrs.sourceId ?? null,
2166
+ locator: node.attrs.locator || "",
2167
+ label: node.attrs.label || "page",
2168
+ mode: node.attrs.mode || "normal",
2169
+ prefix: node.attrs.prefix || "",
2170
+ suffix: node.attrs.suffix || ""
2219
2171
  }
2220
- }
2172
+ });
2221
2173
  }
2222
2174
  return true;
2223
2175
  });
2224
- if (repairs.length > 0) {
2225
- const tr = editor.state.tr;
2226
- for (const { pos, attrs } of repairs) {
2227
- tr.setNodeMarkup(pos, void 0, { ...attrs, citationId: nextCitationId() });
2228
- }
2229
- editor.view.dispatch(tr);
2230
- return;
2176
+ const changed = engine.syncCitations(ordered);
2177
+ if (changed) {
2178
+ const port = editor.storage.editorContext;
2179
+ port?.citation?.onSourcesChange?.(engine.getCitedSourceIds());
2231
2180
  }
2232
- }
2233
- const ordered = [];
2234
- editor.state.doc.descendants((node, pos) => {
2235
- if (node.type.name === "citation" || node.type.name === "footnote" && node.attrs.sourceId) {
2236
- ordered.push({
2237
- citationId: node.attrs.citationId ?? `anon-${pos}`,
2238
- attrs: {
2239
- sourceId: node.attrs.sourceId ?? null,
2240
- locator: node.attrs.locator || "",
2241
- label: node.attrs.label || "page",
2242
- mode: node.attrs.mode || "normal",
2243
- prefix: node.attrs.prefix || "",
2244
- suffix: node.attrs.suffix || ""
2181
+ {
2182
+ const updates = [];
2183
+ editor.state.doc.descendants((node, pos) => {
2184
+ if (node.type.name === "footnote" && node.attrs.sourceId) {
2185
+ const id = node.attrs.citationId;
2186
+ if (!id) return true;
2187
+ const text = engine.renderCluster(id);
2188
+ if (text && text !== node.attrs.content) {
2189
+ updates.push({ pos, content: text });
2190
+ }
2245
2191
  }
2192
+ return true;
2246
2193
  });
2247
- }
2248
- return true;
2249
- });
2250
- const changed = engine.syncCitations(ordered);
2251
- if (changed) {
2252
- const port = editor.storage.editorContext;
2253
- port?.citation?.onSourcesChange?.(engine.getCitedSourceIds());
2254
- }
2255
- {
2256
- const updates = [];
2257
- editor.state.doc.descendants((node, pos) => {
2258
- if (node.type.name === "footnote" && node.attrs.sourceId) {
2259
- const id = node.attrs.citationId;
2260
- if (!id) return true;
2261
- const text = engine.renderCluster(id);
2262
- if (text && text !== node.attrs.content) {
2263
- updates.push({ pos, content: text });
2264
- }
2265
- }
2266
- return true;
2267
- });
2268
- if (updates.length > 0) {
2269
- const tr = editor.state.tr;
2270
- for (const { pos, content } of updates) {
2271
- const node = editor.state.doc.nodeAt(pos);
2272
- if (node) {
2273
- tr.setNodeMarkup(pos, void 0, { ...node.attrs, content });
2194
+ if (updates.length > 0) {
2195
+ const tr = editor.state.tr;
2196
+ for (const { pos, content } of updates) {
2197
+ const node = editor.state.doc.nodeAt(pos);
2198
+ if (node) {
2199
+ tr.setNodeMarkup(pos, void 0, { ...node.attrs, content });
2200
+ }
2274
2201
  }
2202
+ editor.view.dispatch(tr);
2275
2203
  }
2276
- editor.view.dispatch(tr);
2277
2204
  }
2278
2205
  }
2279
- }
2206
+ });
2280
2207
  var CitationNode = import_core7.Node.create({
2281
2208
  name: "citation",
2282
2209
  group: "inline",
@@ -2339,9 +2266,9 @@ var CitationNode = import_core7.Node.create({
2339
2266
  dom.className = "docs-citation";
2340
2267
  dom.setAttribute("data-node-type", "citation");
2341
2268
  const render = () => {
2342
- const engine = getCitationEngine(editor);
2269
+ const engine2 = getCitationEngine(editor);
2343
2270
  const citationId = node.attrs.citationId;
2344
- const text = engine && citationId ? engine.renderCluster(citationId) : "";
2271
+ const text = engine2 && citationId ? engine2.renderCluster(citationId) : "";
2345
2272
  if (text) {
2346
2273
  dom.innerHTML = text;
2347
2274
  dom.classList.remove("docs-citation--missing");
@@ -2353,19 +2280,8 @@ var CitationNode = import_core7.Node.create({
2353
2280
  dom.setAttribute("data-citation-source-id", String(node.attrs.sourceId ?? ""));
2354
2281
  };
2355
2282
  render();
2356
- let unsubscribe = null;
2357
- let bound = null;
2358
- const syncEngine = () => {
2359
- const engine = getCitationEngine(editor);
2360
- if (engine === bound) return;
2361
- unsubscribe?.();
2362
- bound = engine;
2363
- unsubscribe = engine ? engine.onChange(render) : null;
2364
- render();
2365
- };
2366
- syncEngine();
2367
- editor.on("create", syncEngine);
2368
- editor.on("citationEngineReady", syncEngine);
2283
+ const engine = getCitationEngine(editor);
2284
+ const unsubscribe = engine?.onChange(render);
2369
2285
  return {
2370
2286
  dom,
2371
2287
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -2379,8 +2295,6 @@ var CitationNode = import_core7.Node.create({
2379
2295
  return true;
2380
2296
  },
2381
2297
  destroy() {
2382
- editor.off("create", syncEngine);
2383
- editor.off("citationEngineReady", syncEngine);
2384
2298
  unsubscribe?.();
2385
2299
  }
2386
2300
  };
@@ -2452,8 +2366,6 @@ var citationPlugin = (0, import_docflow_core17.definePlugin)({
2452
2366
  const sources = typeof citation.sources === "function" ? citation.sources() : citation.sources;
2453
2367
  const engine = new CiteEngine({ sources, style: citation.style });
2454
2368
  editor.storage.citationEngine.engine = engine;
2455
- editor.emit("citationEngineReady", { editor });
2456
- syncCitationClusters(editor);
2457
2369
  },
2458
2370
  onDestroy(editor) {
2459
2371
  ;
@@ -2496,10 +2408,6 @@ function markdownToFragment(editor, markdown, opts) {
2496
2408
  parseOptions: { preserveWhitespace: "full" }
2497
2409
  });
2498
2410
  if (content instanceof import_model.Fragment) return content;
2499
- const asFragment = content;
2500
- if (asFragment && typeof asFragment.forEach === "function" && typeof asFragment.size === "number") {
2501
- return asFragment;
2502
- }
2503
2411
  return import_model.Fragment.from(content);
2504
2412
  }
2505
2413
  function selectionToInsertionEnd(tr, startLen, bias) {
package/dist/index.d.cts CHANGED
@@ -100,19 +100,6 @@ declare const textColorPlugin: _kedata_indonesia_docflow_core.DocsEditorPlugin;
100
100
 
101
101
  declare const highlightPlugin: _kedata_indonesia_docflow_core.DocsEditorPlugin;
102
102
 
103
- declare module '@tiptap/core' {
104
- interface EditorEvents {
105
- /**
106
- * Emitted once the document-scoped CiteEngine exists. It is created by
107
- * `citationPlugin.hooks.onInit`, i.e. AFTER the editor view (and therefore
108
- * after the node views for the already-loaded document), so node views
109
- * cannot rely on the engine being there when they are constructed.
110
- */
111
- citationEngineReady: {
112
- editor: Editor;
113
- };
114
- }
115
- }
116
103
  declare function getCitationEngine(editor: Editor): CiteEngine | null;
117
104
  /**
118
105
  * Carries the document-scoped CiteEngine in editor storage. The engine is
@@ -288,14 +275,9 @@ declare const LocationChipNode: Node<any, any>;
288
275
  declare const smartElementsPlugin: _kedata_indonesia_docflow_core.DocsEditorPlugin;
289
276
 
290
277
  /**
291
- * Bibliography — a fully derived block. It stores no entries: the node view
292
- * renders the engine's citeproc-sorted bibliography and repaints on every
293
- * engine change (source edit, style switch, citation add/remove).
294
- *
295
- * The two attrs are presentation-only, so the host can place the block under
296
- * an existing section heading (e.g. an NA's "Daftar Pustaka"): `showHeading`
297
- * suppresses the built-in heading, `headingText` relabels it. Both default to
298
- * the previous behaviour, so existing documents render unchanged.
278
+ * Bibliography — a fully derived block. It stores NOTHING (no attrs): the
279
+ * node view renders the engine's citeproc-sorted entries and repaints on
280
+ * every engine change (source edit, style switch, citation add/remove).
299
281
  */
300
282
  declare const BibliographyNode: Node<any, any>;
301
283
 
package/dist/index.d.ts CHANGED
@@ -100,19 +100,6 @@ declare const textColorPlugin: _kedata_indonesia_docflow_core.DocsEditorPlugin;
100
100
 
101
101
  declare const highlightPlugin: _kedata_indonesia_docflow_core.DocsEditorPlugin;
102
102
 
103
- declare module '@tiptap/core' {
104
- interface EditorEvents {
105
- /**
106
- * Emitted once the document-scoped CiteEngine exists. It is created by
107
- * `citationPlugin.hooks.onInit`, i.e. AFTER the editor view (and therefore
108
- * after the node views for the already-loaded document), so node views
109
- * cannot rely on the engine being there when they are constructed.
110
- */
111
- citationEngineReady: {
112
- editor: Editor;
113
- };
114
- }
115
- }
116
103
  declare function getCitationEngine(editor: Editor): CiteEngine | null;
117
104
  /**
118
105
  * Carries the document-scoped CiteEngine in editor storage. The engine is
@@ -288,14 +275,9 @@ declare const LocationChipNode: Node<any, any>;
288
275
  declare const smartElementsPlugin: _kedata_indonesia_docflow_core.DocsEditorPlugin;
289
276
 
290
277
  /**
291
- * Bibliography — a fully derived block. It stores no entries: the node view
292
- * renders the engine's citeproc-sorted bibliography and repaints on every
293
- * engine change (source edit, style switch, citation add/remove).
294
- *
295
- * The two attrs are presentation-only, so the host can place the block under
296
- * an existing section heading (e.g. an NA's "Daftar Pustaka"): `showHeading`
297
- * suppresses the built-in heading, `headingText` relabels it. Both default to
298
- * the previous behaviour, so existing documents render unchanged.
278
+ * Bibliography — a fully derived block. It stores NOTHING (no attrs): the
279
+ * node view renders the engine's citeproc-sorted entries and repaints on
280
+ * every engine change (source edit, style switch, citation add/remove).
299
281
  */
300
282
  declare const BibliographyNode: Node<any, any>;
301
283
 
package/dist/index.js CHANGED
@@ -532,13 +532,6 @@ var footnotePlugin = definePlugin12({
532
532
  // src/toc.ts
533
533
  import { Extension, Node as Node4, mergeAttributes as mergeAttributes4 } from "@tiptap/core";
534
534
  import { definePlugin as definePlugin13 } from "@kedataindo/docflow-core";
535
- var TOC_EXCLUDED_HEADING_NORMS = ["DAFTAR ISI"];
536
- function normHeadingText(text) {
537
- return text.toUpperCase().replace(/[—–]/g, "-").replace(/[^A-Z0-9]+/g, " ").trim();
538
- }
539
- function withoutExcludedHeadings(headings) {
540
- return headings.filter((h) => !TOC_EXCLUDED_HEADING_NORMS.includes(normHeadingText(h.text)));
541
- }
542
535
  function collectHeadingsFromDoc(doc) {
543
536
  const headings = [];
544
537
  doc.descendants((node, pos) => {
@@ -555,9 +548,6 @@ function collectHeadingsFromDoc(doc) {
555
548
  function collectHeadings(editor) {
556
549
  return collectHeadingsFromDoc(editor.state.doc);
557
550
  }
558
- function collectTocHeadings(doc) {
559
- return withoutExcludedHeadings(collectHeadingsFromDoc(doc));
560
- }
561
551
  function createPageResolver(editor) {
562
552
  let breakers = [];
563
553
  try {
@@ -598,7 +588,7 @@ function buildTocEntryNodes(schema, headings, pageFor) {
598
588
  });
599
589
  }
600
590
  function buildTocContentJSON(editor) {
601
- const headings = collectTocHeadings(editor.state.doc);
591
+ const headings = collectHeadings(editor);
602
592
  if (headings.length === 0) {
603
593
  return [{ type: "tocEntry", attrs: { level: 1, pos: 0 } }];
604
594
  }
@@ -627,7 +617,7 @@ function applyTocRegeneration(editor, tr) {
627
617
  return true;
628
618
  });
629
619
  if (targets.length === 0) return false;
630
- const entries = buildTocEntryNodes(editor.schema, collectTocHeadings(tr.doc), createPageResolver(editor));
620
+ const entries = buildTocEntryNodes(editor.schema, collectHeadingsFromDoc(tr.doc), createPageResolver(editor));
631
621
  for (const target of targets.reverse()) {
632
622
  tr.replaceWith(target.pos, target.pos + target.nodeSize, tocType.create(null, entries));
633
623
  }
@@ -772,21 +762,10 @@ var TocNode = Node4.create({
772
762
  var tocPlugin = definePlugin13({
773
763
  id: "toc",
774
764
  tiptapExtensions: [TocPageNumNode, TocEntryNode, TocNode, TocCommandsExtension],
775
- // `menu: 'insert'` puts it in the toolbar's "+" (Sisipkan) dropdown: the
776
- // action is plugin-own (not one of the built-in insert actions), so it has to
777
- // opt in explicitly. `generateToc` = regenerate, else insert → idempotent.
778
- toolbar: [
779
- { id: "insert-toc", label: "Generate Daftar Isi", action: "generateToc", iconComponent: "List", menu: "insert" }
780
- ],
781
765
  slashCommands: [{ name: "Table of contents", description: "Daftar isi", command: "insertToc" }],
782
766
  commands: {
783
767
  insertToc: (editor) => editor.commands.insertToc(),
784
- refreshToc: (editor) => regenerateToc(editor),
785
- // Toolbar-facing "Generate Daftar Isi": regenerate every existing toc, or
786
- // insert a prefilled one when the document has none. Implemented as a
787
- // plugin action (like refreshToc) so the manual `refreshToc` dispatch
788
- // doesn't nest inside a tiptap command transaction.
789
- generateToc: (editor) => regenerateToc(editor) || editor.commands.insertToc()
768
+ refreshToc: (editor) => regenerateToc(editor)
790
769
  }
791
770
  });
792
771
 
@@ -1953,32 +1932,12 @@ function getEngine(editor) {
1953
1932
  const storage = editor.storage.citationEngine;
1954
1933
  return storage?.engine ?? null;
1955
1934
  }
1956
- function isHeadingHidden(value) {
1957
- return value === false || value === "false";
1958
- }
1959
- function headingLabel(value) {
1960
- return typeof value === "string" && value !== "" ? value : "Bibliography";
1961
- }
1962
1935
  var BibliographyNode = Node5.create({
1963
1936
  name: "bibliography",
1964
1937
  group: "block",
1965
1938
  selectable: true,
1966
1939
  draggable: true,
1967
1940
  atom: true,
1968
- addAttributes() {
1969
- return {
1970
- showHeading: {
1971
- default: true,
1972
- parseHTML: (el) => !isHeadingHidden(el.getAttribute("data-show-heading")),
1973
- renderHTML: (attrs) => isHeadingHidden(attrs.showHeading) ? { "data-show-heading": "false" } : {}
1974
- },
1975
- headingText: {
1976
- default: "Bibliography",
1977
- parseHTML: (el) => headingLabel(el.getAttribute("data-heading-text")),
1978
- renderHTML: (attrs) => typeof attrs.headingText === "string" && attrs.headingText !== "Bibliography" ? { "data-heading-text": attrs.headingText } : {}
1979
- }
1980
- };
1981
- },
1982
1941
  parseHTML() {
1983
1942
  return [{ tag: 'div[data-node-type="bibliography"]' }];
1984
1943
  },
@@ -2000,27 +1959,21 @@ var BibliographyNode = Node5.create({
2000
1959
  const dom = document.createElement("div");
2001
1960
  dom.className = "docs-bibliography";
2002
1961
  dom.setAttribute("data-node-type", "bibliography");
2003
- let attrs = props.node?.attrs ?? {};
2004
1962
  const render = () => {
2005
- const engine = getEngine(editor);
2006
- const items = engine?.getBibliography() ?? [];
2007
- const showHeading = !isHeadingHidden(attrs.showHeading);
2008
- const headingText = headingLabel(attrs.headingText);
1963
+ const engine2 = getEngine(editor);
1964
+ const items = engine2?.getBibliography() ?? [];
2009
1965
  dom.innerHTML = "";
2010
1966
  if (items.length === 0) {
2011
- if (!showHeading) return;
2012
1967
  const placeholder = document.createElement("p");
2013
1968
  placeholder.className = "docs-bibliography__empty";
2014
- placeholder.textContent = headingText;
1969
+ placeholder.textContent = "Bibliography";
2015
1970
  dom.appendChild(placeholder);
2016
1971
  return;
2017
1972
  }
2018
- if (showHeading) {
2019
- const heading = document.createElement("h2");
2020
- heading.className = "docs-bibliography__heading";
2021
- heading.textContent = headingText;
2022
- dom.appendChild(heading);
2023
- }
1973
+ const heading = document.createElement("h2");
1974
+ heading.className = "docs-bibliography__heading";
1975
+ heading.textContent = "Bibliography";
1976
+ dom.appendChild(heading);
2024
1977
  const list = document.createElement("div");
2025
1978
  list.className = "docs-bibliography__entries";
2026
1979
  for (const item of items) {
@@ -2032,33 +1985,15 @@ var BibliographyNode = Node5.create({
2032
1985
  dom.appendChild(list);
2033
1986
  };
2034
1987
  render();
2035
- let unsubscribe = null;
2036
- let bound = null;
2037
- const syncEngine = () => {
2038
- const engine = getEngine(editor);
2039
- if (engine === bound) return;
2040
- unsubscribe?.();
2041
- bound = engine;
2042
- unsubscribe = engine ? engine.onChange(render) : null;
2043
- render();
2044
- };
2045
- syncEngine();
2046
- editor.on("create", syncEngine);
2047
- editor.on("citationEngineReady", syncEngine);
1988
+ const engine = getEngine(editor);
1989
+ const unsubscribe = engine?.onChange(render);
2048
1990
  return {
2049
1991
  dom,
2050
1992
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
2051
1993
  update(updatedNode) {
2052
- if (updatedNode?.type?.name !== "bibliography") return false;
2053
- const next = updatedNode.attrs ?? {};
2054
- const changed = isHeadingHidden(next.showHeading) !== isHeadingHidden(attrs.showHeading) || headingLabel(next.headingText) !== headingLabel(attrs.headingText);
2055
- attrs = next;
2056
- if (changed) render();
2057
- return true;
1994
+ return updatedNode?.type?.name === "bibliography";
2058
1995
  },
2059
1996
  destroy() {
2060
- editor.off("create", syncEngine);
2061
- editor.off("citationEngineReady", syncEngine);
2062
1997
  unsubscribe?.();
2063
1998
  }
2064
1999
  };
@@ -2099,91 +2034,83 @@ var CitationEngineExtension = Extension2.create({
2099
2034
  * recomputes only when the ordered signature changed, so plain typing
2100
2035
  * costs one cheap walk and no citeproc work.
2101
2036
  */
2102
- /**
2103
- * Keep the engine's cluster registry in sync with the document — see
2104
- * `syncCitationClusters`. The engine recomputes only when the ordered
2105
- * signature changed, so plain typing costs one cheap walk and no citeproc
2106
- * work.
2107
- */
2108
2037
  onUpdate() {
2109
- syncCitationClusters(this.editor);
2110
- }
2111
- });
2112
- function syncCitationClusters(editor) {
2113
- const engine = getCitationEngine(editor);
2114
- if (!engine) return;
2115
- {
2116
- const seen = /* @__PURE__ */ new Set();
2117
- const repairs = [];
2038
+ const editor = this.editor;
2039
+ const engine = getCitationEngine(editor);
2040
+ if (!engine) return;
2041
+ {
2042
+ const seen = /* @__PURE__ */ new Set();
2043
+ const repairs = [];
2044
+ editor.state.doc.descendants((node, pos) => {
2045
+ if (node.type.name === "citation" || node.type.name === "footnote" && node.attrs.sourceId) {
2046
+ const id = node.attrs.citationId;
2047
+ if (id) {
2048
+ if (seen.has(id)) {
2049
+ repairs.push({ pos, attrs: { ...node.attrs } });
2050
+ } else {
2051
+ seen.add(id);
2052
+ }
2053
+ }
2054
+ }
2055
+ return true;
2056
+ });
2057
+ if (repairs.length > 0) {
2058
+ const tr = editor.state.tr;
2059
+ for (const { pos, attrs } of repairs) {
2060
+ tr.setNodeMarkup(pos, void 0, { ...attrs, citationId: nextCitationId() });
2061
+ }
2062
+ editor.view.dispatch(tr);
2063
+ return;
2064
+ }
2065
+ }
2066
+ const ordered = [];
2118
2067
  editor.state.doc.descendants((node, pos) => {
2119
2068
  if (node.type.name === "citation" || node.type.name === "footnote" && node.attrs.sourceId) {
2120
- const id = node.attrs.citationId;
2121
- if (id) {
2122
- if (seen.has(id)) {
2123
- repairs.push({ pos, attrs: { ...node.attrs } });
2124
- } else {
2125
- seen.add(id);
2069
+ ordered.push({
2070
+ citationId: node.attrs.citationId ?? `anon-${pos}`,
2071
+ attrs: {
2072
+ sourceId: node.attrs.sourceId ?? null,
2073
+ locator: node.attrs.locator || "",
2074
+ label: node.attrs.label || "page",
2075
+ mode: node.attrs.mode || "normal",
2076
+ prefix: node.attrs.prefix || "",
2077
+ suffix: node.attrs.suffix || ""
2126
2078
  }
2127
- }
2079
+ });
2128
2080
  }
2129
2081
  return true;
2130
2082
  });
2131
- if (repairs.length > 0) {
2132
- const tr = editor.state.tr;
2133
- for (const { pos, attrs } of repairs) {
2134
- tr.setNodeMarkup(pos, void 0, { ...attrs, citationId: nextCitationId() });
2135
- }
2136
- editor.view.dispatch(tr);
2137
- return;
2083
+ const changed = engine.syncCitations(ordered);
2084
+ if (changed) {
2085
+ const port = editor.storage.editorContext;
2086
+ port?.citation?.onSourcesChange?.(engine.getCitedSourceIds());
2138
2087
  }
2139
- }
2140
- const ordered = [];
2141
- editor.state.doc.descendants((node, pos) => {
2142
- if (node.type.name === "citation" || node.type.name === "footnote" && node.attrs.sourceId) {
2143
- ordered.push({
2144
- citationId: node.attrs.citationId ?? `anon-${pos}`,
2145
- attrs: {
2146
- sourceId: node.attrs.sourceId ?? null,
2147
- locator: node.attrs.locator || "",
2148
- label: node.attrs.label || "page",
2149
- mode: node.attrs.mode || "normal",
2150
- prefix: node.attrs.prefix || "",
2151
- suffix: node.attrs.suffix || ""
2088
+ {
2089
+ const updates = [];
2090
+ editor.state.doc.descendants((node, pos) => {
2091
+ if (node.type.name === "footnote" && node.attrs.sourceId) {
2092
+ const id = node.attrs.citationId;
2093
+ if (!id) return true;
2094
+ const text = engine.renderCluster(id);
2095
+ if (text && text !== node.attrs.content) {
2096
+ updates.push({ pos, content: text });
2097
+ }
2152
2098
  }
2099
+ return true;
2153
2100
  });
2154
- }
2155
- return true;
2156
- });
2157
- const changed = engine.syncCitations(ordered);
2158
- if (changed) {
2159
- const port = editor.storage.editorContext;
2160
- port?.citation?.onSourcesChange?.(engine.getCitedSourceIds());
2161
- }
2162
- {
2163
- const updates = [];
2164
- editor.state.doc.descendants((node, pos) => {
2165
- if (node.type.name === "footnote" && node.attrs.sourceId) {
2166
- const id = node.attrs.citationId;
2167
- if (!id) return true;
2168
- const text = engine.renderCluster(id);
2169
- if (text && text !== node.attrs.content) {
2170
- updates.push({ pos, content: text });
2171
- }
2172
- }
2173
- return true;
2174
- });
2175
- if (updates.length > 0) {
2176
- const tr = editor.state.tr;
2177
- for (const { pos, content } of updates) {
2178
- const node = editor.state.doc.nodeAt(pos);
2179
- if (node) {
2180
- tr.setNodeMarkup(pos, void 0, { ...node.attrs, content });
2101
+ if (updates.length > 0) {
2102
+ const tr = editor.state.tr;
2103
+ for (const { pos, content } of updates) {
2104
+ const node = editor.state.doc.nodeAt(pos);
2105
+ if (node) {
2106
+ tr.setNodeMarkup(pos, void 0, { ...node.attrs, content });
2107
+ }
2181
2108
  }
2109
+ editor.view.dispatch(tr);
2182
2110
  }
2183
- editor.view.dispatch(tr);
2184
2111
  }
2185
2112
  }
2186
- }
2113
+ });
2187
2114
  var CitationNode = Node6.create({
2188
2115
  name: "citation",
2189
2116
  group: "inline",
@@ -2246,9 +2173,9 @@ var CitationNode = Node6.create({
2246
2173
  dom.className = "docs-citation";
2247
2174
  dom.setAttribute("data-node-type", "citation");
2248
2175
  const render = () => {
2249
- const engine = getCitationEngine(editor);
2176
+ const engine2 = getCitationEngine(editor);
2250
2177
  const citationId = node.attrs.citationId;
2251
- const text = engine && citationId ? engine.renderCluster(citationId) : "";
2178
+ const text = engine2 && citationId ? engine2.renderCluster(citationId) : "";
2252
2179
  if (text) {
2253
2180
  dom.innerHTML = text;
2254
2181
  dom.classList.remove("docs-citation--missing");
@@ -2260,19 +2187,8 @@ var CitationNode = Node6.create({
2260
2187
  dom.setAttribute("data-citation-source-id", String(node.attrs.sourceId ?? ""));
2261
2188
  };
2262
2189
  render();
2263
- let unsubscribe = null;
2264
- let bound = null;
2265
- const syncEngine = () => {
2266
- const engine = getCitationEngine(editor);
2267
- if (engine === bound) return;
2268
- unsubscribe?.();
2269
- bound = engine;
2270
- unsubscribe = engine ? engine.onChange(render) : null;
2271
- render();
2272
- };
2273
- syncEngine();
2274
- editor.on("create", syncEngine);
2275
- editor.on("citationEngineReady", syncEngine);
2190
+ const engine = getCitationEngine(editor);
2191
+ const unsubscribe = engine?.onChange(render);
2276
2192
  return {
2277
2193
  dom,
2278
2194
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -2286,8 +2202,6 @@ var CitationNode = Node6.create({
2286
2202
  return true;
2287
2203
  },
2288
2204
  destroy() {
2289
- editor.off("create", syncEngine);
2290
- editor.off("citationEngineReady", syncEngine);
2291
2205
  unsubscribe?.();
2292
2206
  }
2293
2207
  };
@@ -2359,8 +2273,6 @@ var citationPlugin = definePlugin17({
2359
2273
  const sources = typeof citation.sources === "function" ? citation.sources() : citation.sources;
2360
2274
  const engine = new CiteEngine({ sources, style: citation.style });
2361
2275
  editor.storage.citationEngine.engine = engine;
2362
- editor.emit("citationEngineReady", { editor });
2363
- syncCitationClusters(editor);
2364
2276
  },
2365
2277
  onDestroy(editor) {
2366
2278
  ;
@@ -2403,10 +2315,6 @@ function markdownToFragment(editor, markdown, opts) {
2403
2315
  parseOptions: { preserveWhitespace: "full" }
2404
2316
  });
2405
2317
  if (content instanceof Fragment) return content;
2406
- const asFragment = content;
2407
- if (asFragment && typeof asFragment.forEach === "function" && typeof asFragment.size === "number") {
2408
- return asFragment;
2409
- }
2410
2318
  return Fragment.from(content);
2411
2319
  }
2412
2320
  function selectionToInsertionEnd(tr, startLen, bias) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@kedataindo/docflow-plugins",
3
- "license": "UNLICENSED",
4
- "version": "0.0.85",
3
+ "license": "Apache-2.0",
4
+ "version": "0.0.87",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "module": "./dist/index.js",
@@ -31,7 +31,7 @@
31
31
  "@tiptap/pm": "^2.11.0",
32
32
  "citeproc": "^2.4.63",
33
33
  "tiptap-markdown": "0.8.10",
34
- "@kedataindo/docflow-core": "0.0.83"
34
+ "@kedataindo/docflow-core": "0.0.85"
35
35
  },
36
36
  "peerDependencies": {
37
37
  "@tiptap/core": "^2.11.0",