@shadow-garden/bapbong-model 0.12.0 → 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;
@@ -292,6 +359,42 @@ var schema = new import_prosemirror_model.Schema({
292
359
  parseDOM: [{ tag: "br.column-break" }],
293
360
  toDOM: () => ["br", { class: "column-break" }]
294
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
+ },
295
398
  // Inline image. `src` is typically a data URL (the importer inlines the
296
399
  // embedded media); width/height are CSS pixels, null if unspecified.
297
400
  image: {
@@ -315,6 +418,22 @@ var schema = new import_prosemirror_model.Schema({
315
418
  // { kind: 'rect'|'line', stroke?, strokeWidth?, fill?, flipV? } — src
316
419
  // is then '' and the box paints as vector. Null for real bitmaps.
317
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 },
318
437
  // Textbox (wps:txbx) content riding a shape: { paragraphs: <paragraph
319
438
  // node JSON>[], inset?: {l,t,r,b} px }. The layout engine flows the
320
439
  // paragraphs inside the shape's box (paint-only, not editable v1).
@@ -679,6 +798,15 @@ var schema = new import_prosemirror_model.Schema({
679
798
  0
680
799
  ]
681
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
+ },
682
810
  // w:highlight / w:shd w:fill — run background color ("#RRGGBB")
683
811
  highlight: {
684
812
  attrs: { color: {} },
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;
@@ -260,6 +327,42 @@ var schema = new Schema({
260
327
  parseDOM: [{ tag: "br.column-break" }],
261
328
  toDOM: () => ["br", { class: "column-break" }]
262
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
+ },
263
366
  // Inline image. `src` is typically a data URL (the importer inlines the
264
367
  // embedded media); width/height are CSS pixels, null if unspecified.
265
368
  image: {
@@ -283,6 +386,22 @@ var schema = new Schema({
283
386
  // { kind: 'rect'|'line', stroke?, strokeWidth?, fill?, flipV? } — src
284
387
  // is then '' and the box paints as vector. Null for real bitmaps.
285
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 },
286
405
  // Textbox (wps:txbx) content riding a shape: { paragraphs: <paragraph
287
406
  // node JSON>[], inset?: {l,t,r,b} px }. The layout engine flows the
288
407
  // paragraphs inside the shape's box (paint-only, not editable v1).
@@ -647,6 +766,15 @@ var schema = new Schema({
647
766
  0
648
767
  ]
649
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
+ },
650
778
  // w:highlight / w:shd w:fill — run background color ("#RRGGBB")
651
779
  highlight: {
652
780
  attrs: { color: {} },
@@ -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;AA0F3C;;;;;;;;GAQG;AACH,eAAO,MAAM,MAAM,yWAwqBjB,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.12.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
  }