@shadow-garden/bapbong-model 0.11.2 → 0.13.0

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/dist/index.cjs CHANGED
@@ -71,6 +71,73 @@ function fieldAt(doc, pos) {
71
71
 
72
72
  // packages/model/src/lib/model.ts
73
73
  var import_prosemirror_model = require("prosemirror-model");
74
+
75
+ // packages/contracts/dist/index.js
76
+ var FIELD_SET = {
77
+ family: true,
78
+ sizePt: true,
79
+ bold: true,
80
+ italic: true,
81
+ letterSpacing: true,
82
+ scaleX: true,
83
+ kerning: true
84
+ };
85
+ var FIELDS = Object.keys(FIELD_SET);
86
+ var SUB_DIGIT = "\u2080\u2081\u2082\u2083\u2084\u2085\u2086\u2087\u2088\u2089";
87
+ var SUP_DIGIT = "\u2070\xB9\xB2\xB3\u2074\u2075\u2076\u2077\u2078\u2079";
88
+ var digitsVia = (s, alphabet) => /^[0-9]+$/.test(s) ? [...s].map((d) => alphabet[Number(d)]).join("") : null;
89
+ function astToLinear(row) {
90
+ const count = (s) => [...s].length;
91
+ const paren = (s) => count(s) <= 1 || s.startsWith("(") && s.endsWith(")") ? s : `(${s})`;
92
+ const sub = (s) => digitsVia(s, SUB_DIGIT) ?? (count(s) > 1 ? `_(${s})` : `_${s}`);
93
+ const sup = (s) => digitsVia(s, SUP_DIGIT) ?? (count(s) > 1 ? `^(${s})` : `^${s}`);
94
+ const one = (n) => {
95
+ switch (n.t) {
96
+ case "chr":
97
+ return n.ch;
98
+ case "frac":
99
+ return `${paren(astToLinear(n.num))}/${paren(astToLinear(n.den))}`;
100
+ case "rad": {
101
+ const deg = astToLinear(n.deg);
102
+ return `${deg ? sup(deg) : ""}\u221A(${astToLinear(n.body)})`;
103
+ }
104
+ case "scr": {
105
+ const lo = astToLinear(n.sub);
106
+ const hi = astToLinear(n.sup);
107
+ return astToLinear(n.base) + (lo ? sub(lo) : "") + (hi ? sup(hi) : "");
108
+ }
109
+ case "fence":
110
+ return n.l + astToLinear(n.body) + n.r;
111
+ case "big": {
112
+ const lo = astToLinear(n.lo);
113
+ const hi = astToLinear(n.hi);
114
+ return n.op + (lo ? sub(lo) : "") + (hi ? sup(hi) : "") + paren(astToLinear(n.body));
115
+ }
116
+ }
117
+ };
118
+ return row.map(one).join("");
119
+ }
120
+ function isEqRow(v) {
121
+ if (!Array.isArray(v)) return false;
122
+ return v.every((n) => {
123
+ if (typeof n !== "object" || n === null) return false;
124
+ const t = n.t;
125
+ if (t === "chr") return typeof n.ch === "string";
126
+ if (t === "frac")
127
+ return isEqRow(n.num) && isEqRow(n.den);
128
+ if (t === "rad")
129
+ return isEqRow(n.deg) && isEqRow(n.body);
130
+ if (t === "scr")
131
+ return isEqRow(n.base) && isEqRow(n.sub) && isEqRow(n.sup);
132
+ if (t === "fence")
133
+ return typeof n.l === "string" && typeof n.r === "string" && isEqRow(n.body);
134
+ if (t === "big")
135
+ return typeof n.op === "string" && isEqRow(n.lo) && isEqRow(n.hi) && isEqRow(n.body);
136
+ return false;
137
+ });
138
+ }
139
+
140
+ // packages/model/src/lib/model.ts
74
141
  function dataJson(el, name) {
75
142
  const raw = el.getAttribute(name);
76
143
  if (!raw) return null;
@@ -102,6 +169,7 @@ function pastedImageAttrs(el) {
102
169
  return {
103
170
  src,
104
171
  alt: e.getAttribute("alt") ?? "",
172
+ title: e.getAttribute("title"),
105
173
  width: dim(e.getAttribute("width")),
106
174
  height: dim(e.getAttribute("height")),
107
175
  crop: dataJson(el, "data-crop"),
@@ -148,6 +216,11 @@ var schema = new import_prosemirror_model.Schema({
148
216
  // body w:sectPr. Edited via setDocAttribute (page-setup commands) so
149
217
  // orientation/paper-size changes undo cleanly. null → A4 default.
150
218
  page: { default: null },
219
+ // DocCompat (contracts) — the Word compatibility profile settings.xml
220
+ // declares (compatibilityMode + the compat flags), resolved to the
221
+ // answers layout rules ask. Importer-set on every story doc; null
222
+ // (an editor-authored doc) reads as current Word.
223
+ compat: { default: null },
151
224
  // CommentNode[] (contracts) — comment threads keyed to `comment` marks.
152
225
  // Edited via setDocAttribute so add/reply/resolve/delete undo cleanly.
153
226
  comments: { default: null },
@@ -223,10 +296,12 @@ var schema = new import_prosemirror_model.Schema({
223
296
  shading: { default: null },
224
297
  // The paragraph MARK's own font ({ family?, sizePt?, bold?, italic? }
225
298
  // or null) — w:pPr/w:rPr resolved through the same cascade a run gets.
226
- // Set only on paragraphs imported EMPTY, where the mark is the only
227
- // thing on the line and therefore sets its height. `carry.markRPr`
228
- // keeps the same rPr verbatim for export; this is the measured
229
- // reading of it, and nothing writes it back.
299
+ // The mark is a glyph on the paragraph's LAST line (the only glyph
300
+ // when the paragraph is empty), so it takes part in that line's
301
+ // height. Importer-set on every paragraph; the font commands edit it
302
+ // when the selection takes the mark in (Word: a ¶ inside the
303
+ // selection is re-sized with the text); the exporter writes it back
304
+ // into w:pPr/w:rPr ahead of `carry.markRPr` (the rest of that rPr).
230
305
  markFont: { default: null },
231
306
  // Carry-through fidelity (docx round-trip): OOXML paragraph
232
307
  // properties the model does NOT represent, preserved verbatim so a
@@ -284,6 +359,42 @@ var schema = new import_prosemirror_model.Schema({
284
359
  parseDOM: [{ tag: "br.column-break" }],
285
360
  toDOM: () => ["br", { class: "column-break" }]
286
361
  },
362
+ // An equation with 2D structure — the AST (EqNode[], contracts) rides
363
+ // `ast`. An inline ATOM: the layout engine typesets it into a vector box
364
+ // sized by `sizePt`; editing goes through the equation plugin's slot
365
+ // editor, never the hidden text editor. toDOM carries the linear
366
+ // spelling as text, so the a11y mirror and copy read the math.
367
+ equation: {
368
+ inline: true,
369
+ group: "inline",
370
+ atom: true,
371
+ attrs: { ast: {}, sizePt: { default: 12 } },
372
+ parseDOM: [
373
+ {
374
+ tag: "span[data-eq]",
375
+ getAttrs: (el) => {
376
+ const ast = dataJson(el, "data-eq");
377
+ if (!isEqRow(ast)) return false;
378
+ const size = Number(el.getAttribute("data-eq-size"));
379
+ return {
380
+ ast,
381
+ sizePt: Number.isFinite(size) && size > 0 ? size : 12
382
+ };
383
+ }
384
+ }
385
+ ],
386
+ toDOM(node) {
387
+ const ast = node.attrs["ast"];
388
+ return [
389
+ "span",
390
+ {
391
+ "data-eq": JSON.stringify(ast),
392
+ "data-eq-size": String(node.attrs["sizePt"])
393
+ },
394
+ astToLinear(isEqRow(ast) ? ast : [])
395
+ ];
396
+ }
397
+ },
287
398
  // Inline image. `src` is typically a data URL (the importer inlines the
288
399
  // embedded media); width/height are CSS pixels, null if unspecified.
289
400
  image: {
@@ -293,6 +404,9 @@ var schema = new import_prosemirror_model.Schema({
293
404
  attrs: {
294
405
  src: {},
295
406
  alt: { default: "" },
407
+ // wp:docPr @title — the "Title" field of Word's alt-text pane, kept
408
+ // separate from `alt` (@descr) so both round-trip verbatim.
409
+ title: { default: null },
296
410
  width: { default: null },
297
411
  height: { default: null },
298
412
  // wp:anchor (floating image): { wrap: 'square'|'topAndBottom'|'none',
@@ -304,6 +418,22 @@ var schema = new import_prosemirror_model.Schema({
304
418
  // { kind: 'rect'|'line', stroke?, strokeWidth?, fill?, flipV? } — src
305
419
  // is then '' and the box paints as vector. Null for real bitmaps.
306
420
  shape: { default: null },
421
+ // Replayed metafile (VectorImageSpec) — how MathType/OLE equation
422
+ // previews (vector WMF) paint. `src` keeps the original metafile
423
+ // data URL for export; the painter draws the ops instead. Null for
424
+ // bitmaps and preset shapes.
425
+ vector: { default: null },
426
+ // The equation's LINEAR text recovered from the metafile's embedded
427
+ // MTEF (MathType semantics) — what "Convert to editable equation"
428
+ // replaces the picture with. Null when the picture carries none.
429
+ equation: { default: null },
430
+ // The same equation as an AST (EqNode[]) — what conversion turns
431
+ // into a real 2D equation node. Null when undecodable.
432
+ equationAst: { default: null },
433
+ // w:position on the run holding a legacy object — baseline shift in
434
+ // px, positive UP (same convention as InlineRun.raise). MathType
435
+ // lowers every equation so its baseline meets the text line's.
436
+ raise: { default: 0 },
307
437
  // Textbox (wps:txbx) content riding a shape: { paragraphs: <paragraph
308
438
  // node JSON>[], inset?: {l,t,r,b} px }. The layout engine flows the
309
439
  // paragraphs inside the shape's box (paint-only, not editable v1).
@@ -316,7 +446,10 @@ var schema = new import_prosemirror_model.Schema({
316
446
  // keeps its size; the selected region scales to fill it.
317
447
  crop: { default: null },
318
448
  // a:ln on pic:spPr — Word's picture border, as a BorderSide, or null.
319
- outline: { default: null }
449
+ outline: { default: null },
450
+ // a:solidFill on pic:spPr — "#RRGGBB" painted behind the bitmap
451
+ // (shows through transparency), or null.
452
+ background: { default: null }
320
453
  },
321
454
  parseDOM: [{ tag: "img[src]", getAttrs: pastedImageAttrs }],
322
455
  toDOM(node) {
@@ -325,10 +458,13 @@ var schema = new import_prosemirror_model.Schema({
325
458
  src: a["src"],
326
459
  alt: a["alt"]
327
460
  };
461
+ if (a["title"]) attrs["title"] = a["title"];
328
462
  if (a["width"] != null) attrs["width"] = String(a["width"]);
329
463
  if (a["height"] != null) attrs["height"] = String(a["height"]);
330
464
  if (a["crop"]) attrs["data-crop"] = JSON.stringify(a["crop"]);
331
465
  if (a["outline"]) attrs["data-outline"] = JSON.stringify(a["outline"]);
466
+ if (a["background"])
467
+ attrs["data-background"] = a["background"];
332
468
  return ["img", attrs];
333
469
  }
334
470
  },
@@ -361,6 +497,11 @@ var schema = new import_prosemirror_model.Schema({
361
497
  borders: { default: null },
362
498
  // w:tblPr/w:jc — 'center' | 'right' table alignment, or null (left).
363
499
  align: { default: null },
500
+ // w:tblInd resolved to px: where a left-aligned table's leading
501
+ // border sits relative to the text margin (compat-mode aware — see
502
+ // the importer). null = Word's implicit outdent by the left cell
503
+ // margin. Importer-set; the element itself rides carry for export.
504
+ indent: { default: null },
364
505
  // Carry-through fidelity: unmodelled w:tblPr children (tblStyle,
365
506
  // tblLayout, tblLook, tblInd, floating tblpPr, …) as one raw XML
366
507
  // string ({ tblPr: string }), or null. Importer-set; the exporter
@@ -377,6 +518,7 @@ var schema = new import_prosemirror_model.Schema({
377
518
  borders: dataJson(el, "data-borders"),
378
519
  cellPadding: dataJson(el, "data-cell-padding"),
379
520
  align: el.getAttribute("data-align"),
521
+ indent: dataJson(el, "data-indent"),
380
522
  carry: dataJson(el, "data-carry")
381
523
  })
382
524
  }
@@ -388,6 +530,7 @@ var schema = new import_prosemirror_model.Schema({
388
530
  if (a["cellPadding"])
389
531
  dom["data-cell-padding"] = JSON.stringify(a["cellPadding"]);
390
532
  if (a["align"]) dom["data-align"] = String(a["align"]);
533
+ if (a["indent"] != null) dom["data-indent"] = String(a["indent"]);
391
534
  if (a["carry"]) dom["data-carry"] = JSON.stringify(a["carry"]);
392
535
  return ["table", dom, ["tbody", 0]];
393
536
  }
@@ -655,6 +798,15 @@ var schema = new import_prosemirror_model.Schema({
655
798
  0
656
799
  ]
657
800
  },
801
+ // m:oMath — the run belongs to an equation. Linear v1: the letterforms
802
+ // live in the TEXT as Unicode math alphanumerics (𝑥, 𝒫, ℝ — see
803
+ // math.ts), so the mark is semantic, not visual: it scopes math
804
+ // autocorrect, keeps the region addressable for the coming in-place
805
+ // editor, and tells the exporter to wrap these runs back into m:oMath.
806
+ math: {
807
+ parseDOM: [{ tag: "span[data-math]" }],
808
+ toDOM: () => ["span", { "data-math": "1" }, 0]
809
+ },
658
810
  // w:highlight / w:shd w:fill — run background color ("#RRGGBB")
659
811
  highlight: {
660
812
  attrs: { color: {} },
@@ -713,27 +865,37 @@ var schema = new import_prosemirror_model.Schema({
713
865
  },
714
866
  // w:footnoteReference — the carrier text is the superscript number; `num`
715
867
  // lets the layout engine match the reference to its page-bottom body.
868
+ // `id` is the ORIGINAL w:id from footnotes.xml: that part is carried
869
+ // byte-for-byte on save, so the exported reference must point at the
870
+ // source id, not the display number (Word reserves -1/0 and reuses ids
871
+ // freely — they rarely equal 1..N in document order).
716
872
  footnote: {
717
- attrs: { num: {} },
873
+ attrs: { num: {}, id: { default: null } },
718
874
  inclusive: false,
719
875
  parseDOM: [
720
876
  {
721
877
  tag: "sup[data-footnote]",
722
878
  // `el` is an HTMLElement at runtime; this package has no DOM lib, so
723
879
  // narrow structurally rather than naming the type.
724
- getAttrs: (el) => ({
725
- num: Number(
726
- el.getAttribute(
727
- "data-footnote"
728
- )
729
- ) || 0
730
- })
880
+ getAttrs: (el) => {
881
+ const get = (n) => el.getAttribute(
882
+ n
883
+ );
884
+ return {
885
+ num: Number(get("data-footnote")) || 0,
886
+ id: get("data-footnote-id")
887
+ };
888
+ }
731
889
  }
732
890
  ],
733
891
  toDOM(mark) {
892
+ const id = mark.attrs["id"];
734
893
  return [
735
894
  "sup",
736
- { "data-footnote": String(mark.attrs["num"]) },
895
+ {
896
+ "data-footnote": String(mark.attrs["num"]),
897
+ ...id != null ? { "data-footnote-id": id } : {}
898
+ },
737
899
  0
738
900
  ];
739
901
  }
package/dist/index.js CHANGED
@@ -39,6 +39,73 @@ function fieldAt(doc, pos) {
39
39
 
40
40
  // packages/model/src/lib/model.ts
41
41
  import { Schema } from "prosemirror-model";
42
+
43
+ // packages/contracts/dist/index.js
44
+ var FIELD_SET = {
45
+ family: true,
46
+ sizePt: true,
47
+ bold: true,
48
+ italic: true,
49
+ letterSpacing: true,
50
+ scaleX: true,
51
+ kerning: true
52
+ };
53
+ var FIELDS = Object.keys(FIELD_SET);
54
+ var SUB_DIGIT = "\u2080\u2081\u2082\u2083\u2084\u2085\u2086\u2087\u2088\u2089";
55
+ var SUP_DIGIT = "\u2070\xB9\xB2\xB3\u2074\u2075\u2076\u2077\u2078\u2079";
56
+ var digitsVia = (s, alphabet) => /^[0-9]+$/.test(s) ? [...s].map((d) => alphabet[Number(d)]).join("") : null;
57
+ function astToLinear(row) {
58
+ const count = (s) => [...s].length;
59
+ const paren = (s) => count(s) <= 1 || s.startsWith("(") && s.endsWith(")") ? s : `(${s})`;
60
+ const sub = (s) => digitsVia(s, SUB_DIGIT) ?? (count(s) > 1 ? `_(${s})` : `_${s}`);
61
+ const sup = (s) => digitsVia(s, SUP_DIGIT) ?? (count(s) > 1 ? `^(${s})` : `^${s}`);
62
+ const one = (n) => {
63
+ switch (n.t) {
64
+ case "chr":
65
+ return n.ch;
66
+ case "frac":
67
+ return `${paren(astToLinear(n.num))}/${paren(astToLinear(n.den))}`;
68
+ case "rad": {
69
+ const deg = astToLinear(n.deg);
70
+ return `${deg ? sup(deg) : ""}\u221A(${astToLinear(n.body)})`;
71
+ }
72
+ case "scr": {
73
+ const lo = astToLinear(n.sub);
74
+ const hi = astToLinear(n.sup);
75
+ return astToLinear(n.base) + (lo ? sub(lo) : "") + (hi ? sup(hi) : "");
76
+ }
77
+ case "fence":
78
+ return n.l + astToLinear(n.body) + n.r;
79
+ case "big": {
80
+ const lo = astToLinear(n.lo);
81
+ const hi = astToLinear(n.hi);
82
+ return n.op + (lo ? sub(lo) : "") + (hi ? sup(hi) : "") + paren(astToLinear(n.body));
83
+ }
84
+ }
85
+ };
86
+ return row.map(one).join("");
87
+ }
88
+ function isEqRow(v) {
89
+ if (!Array.isArray(v)) return false;
90
+ return v.every((n) => {
91
+ if (typeof n !== "object" || n === null) return false;
92
+ const t = n.t;
93
+ if (t === "chr") return typeof n.ch === "string";
94
+ if (t === "frac")
95
+ return isEqRow(n.num) && isEqRow(n.den);
96
+ if (t === "rad")
97
+ return isEqRow(n.deg) && isEqRow(n.body);
98
+ if (t === "scr")
99
+ return isEqRow(n.base) && isEqRow(n.sub) && isEqRow(n.sup);
100
+ if (t === "fence")
101
+ return typeof n.l === "string" && typeof n.r === "string" && isEqRow(n.body);
102
+ if (t === "big")
103
+ return typeof n.op === "string" && isEqRow(n.lo) && isEqRow(n.hi) && isEqRow(n.body);
104
+ return false;
105
+ });
106
+ }
107
+
108
+ // packages/model/src/lib/model.ts
42
109
  function dataJson(el, name) {
43
110
  const raw = el.getAttribute(name);
44
111
  if (!raw) return null;
@@ -70,6 +137,7 @@ function pastedImageAttrs(el) {
70
137
  return {
71
138
  src,
72
139
  alt: e.getAttribute("alt") ?? "",
140
+ title: e.getAttribute("title"),
73
141
  width: dim(e.getAttribute("width")),
74
142
  height: dim(e.getAttribute("height")),
75
143
  crop: dataJson(el, "data-crop"),
@@ -116,6 +184,11 @@ var schema = new Schema({
116
184
  // body w:sectPr. Edited via setDocAttribute (page-setup commands) so
117
185
  // orientation/paper-size changes undo cleanly. null → A4 default.
118
186
  page: { default: null },
187
+ // DocCompat (contracts) — the Word compatibility profile settings.xml
188
+ // declares (compatibilityMode + the compat flags), resolved to the
189
+ // answers layout rules ask. Importer-set on every story doc; null
190
+ // (an editor-authored doc) reads as current Word.
191
+ compat: { default: null },
119
192
  // CommentNode[] (contracts) — comment threads keyed to `comment` marks.
120
193
  // Edited via setDocAttribute so add/reply/resolve/delete undo cleanly.
121
194
  comments: { default: null },
@@ -191,10 +264,12 @@ var schema = new Schema({
191
264
  shading: { default: null },
192
265
  // The paragraph MARK's own font ({ family?, sizePt?, bold?, italic? }
193
266
  // or null) — w:pPr/w:rPr resolved through the same cascade a run gets.
194
- // Set only on paragraphs imported EMPTY, where the mark is the only
195
- // thing on the line and therefore sets its height. `carry.markRPr`
196
- // keeps the same rPr verbatim for export; this is the measured
197
- // reading of it, and nothing writes it back.
267
+ // The mark is a glyph on the paragraph's LAST line (the only glyph
268
+ // when the paragraph is empty), so it takes part in that line's
269
+ // height. Importer-set on every paragraph; the font commands edit it
270
+ // when the selection takes the mark in (Word: a ¶ inside the
271
+ // selection is re-sized with the text); the exporter writes it back
272
+ // into w:pPr/w:rPr ahead of `carry.markRPr` (the rest of that rPr).
198
273
  markFont: { default: null },
199
274
  // Carry-through fidelity (docx round-trip): OOXML paragraph
200
275
  // properties the model does NOT represent, preserved verbatim so a
@@ -252,6 +327,42 @@ var schema = new Schema({
252
327
  parseDOM: [{ tag: "br.column-break" }],
253
328
  toDOM: () => ["br", { class: "column-break" }]
254
329
  },
330
+ // An equation with 2D structure — the AST (EqNode[], contracts) rides
331
+ // `ast`. An inline ATOM: the layout engine typesets it into a vector box
332
+ // sized by `sizePt`; editing goes through the equation plugin's slot
333
+ // editor, never the hidden text editor. toDOM carries the linear
334
+ // spelling as text, so the a11y mirror and copy read the math.
335
+ equation: {
336
+ inline: true,
337
+ group: "inline",
338
+ atom: true,
339
+ attrs: { ast: {}, sizePt: { default: 12 } },
340
+ parseDOM: [
341
+ {
342
+ tag: "span[data-eq]",
343
+ getAttrs: (el) => {
344
+ const ast = dataJson(el, "data-eq");
345
+ if (!isEqRow(ast)) return false;
346
+ const size = Number(el.getAttribute("data-eq-size"));
347
+ return {
348
+ ast,
349
+ sizePt: Number.isFinite(size) && size > 0 ? size : 12
350
+ };
351
+ }
352
+ }
353
+ ],
354
+ toDOM(node) {
355
+ const ast = node.attrs["ast"];
356
+ return [
357
+ "span",
358
+ {
359
+ "data-eq": JSON.stringify(ast),
360
+ "data-eq-size": String(node.attrs["sizePt"])
361
+ },
362
+ astToLinear(isEqRow(ast) ? ast : [])
363
+ ];
364
+ }
365
+ },
255
366
  // Inline image. `src` is typically a data URL (the importer inlines the
256
367
  // embedded media); width/height are CSS pixels, null if unspecified.
257
368
  image: {
@@ -261,6 +372,9 @@ var schema = new Schema({
261
372
  attrs: {
262
373
  src: {},
263
374
  alt: { default: "" },
375
+ // wp:docPr @title — the "Title" field of Word's alt-text pane, kept
376
+ // separate from `alt` (@descr) so both round-trip verbatim.
377
+ title: { default: null },
264
378
  width: { default: null },
265
379
  height: { default: null },
266
380
  // wp:anchor (floating image): { wrap: 'square'|'topAndBottom'|'none',
@@ -272,6 +386,22 @@ var schema = new Schema({
272
386
  // { kind: 'rect'|'line', stroke?, strokeWidth?, fill?, flipV? } — src
273
387
  // is then '' and the box paints as vector. Null for real bitmaps.
274
388
  shape: { default: null },
389
+ // Replayed metafile (VectorImageSpec) — how MathType/OLE equation
390
+ // previews (vector WMF) paint. `src` keeps the original metafile
391
+ // data URL for export; the painter draws the ops instead. Null for
392
+ // bitmaps and preset shapes.
393
+ vector: { default: null },
394
+ // The equation's LINEAR text recovered from the metafile's embedded
395
+ // MTEF (MathType semantics) — what "Convert to editable equation"
396
+ // replaces the picture with. Null when the picture carries none.
397
+ equation: { default: null },
398
+ // The same equation as an AST (EqNode[]) — what conversion turns
399
+ // into a real 2D equation node. Null when undecodable.
400
+ equationAst: { default: null },
401
+ // w:position on the run holding a legacy object — baseline shift in
402
+ // px, positive UP (same convention as InlineRun.raise). MathType
403
+ // lowers every equation so its baseline meets the text line's.
404
+ raise: { default: 0 },
275
405
  // Textbox (wps:txbx) content riding a shape: { paragraphs: <paragraph
276
406
  // node JSON>[], inset?: {l,t,r,b} px }. The layout engine flows the
277
407
  // paragraphs inside the shape's box (paint-only, not editable v1).
@@ -284,7 +414,10 @@ var schema = new Schema({
284
414
  // keeps its size; the selected region scales to fill it.
285
415
  crop: { default: null },
286
416
  // a:ln on pic:spPr — Word's picture border, as a BorderSide, or null.
287
- outline: { default: null }
417
+ outline: { default: null },
418
+ // a:solidFill on pic:spPr — "#RRGGBB" painted behind the bitmap
419
+ // (shows through transparency), or null.
420
+ background: { default: null }
288
421
  },
289
422
  parseDOM: [{ tag: "img[src]", getAttrs: pastedImageAttrs }],
290
423
  toDOM(node) {
@@ -293,10 +426,13 @@ var schema = new Schema({
293
426
  src: a["src"],
294
427
  alt: a["alt"]
295
428
  };
429
+ if (a["title"]) attrs["title"] = a["title"];
296
430
  if (a["width"] != null) attrs["width"] = String(a["width"]);
297
431
  if (a["height"] != null) attrs["height"] = String(a["height"]);
298
432
  if (a["crop"]) attrs["data-crop"] = JSON.stringify(a["crop"]);
299
433
  if (a["outline"]) attrs["data-outline"] = JSON.stringify(a["outline"]);
434
+ if (a["background"])
435
+ attrs["data-background"] = a["background"];
300
436
  return ["img", attrs];
301
437
  }
302
438
  },
@@ -329,6 +465,11 @@ var schema = new Schema({
329
465
  borders: { default: null },
330
466
  // w:tblPr/w:jc — 'center' | 'right' table alignment, or null (left).
331
467
  align: { default: null },
468
+ // w:tblInd resolved to px: where a left-aligned table's leading
469
+ // border sits relative to the text margin (compat-mode aware — see
470
+ // the importer). null = Word's implicit outdent by the left cell
471
+ // margin. Importer-set; the element itself rides carry for export.
472
+ indent: { default: null },
332
473
  // Carry-through fidelity: unmodelled w:tblPr children (tblStyle,
333
474
  // tblLayout, tblLook, tblInd, floating tblpPr, …) as one raw XML
334
475
  // string ({ tblPr: string }), or null. Importer-set; the exporter
@@ -345,6 +486,7 @@ var schema = new Schema({
345
486
  borders: dataJson(el, "data-borders"),
346
487
  cellPadding: dataJson(el, "data-cell-padding"),
347
488
  align: el.getAttribute("data-align"),
489
+ indent: dataJson(el, "data-indent"),
348
490
  carry: dataJson(el, "data-carry")
349
491
  })
350
492
  }
@@ -356,6 +498,7 @@ var schema = new Schema({
356
498
  if (a["cellPadding"])
357
499
  dom["data-cell-padding"] = JSON.stringify(a["cellPadding"]);
358
500
  if (a["align"]) dom["data-align"] = String(a["align"]);
501
+ if (a["indent"] != null) dom["data-indent"] = String(a["indent"]);
359
502
  if (a["carry"]) dom["data-carry"] = JSON.stringify(a["carry"]);
360
503
  return ["table", dom, ["tbody", 0]];
361
504
  }
@@ -623,6 +766,15 @@ var schema = new Schema({
623
766
  0
624
767
  ]
625
768
  },
769
+ // m:oMath — the run belongs to an equation. Linear v1: the letterforms
770
+ // live in the TEXT as Unicode math alphanumerics (𝑥, 𝒫, ℝ — see
771
+ // math.ts), so the mark is semantic, not visual: it scopes math
772
+ // autocorrect, keeps the region addressable for the coming in-place
773
+ // editor, and tells the exporter to wrap these runs back into m:oMath.
774
+ math: {
775
+ parseDOM: [{ tag: "span[data-math]" }],
776
+ toDOM: () => ["span", { "data-math": "1" }, 0]
777
+ },
626
778
  // w:highlight / w:shd w:fill — run background color ("#RRGGBB")
627
779
  highlight: {
628
780
  attrs: { color: {} },
@@ -681,27 +833,37 @@ var schema = new Schema({
681
833
  },
682
834
  // w:footnoteReference — the carrier text is the superscript number; `num`
683
835
  // lets the layout engine match the reference to its page-bottom body.
836
+ // `id` is the ORIGINAL w:id from footnotes.xml: that part is carried
837
+ // byte-for-byte on save, so the exported reference must point at the
838
+ // source id, not the display number (Word reserves -1/0 and reuses ids
839
+ // freely — they rarely equal 1..N in document order).
684
840
  footnote: {
685
- attrs: { num: {} },
841
+ attrs: { num: {}, id: { default: null } },
686
842
  inclusive: false,
687
843
  parseDOM: [
688
844
  {
689
845
  tag: "sup[data-footnote]",
690
846
  // `el` is an HTMLElement at runtime; this package has no DOM lib, so
691
847
  // narrow structurally rather than naming the type.
692
- getAttrs: (el) => ({
693
- num: Number(
694
- el.getAttribute(
695
- "data-footnote"
696
- )
697
- ) || 0
698
- })
848
+ getAttrs: (el) => {
849
+ const get = (n) => el.getAttribute(
850
+ n
851
+ );
852
+ return {
853
+ num: Number(get("data-footnote")) || 0,
854
+ id: get("data-footnote-id")
855
+ };
856
+ }
699
857
  }
700
858
  ],
701
859
  toDOM(mark) {
860
+ const id = mark.attrs["id"];
702
861
  return [
703
862
  "sup",
704
- { "data-footnote": String(mark.attrs["num"]) },
863
+ {
864
+ "data-footnote": String(mark.attrs["num"]),
865
+ ...id != null ? { "data-footnote-id": id } : {}
866
+ },
705
867
  0
706
868
  ];
707
869
  }
@@ -8,7 +8,7 @@ import { Schema } from 'prosemirror-model';
8
8
  * extend THIS schema so the importer, layout engine, and (canvas) painter all
9
9
  * agree on one document model.
10
10
  */
11
- export declare const schema: Schema<"paragraph" | "doc" | "text" | "hard_break" | "column_break" | "image" | "page_field" | "table" | "table_row" | "table_cell", "strong" | "em" | "underline" | "strike" | "dstrike" | "smallCaps" | "textColor" | "fontSize" | "vertAlign" | "letterSpacing" | "kern" | "charScale" | "position" | "highlight" | "fontFamily" | "link" | "footnote" | "carryRPr">;
11
+ export declare const schema: Schema<"paragraph" | "text" | "table" | "doc" | "hard_break" | "column_break" | "equation" | "image" | "page_field" | "table_row" | "table_cell", "letterSpacing" | "link" | "strike" | "underline" | "strong" | "em" | "dstrike" | "smallCaps" | "textColor" | "fontSize" | "vertAlign" | "kern" | "charScale" | "position" | "math" | "highlight" | "fontFamily" | "footnote" | "carryRPr">;
12
12
  /** Concrete schema type, handy for typing Node/Mark across packages. */
13
13
  export type BapbongSchema = typeof schema;
14
14
  /**
@@ -17,7 +17,7 @@ export type BapbongSchema = typeof schema;
17
17
  * (@user). Comment bodies are stored as this schema's JSON on the comment
18
18
  * thread, kept separate from the document schema.
19
19
  */
20
- export declare const commentSchema: Schema<"paragraph" | "doc" | "text" | "mention", never>;
20
+ export declare const commentSchema: Schema<"paragraph" | "text" | "doc" | "mention", never>;
21
21
  export type CommentSchema = typeof commentSchema;
22
22
  /** Paragraph horizontal alignment (mirrors w:jc). */
23
23
  export type Align = 'left' | 'center' | 'right' | 'justify';
@@ -1 +1 @@
1
- {"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../src/lib/model.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAyF3C;;;;;;;;GAQG;AACH,eAAO,MAAM,MAAM,yWAuoBjB,CAAC;AAEH,wEAAwE;AACxE,MAAM,MAAM,aAAa,GAAG,OAAO,MAAM,CAAC;AAE1C;;;;;GAKG;AACH,eAAO,MAAM,aAAa,yDA0CxB,CAAC;AAEH,MAAM,MAAM,aAAa,GAAG,OAAO,aAAa,CAAC;AAEjD,qDAAqD;AACrD,MAAM,MAAM,KAAK,GAAG,MAAM,GAAG,QAAQ,GAAG,OAAO,GAAG,SAAS,CAAC;AAE5D;+EAC+E;AAC/E,MAAM,WAAW,MAAM;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,2EAA2E;AAC3E,MAAM,WAAW,OAAO;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC;IACxC;;;6EAGyE;IACzE,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,QAAQ,GAAG,IAAI,CAAC;IACtB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,OAAO,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,OAAO,CAAC,EAAE,OAAO,GAAG,UAAU,GAAG,IAAI,CAAC;CACvC;AA0BD;;;4EAG4E;AAC5E,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB"}
1
+ {"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../src/lib/model.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AA2F3C;;;;;;;;GAQG;AACH,eAAO,MAAM,MAAM,+XAsuBjB,CAAC;AAEH,wEAAwE;AACxE,MAAM,MAAM,aAAa,GAAG,OAAO,MAAM,CAAC;AAE1C;;;;;GAKG;AACH,eAAO,MAAM,aAAa,yDA0CxB,CAAC;AAEH,MAAM,MAAM,aAAa,GAAG,OAAO,aAAa,CAAC;AAEjD,qDAAqD;AACrD,MAAM,MAAM,KAAK,GAAG,MAAM,GAAG,QAAQ,GAAG,OAAO,GAAG,SAAS,CAAC;AAE5D;+EAC+E;AAC/E,MAAM,WAAW,MAAM;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,2EAA2E;AAC3E,MAAM,WAAW,OAAO;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC;IACxC;;;6EAGyE;IACzE,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,QAAQ,GAAG,IAAI,CAAC;IACtB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,OAAO,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,OAAO,CAAC,EAAE,OAAO,GAAG,UAAU,GAAG,IAAI,CAAC;CACvC;AA0BD;;;4EAG4E;AAC5E,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shadow-garden/bapbong-model",
3
- "version": "0.11.2",
3
+ "version": "0.13.0",
4
4
  "description": "bapbong — ProseMirror document model / schema",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -54,6 +54,7 @@
54
54
  }
55
55
  },
56
56
  "dependencies": {
57
- "prosemirror-model": "^1.25.7"
57
+ "prosemirror-model": "^1.25.7",
58
+ "@shadow-garden/bapbong-contracts": "^0.14.0"
58
59
  }
59
60
  }