@barocss/math-editor 0.5.0 → 0.6.1

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.
Files changed (49) hide show
  1. package/API-SESSION.md +25 -1
  2. package/CHANGELOG.md +48 -0
  3. package/CLIPBOARD.md +54 -0
  4. package/EDITING-SCENARIOS.md +55 -20
  5. package/GETTING-STARTED.md +54 -0
  6. package/IMPLEMENTATION.md +10 -0
  7. package/KEYBOARD.md +41 -0
  8. package/LATEX-GUIDE.md +30 -2
  9. package/README.md +52 -10
  10. package/RELEASING.md +22 -8
  11. package/ROADMAP.md +106 -1
  12. package/STYLING.md +28 -1
  13. package/TEXT-EDITORS.md +157 -0
  14. package/VALIDATION.md +102 -0
  15. package/dist/dom/caret-geometry.js +1 -1
  16. package/dist/dom/help.d.ts +5 -0
  17. package/dist/dom/help.js +104 -0
  18. package/dist/dom/menu-position.js +4 -3
  19. package/dist/dom/readable-layout.d.ts +3 -0
  20. package/dist/dom/readable-layout.js +52 -0
  21. package/dist/dom/toolbar-catalog.d.ts +1 -1
  22. package/dist/dom/toolbar.d.ts +2 -0
  23. package/dist/dom/toolbar.js +8 -1
  24. package/dist/dom.d.ts +11 -1
  25. package/dist/dom.js +138 -15
  26. package/dist/enter-policy.js +1 -1
  27. package/dist/lines.d.ts +2 -0
  28. package/dist/lines.js +15 -0
  29. package/dist/locales/en.js +21 -3
  30. package/dist/locales/en.json +21 -3
  31. package/dist/locales/ko.js +21 -3
  32. package/dist/locales/ko.json +21 -3
  33. package/dist/math-editor-toolbar.js +2 -1
  34. package/dist/math-editor.d.ts +1 -0
  35. package/dist/math-editor.js +135 -39
  36. package/dist/model.d.ts +2 -0
  37. package/dist/model.js +23 -1
  38. package/dist/range.d.ts +10 -2
  39. package/dist/range.js +31 -6
  40. package/dist/selection-shortcuts.d.ts +13 -0
  41. package/dist/selection-shortcuts.js +35 -0
  42. package/dist/session.d.ts +1 -0
  43. package/dist/session.js +1 -1
  44. package/dist/symbols.d.ts +1 -1
  45. package/dist/symbols.js +1 -0
  46. package/dist/vertical-navigation.d.ts +5 -0
  47. package/dist/vertical-navigation.js +33 -0
  48. package/package.json +1 -1
  49. package/src/style.css +227 -57
