@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 +176 -14
- package/dist/index.js +176 -14
- package/dist/lib/model.d.ts +2 -2
- package/dist/lib/model.d.ts.map +1 -1
- package/package.json +3 -2
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
|
-
//
|
|
227
|
-
//
|
|
228
|
-
//
|
|
229
|
-
//
|
|
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
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
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
|
-
{
|
|
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
|
-
//
|
|
195
|
-
//
|
|
196
|
-
//
|
|
197
|
-
//
|
|
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
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
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
|
-
{
|
|
863
|
+
{
|
|
864
|
+
"data-footnote": String(mark.attrs["num"]),
|
|
865
|
+
...id != null ? { "data-footnote-id": id } : {}
|
|
866
|
+
},
|
|
705
867
|
0
|
|
706
868
|
];
|
|
707
869
|
}
|
package/dist/lib/model.d.ts
CHANGED
|
@@ -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" | "
|
|
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" | "
|
|
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';
|
package/dist/lib/model.d.ts.map
CHANGED
|
@@ -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;
|
|
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.
|
|
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
|
}
|