@drghaliasri/butex 5.4.6 → 5.5.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.
@@ -1133,7 +1133,7 @@ function parseReferences(json) {
1133
1133
  }
1134
1134
  return references;
1135
1135
  }
1136
- function createInlineField2(value = "", mathObjects = [], options = {}, path = "$", diagnostics = []) {
1136
+ function createInlineField2(value = "", mathObjects = [], options = {}, path = "$", diagnostics = [], formats = []) {
1137
1137
  const mode = options.mode ?? "english";
1138
1138
  const spans = detectInlineSpans2(value);
1139
1139
  const mathSpans = spans.filter((span) => span.kind === "math");
@@ -1145,7 +1145,7 @@ function createInlineField2(value = "", mathObjects = [], options = {}, path = "
1145
1145
  }
1146
1146
  for (const span of spans) {
1147
1147
  if (span.start > index) {
1148
- tokens.push({ id: document2Id("text"), kind: "text", text: value.slice(index, span.start) });
1148
+ pushFormattedTextTokens(tokens, value.slice(index, span.start), index, formats);
1149
1149
  }
1150
1150
  if (span.kind === "cite") {
1151
1151
  tokens.push({
@@ -1201,9 +1201,81 @@ function createInlineField2(value = "", mathObjects = [], options = {}, path = "
1201
1201
  index = span.end;
1202
1202
  }
1203
1203
  if (index < value.length || tokens.length === 0) {
1204
- tokens.push({ id: document2Id("text"), kind: "text", text: value.slice(index) });
1204
+ pushFormattedTextTokens(tokens, value.slice(index), index, formats);
1205
+ }
1206
+ return { id: document2Id("field"), tokens: compactTextTokens(tokens) };
1207
+ }
1208
+ function styleFromFormat(format) {
1209
+ const style = {};
1210
+ if (format.bold === true) {
1211
+ style.bold = true;
1212
+ }
1213
+ if (format.italic === true) {
1214
+ style.italic = true;
1215
+ }
1216
+ if (format.underline === true) {
1217
+ style.underline = true;
1218
+ }
1219
+ return style.bold || style.italic || style.underline ? style : null;
1220
+ }
1221
+ function mergeTextStyle(base, added) {
1222
+ return {
1223
+ ...base?.bold ? { bold: true } : {},
1224
+ ...base?.italic ? { italic: true } : {},
1225
+ ...base?.underline ? { underline: true } : {},
1226
+ ...added.bold ? { bold: true } : {},
1227
+ ...added.italic ? { italic: true } : {},
1228
+ ...added.underline ? { underline: true } : {}
1229
+ };
1230
+ }
1231
+ function textStylesEqual(a, b) {
1232
+ return Boolean(a?.bold) === Boolean(b?.bold) && Boolean(a?.italic) === Boolean(b?.italic) && Boolean(a?.underline) === Boolean(b?.underline);
1233
+ }
1234
+ function compactTextTokens(tokens) {
1235
+ const compacted = [];
1236
+ for (const token of tokens) {
1237
+ const previous = compacted[compacted.length - 1];
1238
+ if (previous?.kind === "text" && token.kind === "text" && textStylesEqual(previous.style, token.style)) {
1239
+ previous.text += token.text;
1240
+ } else {
1241
+ compacted.push(token);
1242
+ }
1243
+ }
1244
+ return compacted;
1245
+ }
1246
+ function styleKey(style) {
1247
+ return `${style?.bold ? "b" : ""}${style?.italic ? "i" : ""}${style?.underline ? "u" : ""}`;
1248
+ }
1249
+ function pushFormattedTextTokens(tokens, text, sourceStart, formats) {
1250
+ if (text.length === 0) {
1251
+ tokens.push({ id: document2Id("text"), kind: "text", text });
1252
+ return;
1253
+ }
1254
+ const styles = Array.from({ length: text.length });
1255
+ for (const format of formats) {
1256
+ const style = styleFromFormat(format);
1257
+ if (!style || !Number.isFinite(format.start) || !Number.isFinite(format.end)) {
1258
+ continue;
1259
+ }
1260
+ const start = Math.max(0, Math.min(text.length, Math.trunc(format.start) - sourceStart));
1261
+ const end = Math.max(0, Math.min(text.length, Math.trunc(format.end) - sourceStart));
1262
+ if (end <= start) {
1263
+ continue;
1264
+ }
1265
+ for (let index = start; index < end; index += 1) {
1266
+ styles[index] = mergeTextStyle(styles[index], style);
1267
+ }
1268
+ }
1269
+ let chunkStart = 0;
1270
+ for (let index = 1; index <= text.length; index += 1) {
1271
+ if (index < text.length && styleKey(styles[index]) === styleKey(styles[chunkStart])) {
1272
+ continue;
1273
+ }
1274
+ const chunk = text.slice(chunkStart, index);
1275
+ const style = styles[chunkStart];
1276
+ tokens.push({ id: document2Id("text"), kind: "text", text: chunk, ...style ? { style } : {} });
1277
+ chunkStart = index;
1205
1278
  }
1206
- return { id: document2Id("field"), tokens };
1207
1279
  }
1208
1280
  function closingForList(command) {
1209
1281
  return command === "\\begin{itemize}" ? "\\end{itemize}" : "\\end{enumerate}";
@@ -1214,7 +1286,7 @@ function parseTextBlock(json, options, path, diagnostics) {
1214
1286
  id: document2Id("block"),
1215
1287
  kind: "textBlock",
1216
1288
  command: json.command,
1217
- field: createInlineField2(value, json.math_objects ?? [], options, `${path}.value`, diagnostics),
1289
+ field: createInlineField2(value, json.math_objects ?? [], options, `${path}.value`, diagnostics, json.command === "\\paragraph" ? json.formats ?? [] : []),
1218
1290
  ...json.centered === true ? { centered: true } : {}
1219
1291
  };
1220
1292
  }
@@ -1223,7 +1295,7 @@ function parseListItem(json, options, path, diagnostics) {
1223
1295
  const blocksJson = Array.isArray(json.blocks) ? json.blocks : [];
1224
1296
  return {
1225
1297
  id: document2Id("item"),
1226
- field: createInlineField2(value, json.math_objects ?? [], options, `${path}.value`, diagnostics),
1298
+ field: createInlineField2(value, json.math_objects ?? [], options, `${path}.value`, diagnostics, json.formats ?? []),
1227
1299
  blocks: blocksJson.map((block, index) => parseBlock(asBlockJson(block), options, `${path}.blocks[${String(index)}]`, diagnostics))
1228
1300
  };
1229
1301
  }
@@ -1256,7 +1328,8 @@ function parseTableBlock(json, options, path, diagnostics) {
1256
1328
  const spanCount = detectMathSpans2(value).length;
1257
1329
  const cellMathObjects = mathObjects.slice(mathObjectIndex, mathObjectIndex + spanCount);
1258
1330
  mathObjectIndex += spanCount;
1259
- return createInlineField2(value, cellMathObjects, options, `${path}.rows[${String(rowIndex)}][${String(columnIndex)}]`, diagnostics);
1331
+ const cellFormats = json.cell_formats?.[rowIndex]?.[columnIndex] ?? [];
1332
+ return createInlineField2(value, cellMathObjects, options, `${path}.rows[${String(rowIndex)}][${String(columnIndex)}]`, diagnostics, cellFormats);
1260
1333
  });
1261
1334
  });
1262
1335
  if (mathObjects.length > 0 && mathObjects.length !== mathObjectIndex) {
@@ -3281,7 +3354,8 @@ ${BUTEX_FONT_FACE_CSS}
3281
3354
  transition: box-shadow 0.15s ease, border-color 0.15s ease;
3282
3355
  }
3283
3356
 
3284
- .surface:focus {
3357
+ .surface:focus,
3358
+ .surface.surface--typing-focus {
3285
3359
  border-color: var(--butex-focus-border, rgba(13, 148, 136, 0.45));
3286
3360
  box-shadow: 0 0 0 3px var(--butex-focus-shadow, rgba(13, 148, 136, 0.12));
3287
3361
  }
@@ -4296,6 +4370,79 @@ function normalizeFieldTokens(tokens) {
4296
4370
  }
4297
4371
  return tokens;
4298
4372
  }
4373
+ function textStylesEqual2(a, b) {
4374
+ return Boolean(a?.bold) === Boolean(b?.bold) && Boolean(a?.italic) === Boolean(b?.italic) && Boolean(a?.underline) === Boolean(b?.underline);
4375
+ }
4376
+ function compactAdjacentTextTokens(tokens) {
4377
+ const compacted = [];
4378
+ for (const token of tokens) {
4379
+ const previous = compacted[compacted.length - 1];
4380
+ if (previous?.kind === "text" && token.kind === "text" && textStylesEqual2(previous.style, token.style)) {
4381
+ previous.text += token.text;
4382
+ } else {
4383
+ compacted.push(token);
4384
+ }
4385
+ }
4386
+ return normalizeFieldTokens(compacted);
4387
+ }
4388
+ function withoutEmptyStyle(style) {
4389
+ const next = {};
4390
+ if (style.bold) {
4391
+ next.bold = true;
4392
+ }
4393
+ if (style.italic) {
4394
+ next.italic = true;
4395
+ }
4396
+ if (style.underline) {
4397
+ next.underline = true;
4398
+ }
4399
+ return next.bold || next.italic || next.underline ? next : void 0;
4400
+ }
4401
+ function textTokenPart(text, style, id = document2Id("text")) {
4402
+ return { id, kind: "text", text, ...style ? { style } : {} };
4403
+ }
4404
+ function toggleTextTokenStyle(document2, fieldId, tokenId, selectionStart, selectionEnd, styleName) {
4405
+ if (selectionEnd <= selectionStart) {
4406
+ return document2;
4407
+ }
4408
+ const next = cloneDocument(document2);
4409
+ visitFields(next.blocks, (field) => {
4410
+ if (field.id !== fieldId) {
4411
+ return false;
4412
+ }
4413
+ const tokenIndex = field.tokens.findIndex((entry) => entry.id === tokenId && entry.kind === "text");
4414
+ if (tokenIndex < 0) {
4415
+ return true;
4416
+ }
4417
+ const token = field.tokens[tokenIndex];
4418
+ const start = Math.max(0, Math.min(token.text.length, selectionStart));
4419
+ const end = Math.max(0, Math.min(token.text.length, selectionEnd));
4420
+ if (end <= start) {
4421
+ return true;
4422
+ }
4423
+ const selectedStyle = token.style ?? {};
4424
+ const enabled = selectedStyle[styleName] === true;
4425
+ const nextStyle = withoutEmptyStyle({ ...selectedStyle, [styleName]: enabled ? void 0 : true });
4426
+ const parts = [];
4427
+ const before = token.text.slice(0, start);
4428
+ const selected = token.text.slice(start, end);
4429
+ const after = token.text.slice(end);
4430
+ if (before.length > 0) {
4431
+ parts.push(textTokenPart(before, token.style, token.id));
4432
+ }
4433
+ parts.push(textTokenPart(selected, nextStyle, before.length > 0 ? document2Id("text") : token.id));
4434
+ if (after.length > 0) {
4435
+ parts.push(textTokenPart(after, token.style));
4436
+ }
4437
+ field.tokens = compactAdjacentTextTokens([
4438
+ ...field.tokens.slice(0, tokenIndex),
4439
+ ...parts,
4440
+ ...field.tokens.slice(tokenIndex + 1)
4441
+ ]);
4442
+ return true;
4443
+ });
4444
+ return next;
4445
+ }
4299
4446
  function stitchTextAroundRemovedToken(tokens, index) {
4300
4447
  const before = tokens[index - 1];
4301
4448
  const after = tokens[index + 1];
@@ -4323,8 +4470,8 @@ function removeMathTokenById(document2, tokenId) {
4323
4470
  function splitTextTokenAt(token, offset) {
4324
4471
  const safeOffset = Math.max(0, Math.min(offset, token.text.length));
4325
4472
  return [
4326
- { id: token.id, kind: "text", text: token.text.slice(0, safeOffset) },
4327
- { id: document2Id("text"), kind: "text", text: token.text.slice(safeOffset) }
4473
+ { id: token.id, kind: "text", text: token.text.slice(0, safeOffset), ...token.style ? { style: token.style } : {} },
4474
+ { id: document2Id("text"), kind: "text", text: token.text.slice(safeOffset), ...token.style ? { style: token.style } : {} }
4328
4475
  ];
4329
4476
  }
4330
4477
  function insertMathTokenAtCaret(document2, fieldId, textTokenId, caretOffset, session, opening = "$", closing = "$", side = "arabic") {
@@ -4861,7 +5008,7 @@ function arabicXeLatexPreamblePkg(twocolumn = false) {
4861
5008
  \usepackage{bidi}
4862
5009
  \usepackage{multirow}
4863
5010
  \usepackage{booktabs}
4864
- \usepackage{graphicx} % For \butexreflect
5011
+ \usepackage{graphicx} % For \reflectbox
4865
5012
  \usepackage{xcolor}
4866
5013
  \usepackage{tikz}
4867
5014
  \usepackage{tcolorbox} % For tcolorbox environment
@@ -4930,10 +5077,8 @@ function arabicXeLatexPreambleCmd() {
4930
5077
  \newcommand{\horzbar}{\rule[.5ex]{2.5ex}{0.5pt}}
4931
5078
 
4932
5079
 
4933
- \newcommand{\butexreflect}[1]{\tikz[baseline=(n.base)]{\node[inner sep=0pt,outer sep=0pt,xscale=-1](n){#1};}}
4934
-
4935
- \newcommand{\arabsqrt}[2]{\butexreflect{\(\sqrt[\butexreflect{\(#1\)}]{\butexreflect{\(#2\)}}\)}}
4936
- \newcommand{\arabvec}[1]{\butexreflect{$\vec{\butexreflect{$#1$}}$}}
5080
+ \newcommand{\arabsqrt}[2]{\reflectbox{\(\sqrt[\reflectbox{\(#1\)}]{\reflectbox{\(#2\)}}\)}}
5081
+ \newcommand{\arabvec}[1]{\reflectbox{$\vec{\reflectbox{$#1$}}$}}
4937
5082
 
4938
5083
  \newcommand{\arabexp}[1]{{}^{#1}\!\raisebox{-4.5pt}{\text{\diwani{ه}}}}
4939
5084
  \newcommand{\arablog}[2]{\left(\text{#2}\right)\!\prescript{}{\text{#1}}{\text{\diwani{لو}}}}
@@ -4981,9 +5126,9 @@ function arabicXeLatexPreambleCmd() {
4981
5126
  \newcommand{\araboddde}[2][-5pt]{\stackrel{\raisebox{#1}{$\vcenter{\hbox{$\cdot\!\cdot\!\cdot$}}$}}{\text{#2}}}
4982
5127
  \newcommand{\arabpde}[1]{\prescript{}{#1\!\!}{\nabla}} % arabic partial differential equation (PDE)
4983
5128
 
4984
- \newcommand{\arabprime}[0]{\butexreflect{$\prime$}\!} % arabic prime notation
4985
- \newcommand{\arabpprime}[0]{\butexreflect{$\prime$}\butexreflect{$\prime$}\!} % arabic prime notation
4986
- \newcommand{\arabppprime}[0]{\butexreflect{$\prime$}\butexreflect{$\prime$}\butexreflect{$\prime$}\!} % arabic prime notation
5129
+ \newcommand{\arabprime}[0]{\reflectbox{$\prime$}\!} % arabic prime notation
5130
+ \newcommand{\arabpprime}[0]{\reflectbox{$\prime$}\reflectbox{$\prime$}\!} % arabic prime notation
5131
+ \newcommand{\arabppprime}[0]{\reflectbox{$\prime$}\reflectbox{$\prime$}\reflectbox{$\prime$}\!} % arabic prime notation
4987
5132
 
4988
5133
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4989
5134
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -5003,7 +5148,7 @@ function arabicXeLatexPreambleCmd() {
5003
5148
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5004
5149
 
5005
5150
 
5006
- \newcommand{\arsqrt}[2][]{\butexreflect{\(\sqrt[\butexreflect{\(#1\)}]{\butexreflect{\(#2\)}}\)}}
5151
+ \newcommand{\arsqrt}[2][]{\reflectbox{\(\sqrt[\reflectbox{\(#1\)}]{\reflectbox{\(#2\)}}\)}}
5007
5152
  \newcommand{\arexp}[0]{\!\raisebox{-4.5pt}{\text{\diwani{ه}}}}
5008
5153
  \newcommand{\arlog}[0]{\!\!\text{\diwani{لو}}}
5009
5154
  \newcommand{\arln}[0]{\!\!\prescript{}{\text{\diwani{ه}}}{\text{\diwani{لو}}}}
@@ -5066,9 +5211,22 @@ function mathBodyLatex(token) {
5066
5211
  const rendered = renderMathNodeLatex(token.math);
5067
5212
  return stripDisplayMathDelimiters(rendered);
5068
5213
  }
5214
+ function styledTextLatex(text, style) {
5215
+ let output = text;
5216
+ if (style?.bold) {
5217
+ output = `\\textbf{${output}}`;
5218
+ }
5219
+ if (style?.italic) {
5220
+ output = `\\textit{${output}}`;
5221
+ }
5222
+ if (style?.underline) {
5223
+ output = `\\underline{${output}}`;
5224
+ }
5225
+ return output;
5226
+ }
5069
5227
  function tokenLatex(token) {
5070
5228
  if (token.kind === "text") {
5071
- return token.text;
5229
+ return styledTextLatex(token.text, token.style);
5072
5230
  }
5073
5231
  if (token.kind === "cite") {
5074
5232
  return citeTokenLatex(token.keys);
@@ -5092,7 +5250,9 @@ function inlineFieldLatex(field) {
5092
5250
  return field.tokens.map((token) => tokenLatex(token)).join("");
5093
5251
  }
5094
5252
  function textBlockLatex(block) {
5095
- const body = `${block.command}{${inlineFieldLatex(block.field)}}`;
5253
+ const content = inlineFieldLatex(block.field);
5254
+ const body = block.command === "\\paragraph" ? `\\par
5255
+ ${content}` : `${block.command}{${content}}`;
5096
5256
  if (block.command === "\\paragraph" && block.centered) {
5097
5257
  return `\\begin{center}
5098
5258
  ${body}
@@ -5344,7 +5504,7 @@ function mathTex(token, equationSide) {
5344
5504
  function previewInlines(field, islands, output, equationSide, document2, previewOptions) {
5345
5505
  return field.tokens.map((token) => {
5346
5506
  if (token.kind === "text") {
5347
- return { kind: "text", text: token.text };
5507
+ return { kind: "text", text: token.text, ...token.style ? { style: token.style } : {} };
5348
5508
  }
5349
5509
  if (token.kind === "cite") {
5350
5510
  return {
@@ -5625,6 +5785,7 @@ export {
5625
5785
  setDocument2References,
5626
5786
  stripDisplayMathDelimiters,
5627
5787
  toggleBlockInSelection,
5788
+ toggleTextTokenStyle,
5628
5789
  updateCiteTokenKeys,
5629
5790
  updateDocument2ImageMeta,
5630
5791
  updateDocument2ImageValue,