@@ -12,4 +12,9 @@ export type MathCaretGeometry = (point: VerticalPoint) => {
12
12
  * Native text selections and literal text retain their own arrow-key behavior.
13
13
  */
14
14
  export declare function moveVertical(state: MathState, direction: -1 | 1, geometry?: MathCaretGeometry): MathState;
15
+ /** View-local preferred column. It is not formula data and must reset after horizontal input. */
16
+ export declare function createVerticalNavigation(): {
17
+ reset(): void;
18
+ move(state: MathState, direction: -1 | 1, geometry?: MathCaretGeometry): MathState;
19
+ };
15
20
  export {};
@@ -143,3 +143,36 @@ export function moveVertical(state, direction, geometry) {
143
143
  const target = lines[line + direction];
144
144
  return target ? focusRows(state, [target], direction, geometry) : state;
145
145
  }
146
+ /** View-local preferred column. It is not formula data and must reset after horizontal input. */
147
+ export function createVerticalNavigation() {
148
+ let preferredX;
149
+ let last;
150
+ return {
151
+ reset() {
152
+ preferredX = undefined;
153
+ last = undefined;
154
+ },
155
+ move(state, direction, geometry) {
156
+ const caret = state.caret;
157
+ if (!last || last.id !== caret.id || last.start !== caret.start || last.end !== caret.end)
158
+ preferredX = undefined;
159
+ const origin = geometry?.({ id: caret.id, offset: caret.start, affinity: caret.affinity });
160
+ if (preferredX === undefined && origin && Number.isFinite(origin.x))
161
+ preferredX = origin.x;
162
+ // Keep the current row's Y, but measure target positions against the original X.
163
+ const projected = geometry &&
164
+ ((point) => {
165
+ const position = geometry(point);
166
+ return position &&
167
+ point.id === caret.id &&
168
+ point.offset === caret.start &&
169
+ preferredX !== undefined
170
+ ? { ...position, x: preferredX }
171
+ : position;
172
+ });
173
+ const next = moveVertical(state, direction, projected);
174
+ last = { ...next.caret };
175
+ return next;
176
+ },
177
+ };
178
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@barocss/math-editor",
3
- "version": "0.5.0",
3
+ "version": "0.6.1",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
package/src/style.css CHANGED
@@ -20,6 +20,8 @@
20
20
  font-style: normal;
21
21
  font-display: swap;
22
22
  }
23
+ /* Editing glyphs have a readable floor. Set --me-min-font-size: 0px for
24
+ unmodified TeX size ratios. This does not change serialized math. */
23
25
  .me-editor {
24
26
  color: var(--me-text, #202c27);
25
27
  font-family: var(--me-ui-font-family, system-ui, sans-serif);
@@ -48,7 +50,10 @@
48
50
  font: 12px var(--me-ui-font-family, system-ui, sans-serif);
49
51
  }
50
52
  .me-tools button span {
51
- font: 20px 'Barocss Math Glyphs', Georgia, serif;
53
+ font:
54
+ 20px 'Barocss Math Glyphs',
55
+ Georgia,
56
+ serif;
52
57
  }
53
58
  .me-tools button:hover {
54
59
  background: var(--me-hover-background, #edf3ef);
@@ -86,7 +91,11 @@
86
91
  }
87
92
  .me-input,
88
93
  .me-measure {
89
- font: italic var(--me-font-size, 22px) 'Barocss Math Glyphs', Georgia, 'Times New Roman', serif;
94
+ font:
95
+ italic max(var(--me-min-font-size, 14px), var(--me-font-size, 22px)) 'Barocss Math Glyphs',
96
+ Georgia,
97
+ 'Times New Roman',
98
+ serif;
90
99
  line-height: 1.35;
91
100
  min-width: 1ch;
92
101
  border: 0;
@@ -125,7 +134,10 @@
125
134
  margin-top: 4px;
126
135
  }
127
136
  .me-radical {
128
- font: 33px 'Barocss Math Glyphs', Georgia, serif;
137
+ font:
138
+ 33px 'Barocss Math Glyphs',
139
+ Georgia,
140
+ serif;
129
141
  }
130
142
  .me-superscript > .me-slot:last-child {
131
143
  align-self: flex-start;
@@ -145,7 +157,7 @@
145
157
  .me-subscript > .me-slot:last-child .me-input,
146
158
  .me-superscript > .me-slot:last-child .me-measure,
147
159
  .me-subscript > .me-slot:last-child .me-measure {
148
- font-size: calc(var(--me-font-size, 22px) * 0.7);
160
+ font-size: max(var(--me-min-font-size, 14px), calc(var(--me-font-size, 22px) * 0.7));
149
161
  }
150
162
  .me-suggestions {
151
163
  position: absolute;
@@ -276,7 +288,10 @@
276
288
  min-height: 54px;
277
289
  }
278
290
  .me-candidate-glyph {
279
- font: 26px 'Barocss Math Glyphs', Georgia, serif;
291
+ font:
292
+ 26px 'Barocss Math Glyphs',
293
+ Georgia,
294
+ serif;
280
295
  min-width: 42px;
281
296
  flex-shrink: 0;
282
297
  text-align: center;
@@ -302,16 +317,22 @@
302
317
  }
303
318
  .me-limit .me-input,
304
319
  .me-limit .me-measure {
305
- font-size: calc(var(--me-font-size, 22px) * 0.7);
320
+ font-size: max(var(--me-min-font-size, 14px), calc(var(--me-font-size, 22px) * 0.7));
306
321
  }
307
322
  .me-operator-glyph {
308
- font: calc(var(--me-font-size, 22px) * 1.59) / 1 'Barocss Math Glyphs', Georgia, serif;
323
+ font:
324
+ calc(var(--me-font-size, 22px) * 1.59) / 1 'Barocss Math Glyphs',
325
+ Georgia,
326
+ serif;
309
327
  }
310
328
  .me-large-operator {
311
329
  gap: 8px;
312
330
  }
313
331
  .me-delimiter {
314
- font: 28px/1 'Barocss Math Glyphs', Georgia, serif;
332
+ font:
333
+ 28px/1 'Barocss Math Glyphs',
334
+ Georgia,
335
+ serif;
315
336
  }
316
337
 
317
338
  /* Slot roles are presentation metadata, not a claim that text is a variable or constant. */
@@ -609,7 +630,11 @@
609
630
  align-items: center;
610
631
  justify-content: center;
611
632
  white-space: pre;
612
- font: italic var(--me-font-size, 22px) / 1.35 'Barocss Math Glyphs', Georgia, 'Times New Roman',
633
+ font:
634
+ italic max(var(--me-min-font-size, 14px), var(--me-font-size, 22px)) / 1.35
635
+ 'Barocss Math Glyphs',
636
+ Georgia,
637
+ 'Times New Roman',
613
638
  serif;
614
639
  color: var(--me-ink);
615
640
  border-radius: 4px;
@@ -632,10 +657,10 @@
632
657
  }
633
658
  .me-superscript > .me-slot:last-child .me-preview-token,
634
659
  .me-subscript > .me-slot:last-child .me-preview-token {
635
- font-size: calc(var(--me-font-size, 22px) * 0.7);
660
+ font-size: max(var(--me-min-font-size, 14px), calc(var(--me-font-size, 22px) * 0.7));
636
661
  }
637
662
  .me-limit .me-preview-token {
638
- font-size: calc(var(--me-font-size, 22px) * 0.7);
663
+ font-size: max(var(--me-min-font-size, 14px), calc(var(--me-font-size, 22px) * 0.7));
639
664
  }
640
665
  .me-range-highlight,
641
666
  .me-empty[data-range-selected='true'] {
@@ -735,7 +760,11 @@
735
760
  border-color: var(--me-accent, #9cbbac);
736
761
  }
737
762
  .me-symbol-glyph {
738
- font: 25px/1.2 'Barocss Math Glyphs', Georgia, 'Times New Roman', serif;
763
+ font:
764
+ 25px/1.2 'Barocss Math Glyphs',
765
+ Georgia,
766
+ 'Times New Roman',
767
+ serif;
739
768
  }
740
769
  .me-symbol-browser button:focus-visible,
741
770
  .me-symbol-search input:focus-visible {
@@ -783,11 +812,15 @@
783
812
  }
784
813
  .me-dom-editor[data-mode='inline'] {
785
814
  display: inline-block;
815
+ max-width: 100%;
816
+ box-sizing: border-box;
786
817
  vertical-align: middle;
787
818
  border: 0;
788
819
  }
789
820
  .me-dom-editor[data-mode='inline'] .me-surface {
790
821
  display: inline-block;
822
+ max-width: 100%;
823
+ box-sizing: border-box;
791
824
  padding: var(--me-inline-padding, 2px 4px);
792
825
  min-height: 0;
793
826
  }
@@ -820,7 +853,7 @@
820
853
  grid-row: 1;
821
854
  }
822
855
  .me-scripts > .me-slot:not(:first-child) :is(.me-input, .me-measure, .me-preview-token) {
823
- font-size: calc(var(--me-font-size, 22px) * 0.7);
856
+ font-size: max(var(--me-min-font-size, 14px), calc(var(--me-font-size, 22px) * 0.7));
824
857
  }
825
858
 
826
859
  .me-textGroup,
@@ -866,7 +899,7 @@
866
899
  margin-top: 4px;
867
900
  }
868
901
  .me-indexedRoot > .me-slot:nth-last-child(2) :is(.me-input, .me-measure, .me-preview-token) {
869
- font-size: calc(var(--me-font-size, 22px) * 0.5);
902
+ font-size: max(var(--me-min-font-size, 14px), calc(var(--me-font-size, 22px) * 0.5));
870
903
  }
871
904
 
872
905
  /* Accents follow the editable body's width, including nested structures. */
@@ -959,13 +992,16 @@
959
992
  gap: 4px;
960
993
  }
961
994
  .me-limit-glyph {
962
- font: normal var(--me-font-size, 22px) / 1.1 'Barocss Math Glyphs', Georgia, serif;
995
+ font:
996
+ normal max(var(--me-min-font-size, 14px), var(--me-font-size, 22px)) / 1.1 'Barocss Math Glyphs',
997
+ Georgia,
998
+ serif;
963
999
  }
964
1000
  .me-limit-operator > .me-limits {
965
1001
  align-self: baseline;
966
1002
  }
967
1003
  .me-limit-operator .me-limit :is(.me-input, .me-measure, .me-preview-token) {
968
- font-size: calc(var(--me-font-size, 22px) * 0.7);
1004
+ font-size: max(var(--me-min-font-size, 14px), calc(var(--me-font-size, 22px) * 0.7));
969
1005
  line-height: 1.1;
970
1006
  padding-block: 1px;
971
1007
  }
@@ -1011,18 +1047,34 @@
1011
1047
  .me-superscript > .me-slot:last-child :is(.me-quad, .me-qquad),
1012
1048
  .me-subscript > .me-slot:last-child :is(.me-quad, .me-qquad),
1013
1049
  .me-scripts > .me-slot:not(:first-child) :is(.me-quad, .me-qquad) {
1014
- font-size: calc(var(--me-font-size, 22px) * 16 / 22);
1050
+ font-size: max(var(--me-min-font-size, 14px), calc(var(--me-font-size, 22px) * 16 / 22));
1015
1051
  }
1016
1052
  .me-limit :is(.me-quad, .me-qquad) {
1017
- font-size: calc(var(--me-font-size, 22px) * 0.7);
1053
+ font-size: max(var(--me-min-font-size, 14px), calc(var(--me-font-size, 22px) * 0.7));
1018
1054
  }
1019
1055
 
1020
1056
  /* Structured delimiters export as \left/\right and stretch with their body. */
1057
+ /* Fence bodies must not add an inline line box below their nested flex row. */
1058
+ :is(
1059
+ .me-parentheses,
1060
+ .me-brackets,
1061
+ .me-absolute,
1062
+ .me-braces,
1063
+ .me-angle,
1064
+ .me-openClosed,
1065
+ .me-closedOpen,
1066
+ .me-fenced,
1067
+ .me-norm
1068
+ )
1069
+ > .me-slot {
1070
+ display: inline-flex;
1071
+ align-items: center;
1072
+ }
1021
1073
  :is(.me-parentheses, .me-brackets, .me-absolute) > .me-delimiter {
1022
1074
  align-self: stretch;
1023
1075
  flex: 0 0 7px;
1024
1076
  box-sizing: border-box;
1025
- min-height: 26px;
1077
+ min-height: calc(var(--me-font-size, 22px) * 1.2);
1026
1078
  font-size: 0;
1027
1079
  }
1028
1080
  :is(.me-parentheses, .me-brackets, .me-absolute) > .me-delimiter:first-child {
@@ -1040,7 +1092,10 @@
1040
1092
  .me-parentheses > .me-delimiter:last-child {
1041
1093
  transform: scaleX(-1);
1042
1094
  }
1043
- .me-parentheses:has(> .me-slot .me-structure) > .me-delimiter {
1095
+ .me-parentheses:has(
1096
+ > .me-slot :is(.me-fraction, .me-root, .me-indexedRoot, .me-matrix, .me-cases, .me-superscript)
1097
+ )
1098
+ > .me-delimiter {
1044
1099
  flex-basis: 14px;
1045
1100
  }
1046
1101
  .me-brackets > .me-delimiter {
@@ -1058,7 +1113,7 @@
1058
1113
  :is(.me-braces, .me-angle, .me-openClosed, .me-closedOpen, .me-fenced) > .me-delimiter {
1059
1114
  align-self: stretch;
1060
1115
  flex: 0 0 12px;
1061
- min-height: 26px;
1116
+ min-height: calc(var(--me-font-size, 22px) * 1.2);
1062
1117
  box-sizing: border-box;
1063
1118
  font-size: 0;
1064
1119
  }
@@ -1117,7 +1172,7 @@
1117
1172
  :is(.me-overset, .me-underset)
1118
1173
  > .me-slot:first-child
1119
1174
  :is(.me-input, .me-preview-token, .me-measure) {
1120
- font-size: calc(var(--me-font-size, 22px) * 0.7);
1175
+ font-size: max(var(--me-min-font-size, 14px), calc(var(--me-font-size, 22px) * 0.7));
1121
1176
  }
1122
1177
  /* Square and indexed roots share a radical joined to the radicand's overbar. */
1123
1178
  .me-root > .me-radical,
@@ -1171,7 +1226,7 @@
1171
1226
  > .me-operator-glyph {
1172
1227
  grid-column: 1;
1173
1228
  grid-row: 1 / 3;
1174
- font-size: calc(var(--me-font-size, 22px) * 2);
1229
+ font-size: max(var(--me-min-font-size, 14px), calc(var(--me-font-size, 22px) * 2));
1175
1230
  }
1176
1231
  :is(
1177
1232
  [data-structure='integral'],
@@ -1231,12 +1286,12 @@
1231
1286
  /* Keep nested fractions readable while avoiding full-size recursive stacks.
1232
1287
  Apply the same size to the input, passive glyph and measuring twin. */
1233
1288
  .me-fraction .me-fraction :is(.me-input, .me-measure, .me-preview-token) {
1234
- font-size: calc(var(--me-font-size, 22px) * 0.7);
1289
+ font-size: max(var(--me-min-font-size, 14px), calc(var(--me-font-size, 22px) * 0.7));
1235
1290
  line-height: 1;
1236
1291
  padding-block: 0;
1237
1292
  }
1238
1293
  .me-fraction .me-fraction .me-fraction :is(.me-input, .me-measure, .me-preview-token) {
1239
- font-size: calc(var(--me-font-size, 22px) * 0.5);
1294
+ font-size: max(var(--me-min-font-size, 14px), calc(var(--me-font-size, 22px) * 0.5));
1240
1295
  }
1241
1296
  .me-fraction .me-fraction .me-row {
1242
1297
  min-height: 0;
@@ -1245,6 +1300,38 @@
1245
1300
  padding: 0 3px;
1246
1301
  }
1247
1302
 
1303
+ /* Resolve every fraction descendant from its own row style. Transparent
1304
+ structures (norms, fences, roots) must not reset their body's term size.
1305
+ The input, measuring twin and passive glyph share the same rule. */
1306
+ .me-editor .me-fraction .me-row[data-layout-style] {
1307
+ min-height: 0;
1308
+ }
1309
+ .me-editor
1310
+ .me-fraction
1311
+ .me-row[data-layout-style]
1312
+ > .me-run
1313
+ :is(.me-input, .me-measure, .me-preview-token) {
1314
+ padding-block: 0;
1315
+ line-height: 1.25;
1316
+ font-size: max(var(--me-min-font-size, 14px), var(--me-font-size, 22px));
1317
+ }
1318
+ .me-editor
1319
+ .me-fraction
1320
+ .me-row[data-layout-style='script']
1321
+ > .me-run
1322
+ :is(.me-input, .me-measure, .me-preview-token) {
1323
+ line-height: 1.1;
1324
+ font-size: max(var(--me-min-font-size, 14px), calc(var(--me-font-size, 22px) * 0.7));
1325
+ }
1326
+ .me-editor
1327
+ .me-fraction
1328
+ .me-row[data-layout-style='scriptscript']
1329
+ > .me-run
1330
+ :is(.me-input, .me-measure, .me-preview-token) {
1331
+ line-height: 1.1;
1332
+ font-size: max(var(--me-min-font-size, 14px), calc(var(--me-font-size, 22px) * 0.5));
1333
+ }
1334
+
1248
1335
  .me-vec,
1249
1336
  .me-hat,
1250
1337
  .me-overline {
@@ -1274,7 +1361,7 @@
1274
1361
  .me-fenced > .me-delimiter[data-fence='‖'] {
1275
1362
  align-self: stretch;
1276
1363
  flex: 0 0 9px;
1277
- min-height: 26px;
1364
+ min-height: calc(var(--me-font-size, 22px) * 1.2);
1278
1365
  box-sizing: border-box;
1279
1366
  font-size: 0;
1280
1367
  border-left: 4px double currentColor;
@@ -1291,7 +1378,9 @@
1291
1378
  unicode-range: U+222E;
1292
1379
  }
1293
1380
  [data-structure='contourIntegral'][data-limit-placement] > .me-limits > .me-operator-glyph {
1294
- font: normal var(--me-font-size, 22px) / 2.3 'Barocss Contour', serif;
1381
+ font:
1382
+ normal max(var(--me-min-font-size, 14px), var(--me-font-size, 22px)) / 2.3 'Barocss Contour',
1383
+ serif;
1295
1384
  width: calc(var(--me-font-size, 22px) * 25 / 22);
1296
1385
  height: calc(var(--me-font-size, 22px) * 2.3);
1297
1386
  transform: none;
@@ -1317,7 +1406,7 @@
1317
1406
  :is(.me-overbrace, .me-underbrace)
1318
1407
  > .me-slot:first-child
1319
1408
  :is(.me-input, .me-measure, .me-preview-token) {
1320
- font-size: calc(var(--me-font-size, 22px) * 0.7);
1409
+ font-size: max(var(--me-min-font-size, 14px), calc(var(--me-font-size, 22px) * 0.7));
1321
1410
  line-height: 1.1;
1322
1411
  }
1323
1412
  :is(.me-overbrace, .me-underbrace) > .me-slot:last-child {
@@ -1500,7 +1589,7 @@
1500
1589
  )[data-limit-placement='nolimits']
1501
1590
  > .me-limits
1502
1591
  > .me-operator-glyph {
1503
- font-size: calc(var(--me-font-size, 22px) * 2);
1592
+ font-size: max(var(--me-min-font-size, 14px), calc(var(--me-font-size, 22px) * 2));
1504
1593
  }
1505
1594
 
1506
1595
  /* TeX math units: 18mu = 1em. Negative spacing has no painted hit box. */
@@ -1541,7 +1630,7 @@
1541
1630
  --me-explicit-size: calc(var(--me-font-size, 22px) * 0.7);
1542
1631
  }
1543
1632
  .me-structure[data-math-style] > .me-slot :is(.me-input, .me-measure, .me-preview-token) {
1544
- font-size: var(--me-explicit-size);
1633
+ font-size: max(var(--me-min-font-size, 14px), var(--me-explicit-size));
1545
1634
  }
1546
1635
  .me-structure[data-math-style='text'] > .me-slot > .me-row {
1547
1636
  min-height: 0;
@@ -1553,7 +1642,7 @@
1553
1642
  :is(.me-superscript, .me-subscript, .me-scripts)
1554
1643
  > .me-slot:not(:first-child)
1555
1644
  :is(.me-input, .me-measure, .me-preview-token) {
1556
- font-size: calc(var(--me-explicit-size) * 0.7273);
1645
+ font-size: max(var(--me-min-font-size, 14px), calc(var(--me-explicit-size) * 0.7273));
1557
1646
  }
1558
1647
 
1559
1648
  /* Alphabet fonts are scoped to editable runs, never delimiter/operator glyphs.
@@ -1662,7 +1751,9 @@
1662
1751
  .me-utility-panel {
1663
1752
  padding: 12px;
1664
1753
  border-top: 1px solid var(--me-line, #dcdfe2);
1665
- font: 13px/1.5 system-ui, sans-serif;
1754
+ font:
1755
+ 13px/1.5 system-ui,
1756
+ sans-serif;
1666
1757
  color: var(--me-text, #253b33);
1667
1758
  }
1668
1759
  .me-utility-panel label {
@@ -1740,7 +1831,7 @@
1740
1831
  > .me-slot:not(:first-child)
1741
1832
  .me-fraction:not([data-math-style='display'])
1742
1833
  :is(.me-input, .me-measure, .me-preview-token) {
1743
- font-size: calc(var(--me-font-size, 22px) * 0.5);
1834
+ font-size: max(var(--me-min-font-size, 14px), calc(var(--me-font-size, 22px) * 0.5));
1744
1835
  line-height: 1.1;
1745
1836
  padding-block: 0;
1746
1837
  }
@@ -1875,7 +1966,7 @@
1875
1966
  )
1876
1967
  > .me-limits
1877
1968
  > .me-operator-glyph {
1878
- font-size: calc(var(--me-font-size, 22px) * 2);
1969
+ font-size: max(var(--me-min-font-size, 14px), calc(var(--me-font-size, 22px) * 2));
1879
1970
  }
1880
1971
  :is(
1881
1972
  [data-structure='integral'],
@@ -1941,10 +2032,14 @@
1941
2032
  line-height: 1.2;
1942
2033
  padding-block: 0;
1943
2034
  }
1944
- .me-matrix[data-structure='matrix']
2035
+ .me-editor
2036
+ .me-matrix[data-structure='matrix']
1945
2037
  .me-fraction:not([data-math-style='display'])
2038
+ > .me-slot
2039
+ > .me-row
2040
+ > .me-run
1946
2041
  :is(.me-input, .me-measure, .me-preview-token) {
1947
- font-size: calc(var(--me-font-size, 22px) * 0.7);
2042
+ font-size: max(var(--me-min-font-size, 14px), calc(var(--me-font-size, 22px) * 0.7));
1948
2043
  line-height: 0.85;
1949
2044
  padding-block: 0;
1950
2045
  }
@@ -1972,7 +2067,10 @@
1972
2067
  /* Use the display operator glyphs themselves, including their italic correction,
1973
2068
  rather than enlarging/skewing a text-font integral or sum. */
1974
2069
  .me-large-operator[data-structure][data-limit-placement] > .me-limits > .me-operator-glyph {
1975
- font: normal var(--me-font-size, 22px) / 1.6 'Barocss Large Operators', serif;
2070
+ font:
2071
+ normal max(var(--me-min-font-size, 14px), var(--me-font-size, 22px)) / 1.6
2072
+ 'Barocss Large Operators',
2073
+ serif;
1976
2074
  transform: none;
1977
2075
  width: auto;
1978
2076
  height: calc(var(--me-font-size, 22px) * 1.6);
@@ -1993,8 +2091,13 @@
1993
2091
  :is(.me-openClosed, .me-closedOpen, .me-fenced) > :is([data-fence='('], [data-fence=')']) {
1994
2092
  flex-basis: calc(var(--me-font-size, 22px) * 0.389);
1995
2093
  }
1996
- .me-parentheses:has(> .me-slot .me-structure) > .me-delimiter,
1997
- :is(.me-openClosed, .me-closedOpen, .me-fenced):has(> .me-slot .me-structure)
2094
+ .me-parentheses:has(
2095
+ > .me-slot :is(.me-fraction, .me-root, .me-indexedRoot, .me-matrix, .me-cases, .me-superscript)
2096
+ )
2097
+ > .me-delimiter,
2098
+ :is(.me-openClosed, .me-closedOpen, .me-fenced):has(
2099
+ > .me-slot :is(.me-fraction, .me-root, .me-indexedRoot, .me-matrix, .me-cases, .me-superscript)
2100
+ )
1998
2101
  > :is([data-fence='('], [data-fence=')']) {
1999
2102
  flex-basis: calc(var(--me-font-size, 22px) * 0.55);
2000
2103
  }
@@ -2096,9 +2199,20 @@
2096
2199
  }
2097
2200
  /* A radical encloses math ink rather than the taller UI text line box.
2098
2201
  Tall nested structures continue to determine their own row height. */
2202
+ :is(.me-root, .me-indexedRoot) > .me-slot:last-child {
2203
+ display: inline-flex;
2204
+ align-items: center;
2205
+ }
2099
2206
  :is(.me-root, .me-indexedRoot) > .me-slot:last-child > .me-row {
2100
2207
  min-height: 0;
2101
2208
  }
2209
+ :is(.me-root, .me-indexedRoot)
2210
+ > .me-slot:last-child
2211
+ > .me-row
2212
+ > .me-empty-boundary:not(:focus-within)
2213
+ > .me-measure {
2214
+ min-height: 0;
2215
+ }
2102
2216
  :is(.me-root, .me-indexedRoot)
2103
2217
  > .me-slot:last-child
2104
2218
  > .me-row
@@ -2122,7 +2236,7 @@
2122
2236
  :is(.me-superscript, .me-subscript, .me-scripts)
2123
2237
  > .me-slot:not(:first-child)
2124
2238
  :is(.me-input, .me-measure, .me-preview-token) {
2125
- font-size: calc(var(--me-font-size, 22px) * 0.5);
2239
+ font-size: max(var(--me-min-font-size, 14px), calc(var(--me-font-size, 22px) * 0.5));
2126
2240
  line-height: 1.1;
2127
2241
  padding-block: 0;
2128
2242
  }
@@ -2133,13 +2247,6 @@
2133
2247
  > .me-row {
2134
2248
  min-height: 0;
2135
2249
  }
2136
- :is(.me-superscript, .me-subscript, .me-scripts)
2137
- > .me-slot:not(:first-child)
2138
- :is(.me-root, .me-indexedRoot)
2139
- > .me-radical {
2140
- width: calc(var(--me-font-size, 22px) * 0.595);
2141
- min-height: calc(var(--me-font-size, 22px) * 0.7);
2142
- }
2143
2250
  .me-superscript > .me-slot:last-child:has(> .me-row > :is(.me-root, .me-indexedRoot)) {
2144
2251
  transform: translateY(-15%);
2145
2252
  }
@@ -2158,12 +2265,18 @@
2158
2265
  :is(.me-openClosed, .me-closedOpen, .me-fenced) > :is([data-fence='('], [data-fence=')']) {
2159
2266
  mask: url('./shapes/parenthesis.svg') center / 61.44% var(--me-font-size, 22px) no-repeat;
2160
2267
  }
2161
- .me-parentheses:has(> .me-slot .me-structure) > .me-delimiter,
2162
- :is(.me-openClosed, .me-closedOpen, .me-fenced):has(> .me-slot .me-structure)
2268
+ .me-parentheses:has(
2269
+ > .me-slot :is(.me-fraction, .me-root, .me-indexedRoot, .me-matrix, .me-cases, .me-superscript)
2270
+ )
2271
+ > .me-delimiter,
2272
+ :is(.me-openClosed, .me-closedOpen, .me-fenced):has(
2273
+ > .me-slot :is(.me-fraction, .me-root, .me-indexedRoot, .me-matrix, .me-cases, .me-superscript)
2274
+ )
2163
2275
  > :is([data-fence='('], [data-fence=')']),
2164
2276
  .me-matrix[data-environment='pmatrix'] > .me-matrix-delimiter {
2165
2277
  --me-fence-cap: min(50%, calc(var(--me-font-size, 22px) * 1.809));
2166
- mask: url('./shapes/parenthesis-top.svg') left top / 100% var(--me-fence-cap) no-repeat,
2278
+ mask:
2279
+ url('./shapes/parenthesis-top.svg') left top / 100% var(--me-fence-cap) no-repeat,
2167
2280
  url('./shapes/parenthesis-bottom.svg') left bottom / 100% var(--me-fence-cap) no-repeat,
2168
2281
  linear-gradient(currentColor, currentColor) left center / 22.826%
2169
2282
  calc(100% - var(--me-fence-cap) - var(--me-fence-cap) + 1px) no-repeat;
@@ -2176,11 +2289,31 @@
2176
2289
  mask: url('./shapes/radical.svg') center / 100% 100% no-repeat;
2177
2290
  }
2178
2291
 
2292
+ /* Radical geometry follows the containing math style, including the editing
2293
+ minimum. The radicand's flex slot supplies extra height for tall contents. */
2294
+ .me-row {
2295
+ --me-radical-size: max(var(--me-min-font-size, 14px), var(--me-font-size, 22px));
2296
+ }
2297
+ .me-row[data-layout-style='script'] {
2298
+ --me-radical-size: max(var(--me-min-font-size, 14px), calc(var(--me-font-size, 22px) * 0.7));
2299
+ }
2300
+ .me-row[data-layout-style='scriptscript'] {
2301
+ --me-radical-size: max(var(--me-min-font-size, 14px), calc(var(--me-font-size, 22px) * 0.5));
2302
+ }
2303
+ .me-editor .me-row > :is(.me-root, .me-indexedRoot) > .me-radical {
2304
+ width: calc(var(--me-radical-size) * 0.85);
2305
+ min-height: var(--me-radical-size);
2306
+ margin-top: calc(var(--me-radical-size) * 0.1);
2307
+ }
2308
+ .me-editor :is(.me-root, .me-indexedRoot) > .me-slot:last-child {
2309
+ margin-top: calc(var(--me-radical-size) * 0.1);
2310
+ }
2311
+
2179
2312
  /* A nested radical needs clearance above its own rule. Without this space,
2180
2313
  successive rules collapse together even when the operand metrics agree. */
2181
2314
  :is(.me-root, .me-indexedRoot)
2182
2315
  > .me-slot:last-child:has(> .me-row > :is(.me-root, .me-indexedRoot)) {
2183
- padding-top: calc(var(--me-font-size, 22px) * 0.25);
2316
+ padding-top: calc(var(--me-radical-size) * 0.25);
2184
2317
  }
2185
2318
 
2186
2319
  /* Resolve reduced operator styles from model slots. Display operators retain
@@ -2202,7 +2335,9 @@
2202
2335
  .me-large-operator[data-operator-style]:not([data-operator-style='display'])
2203
2336
  > .me-limits
2204
2337
  > .me-operator-glyph {
2205
- font: normal var(--me-operator-size) / 1.1 'Barocss Small Operators', serif;
2338
+ font:
2339
+ normal var(--me-operator-size) / 1.1 'Barocss Small Operators',
2340
+ serif;
2206
2341
  height: calc(var(--me-operator-size) * 1.1);
2207
2342
  transform: none;
2208
2343
  }
@@ -2318,7 +2453,7 @@
2318
2453
  .me-row[data-layout-style]
2319
2454
  > .me-run
2320
2455
  :is(.me-input, .me-measure, .me-preview-token) {
2321
- font-size: var(--me-font-size, 22px);
2456
+ font-size: max(var(--me-min-font-size, 14px), var(--me-font-size, 22px));
2322
2457
  line-height: 1.1;
2323
2458
  padding-block: 0;
2324
2459
  }
@@ -2327,14 +2462,14 @@
2327
2462
  .me-row[data-layout-style='script']
2328
2463
  > .me-run
2329
2464
  :is(.me-input, .me-measure, .me-preview-token) {
2330
- font-size: calc(var(--me-font-size, 22px) * 0.7);
2465
+ font-size: max(var(--me-min-font-size, 14px), calc(var(--me-font-size, 22px) * 0.7));
2331
2466
  }
2332
2467
  .me-editor
2333
2468
  [data-operator-style]:not([data-operator-style='display'])
2334
2469
  .me-row[data-layout-style='scriptscript']
2335
2470
  > .me-run
2336
2471
  :is(.me-input, .me-measure, .me-preview-token) {
2337
- font-size: calc(var(--me-font-size, 22px) * 0.5);
2472
+ font-size: max(var(--me-min-font-size, 14px), calc(var(--me-font-size, 22px) * 0.5));
2338
2473
  }
2339
2474
 
2340
2475
  /* Glyph width and mathematical spacing are separate from the caret hit area.
@@ -2368,13 +2503,48 @@
2368
2503
  padding-inline: 0;
2369
2504
  }
2370
2505
  .me-row[data-layout-style='script'] {
2371
- --me-tight-unit: calc(var(--me-font-size, 22px) * 0.7 / 18);
2506
+ --me-tight-unit: calc(max(var(--me-min-font-size, 14px), var(--me-font-size, 22px) * 0.7) / 18);
2372
2507
  }
2373
2508
  .me-row[data-layout-style='scriptscript'] {
2374
- --me-tight-unit: calc(var(--me-font-size, 22px) * 0.5 / 18);
2509
+ --me-tight-unit: calc(max(var(--me-min-font-size, 14px), var(--me-font-size, 22px) * 0.5) / 18);
2375
2510
  }
2376
2511
  .me-row:is([data-layout-style='script'], [data-layout-style='scriptscript'])
2377
2512
  > [data-math-spacing='true'] {
2378
2513
  margin-left: calc(var(--me-tight-before, 0) * var(--me-tight-unit));
2379
2514
  margin-right: calc(var(--me-tight-after, 0) * var(--me-tight-unit));
2380
2515
  }
2516
+
2517
+ /* Help uses the top layer to avoid clipping inside narrow or embedded fields. */
2518
+ .me-help {
2519
+ width: min(560px, calc(100vw - 40px));
2520
+ max-height: calc(100vh - 48px);
2521
+ box-sizing: border-box;
2522
+ overflow: auto;
2523
+ padding: 24px;
2524
+ border: 1px solid var(--me-line, #dcdfe2);
2525
+ border-radius: 12px;
2526
+ background: var(--me-control-bg, #fff);
2527
+ color: var(--me-text, #253b33);
2528
+ text-align: start;
2529
+ }
2530
+ .me-help::backdrop {
2531
+ background: rgb(0 0 0 / 25%);
2532
+ }
2533
+ .me-help h2 {
2534
+ margin: 0 0 8px;
2535
+ font:
2536
+ 600 20px/1.4 system-ui,
2537
+ sans-serif;
2538
+ }
2539
+ .me-help dl {
2540
+ display: grid;
2541
+ gap: 8px 16px;
2542
+ grid-template-columns: minmax(0, 1fr) minmax(0, 2fr);
2543
+ }
2544
+ .me-help dt {
2545
+ font-weight: 600;
2546
+ overflow-wrap: anywhere;
2547
+ }
2548
+ .me-help dd {
2549
+ margin: 0;
2550
+ }