@barocss/math-editor 0.1.0 → 0.2.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 (99) hide show
  1. package/ADAPTERS.md +31 -2
  2. package/API-JAVASCRIPT.md +157 -0
  3. package/API-REACT.md +202 -0
  4. package/API-SESSION.md +163 -0
  5. package/API-SOLID.md +90 -0
  6. package/API-SVELTE.md +83 -0
  7. package/API-VUE.md +104 -0
  8. package/API-WEB-COMPONENT.md +128 -0
  9. package/CHANGELOG.md +21 -0
  10. package/EMBEDDING.md +7 -0
  11. package/IMPLEMENTATION.md +132 -1
  12. package/JSON-MODEL.md +441 -0
  13. package/LATEX-GUIDE.md +254 -0
  14. package/LATEX-MODEL.md +290 -0
  15. package/LATEX-SCOPE.md +260 -4
  16. package/LOCALIZATION.md +7 -1
  17. package/README.md +97 -4
  18. package/RELEASING.md +1 -1
  19. package/ROADMAP.md +111 -16
  20. package/SUPPORT.md +134 -13
  21. package/SYMBOLS.md +33 -1
  22. package/VALIDATION.md +175 -0
  23. package/dist/core.d.ts +2 -0
  24. package/dist/core.d.ts.map +1 -1
  25. package/dist/core.js +2 -0
  26. package/dist/core.js.map +1 -1
  27. package/dist/document-codec.d.ts +4 -0
  28. package/dist/document-codec.d.ts.map +1 -0
  29. package/dist/document-codec.js +37 -0
  30. package/dist/document-codec.js.map +1 -0
  31. package/dist/dom/menu-position.d.ts +3 -0
  32. package/dist/dom/menu-position.d.ts.map +1 -0
  33. package/dist/dom/menu-position.js +44 -0
  34. package/dist/dom/menu-position.js.map +1 -0
  35. package/dist/dom/toolbar.d.ts +2 -0
  36. package/dist/dom/toolbar.d.ts.map +1 -1
  37. package/dist/dom/toolbar.js +16 -1
  38. package/dist/dom/toolbar.js.map +1 -1
  39. package/dist/dom.d.ts +1 -0
  40. package/dist/dom.d.ts.map +1 -1
  41. package/dist/dom.js +153 -24
  42. package/dist/dom.js.map +1 -1
  43. package/dist/editor-labels.d.ts.map +1 -1
  44. package/dist/editor-labels.js +42 -0
  45. package/dist/editor-labels.js.map +1 -1
  46. package/dist/fences.d.ts +11 -0
  47. package/dist/fences.d.ts.map +1 -0
  48. package/dist/fences.js +21 -0
  49. package/dist/fences.js.map +1 -0
  50. package/dist/index.d.ts +3 -1
  51. package/dist/index.d.ts.map +1 -1
  52. package/dist/index.js +2 -0
  53. package/dist/index.js.map +1 -1
  54. package/dist/latex.d.ts +26 -0
  55. package/dist/latex.d.ts.map +1 -0
  56. package/dist/latex.js +689 -0
  57. package/dist/latex.js.map +1 -0
  58. package/dist/locales/en.js +109 -2
  59. package/dist/locales/en.json +109 -2
  60. package/dist/locales/ko.js +121 -3
  61. package/dist/locales/ko.json +121 -3
  62. package/dist/math-editor-toolbar.d.ts +5 -2
  63. package/dist/math-editor-toolbar.d.ts.map +1 -1
  64. package/dist/math-editor-toolbar.js +10 -6
  65. package/dist/math-editor-toolbar.js.map +1 -1
  66. package/dist/math-editor.d.ts +24 -4
  67. package/dist/math-editor.d.ts.map +1 -1
  68. package/dist/math-editor.js +236 -40
  69. package/dist/math-editor.js.map +1 -1
  70. package/dist/model.d.ts +10 -2
  71. package/dist/model.d.ts.map +1 -1
  72. package/dist/model.js +262 -9
  73. package/dist/model.js.map +1 -1
  74. package/dist/range.d.ts +3 -1
  75. package/dist/range.d.ts.map +1 -1
  76. package/dist/range.js +162 -6
  77. package/dist/range.js.map +1 -1
  78. package/dist/react.d.ts +1 -1
  79. package/dist/react.d.ts.map +1 -1
  80. package/dist/session.d.ts +3 -0
  81. package/dist/session.d.ts.map +1 -1
  82. package/dist/session.js +9 -1
  83. package/dist/session.js.map +1 -1
  84. package/dist/suggestions.d.ts +4 -0
  85. package/dist/suggestions.d.ts.map +1 -1
  86. package/dist/suggestions.js +161 -22
  87. package/dist/suggestions.js.map +1 -1
  88. package/dist/symbols.d.ts +1 -1
  89. package/dist/symbols.d.ts.map +1 -1
  90. package/dist/symbols.js +56 -5
  91. package/dist/symbols.js.map +1 -1
  92. package/package.json +3 -2
  93. package/src/fonts/KaTeX_AMS-Regular.woff2 +0 -0
  94. package/src/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
  95. package/src/fonts/KaTeX_Main-Bold.woff2 +0 -0
  96. package/src/fonts/KaTeX_Main-Regular.woff2 +0 -0
  97. package/src/fonts/KaTeX_Size2-Regular.woff2 +0 -0
  98. package/src/fonts/LICENSE-KaTeX.txt +21 -0
  99. package/src/style.css +868 -45
@@ -1,8 +1,11 @@
1
- import type { ReactNode } from 'react';
1
+ import { type ReactNode } from 'react';
2
2
  import type { StructureKind } from './model.js';
3
3
  import { type MathLocale } from './i18n.js';
