@barocss/math-editor 0.2.1 → 0.5.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 (202) 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 +178 -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 +3 -1
  9. package/CHANGELOG.md +106 -0
  10. package/EDITING-SCENARIOS.md +174 -0
  11. package/EMBEDDING.md +4 -0
  12. package/IMPLEMENTATION.md +122 -2
  13. package/JSON-MODEL.md +1 -0
  14. package/LATEX-GUIDE.md +30 -1
  15. package/LATEX-MODEL.md +12 -0
  16. package/LATEX-SCOPE.md +12 -3
  17. package/LICENSE +1 -3
  18. package/README.md +201 -39
  19. package/RELEASING.md +81 -10
  20. package/RENDERING-TESTS.md +79 -0
  21. package/ROADMAP.md +220 -11
  22. package/STYLING.md +157 -0
  23. package/SUPPORT.md +24 -12
  24. package/VALIDATION.md +444 -4
  25. package/dist/context-tools.d.ts +21 -0
  26. package/dist/context-tools.js +37 -0
  27. package/dist/core.d.ts +5 -1
  28. package/dist/core.js +5 -1
  29. package/dist/document-codec.d.ts +0 -1
  30. package/dist/document-codec.js +0 -1
  31. package/dist/dom/caret-geometry.d.ts +3 -0
  32. package/dist/dom/caret-geometry.js +41 -0
  33. package/dist/dom/context-keyboard.d.ts +2 -0
  34. package/dist/dom/context-keyboard.js +22 -0
  35. package/dist/dom/elements.d.ts +1 -2
  36. package/dist/dom/elements.js +2 -3
  37. package/dist/dom/latex-panel.d.ts +10 -0
  38. package/dist/dom/latex-panel.js +59 -0
  39. package/dist/dom/matrix-selection.d.ts +11 -0
  40. package/dist/dom/matrix-selection.js +30 -0
  41. package/dist/dom/menu-position.d.ts +17 -2
  42. package/dist/dom/menu-position.js +129 -16
  43. package/dist/dom/presentation-controls.d.ts +4 -0
  44. package/dist/dom/presentation-controls.js +56 -0
  45. package/dist/dom/quick-panel.d.ts +12 -0
  46. package/dist/dom/quick-panel.js +102 -0
  47. package/dist/dom/selection.d.ts +29 -0
  48. package/dist/dom/selection.js +53 -0
  49. package/dist/dom/symbol-panel.d.ts +9 -0
  50. package/dist/dom/symbol-panel.js +146 -0
  51. package/dist/dom/theme.d.ts +6 -0
  52. package/dist/dom/theme.js +68 -0
  53. package/dist/dom/toolbar-catalog.d.ts +10 -0
  54. package/dist/dom/toolbar-catalog.js +35 -0
  55. package/dist/dom/toolbar.d.ts +13 -2
  56. package/dist/dom/toolbar.js +295 -28
  57. package/dist/dom.d.ts +9 -1
  58. package/dist/dom.js +682 -151
  59. package/dist/editor-labels.d.ts +0 -1
  60. package/dist/editor-labels.js +0 -1
  61. package/dist/enter-policy.d.ts +0 -1
  62. package/dist/enter-policy.js +0 -1
  63. package/dist/fences.d.ts +0 -1
  64. package/dist/fences.js +0 -1
  65. package/dist/i18n.d.ts +0 -1
  66. package/dist/i18n.js +0 -1
  67. package/dist/index.d.ts +4 -1
  68. package/dist/index.js +4 -1
  69. package/dist/latex-insertion.d.ts +12 -0
  70. package/dist/latex-insertion.js +26 -0
  71. package/dist/latex-paste-panel.d.ts +3 -0
  72. package/dist/latex-paste-panel.js +18 -0
  73. package/dist/latex.d.ts +0 -1
  74. package/dist/latex.js +11 -1
  75. package/dist/lines.d.ts +0 -1
  76. package/dist/lines.js +0 -1
  77. package/dist/locales/en.js +39 -1
  78. package/dist/locales/en.json +39 -1
  79. package/dist/locales/ko.js +39 -1
  80. package/dist/locales/ko.json +39 -1
  81. package/dist/math-editor-toolbar.d.ts +4 -2
  82. package/dist/math-editor-toolbar.js +3 -4
  83. package/dist/math-editor.d.ts +7 -2
  84. package/dist/math-editor.js +661 -308
  85. package/dist/math-layout.d.ts +12 -0
  86. package/dist/math-layout.js +59 -0
  87. package/dist/math-spacing.d.ts +13 -0
  88. package/dist/math-spacing.js +87 -0
  89. package/dist/matrix-range.d.ts +40 -0
  90. package/dist/matrix-range.js +242 -0
  91. package/dist/matrix.d.ts +0 -1
  92. package/dist/matrix.js +0 -1
  93. package/dist/model.d.ts +4 -1
  94. package/dist/model.js +49 -5
  95. package/dist/outputs.d.ts +0 -1
  96. package/dist/outputs.js +0 -1
  97. package/dist/preferences.d.ts +24 -0
  98. package/dist/preferences.js +66 -0
  99. package/dist/presentation-controls.d.ts +10 -0
  100. package/dist/presentation-controls.js +10 -0
  101. package/dist/presentation.d.ts +13 -0
  102. package/dist/presentation.js +86 -0
  103. package/dist/quick-panel.d.ts +2 -0
  104. package/dist/quick-panel.js +17 -0
  105. package/dist/range.d.ts +2 -1
  106. package/dist/range.js +11 -1
  107. package/dist/react.d.ts +0 -1
  108. package/dist/react.js +0 -1
  109. package/dist/root-transform.d.ts +19 -0
  110. package/dist/root-transform.js +69 -0
  111. package/dist/session.d.ts +31 -2
  112. package/dist/session.js +108 -7
  113. package/dist/solid.d.ts +0 -1
  114. package/dist/solid.js +0 -1
  115. package/dist/suggestions.d.ts +13 -1
  116. package/dist/suggestions.js +153 -12
  117. package/dist/svelte.d.ts +0 -1
  118. package/dist/svelte.js +0 -1
  119. package/dist/symbol-browser.d.ts +0 -1
  120. package/dist/symbol-browser.js +0 -1
  121. package/dist/symbols.d.ts +0 -1
  122. package/dist/symbols.js +0 -1
  123. package/dist/templates.d.ts +0 -1
  124. package/dist/templates.js +0 -1
  125. package/dist/tokens.d.ts +0 -1
  126. package/dist/tokens.js +0 -1
  127. package/dist/vertical-navigation.d.ts +15 -0
  128. package/dist/vertical-navigation.js +145 -0
  129. package/dist/vue.d.ts +0 -1
  130. package/dist/vue.js +0 -1
  131. package/dist/web-component.d.ts +0 -1
  132. package/dist/web-component.js +9 -2
  133. package/package.json +11 -3
  134. package/src/fonts/KaTeX_Math-Italic.woff2 +0 -0
  135. package/src/fonts/KaTeX_Size1-Regular.woff2 +0 -0
  136. package/src/fonts/README.md +18 -0
  137. package/src/shapes/README.md +21 -0
  138. package/src/shapes/parenthesis-bottom.svg +1 -0
  139. package/src/shapes/parenthesis-top.svg +1 -0
  140. package/src/shapes/parenthesis.svg +1 -0
  141. package/src/shapes/radical.svg +1 -0
  142. package/src/style.css +949 -173
  143. package/dist/core.d.ts.map +0 -1
  144. package/dist/core.js.map +0 -1
  145. package/dist/document-codec.d.ts.map +0 -1
  146. package/dist/document-codec.js.map +0 -1
  147. package/dist/dom/elements.d.ts.map +0 -1
  148. package/dist/dom/elements.js.map +0 -1
  149. package/dist/dom/menu-position.d.ts.map +0 -1
  150. package/dist/dom/menu-position.js.map +0 -1
  151. package/dist/dom/toolbar.d.ts.map +0 -1
  152. package/dist/dom/toolbar.js.map +0 -1
  153. package/dist/dom.d.ts.map +0 -1
  154. package/dist/dom.js.map +0 -1
  155. package/dist/editor-labels.d.ts.map +0 -1
  156. package/dist/editor-labels.js.map +0 -1
  157. package/dist/enter-policy.d.ts.map +0 -1
  158. package/dist/enter-policy.js.map +0 -1
  159. package/dist/fences.d.ts.map +0 -1
  160. package/dist/fences.js.map +0 -1
  161. package/dist/i18n.d.ts.map +0 -1
  162. package/dist/i18n.js.map +0 -1
  163. package/dist/index.d.ts.map +0 -1
  164. package/dist/index.js.map +0 -1
  165. package/dist/latex.d.ts.map +0 -1
  166. package/dist/latex.js.map +0 -1
  167. package/dist/lines.d.ts.map +0 -1
  168. package/dist/lines.js.map +0 -1
  169. package/dist/math-editor-toolbar.d.ts.map +0 -1
  170. package/dist/math-editor-toolbar.js.map +0 -1
  171. package/dist/math-editor.d.ts.map +0 -1
  172. package/dist/math-editor.js.map +0 -1
  173. package/dist/matrix.d.ts.map +0 -1
  174. package/dist/matrix.js.map +0 -1
  175. package/dist/model.d.ts.map +0 -1
  176. package/dist/model.js.map +0 -1
  177. package/dist/outputs.d.ts.map +0 -1
  178. package/dist/outputs.js.map +0 -1
  179. package/dist/range.d.ts.map +0 -1
  180. package/dist/range.js.map +0 -1
  181. package/dist/react.d.ts.map +0 -1
  182. package/dist/react.js.map +0 -1
  183. package/dist/session.d.ts.map +0 -1
  184. package/dist/session.js.map +0 -1
  185. package/dist/solid.d.ts.map +0 -1
  186. package/dist/solid.js.map +0 -1
  187. package/dist/suggestions.d.ts.map +0 -1
  188. package/dist/suggestions.js.map +0 -1
  189. package/dist/svelte.d.ts.map +0 -1
  190. package/dist/svelte.js.map +0 -1
  191. package/dist/symbol-browser.d.ts.map +0 -1
  192. package/dist/symbol-browser.js.map +0 -1
  193. package/dist/symbols.d.ts.map +0 -1
  194. package/dist/symbols.js.map +0 -1
  195. package/dist/templates.d.ts.map +0 -1
  196. package/dist/templates.js.map +0 -1
  197. package/dist/tokens.d.ts.map +0 -1
  198. package/dist/tokens.js.map +0 -1
  199. package/dist/vue.d.ts.map +0 -1
  200. package/dist/vue.js.map +0 -1
  201. package/dist/web-component.d.ts.map +0 -1
  202. package/dist/web-component.js.map +0 -1
