@drghaliasri/butex 5.4.6 → 5.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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) {
@@ -4296,6 +4369,79 @@ function normalizeFieldTokens(tokens) {
4296
4369
  }
4297
4370
  return tokens;
4298
4371
  }
4372
+ function textStylesEqual2(a, b) {
4373
+ return Boolean(a?.bold) === Boolean(b?.bold) && Boolean(a?.italic) === Boolean(b?.italic) && Boolean(a?.underline) === Boolean(b?.underline);
4374
+ }
4375
+ function compactAdjacentTextTokens(tokens) {
4376
+ const compacted = [];
4377
+ for (const token of tokens) {
4378
+ const previous = compacted[compacted.length - 1];
4379
+ if (previous?.kind === "text" && token.kind === "text" && textStylesEqual2(previous.style, token.style)) {
4380
+ previous.text += token.text;
4381
+ } else {
4382
+ compacted.push(token);
4383
+ }
4384
+ }
4385
+ return normalizeFieldTokens(compacted);
4386
+ }
4387
+ function withoutEmptyStyle(style) {
4388
+ const next = {};
4389
+ if (style.bold) {
4390
+ next.bold = true;
4391
+ }
4392
+ if (style.italic) {
4393
+ next.italic = true;
4394
+ }
4395
+ if (style.underline) {
4396
+ next.underline = true;
4397
+ }
4398
+ return next.bold || next.italic || next.underline ? next : void 0;
4399
+ }
4400
+ function textTokenPart(text, style, id = document2Id("text")) {
4401
+ return { id, kind: "text", text, ...style ? { style } : {} };
4402
+ }
4403
+ function toggleTextTokenStyle(document2, fieldId, tokenId, selectionStart, selectionEnd, styleName) {
4404
+ if (selectionEnd <= selectionStart) {
4405
+ return document2;
4406
+ }
4407
+ const next = cloneDocument(document2);
4408
+ visitFields(next.blocks, (field) => {
4409
+ if (field.id !== fieldId) {
4410
+ return false;
4411
+ }
4412
+ const tokenIndex = field.tokens.findIndex((entry) => entry.id === tokenId && entry.kind === "text");
4413
+ if (tokenIndex < 0) {
4414
+ return true;
4415
+ }
4416
+ const token = field.tokens[tokenIndex];
4417
+ const start = Math.max(0, Math.min(token.text.length, selectionStart));
4418
+ const end = Math.max(0, Math.min(token.text.length, selectionEnd));
4419
+ if (end <= start) {
4420
+ return true;
4421
+ }
4422
+ const selectedStyle = token.style ?? {};
4423
+ const enabled = selectedStyle[styleName] === true;
4424
+ const nextStyle = withoutEmptyStyle({ ...selectedStyle, [styleName]: enabled ? void 0 : true });
4425
+ const parts = [];
4426
+ const before = token.text.slice(0, start);
4427
+ const selected = token.text.slice(start, end);
4428
+ const after = token.text.slice(end);
4429
+ if (before.length > 0) {
4430
+ parts.push(textTokenPart(before, token.style, token.id));
4431
+ }
4432
+ parts.push(textTokenPart(selected, nextStyle, before.length > 0 ? document2Id("text") : token.id));
4433
+ if (after.length > 0) {
4434
+ parts.push(textTokenPart(after, token.style));
4435
+ }
4436
+ field.tokens = compactAdjacentTextTokens([
4437
+ ...field.tokens.slice(0, tokenIndex),
4438
+ ...parts,
4439
+ ...field.tokens.slice(tokenIndex + 1)
4440
+ ]);
4441
+ return true;
4442
+ });
4443
+ return next;
4444
+ }
4299
4445
  function stitchTextAroundRemovedToken(tokens, index) {
4300
4446
  const before = tokens[index - 1];
4301
4447
  const after = tokens[index + 1];
@@ -4323,8 +4469,8 @@ function removeMathTokenById(document2, tokenId) {
4323
4469
  function splitTextTokenAt(token, offset) {
4324
4470
  const safeOffset = Math.max(0, Math.min(offset, token.text.length));
4325
4471
  return [
4326
- { id: token.id, kind: "text", text: token.text.slice(0, safeOffset) },
4327
- { id: document2Id("text"), kind: "text", text: token.text.slice(safeOffset) }
4472
+ { id: token.id, kind: "text", text: token.text.slice(0, safeOffset), ...token.style ? { style: token.style } : {} },
4473
+ { id: document2Id("text"), kind: "text", text: token.text.slice(safeOffset), ...token.style ? { style: token.style } : {} }
4328
4474
  ];
4329
4475
  }
4330
4476
  function insertMathTokenAtCaret(document2, fieldId, textTokenId, caretOffset, session, opening = "$", closing = "$", side = "arabic") {
@@ -5066,9 +5212,22 @@ function mathBodyLatex(token) {
5066
5212
  const rendered = renderMathNodeLatex(token.math);
5067
5213
  return stripDisplayMathDelimiters(rendered);
5068
5214
  }
5215
+ function styledTextLatex(text, style) {
5216
+ let output = text;
5217
+ if (style?.bold) {
5218
+ output = `\\textbf{${output}}`;
5219
+ }
5220
+ if (style?.italic) {
5221
+ output = `\\textit{${output}}`;
5222
+ }
5223
+ if (style?.underline) {
5224
+ output = `\\underline{${output}}`;
5225
+ }
5226
+ return output;
5227
+ }
5069
5228
  function tokenLatex(token) {
5070
5229
  if (token.kind === "text") {
5071
- return token.text;
5230
+ return styledTextLatex(token.text, token.style);
5072
5231
  }
5073
5232
  if (token.kind === "cite") {
5074
5233
  return citeTokenLatex(token.keys);
@@ -5092,7 +5251,9 @@ function inlineFieldLatex(field) {
5092
5251
  return field.tokens.map((token) => tokenLatex(token)).join("");
5093
5252
  }
5094
5253
  function textBlockLatex(block) {
5095
- const body = `${block.command}{${inlineFieldLatex(block.field)}}`;
5254
+ const content = inlineFieldLatex(block.field);
5255
+ const body = block.command === "\\paragraph" ? `\\par
5256
+ ${content}` : `${block.command}{${content}}`;
5096
5257
  if (block.command === "\\paragraph" && block.centered) {
5097
5258
  return `\\begin{center}
5098
5259
  ${body}
@@ -5344,7 +5505,7 @@ function mathTex(token, equationSide) {
5344
5505
  function previewInlines(field, islands, output, equationSide, document2, previewOptions) {
5345
5506
  return field.tokens.map((token) => {
5346
5507
  if (token.kind === "text") {
5347
- return { kind: "text", text: token.text };
5508
+ return { kind: "text", text: token.text, ...token.style ? { style: token.style } : {} };
5348
5509
  }
5349
5510
  if (token.kind === "cite") {
5350
5511
  return {
@@ -5625,6 +5786,7 @@ export {
5625
5786
  setDocument2References,
5626
5787
  stripDisplayMathDelimiters,
5627
5788
  toggleBlockInSelection,
5789
+ toggleTextTokenStyle,
5628
5790
  updateCiteTokenKeys,
5629
5791
  updateDocument2ImageMeta,
5630
5792
  updateDocument2ImageValue,