4
4
  interface MathEditorToolbarProps {
5
5
  locale: MathLocale;
6
+ kinds?: readonly StructureKind[];
7
+ maxItems?: number;
8
+ excludedStructures?: readonly StructureKind[];
6
9
  composing: boolean;
7
10
  hasRange: boolean;
8
11
  canWrap: boolean;
@@ -19,6 +22,6 @@ interface MathEditorToolbarProps {
19
22
  children?: ReactNode;
20
23
  }
21
24
  /** Presentation-only toolbar. The editor keeps selection, composition and history ownership. */
22
- export declare function MathEditorToolbar({ locale, composing, hasRange, canWrap, canUndo, canRedo, symbolsOpen, onInsertStructure, onMatrixPreset, onTemplate, onToggleSymbols, onInsertSymbol, onUndo, onRedo, children, }: MathEditorToolbarProps): import("react/jsx-runtime").JSX.Element;
25
+ export declare function MathEditorToolbar({ locale, kinds, maxItems, excludedStructures, composing, hasRange, canWrap, canUndo, canRedo, symbolsOpen, onInsertStructure, onMatrixPreset, onTemplate, onToggleSymbols, onInsertSymbol, onUndo, onRedo, children, }: MathEditorToolbarProps): import("react/jsx-runtime").JSX.Element;
23
26
  export {};
24
27
  //# sourceMappingURL=math-editor-toolbar.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"math-editor-toolbar.d.ts","sourceRoot":"","sources":["../src/math-editor-toolbar.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAIhD,OAAO,EAAa,KAAK,UAAU,EAA8B,MAAM,WAAW,CAAC;AAEnF,UAAU,sBAAsB;IAC9B,MAAM,EAAE,UAAU,CAAC;IACnB,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,OAAO,CAAC;IACrB,iBAAiB,EAAE,CAAC,IAAI,EAAE,aAAa,KAAK,IAAI,CAAC;IACjD,cAAc,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,UAAU,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,eAAe,EAAE,MAAM,IAAI,CAAC;IAC5B,cAAc,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AASD,gGAAgG;AAChG,wBAAgB,iBAAiB,CAAC,EAChC,MAAM,EACN,SAAS,EACT,QAAQ,EACR,OAAO,EACP,OAAO,EACP,OAAO,EACP,WAAW,EACX,iBAAiB,EACjB,cAAc,EACd,UAAU,EACV,eAAe,EACf,cAAc,EACd,MAAM,EACN,MAAM,EACN,QAAQ,GACT,EAAE,sBAAsB,2CAmGxB"}
1
+ {"version":3,"file":"math-editor-toolbar.d.ts","sourceRoot":"","sources":["../src/math-editor-toolbar.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AACjD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAIhD,OAAO,EAAa,KAAK,UAAU,EAA8B,MAAM,WAAW,CAAC;AAEnF,UAAU,sBAAsB;IAC9B,MAAM,EAAE,UAAU,CAAC;IACnB,KAAK,CAAC,EAAE,SAAS,aAAa,EAAE,CAAC;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,kBAAkB,CAAC,EAAE,SAAS,aAAa,EAAE,CAAC;IAC9C,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,OAAO,CAAC;IACrB,iBAAiB,EAAE,CAAC,IAAI,EAAE,aAAa,KAAK,IAAI,CAAC;IACjD,cAAc,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,UAAU,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,eAAe,EAAE,MAAM,IAAI,CAAC;IAC5B,cAAc,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AASD,gGAAgG;AAChG,wBAAgB,iBAAiB,CAAC,EAChC,MAAM,EACN,KAAK,EACL,QAAY,EACZ,kBAAuB,EACvB,SAAS,EACT,QAAQ,EACR,OAAO,EACP,OAAO,EACP,OAAO,EACP,WAAW,EACX,iBAAiB,EACjB,cAAc,EACd,UAAU,EACV,eAAe,EACf,cAAc,EACd,MAAM,EACN,MAAM,EACN,QAAQ,GACT,EAAE,sBAAsB,2CAqHxB"}
@@ -1,4 +1,5 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useState } from 'react';
2
3
  import { mathStructures } from './suggestions.js';
3
4
  import { wrappingKinds } from './range.js';
4
5
  import { mathTemplates } from './templates.js';
@@ -11,11 +12,14 @@ const symbols = [
11
12
  { label: 'symbol.infinity', value: '∞' },
12
13
  ];
13
14
  /** Presentation-only toolbar. The editor keeps selection, composition and history ownership. */
14
- export function MathEditorToolbar({ locale, composing, hasRange, canWrap, canUndo, canRedo, symbolsOpen, onInsertStructure, onMatrixPreset, onTemplate, onToggleSymbols, onInsertSymbol, onUndo, onRedo, children, }) {
15
+ export function MathEditorToolbar({ locale, kinds, maxItems = 8, excludedStructures = [], composing, hasRange, canWrap, canUndo, canRedo, symbolsOpen, onInsertStructure, onMatrixPreset, onTemplate, onToggleSymbols, onInsertSymbol, onUndo, onRedo, children, }) {
16
+ const [expanded, setExpanded] = useState(false);
17
+ const visibleCount = Math.max(0, Math.floor(Number.isFinite(maxItems) ? maxItems : 8));
18
+ const items = mathStructures.filter((s) => s.kind && !excludedStructures.includes(s.kind) && (!kinds || kinds.includes(s.kind)));
15
19
  const t = (key, parameters) => translate(locale, key, parameters);
16
- return (_jsxs("div", { className: "me-tools", role: "toolbar", "aria-label": t('toolbar.label'), children: [mathStructures.map((s) => (_jsxs("button", { type: "button", "aria-label": t(s.label), disabled: composing || (hasRange && (!canWrap || !wrappingKinds.includes(s.kind))), onMouseDown: (event) => event.preventDefault(), onClick: () => onInsertStructure(s.kind), children: [_jsx("span", { children: s.glyph }), t(s.label)] }, s.kind))), _jsxs("select", { disabled: composing || hasRange, className: "me-matrix-size", "aria-label": t('matrix.preset'), value: "", onChange: (event) => {
17
- const value = event.target.value;
18
- onMatrixPreset(value);
19
- }, children: [_jsx("option", { value: "", disabled: true, children: t('matrix.presetPlaceholder') }), [2, 3, 4].map((size) => (_jsx("option", { value: size, children: t('matrix.insert', { rows: size, columns: size }) }, size))), [2, 3, 4].map((size) => (_jsx("option", { value: `identity-${size}`, children: t('matrix.insertIdentity', { rows: size, columns: size }) }, `identity-${size}`)))] }), _jsxs("select", { className: "me-matrix-size", "aria-label": t('template.label'), value: "", disabled: composing, onChange: (event) => onTemplate(event.target.value), children: [_jsx("option", { value: "", disabled: true, children: t('template.placeholder') }), mathTemplates.map((item) => (_jsx("option", { value: item.id, children: t(item.label, item.labelParameters) }, item.id)))] }), _jsx("button", { type: "button", "aria-expanded": symbolsOpen, disabled: composing, onClick: onToggleSymbols, children: t('symbols.title') }), _jsx("span", { className: "me-divider" }), symbols.map((s) => (_jsx("button", { type: "button", "aria-label": t(s.label), disabled: composing || hasRange, onMouseDown: (event) => event.preventDefault(), onClick: () => onInsertSymbol(s.value), children: s.value }, s.label))), _jsx("button", { type: "button", disabled: !canUndo || composing, onMouseDown: (event) => event.preventDefault(), onClick: onUndo, children: t('history.undo') }), _jsx("button", { type: "button", disabled: !canRedo || composing, onMouseDown: (event) => event.preventDefault(), onClick: onRedo, children: t('history.redo') }), children] }));
20
+ return (_jsxs("div", { className: "me-tools", role: "toolbar", "aria-label": t('toolbar.label'), children: [(expanded ? items : items.slice(0, visibleCount)).map((s) => (_jsxs("button", { type: "button", "aria-label": t(s.label), disabled: composing || (hasRange && (!canWrap || !wrappingKinds.includes(s.kind))), onMouseDown: (event) => event.preventDefault(), onClick: () => onInsertStructure(s.kind), children: [_jsx("span", { children: s.glyph }), t(s.label)] }, s.kind))), expanded && (_jsxs(_Fragment, { children: [_jsxs("select", { disabled: composing || hasRange, className: "me-matrix-size", "aria-label": t('matrix.preset'), value: "", onChange: (event) => {
21
+ const value = event.target.value;
22
+ onMatrixPreset(value);
23
+ }, children: [_jsx("option", { value: "", disabled: true, children: t('matrix.presetPlaceholder') }), [2, 3, 4].map((size) => (_jsx("option", { value: size, children: t('matrix.insert', { rows: size, columns: size }) }, size))), [2, 3, 4].map((size) => (_jsx("option", { value: `identity-${size}`, children: t('matrix.insertIdentity', { rows: size, columns: size }) }, `identity-${size}`)))] }), _jsxs("select", { className: "me-matrix-size", "aria-label": t('template.label'), value: "", disabled: composing, onChange: (event) => onTemplate(event.target.value), children: [_jsx("option", { value: "", disabled: true, children: t('template.placeholder') }), mathTemplates.map((item) => (_jsx("option", { value: item.id, children: t(item.label, item.labelParameters) }, item.id)))] }), _jsx("button", { type: "button", "aria-expanded": symbolsOpen, disabled: composing, onClick: onToggleSymbols, children: t('symbols.title') }), _jsx("span", { className: "me-divider" }), symbols.map((s) => (_jsx("button", { type: "button", "aria-label": t(s.label), disabled: composing || hasRange, onMouseDown: (event) => event.preventDefault(), onClick: () => onInsertSymbol(s.value), children: s.value }, s.label)))] })), _jsx("button", { type: "button", className: "me-toolbar-toggle", "aria-expanded": expanded, onMouseDown: (event) => event.preventDefault(), onClick: () => setExpanded((value) => !value), children: t(expanded ? 'toolbar.less' : 'toolbar.more') }), _jsx("button", { type: "button", disabled: !canUndo || composing, onMouseDown: (event) => event.preventDefault(), onClick: onUndo, children: t('history.undo') }), _jsx("button", { type: "button", disabled: !canRedo || composing, onMouseDown: (event) => event.preventDefault(), onClick: onRedo, children: t('history.redo') }), children] }));
20
24
  }
21
25
  //# sourceMappingURL=math-editor-toolbar.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"math-editor-toolbar.js","sourceRoot":"","sources":["../src/math-editor-toolbar.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"math-editor-toolbar.js","sourceRoot":"","sources":["../src/math-editor-toolbar.tsx"],"names":[],"mappings":"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"}
@@ -1,9 +1,29 @@
1
+ import { type LatexParseResult } from './latex.js';
1
2
  import { type MathLocale } from './i18n.js';
2
- import { type ReactNode } from 'react';
3
- import { type MathDocument } from './model.js';
3
+ import { type ReactNode, type Ref } from 'react';
4
+ import { type MathDocument, type StructureKind } from './model.js';
5
+ export interface MathEditorHandle {
6
+ /** Replace with supported LaTeX as one undoable edit; failures preserve all editor state. */
7
+ importLatex(source: string): LatexParseResult;
8
+ }
4
9
  export interface MathEditorProps {
10
+ apiRef?: Ref<MathEditorHandle>;
11
+ /** Start editing at the initial caret when embedded in a host editor. */
12
+ autoFocus?: boolean;
13
+ /** Suggestions take priority; Shift+Enter retains normal line/grid behavior. */
14
+ enterBehavior?: 'newline' | 'commit';
15
+ onCommit?: () => void;
16
+ onCancel?: () => void;
17
+ /** Hide portaled suggestions while a host keeps this editor mounted in an inactive tab. */
18
+ showPopovers?: boolean;
19
+ /** Hide structures a host cannot serialize. Imported/pasted drafts still need host validation. */
20
+ excludedStructures?: readonly StructureKind[];
21
+ /** Whether Enter may create another top-level equation line. Defaults to true. */
22
+ multiline?: boolean;
5
23
  /** Hide all toolbar panels while retaining editing and suggestions. */
6
- toolbar?: boolean;
24
+ toolbar?: boolean | readonly StructureKind[];
25
+ /** Visible structure buttons before More; defaults to 8. */
26
+ toolbarMaxItems?: number;
7
27
  /** Host-owned actions rendered at the end of the main toolbar. */
8
28
  toolbarEnd?: ReactNode;
9
29
  showTokenLegend?: boolean;
@@ -19,5 +39,5 @@ export interface MathEditorProps {
19
39
  label?: string;
20
40
  }
21
41
  /** Native text fields own IME and local text selection; the tree owns structural editing. */
22
- export declare function MathEditor({ defaultValue, onChange, onExit, label, locale, showLineNumbers, toolbar, toolbarEnd, showTokenLegend, }: MathEditorProps): import("react/jsx-runtime").JSX.Element;
42
+ export declare function MathEditor({ apiRef, autoFocus, enterBehavior, onCommit, onCancel, showPopovers, defaultValue, excludedStructures, multiline, onChange, onExit, label, locale, showLineNumbers, toolbar, toolbarMaxItems, toolbarEnd, showTokenLegend, }: MathEditorProps): import("react/jsx-runtime").JSX.Element;
23
43
  //# sourceMappingURL=math-editor.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"math-editor.d.ts","sourceRoot":"","sources":["../src/math-editor.tsx"],"names":[],"mappings":"AAIA,OAAO,EAGL,KAAK,UAAU,EAEhB,MAAM,WAAW,CAAC;AA6BnB,OAAO,EAA4C,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AACjF,OAAO,EAmBL,KAAK,YAAY,EAKlB,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,eAAe;IAC9B,uEAAuE;IACvE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,kEAAkE;IAClE,UAAU,CAAC,EAAE,SAAS,CAAC;IACvB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,oFAAoF;IACpF,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,2EAA2E;IAC3E,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,uEAAuE;IACvE,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3D,+DAA+D;IAC/D,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,CAAC,GAAG,CAAC,KAAK,IAAI,CAAC;IACrC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAGD,6FAA6F;AAC7F,wBAAgB,UAAU,CAAC,EACzB,YAAY,EACZ,QAAQ,EACR,MAAM,EACN,KAAK,EACL,MAAa,EACb,eAAsB,EACtB,OAAc,EACd,UAAU,EACV,eAAsB,GACvB,EAAE,eAAe,2CA8kCjB"}
1
+ {"version":3,"file":"math-editor.d.ts","sourceRoot":"","sources":["../src/math-editor.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAc,KAAK,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAK/D,OAAO,EAGL,KAAK,UAAU,EAEhB,MAAM,WAAW,CAAC;AA8BnB,OAAO,EAML,KAAK,SAAS,EACd,KAAK,GAAG,EACT,MAAM,OAAO,CAAC;AACf,OAAO,EAoBL,KAAK,YAAY,EAIjB,KAAK,aAAa,EACnB,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,gBAAgB;IAC/B,6FAA6F;IAC7F,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,gBAAgB,CAAC;CAC/C;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,EAAE,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAC/B,yEAAyE;IACzE,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,gFAAgF;IAChF,aAAa,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAC;IACrC,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,2FAA2F;IAC3F,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,kGAAkG;IAClG,kBAAkB,CAAC,EAAE,SAAS,aAAa,EAAE,CAAC;IAC9C,kFAAkF;IAClF,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,uEAAuE;IACvE,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,aAAa,EAAE,CAAC;IAC7C,4DAA4D;IAC5D,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,kEAAkE;IAClE,UAAU,CAAC,EAAE,SAAS,CAAC;IACvB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,oFAAoF;IACpF,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,2EAA2E;IAC3E,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,uEAAuE;IACvE,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3D,+DAA+D;IAC/D,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,CAAC,GAAG,CAAC,KAAK,IAAI,CAAC;IACrC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAGD,6FAA6F;AAC7F,wBAAgB,UAAU,CAAC,EACzB,MAAM,EACN,SAAiB,EACjB,aAAyB,EACzB,QAAQ,EACR,QAAQ,EACR,YAAmB,EACnB,YAAY,EACZ,kBAAuB,EACvB,SAAgB,EAChB,QAAQ,EACR,MAAM,EACN,KAAK,EACL,MAAa,EACb,eAAsB,EACtB,OAAc,EACd,eAAmB,EACnB,UAAU,EACV,eAAsB,GACvB,EAAE,eAAe,2CA+0CjB"}
@@ -1,27 +1,31 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { positionMathMenu } from './dom/menu-position.js';
3
+ import { fences } from './fences.js';
4
+ import { parseLatex } from './latex.js';
2
5
  import { createEditorLabels } from './editor-labels.js';
3
6
  import { MathEditorToolbar } from './math-editor-toolbar.js';
4
7
  import { SymbolBrowser } from './symbol-browser.js';
5
8
  import { insertTemplate } from './templates.js';
6
9
  import { translate, mathLocaleDirection, } from './i18n.js';
7
- import { wrapRange, wrappingKinds, copyRange, deleteRange, fragmentLatex, MATH_CLIPBOARD_TYPE, parseFragment, pasteFragment, resolveRange, } from './range.js';
10
+ import { extendKeyboardRange, wrapRange, wrappingKinds, copyRange, deleteRange, fragmentLatex, MATH_CLIPBOARD_TYPE, parseFragment, pasteFragment, resolveRange, } from './range.js';
8
11
  import { tokenizeMathText, tokenIndexAt } from './tokens.js';
9
12
  import { splitLine, joinPreviousLine, moveLineVertical } from './lines.js';
10
13
  import { insertIdentityMatrix, insertMatrix, activeGrid, MATRIX_MAX_SIZE, matrixEnvironments, moveMatrixVertical, resizeMatrix, setMatrixEnvironment, } from './matrix.js';
11
14
  import { createPortal } from 'react-dom';
12
15
  import { acceptSuggestion, findSuggestions, mathStructures } from './suggestions.js';
13
- import { useId, useLayoutEffect, useRef, useState } from 'react';
14
- import { gridDeletionTarget, removeGrid, row, documentRows, isGrid, commit, createHistory, initialState, insertStructure, moveCaret, redo, setText, textNodes, toLatex, undo, unwrapPrevious, } from './model.js';
16
+ import { useId, useImperativeHandle, useLayoutEffect, useRef, useState, } from 'react';
17
+ import { gridDeletionTarget, removeGrid, row, documentRows, isLiteralText, isGrid, commit, createHistory, initialState, insertStructure, moveCaret, redo, setText, textNodes, toLatex, undo, unwrapPrevious, } from './model.js';
15
18
  const structures = mathStructures;
16
19
  /** Native text fields own IME and local text selection; the tree owns structural editing. */
17
- export function MathEditor({ defaultValue, onChange, onExit, label, locale = 'ko', showLineNumbers = true, toolbar = true, toolbarEnd, showTokenLegend = true, }) {
20
+ export function MathEditor({ apiRef, autoFocus = false, enterBehavior = 'newline', onCommit, onCancel, showPopovers = true, defaultValue, excludedStructures = [], multiline = true, onChange, onExit, label, locale = 'ko', showLineNumbers = true, toolbar = true, toolbarMaxItems = 8, toolbarEnd, showTokenLegend = true, }) {
18
21
  const t = (key, parameters) => translate(locale, key, parameters);
19
22
  const labels = createEditorLabels(locale);
20
23
  const [history, setHistory] = useState(() => createHistory(initialState(defaultValue)));
21
24
  const historyRef = useRef(history);
22
- const [menuPosition, setMenuPosition] = useState();
25
+ const suggestionMenu = useRef(null);
26
+ const selectionMenu = useRef(null);
23
27
  const [symbolsOpen, setSymbolsOpen] = useState(false);
24
- const [editing, setEditing] = useState(false);
28
+ const [editing, setEditing] = useState(autoFocus);
25
29
  const [range, setRange] = useState();
26
30
  const [clipboardMessage, setClipboardMessage] = useState('');
27
31
  const surface = useRef(null);
@@ -35,6 +39,8 @@ export function MathEditor({ defaultValue, onChange, onExit, label, locale = 'ko
35
39
  const compositionToken = useRef(null);
36
40
  // Keep the active input identity stable until IME composition commits. Splitting it early loses drafts.
37
41
  const tokensFor = (node) => {
42
+ if (isLiteralText(history.present.document, node.id))
43
+ return [{ text: node.text, start: 0, end: node.text.length, kind: 'draft' }];
38
44
  const frozen = compositionToken.current;
39
45
  if (!frozen || frozen.id !== node.id)
40
46
  return tokenizeMathText(node.text);
@@ -51,17 +57,21 @@ export function MathEditor({ defaultValue, onChange, onExit, label, locale = 'ko
51
57
  });
52
58
  };
53
59
  const inputs = useRef(new Map());
54
- const focusNext = useRef(false);
60
+ const focusNext = useRef(autoFocus);
55
61
  const suggestionId = useId();
56
62
  const state = history.present;
57
63
  const matrixContext = activeGrid(state);
58
64
  const active = textNodes(state.document).find((n) => n.id === state.caret.id);
59
65
  const searchCaret = composing ? state.caret.end : state.caret.start;
60
66
  const matches = findSuggestions(active.text, searchCaret, locale);
67
+ matches.candidates = matches.candidates.filter((candidate) => !candidate.kind || !excludedStructures.includes(candidate.kind));
61
68
  const query = matches.query;
69
+ const operandMenuKey = useRef('');
62
70
  const queryKey = `${active.id}:${searchCaret}:${query}`;
63
71
  const candidates = editing &&
64
72
  !symbolsOpen &&
73
+ !isLiteralText(state.document, state.caret.id) &&
74
+ (composing || state.caret.start === state.caret.end) &&
65
75
  !range &&
66
76
  focused &&
67
77
  (composing || state.caret.start === state.caret.end) &&
@@ -88,6 +98,30 @@ export function MathEditor({ defaultValue, onChange, onExit, label, locale = 'ko
88
98
  setSelectedGrid(undefined);
89
99
  publish(commit(historyRef.current, next), true);
90
100
  };
101
+ useImperativeHandle(apiRef, () => ({
102
+ importLatex(source) {
103
+ const result = parseLatex(source, { multiline, excludedStructures });
104
+ if (!result.ok)
105
+ return result;
106
+ if (composing)
107
+ return {
108
+ ok: false,
109
+ source,
110
+ diagnostics: [
111
+ {
112
+ code: 'unsupported-structure',
113
+ message: 'Finish text composition before importing.',
114
+ start: 0,
115
+ end: source.length,
116
+ },
117
+ ],
118
+ };
119
+ setSymbolsOpen(false);
120
+ setDismissed('');
121
+ apply(initialState(result.document));
122
+ return result;
123
+ },
124
+ }));
91
125
  const select = (caret) => {
92
126
  // React can emit a selection event for the old input after a structural keydown.
93
127
  if (focusNext.current)
@@ -113,8 +147,7 @@ export function MathEditor({ defaultValue, onChange, onExit, label, locale = 'ko
113
147
  focusNext.current = false;
114
148
  }, [history, editing]);
115
149
  useLayoutEffect(() => {
116
- if (!candidates.length) {
117
- setMenuPosition(undefined);
150
+ if (!showPopovers || !candidates.length) {
118
151
  return;
119
152
  }
120
153
  const position = () => {
@@ -131,13 +164,8 @@ export function MathEditor({ defaultValue, onChange, onExit, label, locale = 'ko
131
164
  range.collapse(true);
132
165
  x = range.getBoundingClientRect().left;
133
166
  }
134
- const below = window.innerHeight - rect.bottom - 16;
135
- const height = Math.min(340, candidates.length * 54 + 78, Math.max(below, rect.top - 16));
136
- setMenuPosition({
137
- left: Math.max(8, Math.min(x, window.innerWidth - 344)),
138
- top: below >= height ? rect.bottom + 8 : Math.max(8, rect.top - height - 8),
139
- maxHeight: height,
140
- });
167
+ if (suggestionMenu.current)
168
+ positionMathMenu(suggestionMenu.current, new DOMRect(x, rect.top, 0, rect.height), 340);
141
169
  };
142
170
  position();
143
171
  window.addEventListener('resize', position);
@@ -146,11 +174,19 @@ export function MathEditor({ defaultValue, onChange, onExit, label, locale = 'ko
146
174
  window.removeEventListener('resize', position);
147
175
  window.removeEventListener('scroll', position, true);
148
176
  };
149
- }, [queryKey, candidates.length, active.id]);
177
+ }, [queryKey, candidates.length, active.id, showPopovers]);
150
178
  useLayoutEffect(() => {
151
- document
152
- .getElementById(`${suggestionId}-${selectedIndex}`)
153
- ?.scrollIntoView({ block: 'nearest' });
179
+ // Scroll only the list. scrollIntoView can also move the host document.
180
+ const option = document.getElementById(`${suggestionId}-${selectedIndex}`);
181
+ const list = option?.parentElement;
182
+ if (option && list) {
183
+ const itemRect = option.getBoundingClientRect();
184
+ const listRect = list.getBoundingClientRect();
185
+ if (itemRect.top < listRect.top)
186
+ list.scrollTop -= listRect.top - itemRect.top;
187
+ else if (itemRect.bottom > listRect.bottom)
188
+ list.scrollTop += itemRect.bottom - listRect.bottom;
189
+ }
154
190
  }, [selectedIndex, queryKey]);
155
191
  const choose = (index) => {
156
192
  if (compositionStart.current || composing)
@@ -347,9 +383,24 @@ export function MathEditor({ defaultValue, onChange, onExit, label, locale = 'ko
347
383
  apply(next);
348
384
  setClipboardMessage('clipboard.pasted');
349
385
  };
386
+ const keyboardSelect = (event) => {
387
+ if (!event.shiftKey ||
388
+ event.altKey ||
389
+ event.ctrlKey ||
390
+ event.metaKey ||
391
+ !['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'].includes(event.key))
392
+ return false;
393
+ event.preventDefault();
394
+ setRange(extendKeyboardRange(historyRef.current.present, range, event.key));
395
+ setEditing(false);
396
+ surface.current?.focus();
397
+ return true;
398
+ };
350
399
  const selectionKey = (event) => {
351
400
  if (event.target instanceof HTMLInputElement || compositionStart.current)
352
401
  return;
402
+ if (keyboardSelect(event))
403
+ return;
353
404
  const current = historyRef.current.present, nodes = textNodes(current.document);
354
405
  if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === 'a') {
355
406
  event.preventDefault();
@@ -366,6 +417,23 @@ export function MathEditor({ defaultValue, onChange, onExit, label, locale = 'ko
366
417
  }
367
418
  if (event.metaKey || event.ctrlKey || event.altKey)
368
419
  return;
420
+ if (range && selectionChoices.length && ['ArrowDown', 'ArrowUp', 'Enter'].includes(event.key)) {
421
+ event.preventDefault();
422
+ if (event.key === 'Enter') {
423
+ if (canWrap)
424
+ insertTool(selectionChoices[selectionIndex % selectionChoices.length].kind);
425
+ }
426
+ else
427
+ setSelectionIndex((selectionIndex + (event.key === 'ArrowDown' ? 1 : selectionChoices.length - 1)) %
428
+ selectionChoices.length);
429
+ return;
430
+ }
431
+ // A model selection must behave like a text selection when typing resumes.
432
+ if (range && !event.nativeEvent.isComposing && Array.from(event.key).length === 1) {
433
+ event.preventDefault();
434
+ apply(pasteFragment(current, { version: 1, rows: [row(event.key)] }, range));
435
+ return;
436
+ }
369
437
  if (range && ['Backspace', 'Delete'].includes(event.key)) {
370
438
  event.preventDefault();
371
439
  apply(deleteRange(current, range));
@@ -380,9 +448,84 @@ export function MathEditor({ defaultValue, onChange, onExit, label, locale = 'ko
380
448
  activate(range?.focus ?? { id: current.caret.id, offset: current.caret.start });
381
449
  }
382
450
  };
383
- const selectionFragment = range ? copyRange(state.document, range) : undefined;
384
- const canWrap = selectionFragment?.rows.length === 1;
451
+ const selectedRange = range ??
452
+ (state.caret.start !== state.caret.end
453
+ ? {
454
+ anchor: { id: state.caret.id, offset: state.caret.start },
455
+ focus: { id: state.caret.id, offset: state.caret.end },
456
+ }
457
+ : undefined);
458
+ const selectionFragment = selectedRange ? copyRange(state.document, selectedRange) : undefined;
459
+ const canWrap = selectionFragment?.rows.length === 1 &&
460
+ !isLiteralText(state.document, selectedRange.anchor.id);
461
+ const selectionChoices = structures.filter((s) => !excludedStructures.includes(s.kind) &&
462
+ [
463
+ 'bold',
464
+ 'calligraphic',
465
+ 'blackboard',
466
+ 'fraction',
467
+ 'binomial',
468
+ 'root',
469
+ 'indexedRoot',
470
+ 'norm',
471
+ 'braces',
472
+ 'angle',
473
+ 'openClosed',
474
+ 'closedOpen',
475
+ 'overbrace',
476
+ 'underbrace',
477
+ 'overset',
478
+ 'underset',
479
+ 'vec',
480
+ 'hat',
481
+ 'tilde',
482
+ 'bar',
483
+ 'dot',
484
+ 'ddot',
485
+ 'widehat',
486
+ 'widetilde',
487
+ 'overline',
488
+ 'superscript',
489
+ 'subscript',
490
+ 'parentheses',
491
+ 'brackets',
492
+ 'absolute',
493
+ ].includes(s.kind));
494
+ const [selectionIndex, setSelectionIndex] = useState(0);
495
+ useLayoutEffect(() => setSelectionIndex(0), [range]);
496
+ useLayoutEffect(() => {
497
+ if (!showPopovers || !selectionFragment)
498
+ return;
499
+ const position = () => {
500
+ const selected = surface.current?.querySelectorAll('[data-range-selected]');
501
+ const rect = (selected?.[selected.length - 1] ??
502
+ surface.current?.querySelector('.me-input') ??
503
+ surface.current)?.getBoundingClientRect();
504
+ if (rect && selectionMenu.current)
505
+ positionMathMenu(selectionMenu.current, rect, 340);
506
+ };
507
+ position();
508
+ window.addEventListener('scroll', position, true);
509
+ window.addEventListener('resize', position);
510
+ return () => {
511
+ window.removeEventListener('scroll', position, true);
512
+ window.removeEventListener('resize', position);
513
+ };
514
+ }, [range, state.document, state.caret, showPopovers]);
515
+ useLayoutEffect(() => {
516
+ const list = selectionMenu.current?.querySelector('.me-suggestions');
517
+ const option = list?.querySelector('[aria-selected="true"]');
518
+ if (!list || !option)
519
+ return;
520
+ const item = option.getBoundingClientRect(), box = list.getBoundingClientRect();
521
+ if (item.top < box.top)
522
+ list.scrollTop -= box.top - item.top;
523
+ else if (item.bottom > box.bottom)
524
+ list.scrollTop += item.bottom - box.bottom;
525
+ }, [selectionIndex]);
385
526
  const insertTool = (kind) => {
527
+ if (excludedStructures.includes(kind))
528
+ return;
386
529
  const current = historyRef.current.present;
387
530
  const selection = range ??
388
531
  (current.caret.start !== current.caret.end
@@ -397,7 +540,7 @@ export function MathEditor({ defaultValue, onChange, onExit, label, locale = 'ko
397
540
  }
398
541
  if (range)
399
542
  return;
400
- apply(insertStructure(historyRef.current.present, kind, ['fraction', 'superscript', 'subscript'].includes(kind)));
543
+ apply(insertStructure(historyRef.current.present, kind, ['fraction', 'binomial', 'superscript', 'subscript'].includes(kind)));
401
544
  };
402
545
  // Nested slots share the same caret model. Only the active lexical token mounts an input.
403
546
  const renderRow = (r, name, role = 'expression') => (_jsx("span", { className: "me-row", "data-math-role": role, children: r.children.map((node) => {
@@ -407,10 +550,27 @@ export function MathEditor({ defaultValue, onChange, onExit, label, locale = 'ko
407
550
  : node.type === 'cases'
408
551
  ? 'Bmatrix'
409
552
  : 'matrix', children: [_jsx("span", { className: "me-matrix-delimiter me-matrix-left", "aria-hidden": "true" }), _jsx("span", { className: "me-matrix-grid", style: { gridTemplateColumns: `repeat(${node.columns}, max-content)` }, children: node.slots.map((cell, index) => (_jsx("span", { className: "me-matrix-cell", children: renderRow(cell, labels.grid(name, node.type, index, node.columns), 'group') }, cell.id))) }), _jsx("span", { className: "me-matrix-delimiter me-matrix-right", "aria-hidden": "true" })] }, node.id));
410
- if (node.type !== 'text' && ['sum', 'product', 'integral'].includes(node.type))
411
- return (_jsxs("span", { className: "me-structure me-large-operator", "data-structure": node.type, children: [_jsxs("span", { className: "me-limits", children: [_jsx("span", { className: "me-limit", children: renderRow(node.slots[1], labels.slot(name, node.type, 1), 'upper') }), _jsx("span", { className: "me-operator-glyph", "aria-hidden": "true", children: { sum: '', product: '', integral: '' }[node.type] }), _jsx("span", { className: "me-limit", children: renderRow(node.slots[0], labels.slot(name, node.type, 0), 'lower') })] }), renderRow(node.slots[2], labels.slot(name, node.type, 2))] }, node.id));
553
+ if (node.type === 'limit' || node.type === 'limsup' || node.type === 'liminf')
554
+ return (_jsxs("span", { className: "me-structure me-limit-operator", "data-structure": node.type, "data-math-style": node.mathStyle, "data-limit-placement": node.limits === false ? 'nolimits' : node.limits ? 'limits' : 'auto', children: [_jsxs("span", { className: "me-limits", children: [_jsx("span", { className: "me-limit-glyph", "aria-hidden": "true", children: node.type === 'limit' ? 'lim' : node.type === 'limsup' ? 'lim sup' : 'lim inf' }), _jsx("span", { className: "me-limit me-slot", children: renderRow(node.slots[0], labels.slot(name, node.type, 0), 'lower') })] }), _jsx("span", { className: "me-slot", children: renderRow(node.slots[1], labels.slot(name, node.type, 1)) })] }, node.id));
555
+ if (node.type !== 'text' &&
556
+ [
557
+ 'sum',
558
+ 'product',
559
+ 'integral',
560
+ 'doubleIntegral',
561
+ 'tripleIntegral',
562
+ 'contourIntegral',
563
+ ].includes(node.type))
564
+ return (_jsxs("span", { className: "me-structure me-large-operator", "data-limit-placement": node.limits === false ? 'nolimits' : node.limits ? 'limits' : 'auto', "data-structure": node.type, "data-math-style": node.mathStyle, children: [_jsxs("span", { className: "me-limits", children: [_jsx("span", { className: "me-limit", children: renderRow(node.slots[1], labels.slot(name, node.type, 1), 'upper') }), _jsx("span", { className: "me-operator-glyph", "aria-hidden": "true", children: {
565
+ sum: '∑',
566
+ product: '∏',
567
+ integral: '∫',
568
+ doubleIntegral: '∬',
569
+ tripleIntegral: '∭',
570
+ contourIntegral: '∮',
571
+ }[node.type] }), _jsx("span", { className: "me-limit", children: renderRow(node.slots[0], labels.slot(name, node.type, 0), 'lower') })] }), renderRow(node.slots[2], labels.slot(name, node.type, 2))] }, node.id));
412
572
  if (node.type !== 'text')
413
- return (_jsxs("span", { className: `me-structure me-${node.type}`, "data-structure": node.type, children: [['parentheses', 'brackets', 'absolute'].includes(node.type) && (_jsx("span", { className: "me-delimiter", "aria-hidden": "true", children: { parentheses: '(', brackets: '[', absolute: '|' }[node.type] })), node.type === 'root' && (_jsx("span", { "aria-hidden": "true", className: "me-radical", children: "\u221A" })), node.slots.map((slot, index) => (_jsx("span", { className: "me-slot", children: renderRow(slot, labels.slot(name, node.type, index), node.type === 'fraction'
573
+ return (_jsxs("span", { className: `me-structure me-${node.type}`, "data-structure": node.type, "data-math-style": node.mathStyle, children: [!!fences(node) && (_jsx("span", { className: "me-delimiter", "data-fence": fences(node)[0], "aria-hidden": "true", children: fences(node)[0] })), ['root', 'indexedRoot'].includes(node.type) && (_jsx("span", { "aria-hidden": "true", className: "me-radical", children: "\u221A" })), node.slots.map((slot, index) => (_jsx("span", { className: "me-slot", children: renderRow(slot, labels.slot(name, node.type, index), node.type === 'fraction'
414
574
  ? index === 0
415
575
  ? 'numerator'
416
576
  : 'denominator'
@@ -424,7 +584,7 @@ export function MathEditor({ defaultValue, onChange, onExit, label, locale = 'ko
424
584
  : 'index'
425
585
  : node.type === 'root'
426
586
  ? 'radicand'
427
- : 'group') }, slot.id))), ['parentheses', 'brackets', 'absolute'].includes(node.type) && (_jsx("span", { className: "me-delimiter", "aria-hidden": "true", children: { parentheses: ')', brackets: ']', absolute: '|' }[node.type] }))] }, node.id));
587
+ : 'group') }, slot.id))), !!fences(node) && (_jsx("span", { className: "me-delimiter", "data-fence": fences(node)[1], "aria-hidden": "true", children: fences(node)[1] }))] }, node.id));
428
588
  const parts = tokensFor(node);
429
589
  return parts.map((token, tokenIndex) => {
430
590
  const key = `${node.id}:${tokenIndex}`;
@@ -491,6 +651,8 @@ export function MathEditor({ defaultValue, onChange, onExit, label, locale = 'ko
491
651
  compositionStart.current ||
492
652
  event.keyCode === 229)
493
653
  return;
654
+ if (keyboardSelect(event))
655
+ return;
494
656
  const current = historyRef.current.present;
495
657
  if (selectedGrid) {
496
658
  if (!event.metaKey &&
@@ -549,30 +711,62 @@ export function MathEditor({ defaultValue, onChange, onExit, label, locale = 'ko
549
711
  apply(resizeMatrix(current, event.key === 'Enter' ? 'row' : 'column', 'insert'));
550
712
  return;
551
713
  }
714
+ if (selectedRange &&
715
+ !event.metaKey &&
716
+ !event.ctrlKey &&
717
+ !event.altKey &&
718
+ ['ArrowDown', 'ArrowUp', 'Enter'].includes(event.key)) {
719
+ event.preventDefault();
720
+ if (event.key === 'Enter') {
721
+ if (canWrap)
722
+ insertTool(selectionChoices[selectionIndex % selectionChoices.length].kind);
723
+ }
724
+ else
725
+ setSelectionIndex((selectionIndex +
726
+ (event.key === 'ArrowDown' ? 1 : selectionChoices.length - 1)) %
727
+ selectionChoices.length);
728
+ return;
729
+ }
552
730
  if (candidates.length && ['ArrowDown', 'ArrowUp'].includes(event.key)) {
553
731
  event.preventDefault();
554
- setCandidateIndex((selectedIndex + (event.key === 'ArrowDown' ? 1 : candidates.length - 1)) %
555
- candidates.length);
732
+ const initialOperandChoice = candidates[0]?.wrapOperand && operandMenuKey.current !== queryKey;
733
+ operandMenuKey.current = queryKey;
734
+ setCandidateIndex(initialOperandChoice
735
+ ? event.key === 'ArrowDown'
736
+ ? 0
737
+ : candidates.length - 1
738
+ : (selectedIndex +
739
+ (event.key === 'ArrowDown' ? 1 : candidates.length - 1)) %
740
+ candidates.length);
556
741
  return;
557
742
  }
558
- if (event.key === 'Enter' && candidates.length) {
743
+ if (event.key === 'Enter' &&
744
+ candidates.length &&
745
+ (!candidates[0].wrapOperand || operandMenuKey.current === queryKey)) {
559
746
  event.preventDefault();
560
747
  choose(selectedIndex);
561
748
  return;
562
749
  }
563
750
  if (event.key === 'Enter') {
564
751
  event.preventDefault();
752
+ if (enterBehavior === 'commit' && !event.shiftKey) {
753
+ onCommit?.();
754
+ return;
755
+ }
565
756
  if (activeGrid(current)) {
566
757
  if (activeGrid(current).matrix.type !== 'matrix')
567
758
  apply(resizeMatrix(current, 'row', 'insert'));
568
759
  }
569
- else
760
+ else if (multiline)
570
761
  apply(splitLine(current));
571
762
  return;
572
763
  }
573
764
  if (event.key === 'Escape') {
765
+ event.preventDefault();
574
766
  if (candidates.length)
575
767
  setDismissed(queryKey);
768
+ else if (onCancel)
769
+ onCancel();
576
770
  else {
577
771
  setEditing(false);
578
772
  surface.current?.focus();
@@ -642,15 +836,17 @@ export function MathEditor({ defaultValue, onChange, onExit, label, locale = 'ko
642
836
  } })) : (_jsx("span", { className: "me-preview-token", "data-token-kind": token.kind, "aria-label": name, role: "button", tabIndex: -1, children: selected && token.text ? (_jsxs("span", { children: [token.text.slice(0, selected[0]), _jsx("mark", { className: "me-range-highlight", children: token.text.slice(selected[0], selected[1]) }), token.text.slice(selected[1])] })) : (token.text || (r.children.length === 1 ? '□' : '\u200b')) }))] }, key));
643
837
  });
644
838
  }) }, r.id));
645
- return (_jsxs("div", { className: "me-editor", onCopy: copySelection, onCut: (event) => copySelection(event, true), onPaste: pasteSelection, role: "group", lang: locale, dir: mathLocaleDirection(locale), "aria-label": label ?? t('editor.label'), onBlur: (event) => {
839
+ return (_jsxs("div", { className: "me-editor", "data-math-editor-owner": suggestionId, onCopy: copySelection, onCut: (event) => copySelection(event, true), onPaste: pasteSelection, role: "group", lang: locale, dir: mathLocaleDirection(locale), "aria-label": label ?? t('editor.label'), onBlur: (event) => {
646
840
  if (!event.currentTarget.contains(event.relatedTarget) &&
841
+ !suggestionMenu.current?.contains(event.relatedTarget) &&
842
+ !selectionMenu.current?.contains(event.relatedTarget) &&
647
843
  !compositionStart.current &&
648
844
  !focusNext.current &&
649
845
  !drag.current) {
650
846
  setFocused(false);
651
847
  setEditing(false);
652
848
  }
653
- }, children: [toolbar && (_jsx(MathEditorToolbar, { locale: locale, composing: composing, hasRange: !!range, canWrap: canWrap, canUndo: !!history.past.length, canRedo: !!history.future.length, symbolsOpen: symbolsOpen, onInsertStructure: insertTool, onMatrixPreset: (value) => apply(value.startsWith('identity-')
849
+ }, children: [toolbar && (_jsx(MathEditorToolbar, { locale: locale, kinds: Array.isArray(toolbar) ? toolbar : undefined, maxItems: toolbarMaxItems, excludedStructures: excludedStructures, composing: composing, hasRange: !!range, canWrap: canWrap, canUndo: !!history.past.length, canRedo: !!history.future.length, symbolsOpen: symbolsOpen, onInsertStructure: insertTool, onMatrixPreset: (value) => apply(value.startsWith('identity-')
654
850
  ? insertIdentityMatrix(historyRef.current.present, Number(value.slice(9)))
655
851
  : insertMatrix(historyRef.current.present, Number(value))), onTemplate: (id) => apply(insertTemplate(historyRef.current.present, id, range)), onToggleSymbols: () => setSymbolsOpen((value) => !value), onInsertSymbol: (glyph) => {
656
852
  const current = historyRef.current.present, caret = current.caret;
@@ -662,7 +858,7 @@ export function MathEditor({ defaultValue, onChange, onExit, label, locale = 'ko
662
858
  }, onInsert: (glyph) => {
663
859
  apply(pasteFragment(historyRef.current.present, { version: 1, rows: [row(glyph)] }, range));
664
860
  setSymbolsOpen(false);
665
- } })), showTokenLegend && (_jsxs("div", { className: "me-token-legend", "aria-label": t('token.legend'), children: [_jsx("span", { "data-kind": "variable", children: t('token.variable') }), _jsx("span", { "data-kind": "constant", children: t('token.constant') }), _jsx("span", { "data-kind": "symbol", children: t('token.symbol') }), _jsx("small", { children: t('editor.interactionHint') })] })), clipboardMessage && (_jsx("div", { className: "me-grid-selection-hint", role: "status", children: t(clipboardMessage) })), selectedGrid && (_jsx("div", { className: "me-grid-selection-hint", role: "status", children: t('grid.selectionHint') })), toolbar && matrixContext && (_jsxs("div", { className: "me-matrix-tools", role: "toolbar", "aria-label": t(matrixContext.matrix.type === 'matrix' ? 'matrix.edit' : 'grid.editRows'), children: [_jsxs("span", { children: [matrixContext.rows, " \u00D7 ", matrixContext.matrix.columns, ' ', _jsxs("small", { children: ["\u00B7", ' ', t('grid.position', { row: matrixContext.row + 1, column: matrixContext.column + 1 })] })] }), matrixContext.matrix.type === 'matrix' && (_jsx("select", { "aria-label": t('matrix.delimiters'), value: matrixContext.matrix.environment, disabled: composing, onChange: (event) => apply(setMatrixEnvironment(historyRef.current.present, event.target.value)), children: matrixEnvironments.map((item) => (_jsx("option", { value: item.value, children: t(item.label) }, item.value))) })), ['row', 'column']
861
+ } })), showTokenLegend && (_jsxs("div", { className: "me-token-legend", "aria-label": t('token.legend'), children: [_jsx("span", { "data-kind": "variable", children: t('token.variable') }), _jsx("span", { "data-kind": "constant", children: t('token.constant') }), _jsx("span", { "data-kind": "symbol", children: t('token.symbol') }), _jsx("span", { "data-kind": "function", children: t('token.function') }), _jsx("small", { children: t('editor.interactionHint') })] })), clipboardMessage && (_jsx("div", { className: "me-grid-selection-hint", role: "status", children: t(clipboardMessage) })), selectedGrid && (_jsx("div", { className: "me-grid-selection-hint", role: "status", children: t('grid.selectionHint') })), toolbar && matrixContext && (_jsxs("div", { className: "me-matrix-tools", role: "toolbar", "aria-label": t(matrixContext.matrix.type === 'matrix' ? 'matrix.edit' : 'grid.editRows'), children: [_jsxs("span", { children: [matrixContext.rows, " \u00D7 ", matrixContext.matrix.columns, ' ', _jsxs("small", { children: ["\u00B7", ' ', t('grid.position', { row: matrixContext.row + 1, column: matrixContext.column + 1 })] })] }), matrixContext.matrix.type === 'matrix' && (_jsx("select", { "aria-label": t('matrix.delimiters'), value: matrixContext.matrix.environment, disabled: composing, onChange: (event) => apply(setMatrixEnvironment(historyRef.current.present, event.target.value)), children: matrixEnvironments.map((item) => (_jsx("option", { value: item.value, children: t(item.label) }, item.value))) })), ['row', 'column']
666
862
  .filter((axis) => axis === 'row' || matrixContext.matrix.type === 'matrix')
667
863
  .flatMap((axis) => ['insert', 'delete'].map((action) => {
668
864
  const size = axis === 'row' ? matrixContext.rows : matrixContext.matrix.columns;
@@ -681,12 +877,12 @@ export function MathEditor({ defaultValue, onChange, onExit, label, locale = 'ko
681
877
  : 'grid.deleteColumn') }, `${axis}-${action}`));
682
878
  })), _jsx("small", { children: matrixContext.matrix.type === 'matrix'
683
879
  ? t('matrix.keyboardHint')
684
- : `${t(matrixContext.matrix.type === 'aligned' ? 'aligned.inputHint' : 'cases.inputHint')} · ${t('grid.keyboardHint')}` })] })), candidates.length > 0 &&
685
- menuPosition &&
686
- createPortal(_jsxs("div", { className: "me-suggestion-panel", lang: locale, dir: mathLocaleDirection(locale), style: menuPosition, onMouseDown: (event) => event.preventDefault(), children: [_jsxs("div", { className: "me-suggestion-heading", children: [t('suggestion.create', { query }), ' ', _jsx("span", { children: t(composing ? 'suggestion.composing' : 'suggestion.keyboardHint') })] }), _jsx("div", { className: "me-suggestions", id: suggestionId, role: "listbox", "aria-label": t('suggestion.label'), children: candidates.map((c, index) => (_jsxs("button", { type: "button", id: `${suggestionId}-${index}`, role: "option", disabled: composing, "aria-selected": !composing && index === selectedIndex, onClick: () => choose(index), children: [_jsx("span", { className: "me-candidate-glyph", children: c.glyph }), _jsxs("span", { children: [_jsx("strong", { children: c.label }), _jsx("small", { children: c.detail })] })] }, c.id))) }), _jsx("div", { className: "me-suggestion-footer", children: t(composing ? 'suggestion.imeHint' : 'suggestion.keepSource') })] }), document.body), _jsx("div", { className: "me-surface", dir: "ltr", ref: surface, tabIndex: 0, "aria-label": t('selection.surface'), "data-mode": editing ? 'edit' : 'preview', onPointerDown: beginPointer, onPointerMove: movePointer, onPointerUp: endPointer, onPointerCancel: () => {
880
+ : `${t(matrixContext.matrix.type === 'aligned' ? 'aligned.inputHint' : 'cases.inputHint')} · ${t('grid.keyboardHint')}` })] })), showPopovers &&
881
+ candidates.length > 0 &&
882
+ createPortal(_jsxs("div", { className: "me-suggestion-panel", "data-math-editor-owner": suggestionId, lang: locale, dir: mathLocaleDirection(locale), ref: suggestionMenu, onMouseDown: (event) => event.preventDefault(), children: [_jsxs("div", { className: "me-suggestion-heading", children: [t('suggestion.create', { query }), ' ', _jsx("span", { children: t(composing ? 'suggestion.composing' : 'suggestion.keyboardHint') })] }), _jsx("div", { className: "me-suggestions", id: suggestionId, role: "listbox", "aria-label": t('suggestion.label'), children: candidates.map((c, index) => (_jsxs("button", { type: "button", id: `${suggestionId}-${index}`, role: "option", disabled: composing, "aria-selected": !composing && index === selectedIndex, onClick: () => choose(index), children: [_jsx("span", { className: "me-candidate-glyph", children: c.glyph }), _jsxs("span", { children: [_jsx("strong", { children: c.label }), _jsx("small", { children: c.detail })] })] }, c.id))) }), _jsx("div", { className: "me-suggestion-footer", children: t(composing ? 'suggestion.imeHint' : 'suggestion.keepSource') })] }), surface.current?.closest('dialog, [role="dialog"]') ?? document.body), _jsx("div", { className: "me-surface", dir: "ltr", ref: surface, tabIndex: 0, "aria-label": t('selection.surface'), "data-mode": editing ? 'edit' : 'preview', onPointerDown: beginPointer, onPointerMove: movePointer, onPointerUp: endPointer, onPointerCancel: () => {
687
883
  drag.current = undefined;
688
- }, onKeyDown: selectionKey, children: _jsx("div", { className: "me-document-lines", children: documentRows(state.document).map((line, index) => (_jsxs("div", { className: "me-document-line", children: [showLineNumbers && documentRows(state.document).length > 1 && (_jsx("span", { className: "me-line-number", "aria-hidden": "true", children: index + 1 })), renderRow(line, labels.line(index))] }, line.id))) }) }), toolbar && selectionFragment && (_jsxs("div", { className: "me-wrap-tools", role: "toolbar", "aria-label": t('selection.toolbar'), children: [_jsx("span", { children: t(canWrap ? 'selection.highlighted' : 'selection.singleRowRequired') }), structures
689
- .filter((s) => ['fraction', 'root', 'superscript', 'parentheses'].includes(s.kind))
690
- .map((s) => (_jsxs("button", { type: "button", disabled: !canWrap, onMouseDown: (event) => event.preventDefault(), onClick: () => insertTool(s.kind), "aria-label": t('selection.wrap', { kind: t(s.label) }), children: [s.glyph, " ", t(s.label)] }, s.kind)))] }))] }));
884
+ }, onKeyDown: selectionKey, children: _jsx("div", { className: "me-document-lines", children: documentRows(state.document).map((line, index) => (_jsxs("div", { className: "me-document-line", children: [showLineNumbers && documentRows(state.document).length > 1 && (_jsx("span", { className: "me-line-number", "aria-hidden": "true", children: index + 1 })), renderRow(line, labels.line(index))] }, line.id))) }) }), showPopovers &&
885
+ selectionFragment &&
886
+ createPortal(_jsxs("div", { className: "me-suggestion-panel", "data-math-editor-owner": suggestionId, ref: selectionMenu, role: "presentation", "aria-label": t('selection.toolbar'), onKeyDown: selectionKey, children: [_jsx("div", { className: "me-suggestion-heading", children: t(canWrap ? 'selection.highlighted' : 'selection.singleRowRequired') }), _jsx("div", { className: "me-suggestions", role: "listbox", "aria-label": t('selection.toolbar'), children: selectionChoices.map((s, index) => (_jsxs("button", { type: "button", role: "option", "aria-selected": index === selectionIndex, disabled: !canWrap, onMouseDown: (event) => event.preventDefault(), onClick: () => insertTool(s.kind), "aria-label": t('selection.wrap', { kind: t(s.label) }), children: [_jsx("span", { className: "me-candidate-glyph", children: s.glyph }), _jsxs("span", { children: [_jsx("strong", { children: t(s.label) }), _jsx("small", { children: t(s.detail) })] })] }, s.kind))) })] }), surface.current?.closest('dialog, [role="dialog"]') ?? document.body)] }));
691
887
  }
692
888
  //# sourceMappingURL=math-editor.js.map