@drghaliasri/butex 4.0.1 → 4.2.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.
@@ -970,6 +970,16 @@ var ATOMIC_COMMANDS = {
970
970
  arabicLabel: "\u0645\u062D\u062F\u062F",
971
971
  title: "\u0645\u062D\u062F\u062F"
972
972
  },
973
+ ////////////////////// Derivatives ////////////////////////////////
974
+ ad: {
975
+ id: "ad",
976
+ category: "derivative",
977
+ englishTex: "\\mathrm{d}",
978
+ englishLabel: "d",
979
+ arabicTex: "\\ad",
980
+ arabicLabel: "\u0621",
981
+ title: "\u0639\u0644\u0627\u0645\u0629 \u0627\u0644\u0627\u0634\u062A\u0642\u0627\u0642"
982
+ },
973
983
  ////////////////////// Groups /////////////////////////////////
974
984
  N: {
975
985
  id: "N",
@@ -1793,6 +1803,22 @@ function createEditorSession(englishTree, arabicTree, activeSide = "arabic") {
1793
1803
  };
1794
1804
  }
1795
1805
 
1806
+ // src/editor/divideOperator.ts
1807
+ var DIVIDE_OPERATOR_EN = "/";
1808
+ var DIVIDE_OPERATOR_AR = "\\";
1809
+ function isDivideOperatorExpr(expr) {
1810
+ return expr === DIVIDE_OPERATOR_EN || expr === DIVIDE_OPERATOR_AR;
1811
+ }
1812
+ function divideOperatorSurfaceExpr(expr, side) {
1813
+ if (!isDivideOperatorExpr(expr)) {
1814
+ return expr;
1815
+ }
1816
+ return side === "arabic" ? DIVIDE_OPERATOR_AR : DIVIDE_OPERATOR_EN;
1817
+ }
1818
+ function renderDivideOperatorLatex(side) {
1819
+ return side === "arabic" ? "\\backslash" : DIVIDE_OPERATOR_EN;
1820
+ }
1821
+
1796
1822
  // src/editor/digits.ts
1797
1823
  var WESTERN = "0123456789";
1798
1824
  var ARABIC_INDIC = "\u0660\u0661\u0662\u0663\u0664\u0665\u0666\u0667\u0668\u0669";
@@ -1941,6 +1967,9 @@ function renderNodeEnglish(node, options) {
1941
1967
  if (node.kind === "char") {
1942
1968
  return `${renderCharContent(node, options)}${latinScripts(node, options)}`;
1943
1969
  }
1970
+ if (node.kind === "operator" && isDivideOperatorExpr(node.expr)) {
1971
+ return `${renderDivideOperatorLatex("english")}${latinScripts(node, options)}`;
1972
+ }
1944
1973
  return `${formatDigits(node.expr, selectedDigitForm(options))}${latinScripts(node, options)}`;
1945
1974
  }
1946
1975
  function renderNodeArabic(node, options) {
@@ -1975,6 +2004,9 @@ function renderNodeArabic(node, options) {
1975
2004
  const content = renderCharContent(node, options);
1976
2005
  return arabicScripts(node, (node.characterFont ?? "default") === "default" ? `\\text{${escapeText(expr)}}` : content, options);
1977
2006
  }
2007
+ if (node.kind === "operator" && isDivideOperatorExpr(node.expr)) {
2008
+ return arabicScripts(node, renderDivideOperatorLatex("arabic"), options);
2009
+ }
1978
2010
  return arabicScripts(node, formatDigits(node.expr, selectedDigitForm(options)), options);
1979
2011
  }
