@manuscripts/body-editor 1.8.3 → 1.8.4-LEAN-3136-2MB

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.
@@ -329,7 +329,18 @@ const insertInlineFootnote = (kind) => (state, dispatch) => {
329
329
  ]);
330
330
  const backmatter = (0, prosemirror_utils_1.findChildrenByType)(tr.doc, transform_1.schema.nodes.backmatter)[0];
331
331
  tr.insert(backmatter.pos + 1, section);
332
- selectionPos = backmatter.pos + section.nodeSize - 3;
332
+ let footnotePos = 0;
333
+ tr.doc.descendants((n, pos) => {
334
+ if ((0, transform_1.isFootnoteNode)(n)) {
335
+ footnotePos = pos;
336
+ n.descendants((childNode, childPos) => {
337
+ if ((0, transform_1.isParagraphNode)(childNode)) {
338
+ footnotePos += childPos;
339
+ }
340
+ });
341
+ }
342
+ });
343
+ selectionPos = footnotePos + 2;
333
344
  }
334
345
  else {
335
346
  const footnoteElement = (0, prosemirror_utils_1.findChildrenByType)(footnotesSection.node, transform_1.schema.nodes.footnotes_element);
@@ -341,7 +352,7 @@ const insertInlineFootnote = (kind) => (state, dispatch) => {
341
352
  selectionPos = pos + 2;
342
353
  }
343
354
  if (dispatch && selectionPos) {
344
- const selection = prosemirror_state_1.TextSelection.create(tr.doc, selectionPos);
355
+ const selection = prosemirror_state_1.TextSelection.near(tr.doc.resolve(selectionPos));
345
356
  dispatch(tr.setSelection(selection).scrollIntoView());
346
357
  }
347
358
  return true;
@@ -15,7 +15,7 @@
15
15
  * limitations under the License.
16
16
  */
17
17
  Object.defineProperty(exports, "__esModule", { value: true });
18
- exports.getInlineFootnotes = exports.updateTableInlineFootnoteLabels = exports.orderTableFootnotes = exports.buildTableFootnoteLabels = exports.getNewFootnotePos = exports.findTableInlineFootnoteIds = void 0;
18
+ exports.getAlphaOrderIndices = exports.getInlineFootnotes = exports.updateTableInlineFootnoteLabels = exports.orderTableFootnotes = exports.buildTableFootnoteLabels = exports.getNewFootnotePos = exports.findTableInlineFootnoteIds = void 0;
19
19
  const track_changes_plugin_1 = require("@manuscripts/track-changes-plugin");
20
20
  const transform_1 = require("@manuscripts/transform");
21
21
  const prosemirror_model_1 = require("prosemirror-model");
@@ -108,3 +108,35 @@ const getInlineFootnotes = (id, tableElement) => {
108
108
  return inlineFootnotes;
109
109
  };
110
110
  exports.getInlineFootnotes = getInlineFootnotes;
111
+ function getAlphaOrderIndices(index) {
112
+ const unicodeInterval = [97, 123];
113
+ const places = unicodeInterval[1] - unicodeInterval[0];
114
+ function getClassCount(n, order) {
115
+ return n * Math.pow(places, order - 1);
116
+ }
117
+ let indices = null;
118
+ for (;;) {
119
+ let current = index;
120
+ let position = 1;
121
+ while (current >= places) {
122
+ current = current / places;
123
+ position++;
124
+ }
125
+ const newIndex = Math.floor(current);
126
+ indices = indices ? indices : new Array(position).fill(0);
127
+ indices.splice(indices.length - position, 1, newIndex);
128
+ index -= getClassCount(newIndex, position);
129
+ if (position === 1) {
130
+ break;
131
+ }
132
+ }
133
+ return (indices || [])
134
+ .map((v, i, array) => {
135
+ if (array.length > 1 && i !== array.length - 1) {
136
+ return String.fromCodePoint(v + unicodeInterval[0] - 1);
137
+ }
138
+ return String.fromCodePoint(v + unicodeInterval[0]);
139
+ })
140
+ .join('');
141
+ }
142
+ exports.getAlphaOrderIndices = getAlphaOrderIndices;
@@ -18,7 +18,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
18
18
  return (mod && mod.__esModule) ? mod : { "default": mod };
19
19
  };
