@barocss/math-editor 0.2.0 → 0.4.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.
Files changed (180) hide show
  1. package/ADAPTERS.md +25 -14
  2. package/API-JAVASCRIPT.md +1 -1
  3. package/API-REACT.md +5 -1
  4. package/API-SESSION.md +168 -6
  5. package/API-SOLID.md +1 -1
  6. package/API-SVELTE.md +1 -1
  7. package/API-VUE.md +1 -1
  8. package/API-WEB-COMPONENT.md +1 -1
  9. package/CHANGELOG.md +51 -1
  10. package/EMBEDDING.md +4 -0
  11. package/IMPLEMENTATION.md +27 -2
  12. package/JSON-MODEL.md +1 -0
  13. package/LATEX-GUIDE.md +13 -0
  14. package/LATEX-SCOPE.md +2 -2
  15. package/LICENSE +1 -3
  16. package/README.md +150 -36
  17. package/ROADMAP.md +96 -10
  18. package/STYLING.md +157 -0
  19. package/SUPPORT.md +29 -11
  20. package/VALIDATION.md +117 -4
  21. package/dist/core.d.ts +5 -1
  22. package/dist/core.js +5 -1
  23. package/dist/document-codec.d.ts +0 -1
  24. package/dist/document-codec.js +0 -1
  25. package/dist/dom/caret-geometry.d.ts +3 -0
  26. package/dist/dom/caret-geometry.js +41 -0
  27. package/dist/dom/elements.d.ts +1 -2
  28. package/dist/dom/elements.js +2 -3
  29. package/dist/dom/latex-panel.d.ts +10 -0
  30. package/dist/dom/latex-panel.js +59 -0
  31. package/dist/dom/matrix-selection.d.ts +11 -0
  32. package/dist/dom/matrix-selection.js +30 -0
  33. package/dist/dom/menu-position.d.ts +14 -2
  34. package/dist/dom/menu-position.js +84 -15
  35. package/dist/dom/presentation-controls.d.ts +4 -0
  36. package/dist/dom/presentation-controls.js +56 -0
  37. package/dist/dom/quick-panel.d.ts +12 -0
  38. package/dist/dom/quick-panel.js +102 -0
  39. package/dist/dom/selection.d.ts +29 -0
  40. package/dist/dom/selection.js +53 -0
  41. package/dist/dom/symbol-panel.d.ts +9 -0
  42. package/dist/dom/symbol-panel.js +146 -0
  43. package/dist/dom/theme.d.ts +6 -0
  44. package/dist/dom/theme.js +68 -0
  45. package/dist/dom/toolbar-catalog.d.ts +10 -0
  46. package/dist/dom/toolbar-catalog.js +35 -0
  47. package/dist/dom/toolbar.d.ts +13 -2
  48. package/dist/dom/toolbar.js +295 -28
  49. package/dist/dom.d.ts +7 -1
  50. package/dist/dom.js +527 -126
  51. package/dist/editor-labels.d.ts +0 -1
  52. package/dist/editor-labels.js +0 -1
  53. package/dist/enter-policy.d.ts +0 -1
  54. package/dist/enter-policy.js +0 -1
  55. package/dist/fences.d.ts +0 -1
  56. package/dist/fences.js +0 -1
  57. package/dist/i18n.d.ts +0 -1
  58. package/dist/i18n.js +0 -1
  59. package/dist/index.d.ts +4 -1
  60. package/dist/index.js +4 -1
  61. package/dist/latex-insertion.d.ts +12 -0
  62. package/dist/latex-insertion.js +26 -0
  63. package/dist/latex-paste-panel.d.ts +3 -0
  64. package/dist/latex-paste-panel.js +18 -0
  65. package/dist/latex.d.ts +0 -1
  66. package/dist/latex.js +0 -1
  67. package/dist/lines.d.ts +0 -1
  68. package/dist/lines.js +0 -1
  69. package/dist/locales/en.js +28 -2
  70. package/dist/locales/en.json +28 -2
  71. package/dist/locales/ko.js +28 -2
  72. package/dist/locales/ko.json +28 -2
  73. package/dist/math-editor-toolbar.d.ts +4 -2
  74. package/dist/math-editor-toolbar.js +3 -4
  75. package/dist/math-editor.d.ts +5 -2
  76. package/dist/math-editor.js +300 -44
  77. package/dist/matrix-range.d.ts +40 -0
  78. package/dist/matrix-range.js +242 -0
  79. package/dist/matrix.d.ts +0 -1
  80. package/dist/matrix.js +0 -1
  81. package/dist/model.d.ts +0 -1
  82. package/dist/model.js +0 -1
  83. package/dist/outputs.d.ts +0 -1
  84. package/dist/outputs.js +0 -1
  85. package/dist/preferences.d.ts +24 -0
  86. package/dist/preferences.js +66 -0
  87. package/dist/presentation-controls.d.ts +10 -0
  88. package/dist/presentation-controls.js +10 -0
  89. package/dist/presentation.d.ts +13 -0
  90. package/dist/presentation.js +86 -0
  91. package/dist/quick-panel.d.ts +2 -0
  92. package/dist/quick-panel.js +17 -0
  93. package/dist/range.d.ts +4 -1
  94. package/dist/range.js +57 -1
  95. package/dist/react.d.ts +0 -1
  96. package/dist/react.js +0 -1
  97. package/dist/session.d.ts +31 -2
  98. package/dist/session.js +108 -7
  99. package/dist/solid.d.ts +0 -1
  100. package/dist/solid.js +0 -1
  101. package/dist/suggestions.d.ts +4 -1
  102. package/dist/suggestions.js +103 -13
  103. package/dist/svelte.d.ts +0 -1
  104. package/dist/svelte.js +0 -1
  105. package/dist/symbol-browser.d.ts +0 -1
  106. package/dist/symbol-browser.js +0 -1
  107. package/dist/symbols.d.ts +0 -1
  108. package/dist/symbols.js +0 -1
  109. package/dist/templates.d.ts +0 -1
  110. package/dist/templates.js +0 -1
  111. package/dist/tokens.d.ts +0 -1
  112. package/dist/tokens.js +0 -1
  113. package/dist/vertical-navigation.d.ts +15 -0
  114. package/dist/vertical-navigation.js +145 -0
  115. package/dist/vue.d.ts +0 -1
  116. package/dist/vue.js +0 -1
  117. package/dist/web-component.d.ts +0 -1
  118. package/dist/web-component.js +0 -1
  119. package/package.json +1 -1
  120. package/src/style.css +254 -129
  121. package/dist/core.d.ts.map +0 -1
  122. package/dist/core.js.map +0 -1
  123. package/dist/document-codec.d.ts.map +0 -1
  124. package/dist/document-codec.js.map +0 -1
  125. package/dist/dom/elements.d.ts.map +0 -1
  126. package/dist/dom/elements.js.map +0 -1
  127. package/dist/dom/menu-position.d.ts.map +0 -1
  128. package/dist/dom/menu-position.js.map +0 -1
  129. package/dist/dom/toolbar.d.ts.map +0 -1
  130. package/dist/dom/toolbar.js.map +0 -1
  131. package/dist/dom.d.ts.map +0 -1
  132. package/dist/dom.js.map +0 -1
  133. package/dist/editor-labels.d.ts.map +0 -1
  134. package/dist/editor-labels.js.map +0 -1
  135. package/dist/enter-policy.d.ts.map +0 -1
  136. package/dist/enter-policy.js.map +0 -1
  137. package/dist/fences.d.ts.map +0 -1
  138. package/dist/fences.js.map +0 -1
  139. package/dist/i18n.d.ts.map +0 -1
  140. package/dist/i18n.js.map +0 -1
  141. package/dist/index.d.ts.map +0 -1
  142. package/dist/index.js.map +0 -1
  143. package/dist/latex.d.ts.map +0 -1
  144. package/dist/latex.js.map +0 -1
  145. package/dist/lines.d.ts.map +0 -1
  146. package/dist/lines.js.map +0 -1
  147. package/dist/math-editor-toolbar.d.ts.map +0 -1
  148. package/dist/math-editor-toolbar.js.map +0 -1
  149. package/dist/math-editor.d.ts.map +0 -1
  150. package/dist/math-editor.js.map +0 -1
  151. package/dist/matrix.d.ts.map +0 -1
  152. package/dist/matrix.js.map +0 -1
  153. package/dist/model.d.ts.map +0 -1
  154. package/dist/model.js.map +0 -1
  155. package/dist/outputs.d.ts.map +0 -1
  156. package/dist/outputs.js.map +0 -1
  157. package/dist/range.d.ts.map +0 -1
  158. package/dist/range.js.map +0 -1
  159. package/dist/react.d.ts.map +0 -1
  160. package/dist/react.js.map +0 -1
  161. package/dist/session.d.ts.map +0 -1
  162. package/dist/session.js.map +0 -1
  163. package/dist/solid.d.ts.map +0 -1
  164. package/dist/solid.js.map +0 -1
  165. package/dist/suggestions.d.ts.map +0 -1
  166. package/dist/suggestions.js.map +0 -1
  167. package/dist/svelte.d.ts.map +0 -1
  168. package/dist/svelte.js.map +0 -1
  169. package/dist/symbol-browser.d.ts.map +0 -1
  170. package/dist/symbol-browser.js.map +0 -1
  171. package/dist/symbols.d.ts.map +0 -1
  172. package/dist/symbols.js.map +0 -1
  173. package/dist/templates.d.ts.map +0 -1
  174. package/dist/templates.js.map +0 -1
  175. package/dist/tokens.d.ts.map +0 -1
  176. package/dist/tokens.js.map +0 -1
  177. package/dist/vue.d.ts.map +0 -1
  178. package/dist/vue.js.map +0 -1
  179. package/dist/web-component.d.ts.map +0 -1
  180. package/dist/web-component.js.map +0 -1