1980
2012
  function renderChainEnglish(chain, options) {
@@ -2498,6 +2530,128 @@ function undoRedoCanRedo(stacks) {
2498
2530
  return stacks.future.length > 0;
2499
2531
  }
2500
2532
 
2533
+ // src/editor/derivativeTemplates.ts
2534
+ var ARABIC_VAR = "\u0633";
2535
+ var ENGLISH_VAR = "x";
2536
+ function mirroredAd() {
2537
+ const english = createAtomicCommandNode("ad");
2538
+ const arabic = createAtomicCommandNode("ad");
2539
+ arabic.id = english.id;
2540
+ return { english, arabic };
2541
+ }
2542
+ function mirroredVar() {
2543
+ const english = createEditorNode("char", ENGLISH_VAR);
2544
+ const arabic = createEditorNode("char", ARABIC_VAR);
2545
+ arabic.id = english.id;
2546
+ return { english, arabic };
2547
+ }
2548
+ function withMirroredSuperscript2(english, arabic) {
2549
+ const englishSup = createEditorChain([createEditorNode("number", "2")]);
2550
+ const arabicSup = createEditorChain([createEditorNode("number", "2")]);
2551
+ arabicSup.id = englishSup.id;
2552
+ arabicSup.nodes[0].id = englishSup.nodes[0].id;
2553
+ english.superscript = englishSup;
2554
+ arabic.superscript = arabicSup;
2555
+ }
2556
+ function syncChainIds(english, arabic) {
2557
+ arabic.id = english.id;
2558
+ const limit = Math.min(english.nodes.length, arabic.nodes.length);
2559
+ for (let index = 0; index < limit; index += 1) {
2560
+ arabic.nodes[index].id = english.nodes[index].id;
2561
+ }
2562
+ }
2563
+ function syncFracIds(englishFrac, arabicFrac) {
2564
+ arabicFrac.id = englishFrac.id;
2565
+ if (englishFrac.numerator && arabicFrac.numerator) {
2566
+ syncChainIds(englishFrac.numerator, arabicFrac.numerator);
2567
+ }
2568
+ if (englishFrac.denominator && arabicFrac.denominator) {
2569
+ syncChainIds(englishFrac.denominator, arabicFrac.denominator);
2570
+ }
2571
+ }
2572
+ function buildAdVarChain() {
2573
+ const ad = mirroredAd();
2574
+ const variable = mirroredVar();
2575
+ const english = createEditorChain([ad.english, variable.english]);
2576
+ const arabic = createEditorChain([ad.arabic, variable.arabic]);
2577
+ syncChainIds(english, arabic);
2578
+ return { english, arabic };
2579
+ }
2580
+ function mirroredEmptyDelimiter() {
2581
+ const english = createDelimiterNode("", "");
2582
+ const arabic = createDelimiterNode("", "");
2583
+ arabic.id = english.id;
2584
+ if (english.innerExpr && arabic.innerExpr) {
2585
+ syncChainIds(english.innerExpr, arabic.innerExpr);
2586
+ }
2587
+ return { english, arabic };
2588
+ }
2589
+ function setMirroredDelimiterInner(english, arabic, pairs) {
2590
+ const englishInner = createEditorChain(pairs.map((pair) => pair.english));
2591
+ const arabicInner = createEditorChain(pairs.map((pair) => pair.arabic));
2592
+ syncChainIds(englishInner, arabicInner);
2593
+ english.innerExpr = englishInner;
2594
+ arabic.innerExpr = arabicInner;
2595
+ }
2596
+ function buildFirstDerivativeFrac() {
2597
+ const englishFrac = createFracNode();
2598
+ const arabicFrac = createFracNode();
2599
+ syncFracIds(englishFrac, arabicFrac);
2600
+ const numerator = buildAdVarChain();
2601
+ const denominator = buildAdVarChain();
2602
+ englishFrac.numerator = numerator.english;
2603
+ englishFrac.denominator = denominator.english;
2604
+ arabicFrac.numerator = numerator.arabic;
2605
+ arabicFrac.denominator = denominator.arabic;
2606
+ return { english: englishFrac, arabic: arabicFrac };
2607
+ }
2608
+ function buildSecondDerivativeFrac() {
2609
+ const englishFrac = createFracNode();
2610
+ const arabicFrac = createFracNode();
2611
+ syncFracIds(englishFrac, arabicFrac);
2612
+ const numAd = mirroredAd();
2613
+ const numVar = mirroredVar();
2614
+ withMirroredSuperscript2(numAd.english, numAd.arabic);
2615
+ const englishNumerator = createEditorChain([numAd.english, numVar.english]);
2616
+ const arabicNumerator = createEditorChain([numAd.arabic, numVar.arabic]);
2617
+ syncChainIds(englishNumerator, arabicNumerator);
2618
+ englishFrac.numerator = englishNumerator;
2619
+ arabicFrac.numerator = arabicNumerator;
2620
+ const denGroup = mirroredEmptyDelimiter();
2621
+ const denInnerAd = mirroredAd();
2622
+ const denInnerVar = mirroredVar();
2623
+ setMirroredDelimiterInner(denGroup.english, denGroup.arabic, [
2624
+ { english: denInnerAd.english, arabic: denInnerAd.arabic },
2625
+ { english: denInnerVar.english, arabic: denInnerVar.arabic }
2626
+ ]);
2627
+ withMirroredSuperscript2(denGroup.english, denGroup.arabic);
2628
+ englishFrac.denominator = createEditorChain([denGroup.english]);
2629
+ arabicFrac.denominator = createEditorChain([denGroup.arabic]);
2630
+ syncChainIds(englishFrac.denominator, arabicFrac.denominator);
2631
+ return { english: englishFrac, arabic: arabicFrac };
2632
+ }
2633
+ var DERIVATIVE_TEMPLATE_VAR_CARET_INDEX = 1;
2634
+ function derivativeTemplateInitialCaret(englishFrac) {
2635
+ const denominator = englishFrac.denominator;
2636
+ if (!denominator) {
2637
+ return {
2638
+ chainId: englishFrac.numerator?.id ?? "",
2639
+ index: DERIVATIVE_TEMPLATE_VAR_CARET_INDEX
2640
+ };
2641
+ }
2642
+ const loneDelimiter = denominator.nodes.length === 1 && denominator.nodes[0]?.kind === "delimiter" && denominator.nodes[0].innerExpr;
2643
+ if (loneDelimiter) {
2644
+ return {
2645
+ chainId: denominator.nodes[0].innerExpr.id,
2646
+ index: DERIVATIVE_TEMPLATE_VAR_CARET_INDEX
2647
+ };
2648
+ }
2649
+ return {
2650
+ chainId: denominator.id,
2651
+ index: DERIVATIVE_TEMPLATE_VAR_CARET_INDEX
2652
+ };
2653
+ }
2654
+
2501
2655
  // src/editor/uiLocale.ts
2502
2656
  var ENGLISH_COMMAND_TITLES = {
2503
2657
  cos: "Cosine",
@@ -2528,6 +2682,7 @@ var ENGLISH_COMMAND_TITLES = {
2528
2682
  ln: "Natural logarithm",
2529
2683
  log: "Logarithm",
2530
2684
  det: "Determinant",
2685
+ ad: "Derivative (d / ad)",
2531
2686
  N: "Natural numbers",
2532
2687
  Z: "Integers",
2533
2688
  Q: "Rational numbers",
@@ -2639,6 +2794,10 @@ var EQUATION_EDITOR_MESSAGES = {
2639
2794
  insertFunction: "\u0625\u062F\u0631\u0627\u062C \u062F\u0627\u0644\u0629",
2640
2795
  insertLimitsSeries: "\u0625\u062F\u0631\u0627\u062C \u062D\u062F\u0648\u062F \u0648\u0645\u062A\u0633\u0644\u0633\u0644\u0627\u062A",
2641
2796
  insertAdditionalFunctions: "\u0625\u062F\u0631\u0627\u062C \u062F\u0648\u0627\u0644 \u0625\u0636\u0627\u0641\u064A\u0629",
2797
+ derivatives: "\u0627\u0644\u0627\u0634\u062A\u0642\u0627\u0642",
2798
+ insertDerivative: "\u0625\u062F\u0631\u0627\u062C \u0639\u0644\u0627\u0645\u0629 \u0627\u0634\u062A\u0642\u0627\u0642",
2799
+ insertFirstDerivative: "\u0625\u062F\u0631\u0627\u062C \u0627\u0634\u062A\u0642\u0627\u0642 \u0623\u0648\u0644",
2800
+ insertSecondDerivative: "\u0625\u062F\u0631\u0627\u062C \u0627\u0634\u062A\u0642\u0627\u0642 \u062B\u0627\u0646\u064A",
2642
2801
  insertNumberSets: "\u0625\u062F\u0631\u0627\u062C \u0645\u062C\u0645\u0648\u0639\u0627\u062A \u0623\u0639\u062F\u0627\u062F",
2643
2802
  operations: "\u0627\u0644\u0639\u0645\u0644\u064A\u0627\u062A",
2644
2803
  insertOperation: "\u0625\u062F\u0631\u0627\u062C \u0631\u0645\u0632 \u0639\u0645\u0644\u064A\u0629",
@@ -2742,6 +2901,10 @@ var EQUATION_EDITOR_MESSAGES = {
2742
2901
  insertFunction: "Insert function",
2743
2902
  insertLimitsSeries: "Insert limits and series",
2744
2903
  insertAdditionalFunctions: "Insert additional functions",
2904
+ derivatives: "Derivatives",
2905
+ insertDerivative: "Insert derivative marker",
2906
+ insertFirstDerivative: "Insert first derivative",
2907
+ insertSecondDerivative: "Insert second derivative",
2745
2908
  insertNumberSets: "Insert number sets",
2746
2909
  operations: "Operators",
2747
2910
  insertOperation: "Insert operator",
@@ -3449,10 +3612,27 @@ function insertNumber(session, expr) {
3449
3612
  inserted.forceSplitNextLeaf = false;
3450
3613
  return inserted;
3451
3614
  }
3615
+ function insertDivideOperator(session) {
3616
+ const base = replaceSelectionFirst(session);
3617
+ const next = cloneEditorSession(base);
3618
+ const englishNode = createEditorNode("operator", DIVIDE_OPERATOR_EN);
3619
+ const arabicNode = createEditorNode("operator", DIVIDE_OPERATOR_AR);
3620
+ arabicNode.id = englishNode.id;
3621
+ insertMirroredNode(next, next.caret.chainId, next.caret.index, englishNode, arabicNode);
3622
+ markExprSync(next, englishNode.id);
3623
+ next.caret.index += 1;
3624
+ next.selectedStructureNodeId = null;
3625
+ syncSelectionToCaret(next);
3626
+ appendDebug(next, "insertDivideOperator", "");
3627
+ return next;
3628
+ }
3452
3629
  function insertOperator(session, expr) {
3453
3630
  if (expr === "." && !session.splitLeafTyping && precedingIsExtendableNumber(session)) {
3454
3631
  return insertNumber(session, ".");
3455
3632
  }
3633
+ if (expr === DIVIDE_OPERATOR_EN || expr === DIVIDE_OPERATOR_AR) {
3634
+ return insertDivideOperator(session);
3635
+ }
3456
3636
  const base = replaceSelectionFirst(session);
3457
3637
  return insertLeaf(base, "operator", expr);
3458
3638
  }
@@ -3523,6 +3703,24 @@ function insertFraction(session) {
3523
3703
  appendDebug(next, "insertFraction", "");
3524
3704
  return next;
3525
3705
  }
3706
+ function insertDerivativeFrac(session, build, debugLabel) {
3707
+ const base = replaceSelectionFirst(session);
3708
+ const next = cloneEditorSession(base);
3709
+ const { english, arabic } = build();
3710
+ insertMirroredNode(next, next.caret.chainId, next.caret.index, english, arabic);
3711
+ syncSubtreeRecursive(next, english, arabic);
3712
+ next.caret = derivativeTemplateInitialCaret(english);
3713
+ next.selectedStructureNodeId = null;
3714
+ syncSelectionToCaret(next);
3715
+ appendDebug(next, debugLabel, "");
3716
+ return next;
3717
+ }
3718
+ function insertFirstDerivative(session) {
3719
+ return insertDerivativeFrac(session, buildFirstDerivativeFrac, "insertFirstDerivative");
3720
+ }
3721
+ function insertSecondDerivative(session) {
3722
+ return insertDerivativeFrac(session, buildSecondDerivativeFrac, "insertSecondDerivative");
3723
+ }
3526
3724
  function insertSqrt(session) {
3527
3725
  const base = replaceSelectionFirst(session);
3528
3726
  const next = cloneEditorSession(base);
@@ -3993,6 +4191,11 @@ function pasteText(session, text) {
3993
4191
  i += 1;
3994
4192
  continue;
3995
4193
  }
4194
+ if (ch === DIVIDE_OPERATOR_AR) {
4195
+ s = insertDivideOperator(s);
4196
+ i += 1;
4197
+ continue;
4198
+ }
3996
4199
  s = insertChar(s, ch);
3997
4200
  i += 1;
3998
4201
  }
@@ -4000,7 +4203,7 @@ function pasteText(session, text) {
4000
4203
  }
4001
4204
 
4002
4205
  // src/editor/display/base.ts
4003
- function buildBaseNodeBody(node, digitForm = "western") {
4206
+ function buildBaseNodeBody(node, digitForm = "western", side = "english") {
4004
4207
  const value = document.createElement("span");
4005
4208
  value.className = "node-value";
4006
4209
  if (node.kind === "char" && node.characterFont === "takween") {
@@ -4010,7 +4213,8 @@ function buildBaseNodeBody(node, digitForm = "western") {
4010
4213
  } else if (node.kind === "char" && node.characterFont === "diwaniOutline") {
4011
4214
  value.classList.add("node-value--diwani-outline");
4012
4215
  }
4013
- value.textContent = node.expr === "" ? "\u25A1" : formatDigits(node.expr, digitForm);
4216
+ const surfaceExpr = node.kind === "operator" ? divideOperatorSurfaceExpr(node.expr, side) : node.expr;
4217
+ value.textContent = surfaceExpr === "" ? "\u25A1" : formatDigits(surfaceExpr, digitForm);
4014
4218
  return value;
4015
4219
  }
4016
4220
 
@@ -4531,7 +4735,7 @@ function buildFunctionAtomicCommandBody(node, side, spec) {
4531
4735
  value.appendChild(label);
4532
4736
  return value;
4533
4737
  }
4534
- value.textContent = side === "arabic" ? spec?.arabicLabel ?? node.expr : spec?.englishTex.replace("\\", "") ?? node.expr;
4738
+ value.textContent = side === "arabic" ? spec?.arabicLabel ?? node.expr : spec?.englishLabel ?? spec?.englishTex.replace(/^\\/, "") ?? node.expr;
4535
4739
  return value;
4536
4740
  }
4537
4741
  function buildSpacingAtomicCommandBody(spec, side, uiLocale) {
@@ -4657,7 +4861,7 @@ function buildNodeBody(node, buildChain, side, digitForm = "western", uiLocale =
4657
4861
  if (node.kind === "atomicOperatorCommand") {
4658
4862
  return buildAtomicOperatorCommandNodeBody(node, side, uiLocale);
4659
4863
  }
4660
- return buildBaseNodeBody(node, digitForm);
4864
+ return buildBaseNodeBody(node, digitForm, side);
4661
4865
  }
4662
4866
 
4663
4867
  // src/editor/styles.ts
@@ -5375,6 +5579,10 @@ function createEditorRuntime(options) {
5375
5579
  applyStructural((prev) => insertNumber(prev, char));
5376
5580
  return;
5377
5581
  }
5582
+ if (char === "\\") {
5583
+ applyStructural((prev) => insertDivideOperator(prev));
5584
+ return;
5585
+ }
5378
5586
  if (isOperator(char)) {
5379
5587
  applyStructural((prev) => insertOperator(prev, char));
5380
5588
  return;
@@ -5538,6 +5746,14 @@ function createEditorRuntime(options) {
5538
5746
  applyStructural((prev) => insertFraction(prev));
5539
5747
  surfaceEl.focus();
5540
5748
  },
5749
+ insertFirstDerivative: () => {
5750
+ applyStructural((prev) => insertFirstDerivative(prev));
5751
+ surfaceEl.focus();
5752
+ },
5753
+ insertSecondDerivative: () => {
5754
+ applyStructural((prev) => insertSecondDerivative(prev));
5755
+ surfaceEl.focus();
5756
+ },
5541
5757
  insertMatrixEnv: (style, rows, columns) => {
5542
5758
  applyStructural((prev) => insertMatrixEnv(prev, style, rows, columns));
5543
5759
  surfaceEl.focus();
@@ -5605,7 +5821,95 @@ function createEditorRuntime(options) {
5605
5821
  return api;
5606
5822
  }
5607
5823
 
5824
+ // src/document/normalizeCharImport.ts
5825
+ var WRAPPER_SPECS = [
5826
+ { prefix: "\\butexdiwanioutline", font: "diwaniOutline" },
5827
+ { prefix: "\\diwanioutlineshaded", font: "diwaniOutline" },
5828
+ { prefix: "\\butexdiwani", font: "diwani" },
5829
+ { prefix: "\\butextakween", font: "takween" },
5830
+ { prefix: "\\diwani", font: "diwani" },
5831
+ { prefix: "\\takween", font: "takween" },
5832
+ { prefix: "\\text" }
5833
+ ];
5834
+ function readBracedArg(source, openBraceIndex) {
5835
+ if (source[openBraceIndex] !== "{") {
5836
+ return null;
5837
+ }
5838
+ let depth = 0;
5839
+ for (let index = openBraceIndex; index < source.length; index += 1) {
5840
+ const char = source[index];
5841
+ if (char === "{") {
5842
+ depth += 1;
5843
+ } else if (char === "}") {
5844
+ depth -= 1;
5845
+ if (depth === 0) {
5846
+ return { inner: source.slice(openBraceIndex + 1, index), end: index + 1 };
5847
+ }
5848
+ }
5849
+ }
5850
+ return null;
5851
+ }
5852
+ function peelWrapper(source) {
5853
+ for (const wrapper of WRAPPER_SPECS) {
5854
+ if (!source.startsWith(wrapper.prefix)) {
5855
+ continue;
5856
+ }
5857
+ const braced = readBracedArg(source, wrapper.prefix.length);
5858
+ if (!braced || braced.end !== source.length) {
5859
+ continue;
5860
+ }
5861
+ return { inner: braced.inner, font: wrapper.font };
5862
+ }
5863
+ return null;
5864
+ }
5865
+ function normalizeCharImportExpr(expr) {
5866
+ const original = expr;
5867
+ let current = expr;
5868
+ let characterFont = "default";
5869
+ let changed = false;
5870
+ while (true) {
5871
+ const peeled = peelWrapper(current);
5872
+ if (!peeled) {
5873
+ break;
5874
+ }
5875
+ current = peeled.inner;
5876
+ if (peeled.font) {
5877
+ characterFont = peeled.font;
5878
+ }
5879
+ changed = true;
5880
+ }
5881
+ if (!changed) {
5882
+ return { expr: original, characterFont: "default" };
5883
+ }
5884
+ if (current.includes("\\")) {
5885
+ return { expr: original, characterFont: "default" };
5886
+ }
5887
+ return { expr: current, characterFont };
5888
+ }
5889
+
5608
5890
  // src/document/mathEditorAdapter.ts
5891
+ var COMMAND_FONT_MAP = {
5892
+ "\\text": "infer",
5893
+ "\\butextakween": "takween",
5894
+ "\\takween": "takween",
5895
+ "\\butexdiwani": "diwani",
5896
+ "\\diwani": "diwani",
5897
+ "\\butexdiwanioutline": "diwaniOutline",
5898
+ "\\diwanioutlineshaded": "diwaniOutline"
5899
+ };
5900
+ var CHAR_FONT_EXPORT_TEX = {
5901
+ takween: "\\butextakween",
5902
+ diwani: "\\butexdiwani",
5903
+ diwaniOutline: "\\butexdiwanioutline"
5904
+ };
5905
+ function charEditorNodeToAstNode(node) {
5906
+ const font = node.characterFont ?? "default";
5907
+ const charNode = new CharNode(node.expr);
5908
+ if (font === "default") {
5909
+ return charNode;
5910
+ }
5911
+ return new CommandNode(CHAR_FONT_EXPORT_TEX[font], [], [new ChainNode([charNode])]);
5912
+ }
5609
5913
  function commandIdForTex(tex) {
5610
5914
  for (const spec of Object.values(ATOMIC_COMMANDS)) {
5611
5915
  const renderTex = spec.arabicRenderTex;
@@ -5717,6 +6021,42 @@ function envToEditorNode(node) {
5717
6021
  const scripts = attachScripts(node, env);
5718
6022
  return scripts ?? { editable: true, node: env };
5719
6023
  }
6024
+ function charFromTextLikeCommand(node) {
6025
+ const commandFont = COMMAND_FONT_MAP[node.name];
6026
+ if (commandFont === void 0) {
6027
+ return null;
6028
+ }
6029
+ if (node.optionalArgs.length > 0 || node.mandatoryArgs.length !== 1) {
6030
+ return null;
6031
+ }
6032
+ const arg = node.mandatoryArgs[0];
6033
+ if (arg.chain.length !== 1) {
6034
+ return null;
6035
+ }
6036
+ const child = arg.chain[0];
6037
+ if (child instanceof CommandNode) {
6038
+ const nested = charFromTextLikeCommand(child);
6039
+ if (!nested?.editable) {
6040
+ return null;
6041
+ }
6042
+ if (commandFont !== "infer") {
6043
+ nested.node.characterFont = commandFont;
6044
+ }
6045
+ const scripts2 = attachScripts(node, nested.node);
6046
+ return scripts2 ?? nested;
6047
+ }
6048
+ if (!(child instanceof CharNode)) {
6049
+ return null;
6050
+ }
6051
+ const normalized = normalizeCharImportExpr(child.expr);
6052
+ const characterFont = commandFont === "infer" ? normalized.characterFont : commandFont;
6053
+ const editorNode = createEditorNode("char", normalized.expr);
6054
+ if (characterFont !== "default") {
6055
+ editorNode.characterFont = characterFont;
6056
+ }
6057
+ const scripts = attachScripts(node, editorNode);
6058
+ return scripts ?? { editable: true, node: editorNode };
6059
+ }
5720
6060
  function commandToEditorNode(node) {
5721
6061
  if (node.name === "\\frac" && node.mandatoryArgs.length >= 2) {
5722
6062
  const frac = createFracNode();
@@ -5774,11 +6114,19 @@ function commandToEditorNode(node) {
5774
6114
  }
5775
6115
  return { editable: true, node: atomicOperator };
5776
6116
  }
6117
+ const textLike = charFromTextLikeCommand(node);
6118
+ if (textLike) {
6119
+ return textLike;
6120
+ }
5777
6121
  return { editable: false, reason: `\u0623\u0645\u0631 \u0631\u064A\u0627\u0636\u064A \u063A\u064A\u0631 \u0645\u062F\u0639\u0648\u0645 \u062D\u0627\u0644\u064A\u0627: ${node.name}` };
5778
6122
  }
5779
6123
  function astNodeToEditorNode(node) {
5780
6124
  if (node instanceof CharNode) {
5781
- const editorNode = createEditorNode("char", node.expr);
6125
+ const normalized = normalizeCharImportExpr(node.expr);
6126
+ const editorNode = createEditorNode("char", normalized.expr);
6127
+ if (normalized.characterFont !== "default") {
6128
+ editorNode.characterFont = normalized.characterFont;
6129
+ }
5782
6130
  const scripts = attachScripts(node, editorNode);
5783
6131
  return scripts ?? { editable: true, node: editorNode };
5784
6132
  }
@@ -5887,7 +6235,7 @@ function editorEnvToAstNode(node) {
5887
6235
  function editorNodeToAstNode(node) {
5888
6236
  let astNode;
5889
6237
  if (node.kind === "char") {
5890
- astNode = new CharNode(node.expr);
6238
+ astNode = charEditorNodeToAstNode(node);
5891
6239
  } else if (node.kind === "number") {
5892
6240
  astNode = new NumberNode(node.expr);
5893
6241
  } else if (node.kind === "operator") {
@@ -8681,6 +9029,7 @@ var CHARACTER_FONT_OPTIONS = [
8681
9029
  var FUNCTION_COMMANDS = atomicCommandsByCategory("function");
8682
9030
  var LIMITS_SERIES_COMMANDS = atomicCommandsByCategory("limits_series");
8683
9031
  var FUNCTION2_COMMANDS = atomicCommandsByCategory("function2");
9032
+ var DERIVATIVE_COMMANDS = atomicCommandsByCategory("derivative");
8684
9033
  var GROUP_COMMANDS = atomicCommandsByCategory("groups");
8685
9034
  var SPACING_COMMANDS = atomicCommandsByCategory("spacing").filter(isSpacingCommandSpec);
8686
9035
  var OPERATOR_COMMANDS = atomicOperatorCommandsByCategory("operators1");
@@ -8818,6 +9167,7 @@ var ButexEditor = (0, import_react4.forwardRef)(
8818
9167
  const [functionMenuOpen, setFunctionMenuOpen] = (0, import_react4.useState)(false);
8819
9168
  const [limitsSeriesMenuOpen, setLimitsSeriesMenuOpen] = (0, import_react4.useState)(false);
8820
9169
  const [function2MenuOpen, setFunction2MenuOpen] = (0, import_react4.useState)(false);
9170
+ const [derivativeMenuOpen, setDerivativeMenuOpen] = (0, import_react4.useState)(false);
8821
9171
  const [groupMenuOpen, setGroupMenuOpen] = (0, import_react4.useState)(false);
8822
9172
  const [operatorMenuOpen, setOperatorMenuOpen] = (0, import_react4.useState)(false);
8823
9173
  const [operator2MenuOpen, setOperator2MenuOpen] = (0, import_react4.useState)(false);
@@ -9476,6 +9826,87 @@ ${arabic || currentMessages.empty}`;
9476
9826
  ]
9477
9827
  }
9478
9828
  ),
9829
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
9830
+ "div",
9831
+ {
9832
+ className: "function-command-menu function-command-menu--derivative",
9833
+ onBlur: (event) => {
9834
+ const nextFocus = event.relatedTarget;
9835
+ if (!(nextFocus instanceof Node) || !event.currentTarget.contains(nextFocus)) {
9836
+ setDerivativeMenuOpen(false);
9837
+ }
9838
+ },
9839
+ children: [
9840
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
9841
+ "button",
9842
+ {
9843
+ className: "icon-btn function-command-btn",
9844
+ type: "button",
9845
+ title: messages.insertDerivative,
9846
+ "aria-label": messages.insertDerivative,
9847
+ "aria-haspopup": "listbox",
9848
+ "aria-expanded": derivativeMenuOpen,
9849
+ onClick: () => setDerivativeMenuOpen((open) => !open),
9850
+ children: DERIVATIVE_COMMANDS[0]?.arabicLabel ?? "\u0621"
9851
+ }
9852
+ ),
9853
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "function-command-options", role: "listbox", "aria-label": messages.insertDerivative, hidden: !derivativeMenuOpen, children: [
9854
+ DERIVATIVE_COMMANDS.map((command) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
9855
+ "button",
9856
+ {
9857
+ className: "function-command-option",
9858
+ type: "button",
9859
+ role: "option",
9860
+ title: atomicCommandTitle(command, uiLocale),
9861
+ "aria-label": atomicCommandTitle(command, uiLocale),
9862
+ onMouseDown: (event) => event.preventDefault(),
9863
+ onClick: () => {
9864
+ runtimeRef.current?.insertAtomicCommand(command.id);
9865
+ setDerivativeMenuOpen(false);
9866
+ focusSurface();
9867
+ },
9868
+ children: command.arabicLabel
9869
+ },
9870
+ command.id
9871
+ )),
9872
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
9873
+ "button",
9874
+ {
9875
+ className: "function-command-option",
9876
+ type: "button",
9877
+ role: "option",
9878
+ title: messages.insertFirstDerivative,
9879
+ "aria-label": messages.insertFirstDerivative,
9880
+ onMouseDown: (event) => event.preventDefault(),
9881
+ onClick: () => {
9882
+ runtimeRef.current?.insertFirstDerivative();
9883
+ setDerivativeMenuOpen(false);
9884
+ focusSurface();
9885
+ },
9886
+ children: "\u0621/\u0633"
9887
+ }
9888
+ ),
9889
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
9890
+ "button",
9891
+ {
9892
+ className: "function-command-option",
9893
+ type: "button",
9894
+ role: "option",
9895
+ title: messages.insertSecondDerivative,
9896
+ "aria-label": messages.insertSecondDerivative,
9897
+ onMouseDown: (event) => event.preventDefault(),
9898
+ onClick: () => {
9899
+ runtimeRef.current?.insertSecondDerivative();
9900
+ setDerivativeMenuOpen(false);
9901
+ focusSurface();
9902
+ },
9903
+ children: "\u0621\xB2/\u0633\xB2"
9904
+ }
9905
+ )
9906
+ ] })
9907
+ ]
9908
+ }
9909
+ ),
9479
9910
  /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
9480
9911
  "div",
9481
9912
  {