20
20
  Object.defineProperty(exports, "__esModule", { value: true });
21
- exports.uncitedFootnoteWidget = exports.buildPluginState = exports.footnotesKey = void 0;
21
+ exports.buildPluginState = exports.uncitedFootnoteWidget = exports.footnotesKey = void 0;
22
+ const track_changes_plugin_1 = require("@manuscripts/track-changes-plugin");
22
23
  const transform_1 = require("@manuscripts/transform");
23
24
  const lodash_1 = require("lodash");
24
25
  const prosemirror_state_1 = require("prosemirror-state");
@@ -31,23 +32,6 @@ const ReactSubView_1 = __importDefault(require("../../views/ReactSubView"));
31
32
  const placeholder_1 = require("../placeholder");
32
33
  const footnotes_utils_1 = require("./footnotes-utils");
33
34
  exports.footnotesKey = new prosemirror_state_1.PluginKey('footnotes');
34
- const buildPluginState = (doc) => {
35
- const nodes = [];
36
- let index = 0;
37
- const labels = new Map();
38
- doc.descendants((node, pos, parentNode) => {
39
- if ((0, transform_1.isInlineFootnoteNode)(node) &&
40
- parentNode &&
41
- parentNode.type !== transform_1.schema.nodes.table_cell) {
42
- nodes.push([node, pos]);
43
- node.attrs.rids.forEach((rid) => {
44
- labels.set(rid, String(++index));
45
- });
46
- }
47
- });
48
- return { nodes, labels };
49
- };
50
- exports.buildPluginState = buildPluginState;
51
35
  const scrollToInlineFootnote = (rid, view) => {
52
36
  view.state.doc.descendants((node, pos) => {
53
37
  const footnote = node;
@@ -126,6 +110,38 @@ const deleteFootnoteWidget = (node, props, footnoteType, footnoteMessage, id, ta
126
110
  });
127
111
  return deleteBtn;
128
112
  };
113
+ const buildPluginState = (doc) => {
114
+ const inlineFootnotes = [];
115
+ const footnotes = new Map();
116
+ let footnoteElement;
117
+ doc.descendants((node, pos, parentNode) => {
118
+ if ((0, transform_1.isFootnoteNode)(node)) {
119
+ footnotes.set(node.attrs.id, [node, pos]);
120
+ }
121
+ if ((0, transform_1.isFootnotesSectionNode)(node)) {
122
+ node.descendants((node, childPos) => {
123
+ if ((0, transform_1.isFootnotesElementNode)(node)) {
124
+ footnoteElement = [node, pos + childPos];
125
+ }
126
+ });
127
+ }
128
+ if ((0, transform_1.isInlineFootnoteNode)(node) &&
129
+ parentNode &&
130
+ parentNode.type !== transform_1.schema.nodes.table_cell) {
131
+ inlineFootnotes.push([node, pos]);
132
+ }
133
+ });
134
+ let index = 0;
135
+ const labels = new Map();
136
+ inlineFootnotes.sort((a, b) => a[1] - b[1]);
137
+ inlineFootnotes.forEach(([node]) => {
138
+ node.attrs.rids.forEach((rid) => {
139
+ labels.set(rid, (0, footnotes_utils_1.getAlphaOrderIndices)(index++));
140
+ });
141
+ });
142
+ return { inlineFootnotes, labels, footnotes, footnoteElement };
143
+ };
144
+ exports.buildPluginState = buildPluginState;
129
145
  exports.default = (props) => {
130
146
  return new prosemirror_state_1.Plugin({
131
147
  key: exports.footnotesKey,
@@ -134,25 +150,59 @@ exports.default = (props) => {
134
150
  return (0, exports.buildPluginState)(instance.doc);
135
151
  },
136
152
  apply(tr, value, oldState, newState) {
153
+ const prevState = exports.footnotesKey.getState(oldState);
154
+ if (!tr.docChanged && prevState) {
155
+ return prevState;
156
+ }
137
157
  return (0, exports.buildPluginState)(newState.doc);
138
158
  },
139
159
  },
140
160
  appendTransaction(transactions, oldState, newState) {
141
- const { nodes: oldInlineFootnoteNodes } = exports.footnotesKey.getState(oldState);
142
- const { nodes: inlineFootnoteNodes, labels } = exports.footnotesKey.getState(newState);
143
- if ((0, lodash_1.isEqual)(inlineFootnoteNodes, oldInlineFootnoteNodes)) {
161
+ const { inlineFootnotes: oldInlineFootnoteNodes } = exports.footnotesKey.getState(oldState);
162
+ const { inlineFootnotes: inlineFootnoteNodes, footnotes, labels, footnoteElement, } = exports.footnotesKey.getState(newState);
163
+ const prevIds = oldInlineFootnoteNodes.map(([node]) => node.attrs.rids);
164
+ const newIds = inlineFootnoteNodes.map(([node]) => node.attrs.rids);
165
+ if (!footnoteElement || (0, lodash_1.isEqual)(prevIds, newIds) || newIds.length < 2) {
144
166
  return null;
145
167
  }
146
168
  const { tr } = newState;
169
+ const footnotesReordered = [];
170
+ const footnotesRest = new Map(footnotes);
147
171
  inlineFootnoteNodes.forEach(([node, pos]) => {
148
172
  const footnote = node;
149
173
  const contents = footnote.attrs.rids
150
- .map((rid) => labels.get(rid))
174
+ .map((rid) => {
175
+ const currentfNode = footnotesRest.get(rid);
176
+ if (currentfNode) {
177
+ footnotesReordered.push(currentfNode[0]);
178
+ footnotesRest.delete(rid);
179
+ }
180
+ return labels.get(rid);
181
+ })
151
182
  .join('');
152
183
  if (footnote.attrs.contents !== contents) {
153
184
  tr.setNodeMarkup(pos, undefined, Object.assign(Object.assign({}, footnote.attrs), { rids: footnote.attrs.rids, contents }));
154
185
  }
155
186
  });
187
+ footnotesRest.forEach(([node]) => footnotesReordered.push(node));
188
+ const newFElement = transform_1.schema.nodes.footnotes_element.create(footnoteElement[0].attrs, footnotesReordered);
189
+ if (newFElement) {
190
+ tr.replaceWith(footnoteElement[1], footnoteElement[1] + footnoteElement[0].nodeSize, newFElement);
191
+ }
192
+ const prevSelection = newState.selection;
193
+ const selectedFootnote = (0, prosemirror_utils_1.findParentNodeClosestToPos)(prevSelection.$anchor, (node) => (0, transform_1.isFootnoteNode)(node));
194
+ if (selectedFootnote && footnotes.size > 1) {
195
+ let newFootnotePos = 0;
196
+ for (let i = 0; i < footnotesReordered.length; i++) {
197
+ const node = footnotesReordered[i];
198
+ newFootnotePos += node.nodeSize;
199
+ if (node.attrs.id === selectedFootnote.node.attrs.id) {
200
+ break;
201
+ }
202
+ }
203
+ tr.setSelection(prosemirror_state_1.TextSelection.create(tr.doc, footnoteElement[1] + newFootnotePos));
204
+ }
205
+ (0, track_changes_plugin_1.skipTracking)(tr);
156
206
  return tr;
157
207
  },
158
208
  props: {
@@ -15,7 +15,7 @@
15
15
  */
16
16
  import { ObjectTypes } from '@manuscripts/json-schema';
17
17
  import { skipTracking } from '@manuscripts/track-changes-plugin';
18
- import { buildComment, generateID, isElementNodeType, isInBibliographySection, isListNode, isSectionNodeType, schema, } from '@manuscripts/transform';
18
+ import { buildComment, generateID, isElementNodeType, isFootnoteNode, isInBibliographySection, isListNode, isParagraphNode, isSectionNodeType, schema, } from '@manuscripts/transform';
19
19
  import { NodeRange } from 'prosemirror-model';
20
20
  import { wrapInList } from 'prosemirror-schema-list';
21
21
  import { NodeSelection, TextSelection, } from 'prosemirror-state';
@@ -310,7 +310,18 @@ export const insertInlineFootnote = (kind) => (state, dispatch) => {
310
310
  ]);
311
311
  const backmatter = findChildrenByType(tr.doc, schema.nodes.backmatter)[0];
312
312
  tr.insert(backmatter.pos + 1, section);
313
- selectionPos = backmatter.pos + section.nodeSize - 3;
313
+ let footnotePos = 0;
314
+ tr.doc.descendants((n, pos) => {
315
+ if (isFootnoteNode(n)) {
316
+ footnotePos = pos;
317
+ n.descendants((childNode, childPos) => {
318
+ if (isParagraphNode(childNode)) {
319
+ footnotePos += childPos;
320
+ }
321
+ });
322
+ }
323
+ });
324
+ selectionPos = footnotePos + 2;
314
325
  }
315
326
  else {
316
327
  const footnoteElement = findChildrenByType(footnotesSection.node, schema.nodes.footnotes_element);
@@ -322,7 +333,7 @@ export const insertInlineFootnote = (kind) => (state, dispatch) => {
322
333
  selectionPos = pos + 2;
323
334
  }
324
335
  if (dispatch && selectionPos) {
325
- const selection = TextSelection.create(tr.doc, selectionPos);
336
+ const selection = TextSelection.near(tr.doc.resolve(selectionPos));
326
337
  dispatch(tr.setSelection(selection).scrollIntoView());
327
338
  }
328
339
  return true;
@@ -99,3 +99,34 @@ export const getInlineFootnotes = (id, tableElement) => {
99
99
  });
100
100
  return inlineFootnotes;
101
101
  };
102
+ export function getAlphaOrderIndices(index) {
103
+ const unicodeInterval = [97, 123];
104
+ const places = unicodeInterval[1] - unicodeInterval[0];
105
+ function getClassCount(n, order) {
106
+ return n * Math.pow(places, order - 1);
107
+ }
108
+ let indices = null;
109
+ for (;;) {
110
+ let current = index;
111
+ let position = 1;
112
+ while (current >= places) {
113
+ current = current / places;
114
+ position++;
115
+ }
116
+ const newIndex = Math.floor(current);
117
+ indices = indices ? indices : new Array(position).fill(0);
118
+ indices.splice(indices.length - position, 1, newIndex);
119
+ index -= getClassCount(newIndex, position);
120
+ if (position === 1) {
121
+ break;
122
+ }
123
+ }
124
+ return (indices || [])
125
+ .map((v, i, array) => {
126
+ if (array.length > 1 && i !== array.length - 1) {
127
+ return String.fromCodePoint(v + unicodeInterval[0] - 1);
128
+ }
129
+ return String.fromCodePoint(v + unicodeInterval[0]);
130
+ })
131
+ .join('');
132
+ }
@@ -13,9 +13,10 @@
13
13
  * See the License for the specific language governing permissions and
14
14
  * limitations under the License.
15
15
  */
16
- import { isFootnoteNode, isInlineFootnoteNode, schema, } from '@manuscripts/transform';
16
+ import { skipTracking } from '@manuscripts/track-changes-plugin';
17
+ import { isFootnoteNode, isFootnotesElementNode, isFootnotesSectionNode, isInlineFootnoteNode, schema, } from '@manuscripts/transform';
17
18
  import { isEqual } from 'lodash';
18
- import { NodeSelection, Plugin, PluginKey } from 'prosemirror-state';
19
+ import { NodeSelection, Plugin, PluginKey, TextSelection, } from 'prosemirror-state';
19
20
  import { findParentNodeClosestToPos, findParentNodeOfType, hasParentNodeOfType, } from 'prosemirror-utils';
20
21
  import { Decoration, DecorationSet } from 'prosemirror-view';
21
22
  import { alertIcon, deleteIcon } from '../../assets';
@@ -23,24 +24,8 @@ import { DeleteFootnoteDialog, } from '../../components/views/DeleteFootnoteDial
23
24
  import { findParentNodeWithIdValue, getChildOfType } from '../../lib/utils';
24
25
  import ReactSubView from '../../views/ReactSubView';
25
26
  import { placeholderWidget } from '../placeholder';
26
- import { findTableInlineFootnoteIds, getInlineFootnotes, } from './footnotes-utils';
27
+ import { findTableInlineFootnoteIds, getAlphaOrderIndices, getInlineFootnotes, } from './footnotes-utils';
27
28
  export const footnotesKey = new PluginKey('footnotes');
28
- export const buildPluginState = (doc) => {
29
- const nodes = [];
30
- let index = 0;
31
- const labels = new Map();
32
- doc.descendants((node, pos, parentNode) => {
33
- if (isInlineFootnoteNode(node) &&
34
- parentNode &&
35
- parentNode.type !== schema.nodes.table_cell) {
36
- nodes.push([node, pos]);
37
- node.attrs.rids.forEach((rid) => {
38
- labels.set(rid, String(++index));
39
- });
40
- }
41
- });
42
- return { nodes, labels };
43
- };
44
29
  const scrollToInlineFootnote = (rid, view) => {
45
30
  view.state.doc.descendants((node, pos) => {
46
31
  const footnote = node;
@@ -118,6 +103,37 @@ const deleteFootnoteWidget = (node, props, footnoteType, footnoteMessage, id, ta
118
103
  });
119
104
  return deleteBtn;
120
105
  };
106
+ export const buildPluginState = (doc) => {
107
+ const inlineFootnotes = [];
108
+ const footnotes = new Map();
109
+ let footnoteElement;
110
+ doc.descendants((node, pos, parentNode) => {
111
+ if (isFootnoteNode(node)) {
112
+ footnotes.set(node.attrs.id, [node, pos]);
113
+ }
114
+ if (isFootnotesSectionNode(node)) {
115
+ node.descendants((node, childPos) => {
116
+ if (isFootnotesElementNode(node)) {
117
+ footnoteElement = [node, pos + childPos];
118
+ }
119
+ });
120
+ }
121
+ if (isInlineFootnoteNode(node) &&
122
+ parentNode &&
123
+ parentNode.type !== schema.nodes.table_cell) {
124
+ inlineFootnotes.push([node, pos]);
125
+ }
126
+ });
127
+ let index = 0;
128
+ const labels = new Map();
129
+ inlineFootnotes.sort((a, b) => a[1] - b[1]);
130
+ inlineFootnotes.forEach(([node]) => {
131
+ node.attrs.rids.forEach((rid) => {
132
+ labels.set(rid, getAlphaOrderIndices(index++));
133
+ });
134
+ });
135
+ return { inlineFootnotes, labels, footnotes, footnoteElement };
136
+ };
121
137
  export default (props) => {
122
138
  return new Plugin({
123
139
  key: footnotesKey,
@@ -126,25 +142,59 @@ export default (props) => {
126
142
  return buildPluginState(instance.doc);
127
143
  },
128
144
  apply(tr, value, oldState, newState) {
145
+ const prevState = footnotesKey.getState(oldState);
146
+ if (!tr.docChanged && prevState) {
147
+ return prevState;
148
+ }
129
149
  return buildPluginState(newState.doc);
130
150
  },
131
151
  },
132
152
  appendTransaction(transactions, oldState, newState) {
133
- const { nodes: oldInlineFootnoteNodes } = footnotesKey.getState(oldState);
134
- const { nodes: inlineFootnoteNodes, labels } = footnotesKey.getState(newState);
135
- if (isEqual(inlineFootnoteNodes, oldInlineFootnoteNodes)) {
153
+ const { inlineFootnotes: oldInlineFootnoteNodes } = footnotesKey.getState(oldState);
154
+ const { inlineFootnotes: inlineFootnoteNodes, footnotes, labels, footnoteElement, } = footnotesKey.getState(newState);
155
+ const prevIds = oldInlineFootnoteNodes.map(([node]) => node.attrs.rids);
156
+ const newIds = inlineFootnoteNodes.map(([node]) => node.attrs.rids);
157
+ if (!footnoteElement || isEqual(prevIds, newIds) || newIds.length < 2) {
136
158
  return null;
137
159
  }
138
160
  const { tr } = newState;
161
+ const footnotesReordered = [];
162
+ const footnotesRest = new Map(footnotes);
139
163
  inlineFootnoteNodes.forEach(([node, pos]) => {
140
164
  const footnote = node;
141
165
  const contents = footnote.attrs.rids
142
- .map((rid) => labels.get(rid))
166
+ .map((rid) => {
167
+ const currentfNode = footnotesRest.get(rid);
168
+ if (currentfNode) {
169
+ footnotesReordered.push(currentfNode[0]);
170
+ footnotesRest.delete(rid);
171
+ }
172
+ return labels.get(rid);
173
+ })
143
174
  .join('');
144
175
  if (footnote.attrs.contents !== contents) {
145
176
  tr.setNodeMarkup(pos, undefined, Object.assign(Object.assign({}, footnote.attrs), { rids: footnote.attrs.rids, contents }));
146
177
  }
147
178
  });
179
+ footnotesRest.forEach(([node]) => footnotesReordered.push(node));
180
+ const newFElement = schema.nodes.footnotes_element.create(footnoteElement[0].attrs, footnotesReordered);
181
+ if (newFElement) {
182
+ tr.replaceWith(footnoteElement[1], footnoteElement[1] + footnoteElement[0].nodeSize, newFElement);
183
+ }
184
+ const prevSelection = newState.selection;
185
+ const selectedFootnote = findParentNodeClosestToPos(prevSelection.$anchor, (node) => isFootnoteNode(node));
186
+ if (selectedFootnote && footnotes.size > 1) {
187
+ let newFootnotePos = 0;
188
+ for (let i = 0; i < footnotesReordered.length; i++) {
189
+ const node = footnotesReordered[i];
190
+ newFootnotePos += node.nodeSize;
191
+ if (node.attrs.id === selectedFootnote.node.attrs.id) {
192
+ break;
193
+ }
194
+ }
195
+ tr.setSelection(TextSelection.create(tr.doc, footnoteElement[1] + newFootnotePos));
196
+ }
197
+ skipTracking(tr);
148
198
  return tr;
149
199
  },
150
200
  props: {
@@ -27,4 +27,5 @@ interface InlineFootnote {
27
27
  pos: number;
28
28
  }
29
29
  export declare const getInlineFootnotes: (id: string, tableElement: NodeWithPos) => InlineFootnote[];
30
+ export declare function getAlphaOrderIndices(index: number): string;
30
31
  export {};
@@ -13,15 +13,17 @@
13
13
  * See the License for the specific language governing permissions and
14
14
  * limitations under the License.
15
15
  */
16
- import { InlineFootnoteNode, ManuscriptNode } from '@manuscripts/transform';
16
+ import { FootnoteNode, FootnotesElementNode, InlineFootnoteNode, ManuscriptNode } from '@manuscripts/transform';
17
17
  import { Plugin, PluginKey } from 'prosemirror-state';
18
18
  import { PluginProps } from '../../configs/editor-plugins';
19
19
  interface PluginState {
20
- nodes: [InlineFootnoteNode, number][];
20
+ inlineFootnotes: [InlineFootnoteNode, number][];
21
21
  labels: Map<string, string>;
22
+ footnotes: Map<string, [FootnoteNode, number]>;
23
+ footnoteElement?: [FootnotesElementNode, number];
22
24
  }
23
25
  export declare const footnotesKey: PluginKey<PluginState>;
24
- export declare const buildPluginState: (doc: ManuscriptNode) => PluginState;
25
26
  export declare const uncitedFootnoteWidget: () => () => HTMLSpanElement;
27
+ export declare const buildPluginState: (doc: ManuscriptNode) => PluginState;
26
28
  declare const _default: (props: PluginProps) => Plugin<PluginState>;
27
29
  export default _default;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@manuscripts/body-editor",
3
3
  "description": "Prosemirror components for editing and viewing manuscripts",
4
- "version": "1.8.3",
4
+ "version": "1.8.4-LEAN-3136-2MB",
5
5
  "repository": "github:Atypon-OpenSource/manuscripts-body-editor",
6
6
  "license": "Apache-2.0",
7
7
  "main": "dist/cjs",
@@ -31,9 +31,9 @@
31
31
  "@manuscripts/assets": "^0.6.4",
32
32
  "@manuscripts/json-schema": "^2.2.7",
33
33
  "@manuscripts/library": "^1.3.6",
34
- "@manuscripts/style-guide": "1.11.1",
35
- "@manuscripts/track-changes-plugin": "1.7.9",
36
- "@manuscripts/transform": "2.2.0",
34
+ "@manuscripts/style-guide": "^1.11.2",
35
+ "@manuscripts/track-changes-plugin": "1.7.10-LEAN-3136-2MB",
36
+ "@manuscripts/transform": "^2.2.1",
37
37
  "astrocite-eutils": "^0.16.4",
38
38
  "axios": "^1.6.7",
39
39
  "mathjax-full": "^3.2.2",