@@ -1,61 +1,328 @@
1
- import { element } from './elements.js';
2
- import { translate, mathLocaleDirection } from '../i18n.js';
1
+ import { drawPresentationControls } from './presentation-controls.js';
2
+ import { mountQuickPanel } from './quick-panel.js';
3
+ import { mountLatexPanel } from './latex-panel.js';
4
+ import { elementFactory } from './elements.js';
5
+ import { translate, mathLocaleDirection, } from '../i18n.js';
3
6
  import { mathStructures } from '../suggestions.js';
7
+ import { mathSymbols } from '../symbols.js';
8
+ import { resolveRange, wrappingKinds } from '../range.js';
9
+ import { activeGrid, insertIdentityMatrix, insertMatrix, matrixEnvironments, MATRIX_MAX_SIZE, resizeMatrix, setMatrixEnvironment, } from '../matrix.js';
10
+ import { parseToolbarMatrixPreset, toolbarTemplates } from './toolbar-catalog.js';
11
+ import { mountSymbolPanel } from './symbol-panel.js';
4
12
  /** Independently mounted toolbar; the host may instead build any UI around session.execute(). */
5
13
  export function mountMathToolbar(host, session, options = {}) {
14
+ const doc = host.ownerDocument;
15
+ const element = elementFactory(doc);
6
16
  let expanded = false;
17
+ let activeSession = session;
18
+ let locale = options.locale ?? 'en';
19
+ let destroyed = false;
20
+ let disabled = false;
21
+ let quickPanel;
22
+ let latexPanel;
23
+ let symbolPanel;
7
24
  const maxItems = Math.max(0, Math.floor(Number.isFinite(options.maxItems) ? options.maxItems : 8));
25
+ const templates = toolbarTemplates(options.kinds);
8
26
  const toolbar = element('div', 'me-tools');
9
- host.append(toolbar);
27
+ const gridTools = element('div', 'me-matrix-tools me-dom-grid-tools');
28
+ const presentation = element('div');
29
+ const panels = element('div', 'me-dom-toolbar-panels');
30
+ panels.hidden = true;
31
+ host.append(toolbar, gridTools, presentation, panels);
32
+ const closeSymbols = (restoreFocus = false) => {
33
+ quickPanel?.destroy();
34
+ quickPanel = undefined;
35
+ latexPanel?.destroy();
36
+ latexPanel = undefined;
37
+ symbolPanel?.destroy();
38
+ symbolPanel = undefined;
39
+ panels.hidden = true;
40
+ const toggle = toolbar.querySelector('[data-math-tool="symbols"]');
41
+ toggle?.setAttribute('aria-expanded', 'false');
42
+ if (restoreFocus)
43
+ toggle?.focus();
44
+ };
45
+ const enabled = () => !!activeSession && !disabled && !destroyed;
46
+ const execute = (command) => {
47
+ if (enabled() && activeSession.execute(command))
48
+ options.onExecute?.();
49
+ };
50
+ const transform = (operation) => {
51
+ if (!enabled())
52
+ return;
53
+ const current = activeSession.getSnapshot().state;
54
+ const next = operation(current);
55
+ if (next !== current && activeSession.apply(next))
56
+ options.onExecute?.();
57
+ };
10
58
  const draw = () => {
11
- const snapshot = session.getSnapshot(), t = (text) => translate(snapshot.locale, text);
59
+ const snapshot = activeSession?.getSnapshot();
60
+ if (snapshot && (snapshot.locale !== locale || snapshot.matrixRange))
61
+ closeSymbols();
62
+ locale = snapshot?.locale ?? locale;
63
+ const t = (text, parameters) => translate(locale, text, parameters);
12
64
  toolbar.replaceChildren();
65
+ gridTools.replaceChildren();
66
+ gridTools.hidden = true;
13
67
  toolbar.setAttribute('role', 'toolbar');
14
68
  toolbar.setAttribute('aria-label', t('toolbar.label'));
15
- toolbar.dir = mathLocaleDirection(snapshot.locale);
69
+ toolbar.dir = mathLocaleDirection(locale);
70
+ const button = (label, action, blocked = false) => {
71
+ const result = element('button', '', label);
72
+ result.type = 'button';
73
+ result.disabled = !enabled() || blocked;
74
+ result.onmousedown = (event) => event.preventDefault();
75
+ result.onclick = () => {
76
+ if (!result.disabled && enabled())
77
+ action();
78
+ };
79
+ return result;
80
+ };
81
+ const hasRange = !!snapshot?.range || !!snapshot?.matrixRange;
82
+ const canWrap = snapshot?.range && resolveRange(snapshot.state.document, snapshot.range)?.length === 1;
16
83
  const items = mathStructures.filter((item) => !options.kinds || options.kinds.includes(item.kind));
17
84
  for (const item of expanded ? items : items.slice(0, maxItems)) {
18
- const button = element('button', '', `${item.glyph} ${t(item.label)}`);
19
- button.type = 'button';
20
- button.setAttribute('aria-label', t(item.label));
21
- button.onmousedown = (event) => event.preventDefault();
22
- button.onclick = () => {
23
- session.execute({ type: 'structure', kind: item.kind });
24
- options.onExecute?.();
25
- };
26
- toolbar.append(button);
85
+ const control = button(`${item.glyph} ${t(item.label)}`, () => execute({ type: 'structure', kind: item.kind }), hasRange && (!canWrap || !wrappingKinds.includes(item.kind)));
86
+ control.setAttribute('aria-label', t(item.label));
87
+ control.dataset.mathTool = item.kind;
88
+ toolbar.append(control);
27
89
  }
28
- if (items.length > maxItems) {
90
+ if (expanded) {
91
+ const quick = button(t('quick.title'), () => {
92
+ options.onOpenPanel?.();
93
+ closeSymbols();
94
+ const target = activeSession;
95
+ panels.hidden = false;
96
+ quickPanel = mountQuickPanel(panels, {
97
+ locale,
98
+ preferences: target.preferences,
99
+ allowedTemplates: templates.map((item) => item.id),
100
+ onInsert: (item) => enabled() &&
101
+ activeSession === target &&
102
+ target.execute(item.template
103
+ ? { type: 'template', id: item.template }
104
+ : { type: 'text', value: item.glyph }),
105
+ onClose: () => {
106
+ closeSymbols();
107
+ options.onExecute?.();
108
+ },
109
+ });
110
+ }, !!snapshot?.matrixRange);
111
+ quick.dataset.mathTool = 'quick';
112
+ toolbar.append(quick);
113
+ const paste = button(t('clipboard.latexTitle'), () => {
114
+ options.onOpenPanel?.();
115
+ closeSymbols();
116
+ if (options.onPasteLatex) {
117
+ options.onPasteLatex();
118
+ return;
119
+ }
120
+ panels.hidden = false;
121
+ latexPanel = mountLatexPanel(panels, {
122
+ locale,
123
+ onInsert: (source) => activeSession.pasteLatex(source),
124
+ onClose: () => {
125
+ closeSymbols();
126
+ options.onExecute?.();
127
+ },
128
+ });
129
+ }, !!snapshot?.matrixRange);
130
+ paste.dataset.mathTool = 'paste-latex';
131
+ paste.title = t('clipboard.latexShortcut');
132
+ toolbar.append(paste);
133
+ if (!options.kinds || options.kinds.includes('matrix')) {
134
+ const select = element('select', 'me-matrix-size');
135
+ select.setAttribute('aria-label', t('matrix.preset'));
136
+ select.dataset.mathTool = 'matrix-preset';
137
+ select.disabled = !enabled() || hasRange;
138
+ const placeholder = element('option', '', t('matrix.presetPlaceholder'));
139
+ placeholder.value = '';
140
+ placeholder.disabled = true;
141
+ select.append(placeholder);
142
+ for (const identity of [false, true])
143
+ for (const size of [2, 3, 4]) {
144
+ const item = element('option', '', t(identity ? 'matrix.insertIdentity' : 'matrix.insert', {
145
+ rows: size,
146
+ columns: size,
147
+ }));
148
+ item.value = `${identity ? 'identity-' : ''}${size}`;
149
+ select.append(item);
150
+ }
151
+ select.value = '';
152
+ select.onchange = () => {
153
+ const preset = parseToolbarMatrixPreset(select.value);
154
+ select.value = '';
155
+ if (preset && !select.disabled)
156
+ transform((state) => (preset.identity ? insertIdentityMatrix : insertMatrix)(state, preset.size));
157
+ };
158
+ toolbar.append(select);
159
+ }
160
+ if (templates.length) {
161
+ const select = element('select', 'me-matrix-size');
162
+ select.setAttribute('aria-label', t('template.label'));
163
+ select.dataset.mathTool = 'template';
164
+ select.disabled = !enabled() || !!snapshot?.matrixRange;
165
+ const placeholder = element('option', '', t('template.placeholder'));
166
+ placeholder.value = '';
167
+ placeholder.disabled = true;
168
+ select.append(placeholder);
169
+ for (const template of templates) {
170
+ const item = element('option', '', t(template.label, template.labelParameters));
171
+ item.value = template.id;
172
+ select.append(item);
173
+ }
174
+ select.value = '';
175
+ select.onchange = () => {
176
+ const id = select.value;
177
+ select.value = '';
178
+ if (!select.disabled && templates.some((template) => template.id === id))
179
+ execute({ type: 'template', id });
180
+ };
181
+ toolbar.append(select);
182
+ }
183
+ const symbols = button(t('symbols.title'), () => {
184
+ if (symbolPanel) {
185
+ closeSymbols(true);
186
+ return;
187
+ }
188
+ options.onOpenPanel?.();
189
+ closeSymbols();
190
+ const target = activeSession;
191
+ panels.hidden = false;
192
+ symbols.setAttribute('aria-expanded', 'true');
193
+ symbolPanel = mountSymbolPanel(panels, {
194
+ locale,
195
+ onClose: () => closeSymbols(true),
196
+ onInsert: (glyph) => {
197
+ if (activeSession !== target || !enabled())
198
+ return;
199
+ closeSymbols();
200
+ execute({ type: 'text', value: glyph });
201
+ },
202
+ });
203
+ }, !!snapshot?.matrixRange);
204
+ symbols.dataset.mathTool = 'symbols';
205
+ symbols.setAttribute('aria-expanded', String(!!symbolPanel));
206
+ toolbar.append(symbols);
207
+ for (const glyph of ['α', 'β', 'π', 'θ', '∞']) {
208
+ const item = mathSymbols.find((symbol) => symbol[0] === glyph);
209
+ const control = button(glyph, () => execute({ type: 'text', value: glyph }), !!snapshot?.matrixRange);
210
+ control.setAttribute('aria-label', t(item[1]));
211
+ toolbar.append(control);
212
+ }
213
+ }
214
+ // More also reveals discovery controls when all allowed structures already fit.
215
+ {
29
216
  const toggle = element('button', 'me-toolbar-toggle', t(expanded ? 'toolbar.less' : 'toolbar.more'));
30
217
  toggle.type = 'button';
31
218
  toggle.setAttribute('aria-expanded', String(expanded));
32
219
  toggle.onmousedown = (event) => event.preventDefault();
33
220
  toggle.onclick = () => {
34
221
  expanded = !expanded;
222
+ if (!expanded)
223
+ closeSymbols();
35
224
  draw();
36
- toolbar.querySelector('[aria-expanded]')?.focus();
225
+ toolbar.querySelector('.me-toolbar-toggle')?.focus();
37
226
  };
38
227
  toolbar.append(toggle);
39
228
  }
40
229
  for (const type of ['undo', 'redo']) {
41
- const button = element('button', '', t(type === 'undo' ? 'history.undo' : 'history.redo'));
42
- button.type = 'button';
43
- button.disabled = !(type === 'undo' ? snapshot.canUndo : snapshot.canRedo);
44
- button.onmousedown = (event) => event.preventDefault();
45
- button.onclick = () => {
46
- session.execute({ type });
47
- options.onExecute?.();
48
- };
49
- toolbar.append(button);
230
+ toolbar.append(button(t(type === 'undo' ? 'history.undo' : 'history.redo'), () => execute({ type }), !(type === 'undo' ? snapshot?.canUndo : snapshot?.canRedo)));
231
+ }
232
+ if (snapshot)
233
+ drawPresentationControls(presentation, snapshot.state, locale, (id, change) => execute({ type: 'presentation', id, change }), !enabled() || hasRange, options.kinds);
234
+ else
235
+ presentation.hidden = true;
236
+ const context = snapshot && activeGrid(snapshot.state);
237
+ if (context && (!options.kinds || options.kinds.includes(context.matrix.type))) {
238
+ gridTools.hidden = false;
239
+ gridTools.dir = mathLocaleDirection(locale);
240
+ gridTools.setAttribute('role', 'toolbar');
241
+ gridTools.setAttribute('aria-label', t(context.matrix.type === 'matrix' ? 'matrix.edit' : 'grid.editRows'));
242
+ gridTools.append(element('span', '', `${context.rows} × ${context.matrix.columns}`), element('small', '', t('grid.position', { row: context.row + 1, column: context.column + 1 })));
243
+ const selected = hasRange || snapshot.state.caret.start !== snapshot.state.caret.end;
244
+ if (context.matrix.type === 'matrix') {
245
+ const select = element('select');
246
+ select.setAttribute('aria-label', t('matrix.delimiters'));
247
+ select.dataset.mathTool = 'matrix-delimiters';
248
+ select.disabled = !enabled() || selected;
249
+ for (const item of matrixEnvironments) {
250
+ const option = element('option', '', t(item.label));
251
+ option.value = item.value;
252
+ select.append(option);
253
+ }
254
+ select.value = context.matrix.environment;
255
+ select.onchange = () => {
256
+ if (!select.disabled)
257
+ transform((state) => setMatrixEnvironment(state, select.value));
258
+ };
259
+ gridTools.append(select);
260
+ for (const [tool, label, command] of [
261
+ ['matrix-select', 'matrix.selectCells', { type: 'matrix-select' }],
262
+ ['matrix-select-all', 'matrix.selectAll', { type: 'matrix-select', all: true }],
263
+ ['matrix-transpose', 'matrix.transpose', { type: 'matrix-transpose' }],
264
+ ]) {
265
+ const control = button(t(label), () => execute(command), !!snapshot.range);
266
+ control.dataset.mathTool = tool;
267
+ gridTools.append(control);
268
+ }
269
+ }
270
+ for (const axis of ['row', 'column']) {
271
+ if (axis === 'column' && context.matrix.type !== 'matrix')
272
+ continue;
273
+ const size = axis === 'row' ? context.rows : context.matrix.columns;
274
+ for (const action of ['insert', 'delete']) {
275
+ const key = axis === 'row'
276
+ ? action === 'insert'
277
+ ? 'grid.addRow'
278
+ : 'grid.deleteRow'
279
+ : action === 'insert'
280
+ ? 'grid.addColumn'
281
+ : 'grid.deleteColumn';
282
+ const control = button(t(key), () => transform((state) => resizeMatrix(state, axis, action)), selected || (action === 'insert' ? size >= MATRIX_MAX_SIZE : size <= 1));
283
+ control.dataset.mathTool = `${axis}-${action}`;
284
+ if (action === 'delete') {
285
+ control.title = t(axis === 'row' ? 'grid.deleteRowShortcut' : 'grid.deleteColumnShortcut');
286
+ control.setAttribute('aria-keyshortcuts', axis === 'row' ? 'Alt+Shift+ArrowUp' : 'Alt+Shift+ArrowLeft');
287
+ }
288
+ gridTools.append(control);
289
+ }
290
+ }
50
291
  }
51
292
  };
52
- const unsubscribe = session.subscribe(draw);
293
+ let unsubscribe = activeSession?.subscribe(draw);
53
294
  draw();
54
295
  return {
296
+ /** Close discovery/paste panels without changing the formula or its selection. */
297
+ closePanels: () => closeSymbols(),
298
+ /** Reuse one toolbar for multiple editors without retaining previous subscriptions. */
299
+ setSession(next) {
300
+ if (destroyed)
301
+ return;
302
+ if (next !== activeSession || !next)
303
+ closeSymbols();
304
+ unsubscribe?.();
305
+ activeSession = next;
306
+ unsubscribe = next?.subscribe(draw);
307
+ draw();
308
+ },
309
+ /** Disable mutation controls while the attached native editor is composing. */
310
+ setDisabled(next) {
311
+ if (destroyed || disabled === next)
312
+ return;
313
+ disabled = next;
314
+ if (next)
315
+ closeSymbols();
316
+ draw();
317
+ },
55
318
  destroy() {
56
- unsubscribe();
319
+ destroyed = true;
320
+ unsubscribe?.();
321
+ closeSymbols();
57
322
  toolbar.remove();
323
+ gridTools.remove();
324
+ presentation.remove();
325
+ panels.remove();
58
326
  },
59
327
  };
60
328
  }
61
- //# sourceMappingURL=toolbar.js.map
package/dist/dom.d.ts CHANGED
@@ -1,19 +1,28 @@
1
+ import type { MathPreferences } from './preferences.js';
1
2
  export { mountMathLatex, mountMathPreview, type MathOutput, type MathPreviewOptions, } from './outputs.js';
2
3
  export { mountMathToolbar, type MathToolbarOptions } from './dom/toolbar.js';
3
4
  import { type MathSession, type MathEditorMode } from './session.js';
4
5
  import { type MathLocale } from './i18n.js';
5
6
  import { type MathDocument, type StructureKind } from './model.js';
6
7
  export interface DOMMathEditorOptions {
8
+ /** Mount-only UI preferences; ignored when an external session owns the store. */
9
+ preferences?: MathPreferences;
7
10
  session?: MathSession;
8
11
  /** Mount-only portal container; defaults to the nearest dialog, otherwise document.body. */
9
12
  menuHost?: HTMLElement;
13
+ /** Keep suggestion and wrapping menus clear of host controls outside the math surface. */
14
+ menuAvoidElements?: () => readonly HTMLElement[];
10
15
  defaultValue?: MathDocument;
11
16
  locale?: MathLocale;
12
17
  mode?: MathEditorMode;
13
18
  toolbar?: boolean | readonly StructureKind[];
14
19
  toolbarMaxItems?: number;
20
+ /** Show contextual structure actions below the editing surface. Defaults to true. */
21
+ contextTools?: boolean;
15
22
  showLineNumbers?: boolean;
16
23
  onChange?: (document: MathDocument, latex: string) => void;
24
+ /** Final DOM/caret notification, including focus and selection-only updates. */
25
+ onRender?: () => void;
17
26
  onExit?: (direction: -1 | 1) => void;
18
27
  /** Default: block=newline, inline=commit. Suggestions always take priority. */
19
28
  enterBehavior?: 'newline' | 'commit';
@@ -30,4 +39,3 @@ export interface DOMMathEditor {
30
39
  }
31
40
  /** Native DOM renderer. No React or framework runtime is imported by this module. */
32
41
  export declare function mountMathEditor(host: HTMLElement, initial?: DOMMathEditorOptions): DOMMathEditor;
33
- //# sourceMappingURL=dom.d.ts.map