@scrider/formatter 1.10.5 → 1.10.7

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.
package/dist/index.d.cts CHANGED
@@ -32,8 +32,8 @@ interface TablePresentation {
32
32
  }
33
33
  /** Default table border (HTML / clipboard / PDF host should share). */
34
34
  declare const TABLE_BORDER_COLOR = "#e7e7e7";
35
- /** Header shade fill — matches editor light `--color-bg-hover` family. */
36
- declare const TABLE_HEADER_BG = "#f5f5f5";
35
+ /** Header shade fill — matches editor light `--color-bg-hover` (`#e9ecef`). */
36
+ declare const TABLE_HEADER_BG = "#e9ecef";
37
37
  /**
38
38
  * Zebra body-row fill — canon aligned with editor/demo light
39
39
  * `--color-table-zebra` (`#f6f8fa`, arch-set1 #12).
@@ -50,8 +50,12 @@ interface ResolvedTablePresentation {
50
50
  defaultCellAlign: TableCellAlign;
51
51
  }
52
52
  declare function resolveTablePresentation(presentation?: TablePresentation): ResolvedTablePresentation;
53
- /** Match CSS `tr:nth-child(even) td` when header rows precede body in `<table>`. */
54
- declare function isZebraBodyRow(headerRowCount: number, bodyRowIndex: number): boolean;
53
+ /**
54
+ * Match editor/CSS zebra on `tbody tr:nth-child(even) td`.
55
+ * First body row stays light; 2nd, 4th, … get {@link TABLE_ZEBRA_BG}.
56
+ * `headerRowCount` is unused (thead/tbody split) — kept for call-site stability.
57
+ */
58
+ declare function isZebraBodyRow(_headerRowCount: number, bodyRowIndex: number): boolean;
55
59
 
56
60
  /**
57
61
  * Document-level metadata (Scrider format extension).
package/dist/index.d.ts CHANGED
@@ -32,8 +32,8 @@ interface TablePresentation {
32
32
  }
33
33
  /** Default table border (HTML / clipboard / PDF host should share). */
34
34
  declare const TABLE_BORDER_COLOR = "#e7e7e7";
35
- /** Header shade fill — matches editor light `--color-bg-hover` family. */
36
- declare const TABLE_HEADER_BG = "#f5f5f5";
35
+ /** Header shade fill — matches editor light `--color-bg-hover` (`#e9ecef`). */
36
+ declare const TABLE_HEADER_BG = "#e9ecef";
37
37
  /**
38
38
  * Zebra body-row fill — canon aligned with editor/demo light
39
39
  * `--color-table-zebra` (`#f6f8fa`, arch-set1 #12).
@@ -50,8 +50,12 @@ interface ResolvedTablePresentation {
50
50
  defaultCellAlign: TableCellAlign;
51
51
  }
52
52
  declare function resolveTablePresentation(presentation?: TablePresentation): ResolvedTablePresentation;
53
- /** Match CSS `tr:nth-child(even) td` when header rows precede body in `<table>`. */
54
- declare function isZebraBodyRow(headerRowCount: number, bodyRowIndex: number): boolean;
53
+ /**
54
+ * Match editor/CSS zebra on `tbody tr:nth-child(even) td`.
55
+ * First body row stays light; 2nd, 4th, … get {@link TABLE_ZEBRA_BG}.
56
+ * `headerRowCount` is unused (thead/tbody split) — kept for call-site stability.
57
+ */
58
+ declare function isZebraBodyRow(_headerRowCount: number, bodyRowIndex: number): boolean;
55
59
 