package/dist/session.d.ts CHANGED
@@ -1,14 +1,24 @@
1
+ import { type MathPresentationChange } from './presentation.js';
2
+ import { type MathPreferences } from './preferences.js';
3
+ import { type LatexInsertionResult } from './latex-insertion.js';
1
4
  import { type LatexParseResult } from './latex.js';
2
5
  import { type MathDocument, type MathState, type MathCaret, type StructureKind } from './model.js';
3
6
  import { type MathRange, type MathFragment } from './range.js';
4
7
  import type { MathLocale } from './i18n.js';
8
+ import { type MathMatrixRange, type MathMatrixFragment } from './matrix-range.js';
5
9
  export type MathEditorMode = 'block' | 'inline';
6
10
  export interface MathSessionOptions {
11
+ /** UI-only preference store. Supply one store to share it across editors. */
12
+ preferences?: MathPreferences;
7
13
  document?: MathDocument;
8
14
  mode?: MathEditorMode;
9
15
  locale?: MathLocale;
10
16
  }
11
17
  export type MathCommand = {
18
+ type: 'presentation';
19
+ id: string;
20
+ change: MathPresentationChange;
21
+ } | {
12
22
  type: 'text';
13
23
  value: string;
14
24
  } | {
@@ -23,10 +33,18 @@ export type MathCommand = {
23
33
  type: 'undo';
24
34
  } | {
25
35
  type: 'redo';
36
+ } | {
37
+ type: 'matrix-select';
38
+ all?: boolean;
39
+ } | {
40
+ type: 'matrix-transpose';
41
+ } | {
42
+ type: 'matrix-clear';
26
43
  };
27
44
  export interface MathSessionSnapshot {
28
45
  state: MathState;
29
46
  range?: MathRange;
47
+ matrixRange?: MathMatrixRange;
30
48
  locale: MathLocale;
31
49
  mode: MathEditorMode;
32
50
  canUndo: boolean;
@@ -35,14 +53,26 @@ export interface MathSessionSnapshot {
35
53
  }
36
54
  /** Framework-free session. Snapshots are copies; consumers cannot mutate history. */
37
55
  export declare function createMathSession(options?: MathSessionOptions): {
56
+ preferences: {
57
+ getSnapshot: () => import("./preferences.js").MathPreferencesSnapshot;
58
+ record(id: string): void;
59
+ toggleFavorite(id: string): void;
60
+ clearRecent(): void;
61
+ subscribe(listener: () => void): () => void;
62
+ };
38
63
  getSnapshot: () => MathSessionSnapshot;
39
64
  subscribe(listener: (snapshot: MathSessionSnapshot, documentChanged: boolean) => void): () => void;
40
65
  apply: (next: MathState) => boolean;
41
66
  select(caret: MathCaret): void;
42
67
  selectRange(next?: MathRange): void;
43
- configure(next: Omit<MathSessionOptions, "document">): void;
68
+ selectMatrixRange(next?: MathMatrixRange): boolean;
69
+ copyMatrix(): MathMatrixFragment | undefined;
70
+ pasteMatrix(fragment: MathMatrixFragment): boolean;
71
+ configure(next: Omit<MathSessionOptions, "document" | "preferences">): void;
44
72
  /** Atomic replacement in the existing history. Syntax/policy failures emit no changes. */
45
73
  importLatex(source: string): LatexParseResult;
74
+ /** Explicit LaTeX paste replaces only the current range and creates one Undo entry. */
75
+ pasteLatex(source: string): LatexInsertionResult;
46
76
  load(document: MathDocument): void;
47
77
  copy(): MathFragment | undefined;
48
78
  paste(fragment: MathFragment): boolean;
@@ -50,4 +80,3 @@ export declare function createMathSession(options?: MathSessionOptions): {
50
80
  destroy(): void;
51
81
  };
52
82
  export type MathSession = ReturnType<typeof createMathSession>;
53
- //# sourceMappingURL=session.d.ts.map
package/dist/session.js CHANGED
@@ -1,12 +1,17 @@
1
+ import { setMathPresentation } from './presentation.js';
2
+ import { createMathPreferences } from './preferences.js';
3
+ import { insertLatex } from './latex-insertion.js';
1
4
  import { parseLatex } from './latex.js';
2
5
  import { commit, createHistory, initialState, insertStructure, toLatex, row, undo, redo, } from './model.js';
3
6
  import { copyRange, pasteFragment, wrapRange, wrappingKinds, } from './range.js';
4
7
  import { insertTemplate } from './templates.js';
5
8
  import { splitLine } from './lines.js';
9
+ import { activeMatrix } from './matrix.js';
10
+ import { clearMatrixRange, copyMatrixRange, focusMatrixCell, matrixFragment, pasteMatrixFragment, resolveMatrixRange, transposeMatrix, } from './matrix-range.js';
6
11
  /** Framework-free session. Snapshots are copies; consumers cannot mutate history. */
7
12
  export function createMathSession(options = {}) {
8
13
  let history = createHistory(initialState(options.document ? structuredClone(options.document) : undefined));
9
- let mode = options.mode ?? 'block', locale = options.locale ?? 'ko', range;
14
+ let mode = options.mode ?? 'block', locale = options.locale ?? 'ko', range, matrixRange;
10
15
  const listeners = new Set();
11
16
  const valid = (document) => mode !== 'inline' || !document.additionalLines?.length;
12
17
  if (!valid(history.present.document))
@@ -14,6 +19,7 @@ export function createMathSession(options = {}) {
14
19
  const getSnapshot = () => ({
15
20
  state: structuredClone(history.present),
16
21
  range: range && structuredClone(range),
22
+ matrixRange: matrixRange && structuredClone(matrixRange),
17
23
  locale,
18
24
  mode,
19
25
  canUndo: !!history.past.length,
@@ -32,12 +38,16 @@ export function createMathSession(options = {}) {
32
38
  history = changed
33
39
  ? commit(history, next)
34
40
  : { ...history, present: { ...history.present, caret: next.caret } };
35
- if (changed)
41
+ if (changed) {
36
42
  range = undefined;
43
+ matrixRange = undefined;
44
+ }
37
45
  emit(changed);
38
46
  return true;
39
47
  };
48
+ const preferences = options.preferences ?? createMathPreferences();
40
49
  return {
50
+ preferences,
41
51
  getSnapshot,
42
52
  subscribe(listener) {
43
53
  listeners.add(listener);
@@ -52,12 +62,34 @@ export function createMathSession(options = {}) {
52
62
  present: { ...history.present, caret: { ...caret } },
53
63
  };
54
64
  range = undefined;
65
+ matrixRange = undefined;
55
66
  emit(false);
56
67
  },
57
68
  selectRange(next) {
69
+ matrixRange = undefined;
58
70
  range = next && structuredClone(next);
59
71
  emit(false);
60
72
  },
73
+ selectMatrixRange(next) {
74
+ if (next && !resolveMatrixRange(history.present.document, next))
75
+ return false;
76
+ matrixRange = next && structuredClone(next);
77
+ range = undefined;
78
+ if (next)
79
+ history = {
80
+ ...history,
81
+ present: focusMatrixCell(history.present, next.matrixId, next.focus),
82
+ };
83
+ emit(false);
84
+ return true;
85
+ },
86
+ copyMatrix() {
87
+ return matrixRange ? copyMatrixRange(history.present.document, matrixRange) : undefined;
88
+ },
89
+ pasteMatrix(fragment) {
90
+ const next = pasteMatrixFragment(history.present, fragment, matrixRange);
91
+ return next !== history.present && apply(next);
92
+ },
61
93
  configure(next) {
62
94
  const targetMode = next.mode ?? mode;
63
95
  if (targetMode === 'inline' && history.present.document.additionalLines?.length)
@@ -73,17 +105,44 @@ export function createMathSession(options = {}) {
73
105
  apply(initialState(result.document));
74
106
  return result;
75
107
  },
108
+ /** Explicit LaTeX paste replaces only the current range and creates one Undo entry. */
109
+ pasteLatex(source) {
110
+ if (matrixRange)
111
+ return {
112
+ ok: false,
113
+ source,
114
+ diagnostics: [
115
+ {
116
+ code: 'unsupported-structure',
117
+ message: 'Select one insertion point before pasting LaTeX.',
118
+ start: 0,
119
+ end: source.length,
120
+ },
121
+ ],
122
+ };
123
+ const result = insertLatex(history.present, source, range, { multiline: mode !== 'inline' });
124
+ if (result.ok)
125
+ apply(result.state);
126
+ return result;
127
+ },
76
128
  load(document) {
77
129
  if (!valid(document))
78
130
  throw new Error('Inline mode requires a single top-level row.');
79
131
  history = createHistory(initialState(structuredClone(document)));
80
132
  range = undefined;
133
+ matrixRange = undefined;
81
134
  emit(true);
82
135
  },
83
136
  copy() {
137
+ if (matrixRange) {
138
+ const fragment = copyMatrixRange(history.present.document, matrixRange);
139
+ return fragment && matrixFragment(fragment);
140
+ }
84
141
  return range ? copyRange(history.present.document, range) : undefined;
85
142
  },
86
143
  paste(fragment) {
144
+ if (matrixRange)
145
+ return false;
87
146
  return apply(pasteFragment(history.present, fragment, range));
88
147
  },
89
148
  execute(command) {
@@ -96,19 +155,62 @@ export function createMathSession(options = {}) {
96
155
  const changed = next.present.document !== state.document;
97
156
  history = next;
98
157
  range = undefined;
158
+ matrixRange = undefined;
99
159
  emit(changed);
100
160
  return changed;
101
161
  }
102
- if (command.type === 'text')
103
- return apply(pasteFragment(state, {
162
+ if (command.type === 'presentation') {
163
+ if (range || matrixRange)
164
+ return false;
165
+ const next = setMathPresentation(state, command.id, command.change);
166
+ return next !== state && apply(next);
167
+ }
168
+ if (command.type === 'matrix-select') {
169
+ const context = activeMatrix(state);
170
+ if (!context)
171
+ return false;
172
+ matrixRange = {
173
+ matrixId: context.matrix.id,
174
+ anchor: command.all ? 0 : context.cell,
175
+ focus: command.all ? context.matrix.slots.length - 1 : context.cell,
176
+ };
177
+ range = undefined;
178
+ history = {
179
+ ...history,
180
+ present: focusMatrixCell(state, matrixRange.matrixId, matrixRange.focus),
181
+ };
182
+ emit(false);
183
+ return true;
184
+ }
185
+ if (command.type === 'matrix-transpose') {
186
+ const next = transposeMatrix(state, matrixRange?.matrixId);
187
+ return next !== state && apply(next);
188
+ }
189
+ if (command.type === 'matrix-clear')
190
+ return !!matrixRange && apply(clearMatrixRange(state, matrixRange));
191
+ if (matrixRange)
192
+ return (command.type === 'text' &&
193
+ !command.value.includes('\n') &&
194
+ !command.value.includes('\r') &&
195
+ apply(clearMatrixRange(state, matrixRange, command.value)));
196
+ if (command.type === 'text') {
197
+ const inserted = apply(pasteFragment(state, {
104
198
  version: 1,
105
199
  rows: command.value
106
200
  .replace(/\r\n?/g, '\n')
107
201
  .split('\n')
108
202
  .map((value) => row(value)),
109
203
  }, range));
110
- if (command.type === 'template')
111
- return apply(insertTemplate(state, command.id, range));
204
+ if (inserted)
205
+ preferences.record(`symbol-${command.value}`);
206
+ return inserted;
207
+ }
208
+ if (command.type === 'template') {
209
+ const inserted = apply(insertTemplate(state, command.id, range));
210
+ if (inserted)
211
+ preferences.record(`template-${command.id}`);
212
+ return inserted;
213
+ }
112
214
  if (command.type === 'newline')
113
215
  return mode === 'inline' ? false : apply(splitLine(state));
114
216
  if (range)
@@ -121,4 +223,3 @@ export function createMathSession(options = {}) {
121
223
  },
122
224
  };
123
225
  }
124
- //# sourceMappingURL=session.js.map
package/dist/solid.d.ts CHANGED
@@ -9,4 +9,3 @@ declare module 'solid-js' {
9
9
  }
10
10
  /** Solid directive: <div use:mathEditor={options()} />. */
11
11
  export declare function mathEditor(host: HTMLElement, options: Accessor<DOMMathEditorOptions>): void;
12
- //# sourceMappingURL=solid.d.ts.map
package/dist/solid.js CHANGED
@@ -6,4 +6,3 @@ export function mathEditor(host, options) {
6
6
  createEffect(() => editor.update(options()));
7
7
  onCleanup(() => editor.destroy());
8
8
  }
9
- //# sourceMappingURL=solid.js.map
@@ -7,7 +7,11 @@ export interface MathSuggestion {
7
7
  mathStyle?: 'display' | 'text';
8
8
  templateId?: string;
9
9
  identity?: boolean;
10
+ /** Fill each matrix cell with an editable zero. */
11
+ zero?: boolean;
12
+ /** Row count; matrices are square unless matrixColumns is supplied. */
10
13
  matrixSize?: number;
14
+ matrixColumns?: number;
11
15
  id: string;
12
16
  /** Catalog entries hold message keys; findSuggestions returns localized display text. */
13
17
  label: string;
@@ -28,4 +32,3 @@ export declare function findSuggestions(text: string, caret: number, locale?: Ma
28
32
  candidates: MathSuggestion[];
29
33
  };
30
34
  export declare function acceptSuggestion(state: MathState, query: string, candidate: MathSuggestion): MathState;
31
- //# sourceMappingURL=suggestions.d.ts.map
@@ -1,8 +1,9 @@
1
1
  import { mathLocaleAliases, translate, } from './i18n.js';
2
2
  import { insertTemplate, mathTemplates } from './templates.js';
3
- import { insertIdentityMatrix, insertMatrix } from './matrix.js';
3
+ import { activeMatrix, insertIdentityMatrix, insertMatrix, MATRIX_MAX_SIZE } from './matrix.js';
4
4
  import { insertStructure, namedFunctions, moveCaret, setText, textNodes, } from './model.js';
5
5
  import { mathSymbols } from './symbols.js';
6
+ import { wrappingKinds } from './range.js';
6
7
  const structure = (kind, glyph, label, aliases, triggers, detail) => ({
7
8
  id: kind,
8
9
  kind,
@@ -15,7 +16,7 @@ const structure = (kind, glyph, label, aliases, triggers, detail) => ({
15
16
  export const mathStructures = [
16
17
  structure('matrix', '[▦]', 'structure.matrix', ['matrix', '행렬', 'vector', '벡터'], [], 'structure.matrixDetail'),
17
18
  structure('aligned', '≡', 'structure.aligned', ['aligned', 'align', '정렬', '등호'], [], 'structure.alignedDetail'),
18
- structure('cases', '{', 'structure.cases', ['cases', 'case', '조건', '구간'], [], 'structure.casesDetail'),
19
+ structure('cases', '{', 'structure.cases', ['cases', 'case', '조건', '구간'], ['{'], 'structure.casesDetail'),
19
20
  structure('fraction', 'a/b', 'structure.fraction', ['frac', 'fraction', 'division'], ['/'], 'structure.fractionDetail'),
20
21
  structure('root', '√', 'structure.root', ['sqrt', 'root'], ['√'], 'structure.rootDetail'),
21
22
  structure('indexedRoot', 'ⁿ√', 'structure.indexedRoot', ['nthroot', 'nroot', '제곱근'], [], 'structure.indexedRootDetail'),
@@ -129,14 +130,58 @@ const catalog = [
129
130
  })),
130
131
  ];
131
132
  const triggers = [...new Set(catalog.flatMap((c) => c.triggers))].sort((a, b) => b.length - a.length);
133
+ /** Generate only the requested size, with identity matrices reserved for square shapes. */
134
+ function matrixDimensionSuggestions(rows, columns) {
135
+ const sizeId = rows === columns ? String(rows) : `${rows}x${columns}`;
136
+ const dimensions = { rows, columns };
137
+ const empty = {
138
+ ...mathStructures[0],
139
+ id: `matrix-${sizeId}`,
140
+ label: 'matrix.size',
141
+ labelParameters: dimensions,
142
+ detail: 'grid.dimensions',
143
+ detailParameters: dimensions,
144
+ matrixSize: rows,
145
+ matrixColumns: columns,
146
+ };
147
+ const candidates = [empty];
148
+ if (rows === columns) {
149
+ candidates.push({
150
+ ...empty,
151
+ id: `identity-${sizeId}`,
152
+ glyph: 'I',
153
+ label: 'matrix.identity',
154
+ detail: 'matrix.identityDetail',
155
+ identity: true,
156
+ });
157
+ }
158
+ candidates.push({
159
+ ...empty,
160
+ id: `template-zero-${sizeId}`,
161
+ glyph: '0',
162
+ label: 'matrix.zero',
163
+ zero: true,
164
+ });
165
+ return candidates;
166
+ }
132
167
  export function findSuggestions(text, caret, locale = 'ko') {
133
168
  const before = text.slice(0, caret);
169
+ // Size shortcuts are complete tokens, not the tail of a number or identifier.
170
+ // They remain ordinary text until the user accepts a matrix candidate.
171
+ const dimensionMatch = /^[\p{L}\p{M}\p{N}_.]/u.test(text.slice(caret))
172
+ ? null
173
+ : before.match(/(?<![\p{L}\p{M}\p{N}_.\\])([1-9]\d?)[xX×]([1-9]\d?)$/u);
174
+ const dimension = dimensionMatch &&
175
+ Number(dimensionMatch[1]) <= MATRIX_MAX_SIZE &&
176
+ Number(dimensionMatch[2]) <= MATRIX_MAX_SIZE
177
+ ? dimensionMatch
178
+ : null;
134
179
  // Recognize the longest symbolic spelling first: E/ must never become a fraction query.
135
180
  const trigger = triggers.find((t) => before.endsWith(t) &&
136
181
  (t.length > 1 || !/[A-Za-z]/.test(t) || !/[\p{L}\p{N}]/u.test(before.at(-2) ?? '')));
137
182
  const word = before.match(/(?:\\)?[\p{L}\p{M}]+$/u)?.[0];
138
183
  const operand = before.match(/(?:\d+(?:\.\d+)?|[\p{L}\p{M}]+)$/u)?.[0];
139
- const query = trigger ?? word ?? operand ?? '';
184
+ const query = dimension?.[0] ?? trigger ?? word ?? operand ?? '';
140
185
  if (!query)
141
186
  return { query: '', candidates: [] };
142
187
  const name = query.replace(/^\\/, '');
@@ -160,12 +205,16 @@ export function findSuggestions(text, caret, locale = 'ko') {
160
205
  return 3;
161
206
  return 4;
162
207
  };
163
- const candidates = trigger
164
- ? catalog.filter((item) => item.triggers.includes(trigger))
165
- : /^\d+(?:\.\d+)?$/.test(name)
166
- ? []
167
- : catalog.filter((item) => names(item).some((value) => normalize(value).includes(needle)));
208
+ const candidates = dimension
209
+ ? matrixDimensionSuggestions(Number(dimension[1]), Number(dimension[2]))
210
+ : trigger
211
+ ? catalog.filter((item) => item.triggers.includes(trigger))
212
+ : /^\d+(?:\.\d+)?$/.test(name)
213
+ ? []
214
+ : catalog.filter((item) => names(item).some((value) => normalize(value).includes(needle)));
168
215
  candidates.sort((a, b) => {
216
+ if (dimension)
217
+ return 0;
169
218
  if (trigger === '-' || trigger === '->') {
170
219
  const preferred = trigger === '-' ? ['−', '→'] : ['→'];
171
220
  const order = (item) => {
@@ -175,6 +224,8 @@ export function findSuggestions(text, caret, locale = 'ko') {
175
224
  if (order(a) !== order(b))
176
225
  return order(a) - order(b);
177
226
  }
227
+ if (trigger === '{')
228
+ return Number(b.kind === 'braces') - Number(a.kind === 'braces');
178
229
  if (trigger === '_')
179
230
  return Number(b.kind === 'subscript') - Number(a.kind === 'subscript');
180
231
  if (!trigger && rank(a) !== rank(b))
@@ -214,6 +265,16 @@ export function findSuggestions(text, caret, locale = 'ko') {
214
265
  });
215
266
  }
216
267
  }
268
+ // A partial name after a coefficient is an operand (4ab), not an "absolute"
269
+ // command. Keep explicit commands such as 4abs and \\abs working as before.
270
+ if (!trigger && /^\p{L}+$/u.test(query) && /\d$/.test(before.slice(0, -query.length))) {
271
+ for (let index = 0; index < candidates.length; index++) {
272
+ const item = candidates[index];
273
+ if (item.kind && wrappingKinds.some((kind) => kind === item.kind) && rank(item) >= 3) {
274
+ candidates[index] = { ...item, wrapOperand: true, detail: 'suggestion.wrapOperand' };
275
+ }
276
+ }
277
+ }
217
278
  return {
218
279
  query,
219
280
  candidates: candidates.map((item) => ({
@@ -228,6 +289,13 @@ export function acceptSuggestion(state, query, candidate) {
228
289
  const start = state.caret.start - query.length;
229
290
  if (!node || start < 0 || node.text.slice(start, state.caret.start) !== query)
230
291
  return state;
292
+ if (candidate.matrixSize !== undefined) {
293
+ const rows = candidate.matrixSize;
294
+ const columns = candidate.matrixColumns ?? rows;
295
+ if (![rows, columns].every((size) => Number.isInteger(size) && size >= 1 && size <= MATRIX_MAX_SIZE) ||
296
+ (candidate.identity && rows !== columns))
297
+ return state;
298
+ }
231
299
  if (candidate.wrapOperand && candidate.kind) {
232
300
  const selected = { ...state, caret: { ...state.caret, start, end: state.caret.start } };
233
301
  const wrapped = insertStructure(selected, candidate.kind);
@@ -251,12 +319,34 @@ export function acceptSuggestion(state, query, candidate) {
251
319
  const inserted = insertStructure(next, 'operatorName');
252
320
  return moveCaret(setText(inserted, candidate.operatorName, candidate.operatorName.length), 1);
253
321
  }
254
- if (candidate.templateId)
255
- return insertTemplate(next, candidate.templateId);
256
- if (candidate.matrixSize)
257
- return (candidate.identity ? insertIdentityMatrix : insertMatrix)(next, candidate.matrixSize);
322
+ if (candidate.templateId) {
323
+ const inserted = insertTemplate(next, candidate.templateId);
324
+ if (!candidate.templateId.startsWith('zero-') || inserted === next)
325
+ return inserted;
326
+ // Matrix suggestions start cell editing, including populated zero templates.
327
+ // Pasting a template otherwise leaves the caret after its last structure.
328
+ const matrix = activeMatrix(moveCaret(inserted, -1))?.matrix;
329
+ const first = matrix?.slots[0].children[0];
330
+ return first?.type === 'text'
331
+ ? { ...inserted, caret: { id: first.id, start: 0, end: 0 } }
332
+ : inserted;
333
+ }
334
+ if (candidate.matrixSize) {
335
+ const inserted = candidate.identity
336
+ ? insertIdentityMatrix(next, candidate.matrixSize)
337
+ : insertMatrix(next, candidate.matrixSize, candidate.matrixColumns);
338
+ if (inserted === next)
339
+ return state;
340
+ if (candidate.zero) {
341
+ activeMatrix(inserted)?.matrix.slots.forEach((cell) => {
342
+ const first = cell.children[0];
343
+ if (first.type === 'text')
344
+ first.text = '0';
345
+ });
346
+ }
347
+ return inserted;
348
+ }
258
349
  return candidate.kind
259
350
  ? insertStructure(next, candidate.kind, ['fraction', 'binomial', 'superscript', 'subscript'].includes(candidate.kind), candidate.mathStyle)
260
351
  : next;
261
352
  }
262
- //# sourceMappingURL=suggestions.js.map
package/dist/svelte.d.ts CHANGED
@@ -2,4 +2,3 @@ import type { Action } from 'svelte/action';
2
2
  import { type DOMMathEditorOptions } from './dom.js';
3
3
  /** Svelte action: <div use:mathEditor={options} />. */
4
4
  export declare const mathEditor: Action<HTMLElement, DOMMathEditorOptions | undefined>;
5
- //# sourceMappingURL=svelte.d.ts.map
package/dist/svelte.js CHANGED
@@ -11,4 +11,3 @@ export const mathEditor = (host, options) => {
11
11
  },
12
12
  };
13
13
  };
14
- //# sourceMappingURL=svelte.js.map
@@ -5,4 +5,3 @@ export declare function SymbolBrowser({ locale, onInsert, onClose, }: {
5
5
  onInsert: (glyph: string) => void;
6
6
  onClose: () => void;
7
7
  }): import("react/jsx-runtime").JSX.Element;
8
- //# sourceMappingURL=symbol-browser.d.ts.map
@@ -29,4 +29,3 @@ export function SymbolBrowser({ locale, onInsert, onClose, }) {
29
29
  }
30
30
  }, children: [_jsxs("div", { className: "me-symbol-browser-heading", children: [_jsxs("strong", { children: [t('symbols.title'), ' ', _jsxs("small", { children: [matches.length, " / ", mathSymbols.length] })] }), _jsx("button", { type: "button", onClick: onClose, children: t('common.close') })] }), _jsxs("label", { className: "me-symbol-search", children: [t('symbols.search'), _jsx("input", { ref: search, type: "search", value: query, onChange: (event) => setQuery(event.target.value), placeholder: t('symbols.searchPlaceholder') })] }), _jsx("p", { className: "me-symbol-help", children: t('symbols.help') }), _jsx("div", { className: "me-symbol-grid", children: matches.map(([glyph, label]) => (_jsxs("button", { type: "button", "aria-label": `${glyph} — ${t(label)}`, title: `${translate('en', label)} / ${translate('ko', label)}`, onClick: () => onInsert(glyph), children: [_jsx("span", { className: "me-symbol-glyph", "aria-hidden": "true", children: glyph }), _jsx("span", { children: t(label) })] }, glyph))) }), !matches.length && _jsx("p", { role: "status", children: t('symbols.noMatches') })] }));
31
31
  }
32
- //# sourceMappingURL=symbol-browser.js.map
package/dist/symbols.d.ts CHANGED
@@ -1,3 +1,2 @@
1
1
  /** One catalog powers discovery and LaTeX export. */
2
2
  export declare const mathSymbols: readonly [readonly ["↑", "symbol.upArrow", "uparrow arrow 화살표", "- ->", "\\uparrow"], readonly ["↕", "symbol.upDownArrow", "updownarrow arrow 화살표", "- ->", "\\updownarrow"], readonly ["⇐", "symbol.doubleLeftArrow", "Leftarrow arrow 화살표", "- ->", "\\Leftarrow"], readonly ["⇑", "symbol.doubleUpArrow", "Uparrow arrow 화살표", "- ->", "\\Uparrow"], readonly ["⇓", "symbol.doubleDownArrow", "Downarrow arrow 화살표", "- ->", "\\Downarrow"], readonly ["⇕", "symbol.doubleUpDownArrow", "Updownarrow arrow 화살표", "- ->", "\\Updownarrow"], readonly ["↗", "symbol.northEastArrow", "nearrow arrow 화살표", "- ->", "\\nearrow"], readonly ["↘", "symbol.southEastArrow", "searrow arrow 화살표", "- ->", "\\searrow"], readonly ["↙", "symbol.southWestArrow", "swarrow arrow 화살표", "- ->", "\\swarrow"], readonly ["↖", "symbol.northWestArrow", "nwarrow arrow 화살표", "- ->", "\\nwarrow"], readonly ["⟶", "symbol.longRightArrow", "longrightarrow arrow 화살표", "- ->", "\\longrightarrow"], readonly ["⟵", "symbol.longLeftArrow", "longleftarrow arrow 화살표", "- ->", "\\longleftarrow"], readonly ["⟷", "symbol.longLeftRightArrow", "longleftrightarrow arrow 화살표", "-", "\\longleftrightarrow"], readonly ["⟹", "symbol.longDoubleRightArrow", "Longrightarrow arrow 화살표", "- ->", "\\Longrightarrow"], readonly ["⟸", "symbol.longDoubleLeftArrow", "Longleftarrow arrow 화살표", "- ->", "\\Longleftarrow"], readonly ["⟺", "symbol.longDoubleLeftRightArrow", "Longleftrightarrow arrow 화살표", "", "\\Longleftrightarrow"], readonly ["↪", "symbol.hookRightArrow", "hookrightarrow arrow 화살표", "- ->", "\\hookrightarrow"], readonly ["↩", "symbol.hookLeftArrow", "hookleftarrow arrow 화살표", "- ->", "\\hookleftarrow"], readonly ["⇀", "symbol.rightHarpoonUp", "rightharpoonup arrow 화살표", "- ->", "\\rightharpoonup"], readonly ["↼", "symbol.leftHarpoonUp", "leftharpoonup arrow 화살표", "- ->", "\\leftharpoonup"], readonly ["⇁", "symbol.rightHarpoonDown", "rightharpoondown arrow 화살표", "- ->", "\\rightharpoondown"], readonly ["↽", "symbol.leftHarpoonDown", "leftharpoondown arrow 화살표", "- ->", "\\leftharpoondown"], readonly ["⇌", "symbol.rightLeftHarpoons", "rightleftharpoons arrow 화살표", "- ->", "\\rightleftharpoons"], readonly ["⇋", "symbol.leftRightHarpoons", "leftrightharpoons arrow 화살표", "- ->", "\\leftrightharpoons"], readonly ["↓", "symbol.downArrow", "downarrow down 아래화살표 아래쪽화살표", "- ->", "\\downarrow"], readonly ["⩾", "symbol.greaterOrEqualSlanted", "geqslant", "", "\\geqslant"], readonly ["⩽", "symbol.lessOrEqualSlanted", "leqslant", "", "\\leqslant"], readonly ["∋", "symbol.contains", "ni contains", "", "\\ni"], readonly ["⊃", "symbol.superset", "supset superset", "", "\\supset"], readonly ["⊇", "symbol.supersetOrEqual", "supseteq", "", "\\supseteq"], readonly ["⊈", "symbol.notSubsetOrEqual", "nsubseteq", "", "\\nsubseteq"], readonly ["∖", "symbol.setDifference", "setminus difference", "", "\\setminus"], readonly ["ℕ", "symbol.naturals", "naturals natural", "", "\\mathbb{N}"], readonly ["ℤ", "symbol.integers", "integers integer", "", "\\mathbb{Z}"], readonly ["ℚ", "symbol.rationals", "rationals rational", "", "\\mathbb{Q}"], readonly ["ℝ", "symbol.reals", "reals real", "", "\\mathbb{R}"], readonly ["ℂ", "symbol.complexes", "complexes complex", "", "\\mathbb{C}"], readonly ["⊥", "symbol.perpendicular", "perp perpendicular", "", "\\perp"], readonly ["∥", "symbol.parallel", "parallel", "", "\\parallel"], readonly ["≅", "symbol.congruent", "cong congruent", "", "\\cong"], readonly ["∼", "symbol.similar", "sim similar", "", "\\sim"], readonly ["↦", "symbol.mapsTo", "mapsto maps arrow 화살표", "|-> - ->", "\\mapsto"], readonly ["…", "symbol.ellipsis", "ldots ellipsis", "", "\\ldots"], readonly ["⋯", "symbol.centeredDots", "cdots", "", "\\cdots"], readonly ["⋮", "symbol.verticalDots", "vdots", "", "\\vdots"], readonly ["⋱", "symbol.diagonalDots", "ddots", "", "\\ddots"], readonly ["°", "symbol.degree", "degree degrees 각도", "", "{}^{\\circ}"], readonly ["∠", "symbol.angle", "angle", "", "\\angle"], readonly ["+", "symbol.plus", "plus sum", "+", "+"], readonly ["−", "symbol.minus", "minus", "-", "-"], readonly ["±", "symbol.plusMinus", "plusminus sum", "+- +", "\\pm"], readonly ["∓", "symbol.minusPlus", "minusplus", "-+ -", "\\mp"], readonly ["×", "symbol.times", "times multiply", "*", "\\times"], readonly ["·", "symbol.dot", "dot multiply", "*", "\\cdot"], readonly ["÷", "symbol.divide", "divide division", "/", "\\div"], readonly ["=", "symbol.equal", "equal", "=", "="], readonly ["≠", "symbol.notEqual", "neq unequal", "!= /= =", "\\ne"], readonly ["≈", "symbol.approximatelyEqual", "approx", "~ ~~ =", "\\approx"], readonly ["≡", "symbol.equivalent", "equiv", "=== =", "\\equiv"], readonly ["<", "symbol.lessThan", "less", "<", "<"], readonly [">", "symbol.greaterThan", "greater", ">", ">"], readonly ["≤", "symbol.lessOrEqual", "leq less", "<= <- <", "\\le"], readonly ["≥", "symbol.greaterOrEqual", "geq greater", ">= >- >", "\\ge"], readonly ["∝", "symbol.proportional", "proportional", "~", "\\propto"], readonly ["→", "symbol.rightArrow", "rightarrow arrow", "-> -", "\\to"], readonly ["←", "symbol.leftArrow", "leftarrow arrow", "<- < - ->", "\\leftarrow"], readonly ["↔", "symbol.leftRightArrow", "leftrightarrow arrow", "<-> - ->", "\\leftrightarrow"], readonly ["⇒", "symbol.implies", "implies arrow 화살표", "=> = - ->", "\\Rightarrow"], readonly ["⇔", "symbol.iff", "iff arrow 화살표", "<=> - ->", "\\Leftrightarrow"], readonly ["∈", "symbol.elementOf", "in element", "E", "\\in"], readonly ["∉", "symbol.notElementOf", "notin element", "E/", "\\notin"], readonly ["∀", "symbol.forAll", "forall", "A", "\\forall"], readonly ["∃", "symbol.exists", "exists", "E", "\\exists"], readonly ["∪", "symbol.union", "union", "U", "\\cup"], readonly ["∩", "symbol.intersection", "intersection", "", "\\cap"], readonly ["⊂", "symbol.subset", "subset", "<", "\\subset"], readonly ["⊆", "symbol.subsetOrEqual", "subseteq", "<=", "\\subseteq"], readonly ["∅", "symbol.emptySet", "emptyset", "", "\\emptyset"], readonly ["∞", "symbol.infinity", "infinity infty", "", "\\infty"], readonly ["∂", "symbol.partialDerivative", "partial derivative", "", "\\partial"], readonly ["∇", "symbol.nabla", "nabla gradient", "", "\\nabla"], readonly ["∧", "symbol.logicalAnd", "and wedge", "^", "\\wedge"], readonly ["∨", "symbol.logicalOr", "or vee", "", "\\vee"], readonly ["¬", "symbol.logicalNot", "not neg", "!", "\\neg"], readonly ["α", "symbol.alpha", "alpha", "", "\\alpha"], readonly ["β", "symbol.beta", "beta", "", "\\beta"], readonly ["γ", "symbol.gamma", "gamma", "", "\\gamma"], readonly ["δ", "symbol.delta", "delta", "", "\\delta"], readonly ["ε", "symbol.epsilon", "epsilon", "", "\\epsilon"], readonly ["ζ", "symbol.zeta", "zeta", "", "\\zeta"], readonly ["η", "symbol.eta", "eta", "", "\\eta"], readonly ["θ", "symbol.theta", "theta", "", "\\theta"], readonly ["ι", "symbol.iota", "iota", "", "\\iota"], readonly ["κ", "symbol.kappa", "kappa", "", "\\kappa"], readonly ["λ", "symbol.lambda", "lambda", "", "\\lambda"], readonly ["μ", "symbol.mu", "mu", "", "\\mu"], readonly ["ν", "symbol.nu", "nu", "", "\\nu"], readonly ["ξ", "symbol.xi", "xi", "", "\\xi"], readonly ["π", "symbol.pi", "pi", "", "\\pi"], readonly ["ρ", "symbol.rho", "rho", "", "\\rho"], readonly ["σ", "symbol.sigma", "sigma", "", "\\sigma"], readonly ["τ", "symbol.tau", "tau", "", "\\tau"], readonly ["υ", "symbol.upsilon", "upsilon", "", "\\upsilon"], readonly ["φ", "symbol.phi", "phi", "", "\\phi"], readonly ["χ", "symbol.chi", "chi", "", "\\chi"], readonly ["ψ", "symbol.psi", "psi", "", "\\psi"], readonly ["ω", "symbol.omega", "omega", "", "\\omega"], readonly ["Γ", "symbol.upperGamma", "Gamma", "", "\\Gamma"], readonly ["Δ", "symbol.upperDelta", "Delta", "", "\\Delta"], readonly ["Θ", "symbol.upperTheta", "Theta", "", "\\Theta"], readonly ["Λ", "symbol.upperLambda", "Lambda", "", "\\Lambda"], readonly ["Ξ", "symbol.upperXi", "Xi", "", "\\Xi"], readonly ["Π", "symbol.upperPi", "Pi", "", "\\Pi"], readonly ["Σ", "symbol.upperSigma", "Sigma", "", "\\Sigma"], readonly ["Φ", "symbol.upperPhi", "Phi", "", "\\Phi"], readonly ["Ψ", "symbol.upperPsi", "Psi", "", "\\Psi"], readonly ["Ω", "symbol.upperOmega", "Omega", "", "\\Omega"]];
3
- //# sourceMappingURL=symbols.d.ts.map
package/dist/symbols.js CHANGED
@@ -142,4 +142,3 @@ export const mathSymbols = [
142
142
  ['Ψ', 'symbol.upperPsi', 'Psi', '', '\\Psi'],
143
143
  ['Ω', 'symbol.upperOmega', 'Omega', '', '\\Omega'],
144
144
  ];
145
- //# sourceMappingURL=symbols.js.map
@@ -13,4 +13,3 @@ export declare const mathTemplates: MathTemplate[];
13
13
  /** Each call creates independent, editable nodes; templates are not opaque LaTeX. */
14
14
  export declare function createTemplate(id: string): MathFragment | undefined;
15
15
  export declare function insertTemplate(state: MathState, id: string, range?: MathRange): MathState;
16
- //# sourceMappingURL=templates.d.ts.map
package/dist/templates.js CHANGED
@@ -83,4 +83,3 @@ export function insertTemplate(state, id, range) {
83
83
  const fragment = createTemplate(id);
84
84
  return fragment ? pasteFragment(state, fragment, range) : state;
85
85
  }
86
- //# sourceMappingURL=templates.js.map
package/dist/tokens.d.ts CHANGED
@@ -8,4 +8,3 @@ export interface MathToken {
8
8
  /** Lexical input units, not inferred mathematical definitions (e and c remain variables). */
9
9
  export declare function tokenizeMathText(text: string): MathToken[];
10
10
  export declare function tokenIndexAt(tokens: MathToken[], offset: number, affinity?: 'forward' | 'backward'): number;
11
- //# sourceMappingURL=tokens.d.ts.map
package/dist/tokens.js CHANGED
@@ -21,4 +21,3 @@ export function tokenIndexAt(tokens, offset, affinity = 'backward') {
21
21
  : offset > token.start && offset <= token.end);
22
22
  return index >= 0 ? index : offset <= 0 ? 0 : tokens.length - 1;
23
23
  }
24
- //# sourceMappingURL=tokens.js.map
@@ -0,0 +1,15 @@
1
+ import { type MathCaret, type MathState } from './model.js';
2
+ import type { MathPoint } from './range.js';
3
+ type VerticalPoint = MathPoint & Pick<MathCaret, 'affinity'>;
4
+ /** Viewport coordinates of a rendered insertion point, including either side of a token boundary. */
5
+ export type MathCaretGeometry = (point: VerticalPoint) => {
6
+ x: number;
7
+ y: number;
8
+ } | undefined;
9
+ /**
10
+ * Move through the nearest vertical structure, then enclosing grids and equation lines.
11
+ * Geometry is optional so the same operation works in headless sessions and either renderer.
12
+ * Native text selections and literal text retain their own arrow-key behavior.
13
+ */
14
+ export declare function moveVertical(state: MathState, direction: -1 | 1, geometry?: MathCaretGeometry): MathState;
15
+ export {};