56
60
  /**
57
61
  * Document-level metadata (Scrider format extension).
package/dist/index.js CHANGED
@@ -2597,6 +2597,10 @@ var imageFormat = {
2597
2597
  if (element.tagName.toLowerCase() !== "img") return null;
2598
2598
  const src = element.getAttribute("src");
2599
2599
  if (!src) return null;
2600
+ const style = element.getAttribute("style") || "";
2601
+ if (/(?:^|;)\s*opacity\s*:\s*0(?:\.0+)?\s*(?:;|$)/i.test(style)) {
2602
+ return null;
2603
+ }
2600
2604
  const attrs = {};
2601
2605
  const alt = element.getAttribute("alt");
2602
2606
  const width = element.getAttribute("width");
@@ -3214,7 +3218,7 @@ function headingPolicyStyleParts(tag, blockAttributes, policy) {
3214
3218
 
3215
3219
  // src/conversion/html/table-presentation.ts
3216
3220
  var TABLE_BORDER_COLOR = "#e7e7e7";
3217
- var TABLE_HEADER_BG = "#f5f5f5";
3221
+ var TABLE_HEADER_BG = "#e9ecef";
3218
3222
  var TABLE_ZEBRA_BG = "#f6f8fa";
3219
3223
  var DEFAULT_BORDER_COLOR = TABLE_BORDER_COLOR;
3220
3224
  var DEFAULT_HEADER_BG = TABLE_HEADER_BG;
@@ -3232,8 +3236,8 @@ function resolveTablePresentation(presentation) {
3232
3236
  defaultCellAlign: presentation?.defaultCellAlign ?? "left"
3233
3237
  };
3234
3238
  }
3235
- function isZebraBodyRow(headerRowCount, bodyRowIndex) {
3236
- return (headerRowCount + bodyRowIndex + 1) % 2 === 0;
3239
+ function isZebraBodyRow(_headerRowCount, bodyRowIndex) {
3240
+ return bodyRowIndex % 2 === 1;
3237
3241
  }
3238
3242
  function isTableCellAlign(value) {
3239
3243
  return value === "left" || value === "center" || value === "right";
@@ -3870,15 +3874,6 @@ function renderInlineText(text, attributes) {
3870
3874
  if (!text) return "";
3871
3875
  let html = escapeHtml(text);
3872
3876
  if (!attributes) return html;
3873
- const styles = [];
3874
- for (const [format, cssProperty] of Object.entries(INLINE_STYLE_FORMATS)) {
3875
- if (format in attributes) {
3876
- styles.push(`${cssProperty}: ${String(attributes[format])}`);
3877
- }
3878
- }
3879
- if (styles.length > 0) {
3880
- html = `<span style="${styles.join("; ")}">${html}</span>`;
3881
- }
3882
3877
  for (let i = INLINE_FORMAT_ORDER.length - 1; i >= 0; i--) {
3883
3878
  const format = INLINE_FORMAT_ORDER[i];
3884
3879
  if (!format) continue;
@@ -3892,6 +3887,15 @@ function renderInlineText(text, attributes) {
3892
3887
  html = `<${tag}>${html}</${tag}>`;
3893
3888
  }
3894
3889
  }
3890
+ const styles = [];
3891
+ for (const [format, cssProperty] of Object.entries(INLINE_STYLE_FORMATS)) {
3892
+ if (format in attributes) {
3893
+ styles.push(`${cssProperty}: ${String(attributes[format])}`);
3894
+ }
3895
+ }
3896
+ if (styles.length > 0) {
3897
+ html = `<span style="${styles.join("; ")}">${html}</span>`;
3898
+ }
3895
3899
  return html;
3896
3900
  }
3897
3901
  function renderEmbed(value, attributes, renderers, blockHandlers, options) {
@@ -3937,6 +3941,44 @@ function renderEmbed(value, attributes, renderers, blockHandlers, options) {
3937
3941
 
3938
3942
  // src/conversion/html/html-to-delta.ts
3939
3943
  import { Delta as Delta8 } from "@scrider/delta";
3944
+ function isCssWideKeyword(value) {
3945
+ return /^(inherit|initial|unset|revert|revert-layer)$/i.test(value.trim());
3946
+ }
3947
+ function cleanPastedFontFamily(value) {
3948
+ if (value == null) return null;
3949
+ const trimmed = value.trim();
3950
+ if (!trimmed || isCssWideKeyword(trimmed)) return null;
3951
+ const first = trimmed.split(",").map((part) => part.trim().replace(/^["']|["']$/g, "")).find((part) => part && !isCssWideKeyword(part) && !part.startsWith("var("));
3952
+ if (!first) return null;
3953
+ const lower = first.toLowerCase();
3954
+ if (lower === "serif" || lower === "sans-serif" || lower === "monospace" || lower === "cursive" || lower === "fantasy" || lower === "system-ui") {
3955
+ return null;
3956
+ }
3957
+ return first;
3958
+ }
3959
+ function cleanPastedFontSize(value) {
3960
+ if (value == null) return null;
3961
+ const trimmed = value.trim();
3962
+ if (!trimmed || isCssWideKeyword(trimmed)) return null;
3963
+ if (/^(medium|smaller|larger|xx-small|x-small|small|large|x-large|xx-large)$/i.test(trimmed)) {
3964
+ return null;
3965
+ }
3966
+ const px = trimmed.match(/^([\d.]+)\s*px$/i);
3967
+ if (px?.[1]) {
3968
+ const pt = Math.round(Number(px[1]) * 72 / 96);
3969
+ return pt > 0 ? `${pt}pt` : null;
3970
+ }
3971
+ return trimmed;
3972
+ }
3973
+ function isSelectionBackgroundColor(value) {
3974
+ const m = value.trim().match(/^rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)/i);
3975
+ if (!m) return false;
3976
+ const r = Number(m[1]);
3977
+ const g = Number(m[2]);
3978
+ const b = Number(m[3]);
3979
+ if (![r, g, b].every((n) => Number.isFinite(n))) return false;
3980
+ return r >= 180 && g >= 180 && b >= 180 && b >= r - 5 && b >= g - 5 && b - Math.min(r, g) <= 40;
3981
+ }
3940
3982
  function htmlToDelta(html, options = {}) {
3941
3983
  const adapter = options.adapter ?? getAdapter();
3942
3984
  const normalizeWhitespace = options.normalizeWhitespace ?? true;
@@ -4285,20 +4327,30 @@ function htmlToDelta(html, options = {}) {
4285
4327
  flushText();
4286
4328
  const prevAttrs = { ...currentAttributes };
4287
4329
  const color = element.style?.color || element.style?.getPropertyValue?.("color");
4288
- if (color) {
4330
+ if (color && !isCssWideKeyword(color)) {
4289
4331
  currentAttributes.color = color;
4290
4332
  }
4291
4333
  const bg = element.style?.backgroundColor || element.style?.getPropertyValue?.("background-color");
4292
- if (bg) {
4334
+ if (bg && !isCssWideKeyword(bg) && !isSelectionBackgroundColor(bg)) {
4293
4335
  currentAttributes.background = bg;
4294
4336
  }
4295
4337
  const fontFamily = element.style?.fontFamily || element.style?.getPropertyValue?.("font-family");
4296
- if (fontFamily) {
4297
- currentAttributes.font = fontFamily.replace(/^["']|["']$/g, "");
4338
+ const cleanedFont = cleanPastedFontFamily(fontFamily);
4339
+ if (cleanedFont) {
4340
+ currentAttributes.font = cleanedFont;
4298
4341
  }
4299
4342
  const fontSize = element.style?.fontSize || element.style?.getPropertyValue?.("font-size");
4300
- if (fontSize) {
4301
- currentAttributes.size = fontSize;
4343
+ const cleanedSize = cleanPastedFontSize(fontSize);
4344
+ if (cleanedSize) {
4345
+ currentAttributes.size = cleanedSize;
4346
+ }
4347
+ const fontWeight = element.style?.fontWeight || element.style?.getPropertyValue?.("font-weight");
4348
+ if (fontWeight === "bold" || fontWeight === "bolder" || fontWeight != null && Number.parseInt(String(fontWeight), 10) >= 600) {
4349
+ currentAttributes.bold = true;
4350
+ }
4351
+ const fontStyle = element.style?.fontStyle || element.style?.getPropertyValue?.("font-style");
4352
+ if (fontStyle === "italic" || fontStyle === "oblique") {
4353
+ currentAttributes.italic = true;
4302
4354
  }
4303
4355
  processChildren(element);
4304
4356
  flushText();
@@ -4307,6 +4359,10 @@ function htmlToDelta(html, options = {}) {
4307
4359
  function processImageElement(element) {
4308
4360
  const src = element.getAttribute("src");
4309
4361
  if (!src) return;
4362
+ const style = element.getAttribute("style") || "";
4363
+ if (/(?:^|;)\s*opacity\s*:\s*0(?:\.0+)?\s*(?:;|$)/i.test(style)) {
4364
+ return;
4365
+ }
4310
4366
  const attrs = {};
4311
4367
  const alt = element.getAttribute("alt");
4312
4368
  const width = element.getAttribute("width");