Repository: maxGraph/maxGraph
Pull request #804: refactor!: replace FONT enum by FONT_STYLE_MASK value object
Author: login: tbouffard
Labels: name: refactor
description: Code refactoring
Created: 2025-05-03T20:00:29Z | Merged: 2025-05-04T09:31:56Z

BREAKING CHANGES: The `constants.FONT` enum has been removed and replaced by the `constants.FONT_STYLE_FLAG` value object.

## Notes

Covers #378



<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- **New Features**
  - None.

- **Refactor**
  - Replaced the font style enum with a constant object for font style flags throughout the application, updating all font style references including UI text styling and editor actions.
  - Improved internal handling of font style flags with enhanced bitmask matching supporting nullish values.

- **Documentation**
  - Updated changelog and code comments to reflect the new font style flag usage.

- **Tests**
  - Updated test cases to use the new font style flag constants.

- **Chores**
  - Increased the chunk size warning limit in the build configuration.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->


Diff:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5b8e1794cd..1e3e88f755 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -24,6 +24,7 @@ _**Note:** Yet to be released breaking changes appear here._
 - The `constants.NODETYPE` enum has been removed and replaced by the `constants.NODE_TYPE` value object.
   The former `DOCUMENTTYPE` enum member has been renamed to `DOCUMENT_TYPE`.
 - `constants.DIRECTION_MASK` is now read-only (types only).
+- The `constants.FONT` enum has been removed and replaced by the `constants.FONT_STYLE_FLAG` value object.
 
 ## 0.19.0
 
diff --git a/packages/core/__tests__/internal/utils.test.ts b/packages/core/__tests__/internal/utils.test.ts
index 53e3f306ba..fa88944dcb 100644
--- a/packages/core/__tests__/internal/utils.test.ts
+++ b/packages/core/__tests__/internal/utils.test.ts
@@ -15,20 +15,31 @@ limitations under the License.
 */
 
 import { describe, expect, test } from '@jest/globals';
-import { FONT } from '../../src/util/Constants';
+import { DIRECTION_MASK, FONT_STYLE_MASK } from '../../src/util/Constants';
 import { matchBinaryMask } from '../../src/internal/utils';
 
 describe('matchBinaryMask', () => {
   test('match self', () => {
-    expect(matchBinaryMask(FONT.STRIKETHROUGH, FONT.STRIKETHROUGH)).toBeTruthy();
+    expect(
+      matchBinaryMask(FONT_STYLE_MASK.STRIKETHROUGH, FONT_STYLE_MASK.STRIKETHROUGH)
+    ).toBeTruthy();
   });
   test('match', () => {
-    expect(matchBinaryMask(9465, FONT.BOLD)).toBeTruthy();
+    expect(matchBinaryMask(9465, FONT_STYLE_MASK.BOLD)).toBeTruthy();
+    expect(9465 & FONT_STYLE_MASK.BOLD).toBeTruthy(); // equivalent implementation returning a number (matchBinaryMask returns a boolean)
   });
   test('match another', () => {
-    expect(matchBinaryMask(19484, FONT.UNDERLINE)).toBeTruthy();
+    expect(matchBinaryMask(19484, FONT_STYLE_MASK.UNDERLINE)).toBeTruthy();
   });
   test('no match', () => {
-    expect(matchBinaryMask(46413, FONT.ITALIC)).toBeFalsy();
+    expect(matchBinaryMask(46413, FONT_STYLE_MASK.ITALIC)).toBeFalsy();
+    expect(46413 & FONT_STYLE_MASK.ITALIC).toBeFalsy(); // equivalent implementation returning a number (matchBinaryMask returns a boolean)
+  });
+
+  test.each([null, undefined])('no match when value is nullish: %s', (value) => {
+    expect(matchBinaryMask(value, DIRECTION_MASK.EAST)).toBeFalsy();
+  });
+  test.each([null, undefined])('match for zero when value is nullish: %s', (value) => {
+    expect(matchBinaryMask(value, 0)).toBeTruthy();
   });
 });
diff --git a/packages/core/__tests__/util/styleUtils.test.ts b/packages/core/__tests__/util/styleUtils.test.ts
index b4f85bb6a8..7b4a83696a 100644
--- a/packages/core/__tests__/util/styleUtils.test.ts
+++ b/packages/core/__tests__/util/styleUtils.test.ts
@@ -21,7 +21,7 @@ import {
   setCellStyleFlags,
   setCellStyles,
 } from '../../src/util/styleUtils';
-import { FONT } from '../../src/util/Constants';
+import { FONT_STYLE_MASK } from '../../src/util/Constants';
 import { type CellStyle, BaseGraph } from '../../src';
 import { createGraphWithoutPlugins } from '../utils';
 
@@ -45,10 +45,14 @@ describe('parseCssNumber', () => {
 
 describe('setStyleFlag', () => {
   test('preserves other style properties', () => {
-    const style = { fontStyle: FONT.BOLD, fillColor: 'red', strokeColor: 'blue' };
-    setStyleFlag(style, 'fontStyle', FONT.ITALIC, true);
+    const style = {
+      fontStyle: FONT_STYLE_MASK.BOLD,
+      fillColor: 'red',
+      strokeColor: 'blue',
+    };
+    setStyleFlag(style, 'fontStyle', FONT_STYLE_MASK.ITALIC, true);
     expect(style).toEqual({
-      fontStyle: FONT.BOLD | FONT.ITALIC,
+      fontStyle: FONT_STYLE_MASK.BOLD | FONT_STYLE_MASK.ITALIC,
       fillColor: 'red',
       strokeColor: 'blue',
     });
@@ -56,68 +60,70 @@ describe('setStyleFlag', () => {
 
   test('multiple flags can be combined', () => {
     const style: CellStyle = {};
-    setStyleFlag(style, 'fontStyle', FONT.BOLD, true);
-    setStyleFlag(style, 'fontStyle', FONT.ITALIC, true);
-    setStyleFlag(style, 'fontStyle', FONT.UNDERLINE, true);
-    expect(style.fontStyle).toBe(FONT.BOLD | FONT.ITALIC | FONT.UNDERLINE);
+    setStyleFlag(style, 'fontStyle', FONT_STYLE_MASK.BOLD, true);
+    setStyleFlag(style, 'fontStyle', FONT_STYLE_MASK.ITALIC, true);
+    setStyleFlag(style, 'fontStyle', FONT_STYLE_MASK.UNDERLINE, true);
+    expect(style.fontStyle).toBe(
+      FONT_STYLE_MASK.BOLD | FONT_STYLE_MASK.ITALIC | FONT_STYLE_MASK.UNDERLINE
+    );
   });
 
   test('fontStyle undefined, set bold, no value', () => {
     const style: CellStyle = {};
-    setStyleFlag(style, 'fontStyle', FONT.BOLD);
+    setStyleFlag(style, 'fontStyle', FONT_STYLE_MASK.BOLD);
     expect(style.fontStyle).toBe(1);
   });
   test('fontStyle undefined, set bold, value is false', () => {
     const style: CellStyle = {};
-    setStyleFlag(style, 'fontStyle', FONT.BOLD, false);
+    setStyleFlag(style, 'fontStyle', FONT_STYLE_MASK.BOLD, false);
     expect(style.fontStyle).toBe(0);
   });
   test('fontStyle undefined, set italic, value is false', () => {
     const style: CellStyle = {};
-    setStyleFlag(style, 'fontStyle', FONT.ITALIC, false);
+    setStyleFlag(style, 'fontStyle', FONT_STYLE_MASK.ITALIC, false);
     expect(style.fontStyle).toBe(0);
   });
   test('fontStyle undefined, set underline, value is true', () => {
     const style: CellStyle = {};
-    setStyleFlag(style, 'fontStyle', FONT.UNDERLINE, true);
+    setStyleFlag(style, 'fontStyle', FONT_STYLE_MASK.UNDERLINE, true);
     expect(style.fontStyle).toBe(4);
   });
   test('fontStyle undefined, set strike-through, value is true', () => {
     const style: CellStyle = {};
-    setStyleFlag(style, 'fontStyle', FONT.STRIKETHROUGH, true);
+    setStyleFlag(style, 'fontStyle', FONT_STYLE_MASK.STRIKETHROUGH, true);
     expect(style.fontStyle).toBe(8);
   });
 
   test('fontStyle set without bold, toggle bold', () => {
     const style: CellStyle = { fontStyle: 2 };
-    setStyleFlag(style, 'fontStyle', FONT.BOLD);
+    setStyleFlag(style, 'fontStyle', FONT_STYLE_MASK.BOLD);
     expect(style.fontStyle).toBe(3);
   });
   test('fontStyle set with bold, toggle bold', () => {
     const style: CellStyle = { fontStyle: 9 };
-    setStyleFlag(style, 'fontStyle', FONT.BOLD);
+    setStyleFlag(style, 'fontStyle', FONT_STYLE_MASK.BOLD);
     expect(style.fontStyle).toBe(8);
   });
 
   test('fontStyle set without strike-through, set strike-through', () => {
     const style: CellStyle = { fontStyle: 7 };
-    setStyleFlag(style, 'fontStyle', FONT.STRIKETHROUGH, true);
+    setStyleFlag(style, 'fontStyle', FONT_STYLE_MASK.STRIKETHROUGH, true);
     expect(style.fontStyle).toBe(15);
   });
   test('fontStyle set without strike-through, unset strike-through', () => {
     const style: CellStyle = { fontStyle: 7 };
-    setStyleFlag(style, 'fontStyle', FONT.STRIKETHROUGH, false);
+    setStyleFlag(style, 'fontStyle', FONT_STYLE_MASK.STRIKETHROUGH, false);
     expect(style.fontStyle).toBe(7);
   });
 
   test('fontStyle set with underline, set underline', () => {
     const style: CellStyle = { fontStyle: 6 };
-    setStyleFlag(style, 'fontStyle', FONT.UNDERLINE, true);
+    setStyleFlag(style, 'fontStyle', FONT_STYLE_MASK.UNDERLINE, true);
     expect(style.fontStyle).toBe(6);
   });
   test('fontStyle set with underline, unset underline', () => {
     const style: CellStyle = { fontStyle: 6 };
-    setStyleFlag(style, 'fontStyle', FONT.UNDERLINE, false);
+    setStyleFlag(style, 'fontStyle', FONT_STYLE_MASK.UNDERLINE, false);
     expect(style.fontStyle).toBe(2);
   });
 });
@@ -137,7 +143,13 @@ test.each([
   });
   expect(cell.style).toStrictEqual(style);
 
-  setCellStyleFlags(graph.getDataModel(), [cell], 'fontStyle', FONT.BOLD, true);
+  setCellStyleFlags(
+    graph.getDataModel(),
+    [cell],
+    'fontStyle',
+    FONT_STYLE_MASK.BOLD,
+    true
+  );
   expect(cell.style.fontStyle).toBe(5);
   expect(graph.getView().getState(cell)?.style?.fontStyle).toBe(5);
 });
diff --git a/packages/core/__tests__/view/mixins/CellMixin.test.ts b/packages/core/__tests__/view/mixins/CellMixin.test.ts
index 24b20a0771..758fc91f0e 100644
--- a/packages/core/__tests__/view/mixins/CellMixin.test.ts
+++ b/packages/core/__tests__/view/mixins/CellMixin.test.ts
@@ -17,7 +17,7 @@ limitations under the License.
 import { describe, expect, test } from '@jest/globals';
 import { createCellWithStyle, createGraphWithoutPlugins } from '../../utils';
 import { Cell, type CellStyle } from '../../../src';
-import { FONT } from '../../../src/util/Constants';
+import { FONT_STYLE_MASK } from '../../../src/util/Constants';
 
 test('setCellStyles on vertex', () => {
   const graph = createGraphWithoutPlugins();
@@ -50,7 +50,7 @@ test('setCellStyleFlags on vertex', () => {
   });
   expect(cell.style).toStrictEqual(style);
 
-  graph.setCellStyleFlags('fontStyle', FONT.UNDERLINE, null, [cell]);
+  graph.setCellStyleFlags('fontStyle', FONT_STYLE_MASK.UNDERLINE, null, [cell]);
   expect(cell.style.fontStyle).toBe(7);
   expect(graph.getView().getState(cell)?.style?.fontStyle).toBe(7);
 });
diff --git a/packages/core/src/editor/Editor.ts b/packages/core/src/editor/Editor.ts
index 36ff2e31b9..18f38d0cd3 100644
--- a/packages/core/src/editor/Editor.ts
+++ b/packages/core/src/editor/Editor.ts
@@ -33,7 +33,7 @@ import MaxForm from '../gui/MaxForm';
 import Outline from '../view/other/Outline';
 import Cell from '../view/cell/Cell';
 import Geometry from '../view/geometry/Geometry';
-import { FONT } from '../util/Constants';
+import { FONT_STYLE_MASK } from '../util/Constants';
 import type { AbstractGraph } from '../view/AbstractGraph';
 import { Graph } from '../view/Graph';
 import SwimlaneManager from '../view/layout/SwimlaneManager';
@@ -1140,19 +1140,19 @@ export class Editor extends EventSource {
 
     this.addAction('bold', (editor: Editor) => {
       if (editor.graph.isEnabled()) {
-        editor.graph.toggleCellStyleFlags('fontStyle', FONT.BOLD);
+        editor.graph.toggleCellStyleFlags('fontStyle', FONT_STYLE_MASK.BOLD);
       }
     });
 
     this.addAction('italic', (editor: Editor) => {
       if (editor.graph.isEnabled()) {
-        editor.graph.toggleCellStyleFlags('fontStyle', FONT.ITALIC);
+        editor.graph.toggleCellStyleFlags('fontStyle', FONT_STYLE_MASK.ITALIC);
       }
     });
 
     this.addAction('underline', (editor: Editor) => {
       if (editor.graph.isEnabled()) {
-        editor.graph.toggleCellStyleFlags('fontStyle', FONT.UNDERLINE);
+        editor.graph.toggleCellStyleFlags('fontStyle', FONT_STYLE_MASK.UNDERLINE);
       }
     });
 
diff --git a/packages/core/src/internal/utils.ts b/packages/core/src/internal/utils.ts
index d06937877e..73267a8c94 100644
--- a/packages/core/src/internal/utils.ts
+++ b/packages/core/src/internal/utils.ts
@@ -65,6 +65,6 @@ export const mixInto = (dest: any) => (mixin: any) => {
  * @returns `true` if the value matches the binary mask.
  * @private Subject to change prior being part of the public API.
  */
-export const matchBinaryMask = (value: number, mask: number) => {
-  return (value & mask) === mask;
+export const matchBinaryMask = (value: number | undefined | null, mask: number) => {
+  return (value! & mask) === mask;
 };
diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts
index e3195b9ac9..0df7acf370 100644
--- a/packages/core/src/types.ts
+++ b/packages/core/src/types.ts
@@ -334,8 +334,7 @@ export type CellStateStyle = {
    */
   fontSize?: number;
   /**
-   * Values may be any logical AND (sum) of the {@link FONT}. For instance, FONT.BOLD,
-   * FONT.ITALIC, ...
+   * Values may be any logical AND (sum) of the {@link FONT_STYLE_FLAG}. For instance, FONT_STYLE_FLAG.BOLD, FONT_STYLE_FLAG.ITALIC, ...
    */
   fontStyle?: number;
   /**
diff --git a/packages/core/src/util/Constants.ts b/packages/core/src/util/Constants.ts
index 3c8f0da9f2..65c25b694c 100644
--- a/packages/core/src/util/Constants.ts
+++ b/packages/core/src/util/Constants.ts
@@ -399,16 +399,16 @@ export const PAGE_FORMAT_LETTER_LANDSCAPE = [0, 0, 1100, 850];
  */
 export const NONE = 'none';
 
-export enum FONT {
+export const FONT_STYLE_MASK = {
   /** for bold fonts. */
-  BOLD = 1,
+  BOLD: 1,
   /** for italic fonts. */
-  ITALIC = 2,
+  ITALIC: 2,
   /** for underlined fonts. */
-  UNDERLINE = 4,
+  UNDERLINE: 4,
   /** for strikethrough fonts. */
-  STRIKETHROUGH = 8,
-}
+  STRIKETHROUGH: 8,
+} as const;
 
 /**
  * Bitwise mask for all directions.
diff --git a/packages/core/src/util/styleUtils.ts b/packages/core/src/util/styleUtils.ts
index 28a5bfd4e1..297f97e1de 100644
--- a/packages/core/src/util/styleUtils.ts
+++ b/packages/core/src/util/styleUtils.ts
@@ -17,7 +17,12 @@ limitations under the License.
 */
 
 import Client from '../Client';
-import { DEFAULT_FONTFAMILY, DEFAULT_FONTSIZE, FONT, LINE_HEIGHT } from './Constants';
+import {
+  DEFAULT_FONTFAMILY,
+  DEFAULT_FONTSIZE,
+  FONT_STYLE_MASK,
+  LINE_HEIGHT,
+} from './Constants';
 import Point from '../view/geometry/Point';
 import Dictionary from './Dictionary';
 import CellPath from '../view/cell/CellPath';
@@ -343,7 +348,7 @@ export const setCellStyles = (
  * setCellStyleFlags(graph.model,
  *       cells,
  *       'fontStyle',
- *       constants.FONT.BOLD);
+ *       constants.FONT_STYLE_FLAG.BOLD);
  * ```
  *
  * @param model {@link GraphDataModel} that contains the cells.
@@ -451,12 +456,14 @@ export const getSizeForString = (
 
   // Sets the font style
   if (fontStyle !== null) {
-    matchBinaryMask(fontStyle, FONT.BOLD) && (div.style.fontWeight = 'bold');
-    matchBinaryMask(fontStyle, FONT.ITALIC) && (div.style.fontStyle = 'italic');
+    matchBinaryMask(fontStyle, FONT_STYLE_MASK.BOLD) && (div.style.fontWeight = 'bold');
+    matchBinaryMask(fontStyle, FONT_STYLE_MASK.ITALIC) &&
+      (div.style.fontStyle = 'italic');
 
     const txtDecor = [];
-    matchBinaryMask(fontStyle, FONT.UNDERLINE) && txtDecor.push('underline');
-    matchBinaryMask(fontStyle, FONT.STRIKETHROUGH) && txtDecor.push('line-through');
+    matchBinaryMask(fontStyle, FONT_STYLE_MASK.UNDERLINE) && txtDecor.push('underline');
+    matchBinaryMask(fontStyle, FONT_STYLE_MASK.STRIKETHROUGH) &&
+      txtDecor.push('line-through');
     txtDecor.length > 0 && (div.style.textDecoration = txtDecor.join(' '));
   }
 
diff --git a/packages/core/src/view/canvas/SvgCanvas2D.ts b/packages/core/src/view/canvas/SvgCanvas2D.ts
index 1413c3976e..34e85ef221 100644
--- a/packages/core/src/view/canvas/SvgCanvas2D.ts
+++ b/packages/core/src/view/canvas/SvgCanvas2D.ts
@@ -24,7 +24,7 @@ import {
   ABSOLUTE_LINE_HEIGHT,
   DEFAULT_FONTFAMILY,
   DEFAULT_FONTSIZE,
-  FONT,
+  FONT_STYLE_MASK,
   LINE_HEIGHT,
   NONE,
   NS_SVG,
@@ -472,12 +472,15 @@ class SvgCanvas2D extends AbstractCanvas2D {
       // Text-anchor start is default in SVG
       anchor !== 'start' && alt.setAttribute('text-anchor', anchor);
       const fontStyle = s.fontStyle;
-      matchBinaryMask(fontStyle, FONT.BOLD) && alt.setAttribute('font-weight', 'bold');
-      matchBinaryMask(fontStyle, FONT.ITALIC) && alt.setAttribute('font-style', 'italic');
+      matchBinaryMask(fontStyle, FONT_STYLE_MASK.BOLD) &&
+        alt.setAttribute('font-weight', 'bold');
+      matchBinaryMask(fontStyle, FONT_STYLE_MASK.ITALIC) &&
+        alt.setAttribute('font-style', 'italic');
 
       const txtDecor = [];
-      matchBinaryMask(fontStyle, FONT.UNDERLINE) && txtDecor.push('underline');
-      matchBinaryMask(fontStyle, FONT.STRIKETHROUGH) && txtDecor.push('line-through');
+      matchBinaryMask(fontStyle, FONT_STYLE_MASK.UNDERLINE) && txtDecor.push('underline');
+      matchBinaryMask(fontStyle, FONT_STYLE_MASK.STRIKETHROUGH) &&
+        txtDecor.push('line-through');
       txtDecor.length > 0 && alt.setAttribute('text-decoration', txtDecor.join(' '));
 
       write(alt, <string>text);
@@ -1355,12 +1358,13 @@ class SvgCanvas2D extends AbstractCanvas2D {
       }; `;
 
     const fontStyle = s.fontStyle;
-    matchBinaryMask(fontStyle, FONT.BOLD) && (css += 'font-weight: bold; ');
-    matchBinaryMask(fontStyle, FONT.ITALIC) && (css += 'font-style: italic; ');
+    matchBinaryMask(fontStyle, FONT_STYLE_MASK.BOLD) && (css += 'font-weight: bold; ');
+    matchBinaryMask(fontStyle, FONT_STYLE_MASK.ITALIC) && (css += 'font-style: italic; ');
 
     const txtDecor = [];
-    matchBinaryMask(fontStyle, FONT.UNDERLINE) && txtDecor.push('underline');
-    matchBinaryMask(fontStyle, FONT.STRIKETHROUGH) && txtDecor.push('line-through');
+    matchBinaryMask(fontStyle, FONT_STYLE_MASK.UNDERLINE) && txtDecor.push('underline');
+    matchBinaryMask(fontStyle, FONT_STYLE_MASK.STRIKETHROUGH) &&
+      txtDecor.push('line-through');
     txtDecor.length > 0 && (css += `text-decoration: ${txtDecor.join(' ')}; `);
 
     return css;
@@ -1646,12 +1650,15 @@ class SvgCanvas2D extends AbstractCanvas2D {
     }
 
     const fontStyle = s.fontStyle;
-    matchBinaryMask(fontStyle, FONT.BOLD) && node.setAttribute('font-weight', 'bold');
-    matchBinaryMask(fontStyle, FONT.ITALIC) && node.setAttribute('font-style', 'italic');
+    matchBinaryMask(fontStyle, FONT_STYLE_MASK.BOLD) &&
+      node.setAttribute('font-weight', 'bold');
+    matchBinaryMask(fontStyle, FONT_STYLE_MASK.ITALIC) &&
+      node.setAttribute('font-style', 'italic');
 
     const txtDecor = [];
-    matchBinaryMask(fontStyle, FONT.UNDERLINE) && txtDecor.push('underline');
-    matchBinaryMask(fontStyle, FONT.STRIKETHROUGH) && txtDecor.push('line-through');
+    matchBinaryMask(fontStyle, FONT_STYLE_MASK.UNDERLINE) && txtDecor.push('underline');
+    matchBinaryMask(fontStyle, FONT_STYLE_MASK.STRIKETHROUGH) &&
+      txtDecor.push('line-through');
     txtDecor.length > 0 && node.setAttribute('text-decoration', txtDecor.join(' '));
   }
 
@@ -1720,8 +1727,10 @@ class SvgCanvas2D extends AbstractCanvas2D {
         div.style.visibility = 'hidden';
         div.style.display = 'inline-block';
 
-        matchBinaryMask(s.fontStyle, FONT.BOLD) && (div.style.fontWeight = 'bold');
-        matchBinaryMask(s.fontStyle, FONT.ITALIC) && (div.style.fontStyle = 'italic');
+        matchBinaryMask(s.fontStyle, FONT_STYLE_MASK.BOLD) &&
+          (div.style.fontWeight = 'bold');
+        matchBinaryMask(s.fontStyle, FONT_STYLE_MASK.ITALIC) &&
+          (div.style.fontStyle = 'italic');
 
         str = htmlEntities(str, false);
         div.innerHTML = str.replace(/\n/g, '<br/>');
diff --git a/packages/core/src/view/geometry/node/TextShape.ts b/packages/core/src/view/geometry/node/TextShape.ts
index c56da9a661..4c1054ef90 100644
--- a/packages/core/src/view/geometry/node/TextShape.ts
+++ b/packages/core/src/view/geometry/node/TextShape.ts
@@ -23,7 +23,7 @@ import {
   DEFAULT_FONTSIZE,
   DEFAULT_FONTSTYLE,
   DEFAULT_TEXT_DIRECTION,
-  FONT,
+  FONT_STYLE_MASK,
   NONE,
   WORD_WRAP,
   LINE_HEIGHT,
@@ -594,12 +594,16 @@ class TextShape extends Shape {
         this.color
       }; line-height: ${lh}; pointer-events: ${this.pointerEvents ? 'all' : 'none'}; `;
 
-    matchBinaryMask(this.fontStyle, FONT.BOLD) && (css += 'font-weight: bold; ');
-    matchBinaryMask(this.fontStyle, FONT.ITALIC) && (css += 'font-style: italic; ');
+    matchBinaryMask(this.fontStyle, FONT_STYLE_MASK.BOLD) &&
+      (css += 'font-weight: bold; ');
+    matchBinaryMask(this.fontStyle, FONT_STYLE_MASK.ITALIC) &&
+      (css += 'font-style: italic; ');
 
     const txtDecor = [];
-    matchBinaryMask(this.fontStyle, FONT.UNDERLINE) && txtDecor.push('underline');
-    matchBinaryMask(this.fontStyle, FONT.STRIKETHROUGH) && txtDecor.push('line-through');
+    matchBinaryMask(this.fontStyle, FONT_STYLE_MASK.UNDERLINE) &&
+      txtDecor.push('underline');
+    matchBinaryMask(this.fontStyle, FONT_STYLE_MASK.STRIKETHROUGH) &&
+      txtDecor.push('line-through');
     txtDecor.length > 0 && (css += `text-decoration: ${txtDecor.join(' ')}; `);
 
     return css;
@@ -795,17 +799,19 @@ class TextShape extends Shape {
     style.verticalAlign = 'top';
     style.color = this.color;
 
-    matchBinaryMask(this.fontStyle, FONT.BOLD)
+    matchBinaryMask(this.fontStyle, FONT_STYLE_MASK.BOLD)
       ? (style.fontWeight = 'bold')
       : (style.fontWeight = '');
 
-    matchBinaryMask(this.fontStyle, FONT.ITALIC)
+    matchBinaryMask(this.fontStyle, FONT_STYLE_MASK.ITALIC)
       ? (style.fontStyle = 'italic')
       : (style.fontStyle = '');
 
     const txtDecor = [];
-    matchBinaryMask(this.fontStyle, FONT.UNDERLINE) && txtDecor.push('underline');
-    matchBinaryMask(this.fontStyle, FONT.STRIKETHROUGH) && txtDecor.push('line-through');
+    matchBinaryMask(this.fontStyle, FONT_STYLE_MASK.UNDERLINE) &&
+      txtDecor.push('underline');
+    matchBinaryMask(this.fontStyle, FONT_STYLE_MASK.STRIKETHROUGH) &&
+      txtDecor.push('line-through');
     txtDecor.length > 0 && (style.textDecoration = txtDecor.join(' '));
 
     if (this.align === 'center') {
diff --git a/packages/core/src/view/plugins/CellEditorHandler.ts b/packages/core/src/view/plugins/CellEditorHandler.ts
index cb9a8e4ade..2349408d4b 100644
--- a/packages/core/src/view/plugins/CellEditorHandler.ts
+++ b/packages/core/src/view/plugins/CellEditorHandler.ts
@@ -25,7 +25,7 @@ import {
   DEFAULT_FONTFAMILY,
   DEFAULT_FONTSIZE,
   DEFAULT_TEXT_DIRECTION,
-  FONT,
+  FONT_STYLE_MASK,
   LINE_HEIGHT,
   NONE,
   WORD_WRAP,
@@ -45,7 +45,7 @@ import {
   isShiftDown,
 } from '../../util/EventUtils';
 import EventSource from '../event/EventSource';
-
+import { matchBinaryMask } from '../../internal/utils';
 import type { AbstractGraph } from '../AbstractGraph';
 import type { AlignValue, GraphPlugin } from '../../types';
 import type TooltipHandler from './TooltipHandler';
@@ -691,47 +691,47 @@ class CellEditorHandler implements GraphPlugin {
 
     if (state) {
       // Configures the style of the in-place editor
-      const { scale } = this.graph.getView();
-      const size = state.style.fontSize ?? DEFAULT_FONTSIZE;
-      const family = state.style.fontFamily ?? DEFAULT_FONTFAMILY;
-      const color = state.style.fontColor ?? 'black';
-      const align = state.style.align ?? 'left';
-      const bold = (state.style.fontStyle || 0) & FONT.BOLD;
-      const italic = (state.style.fontStyle || 0) & FONT.ITALIC;
+      // Notice that the logic here is duplicated with styleUtils.getSizeForString
+      const stateStyle = state.style;
+      const size = stateStyle.fontSize ?? DEFAULT_FONTSIZE;
+      const family = stateStyle.fontFamily ?? DEFAULT_FONTFAMILY;
+      const color = stateStyle.fontColor ?? 'black';
+      const align = stateStyle.align ?? 'left';
+      const fontStyle = stateStyle.fontStyle ?? 0;
+      const bold = matchBinaryMask(fontStyle, FONT_STYLE_MASK.BOLD);
+      const italic = matchBinaryMask(fontStyle, FONT_STYLE_MASK.ITALIC);
 
       const txtDecor = [];
-      if ((state.style.fontStyle || 0) & FONT.UNDERLINE) {
-        txtDecor.push('underline');
-      }
-      if ((state.style.fontStyle || 0) & FONT.STRIKETHROUGH) {
+      matchBinaryMask(fontStyle, FONT_STYLE_MASK.UNDERLINE) && txtDecor.push('underline');
+      matchBinaryMask(fontStyle, FONT_STYLE_MASK.STRIKETHROUGH) &&
         txtDecor.push('line-through');
-      }
 
-      const textarea = <HTMLElement>this.textarea;
-      textarea.style.lineHeight = ABSOLUTE_LINE_HEIGHT
+      const textarea = this.textarea!; // code above ensure it is always set
+      const textareaStyle = textarea.style;
+      textareaStyle.lineHeight = ABSOLUTE_LINE_HEIGHT
         ? `${Math.round(size * LINE_HEIGHT)}px`
         : String(LINE_HEIGHT);
-      textarea.style.backgroundColor = this.getBackgroundColor(state) || 'transparent';
-      textarea.style.textDecoration = txtDecor.join(' ');
-      textarea.style.fontWeight = bold ? 'bold' : 'normal';
-      textarea.style.fontStyle = italic ? 'italic' : '';
-      textarea.style.fontSize = `${Math.round(size)}px`;
-      textarea.style.zIndex = String(this.zIndex);
-      textarea.style.fontFamily = family;
-      textarea.style.textAlign = align;
-      textarea.style.outline = 'none';
-      textarea.style.color = color;
-
-      let dir = (this.textDirection =
-        state.style.textDirection ?? DEFAULT_TEXT_DIRECTION);
-
+      textareaStyle.backgroundColor = this.getBackgroundColor(state) || 'transparent';
+      textareaStyle.textDecoration = txtDecor.join(' ');
+      textareaStyle.fontWeight = bold ? 'bold' : 'normal';
+      textareaStyle.fontStyle = italic ? 'italic' : '';
+      textareaStyle.fontSize = `${Math.round(size)}px`;
+      textareaStyle.zIndex = String(this.zIndex);
+      textareaStyle.fontFamily = family;
+      textareaStyle.textAlign = align;
+      textareaStyle.outline = 'none';
+      textareaStyle.color = color;
+
+      let dir = (this.textDirection = stateStyle.textDirection ?? DEFAULT_TEXT_DIRECTION);
+
+      const stateText = state.text;
       if (dir === 'auto') {
         if (
-          state.text !== null &&
-          state.text.dialect !== 'strictHtml' &&
-          !isNode(state.text.value)
+          stateText !== null &&
+          stateText.dialect !== 'strictHtml' &&
+          !isNode(stateText.value)
         ) {
-          dir = state.text.getAutoDirection();
+          dir = stateText.getAutoDirection();
         }
       }
 
@@ -763,13 +763,13 @@ class CellEditorHandler implements GraphPlugin {
       this.trigger = trigger;
       this.textNode = null;
 
-      if (state.text !== null && this.isHideLabel(state)) {
-        this.textNode = <SVGGElement>state.text.node;
+      if (stateText !== null && this.isHideLabel(state)) {
+        this.textNode = stateText.node;
         this.textNode.style.visibility = 'hidden';
       }
 
       // Workaround for initial offsetHeight not ready for heading in markup
-      if (this.autoSize && (state.cell.isEdge() || state.style.overflow !== 'fill')) {
+      if (this.autoSize && (state.cell.isEdge() || stateStyle.overflow !== 'fill')) {
         window.setTimeout(() => {
           this.resize();
         }, 0);
diff --git a/packages/html/stories/EdgeCurvedAndRounded.stories.ts b/packages/html/stories/EdgeCurvedAndRounded.stories.ts
index 694f8965d2..6424684d79 100644
--- a/packages/html/stories/EdgeCurvedAndRounded.stories.ts
+++ b/packages/html/stories/EdgeCurvedAndRounded.stories.ts
@@ -91,12 +91,12 @@ const Template = ({ label, ...args }: Record<string, string>) => {
   style.perimeter = Perimeter.EllipsePerimeter;
   style.fontColor = 'white';
   style.gradientColor = 'white';
-  style.fontStyle = constants.FONT.BOLD;
+  style.fontStyle = constants.FONT_STYLE_MASK.BOLD;
   style.fontSize = 14;
 
   // Makes all edges with a black, bold label
   style = graph.stylesheet.getDefaultEdgeStyle();
-  style.fontStyle = constants.FONT.BOLD;
+  style.fontStyle = constants.FONT_STYLE_MASK.BOLD;
   style.fontColor = 'black';
   style.strokeWidth = 2;
   if (args.style === 'curved') style.curved = true;
diff --git a/packages/html/stories/HoverStyle.stories.js b/packages/html/stories/HoverStyle.stories.js
index 3032f216f1..7bb2d7d369 100644
--- a/packages/html/stories/HoverStyle.stories.js
+++ b/packages/html/stories/HoverStyle.stories.js
@@ -45,7 +45,7 @@ const Template = ({ label, ...args }) => {
     // once it is set, whereas the above overrides the default value
     state.style.rounded = hover ? '1' : '0';
     state.style.strokeWidth = hover ? '4' : '1';
-    state.style.fontStyle = hover ? constants.FONT.BOLD : '0';
+    state.style.fontStyle = hover ? constants.FONT_STYLE_MASK.BOLD : '0';
   }
 
   // Changes fill color to red on mouseover
diff --git a/packages/html/stories/Merge.stories.ts b/packages/html/stories/Merge.stories.ts
index f456a612b7..0e35048036 100644
--- a/packages/html/stories/Merge.stories.ts
+++ b/packages/html/stories/Merge.stories.ts
@@ -71,13 +71,13 @@ const Template = ({ label, ...args }: Record<string, any>) => {
   style.perimeter = Perimeter.EllipsePerimeter;
   style.fontColor = 'white';
   style.gradientColor = 'white';
-  style.fontStyle = constants.FONT.BOLD;
+  style.fontStyle = constants.FONT_STYLE_MASK.BOLD;
   style.fontSize = 14;
   style.shadow = true;
 
   // Makes all edges with a black, bold label
   style = graph.stylesheet.getDefaultEdgeStyle();
-  style.fontStyle = constants.FONT.BOLD;
+  style.fontStyle = constants.FONT_STYLE_MASK.BOLD;
   style.fontColor = 'black';
   style.strokeWidth = 2;
 
diff --git a/packages/html/stories/SecondLabel.stories.js b/packages/html/stories/SecondLabel.stories.js
index ccdc020c88..0dd0e1733b 100644
--- a/packages/html/stories/SecondLabel.stories.js
+++ b/packages/html/stories/SecondLabel.stories.js
@@ -127,7 +127,7 @@ const Template = ({ label, ...args }) => {
         state.secondLabel.color = 'black';
         state.secondLabel.family = 'Verdana';
         state.secondLabel.size = 8;
-        state.secondLabel.fontStyle = constants.FONT.ITALIC;
+        state.secondLabel.fontStyle = constants.FONT_STYLE_MASK.ITALIC;
         state.secondLabel.background = 'yellow';
         state.secondLabel.border = 'black';
         state.secondLabel.valign = 'bottom';
diff --git a/packages/ts-example-selected-features/vite.config.js b/packages/ts-example-selected-features/vite.config.js
index 4cd6d0754e..f4bab01b25 100644
--- a/packages/ts-example-selected-features/vite.config.js
+++ b/packages/ts-example-selected-features/vite.config.js
@@ -27,7 +27,7 @@ export default defineConfig(({ mode }) => {
           },
         },
       },
-      chunkSizeWarningLimit: 377, // @maxgraph/core
+      chunkSizeWarningLimit: 378, // @maxgraph/core
     },
   };
 });


Reviews:
user: login: coderabbitai[bot]
body: **Actionable comments posted: 1**

<details>
<summary>🧹 Nitpick comments (2)</summary><blockquote>

<details>
<summary>packages/ts-example-selected-features/vite.config.js (1)</summary><blockquote>

`30-30`: **Verify the chunk size warning limit bump**  
The `chunkSizeWarningLimit` was incremented from 377 to 378. Please confirm this change reflects the actual bundle size increase for the `@maxgraph/core` chunk after your refactor. If this is intentional, consider adding a brief comment to document why the threshold was adjusted.

</blockquote></details>
<details>
<summary>packages/core/src/util/styleUtils.ts (1)</summary><blockquote>

`350-352`: **Update example usage in JSDoc snippet**  
The documentation example now demonstrates `constants.FONT_STYLE_FLAG.BOLD`. For clarity, consider showing the corresponding import in the snippet.

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 6d99b3aa46913a3ef135436ae0b3890679f2b109 and 98a03be5676dc33d6849027603729d119caacd73.

</details>

<details>
<summary>📒 Files selected for processing (16)</summary>

* `CHANGELOG.md` (1 hunks)
* `packages/core/__tests__/internal/utils.test.ts` (1 hunks)
* `packages/core/__tests__/util/styleUtils.test.ts` (3 hunks)
* `packages/core/__tests__/view/mixins/CellMixin.test.ts` (2 hunks)
* `packages/core/src/editor/Editor.ts` (2 hunks)
* `packages/core/src/types.ts` (1 hunks)
* `packages/core/src/util/Constants.ts` (1 hunks)
* `packages/core/src/util/styleUtils.ts` (3 hunks)
* `packages/core/src/view/canvas/SvgCanvas2D.ts` (5 hunks)
* `packages/core/src/view/geometry/node/TextShape.ts` (3 hunks)
* `packages/core/src/view/plugins/CellEditorHandler.ts` (2 hunks)
* `packages/html/stories/EdgeCurvedAndRounded.stories.ts` (1 hunks)
* `packages/html/stories/HoverStyle.stories.js` (1 hunks)
* `packages/html/stories/Merge.stories.ts` (1 hunks)
* `packages/html/stories/SecondLabel.stories.js` (1 hunks)
* `packages/ts-example-selected-features/vite.config.js` (1 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code Graph Analysis (5)</summary>

<details>
<summary>packages/core/__tests__/internal/utils.test.ts (2)</summary><blockquote>

<details>
<summary>packages/core/src/internal/utils.ts (1)</summary>

* `matchBinaryMask` (68-70)

</details>
<details>
<summary>packages/core/src/util/Constants.ts (1)</summary>

* `FONT_STYLE_FLAG` (402-411)

</details>

</blockquote></details>
<details>
<summary>packages/core/__tests__/util/styleUtils.test.ts (3)</summary><blockquote>

<details>
<summary>packages/core/src/util/Constants.ts (1)</summary>

* `FONT_STYLE_FLAG` (402-411)

</details>
<details>
<summary>packages/core/src/util/styleUtils.ts (2)</summary>

* `setStyleFlag` (391-412)
* `setCellStyleFlags` (360-380)

</details>
<details>
<summary>packages/core/src/view/mixins/CellsMixin.ts (1)</summary>

* `setCellStyleFlags` (349-361)

</details>

</blockquote></details>
<details>
<summary>packages/core/__tests__/view/mixins/CellMixin.test.ts (1)</summary><blockquote>

<details>
<summary>packages/core/src/util/Constants.ts (1)</summary>

* `FONT_STYLE_FLAG` (402-411)

</details>

</blockquote></details>
<details>
<summary>packages/core/src/view/plugins/CellEditorHandler.ts (2)</summary><blockquote>

<details>
<summary>packages/html/stashed/grapheditor/www/js/Graph.js (2)</summary>

* `bold` (9508-9510)
* `italic` (9511-9513)

</details>
<details>
<summary>packages/core/src/util/Constants.ts (1)</summary>

* `FONT_STYLE_FLAG` (402-411)

</details>

</blockquote></details>
<details>
<summary>packages/core/src/editor/Editor.ts (1)</summary><blockquote>

<details>
<summary>packages/core/src/util/Constants.ts (1)</summary>

* `FONT_STYLE_FLAG` (402-411)

</details>

</blockquote></details>

</details>

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (4)</summary>

* GitHub Check: build
* GitHub Check: build (windows-2022)
* GitHub Check: build (macos-14)
* GitHub Check: build (ubuntu-22.04)

</details>

<details>
<summary>🔇 Additional comments (34)</summary><blockquote>

<details>
<summary>packages/core/src/util/Constants.ts (1)</summary>

`402-411`: **Good implementation of the FONT_STYLE_FLAG value object.**

The conversion from an enum to a constant object with `as const` is a good approach. This provides better type safety while maintaining the same functionality and bit flag values (1, 2, 4, 8) for font styles.

</details>
<details>
<summary>packages/html/stories/SecondLabel.stories.js (1)</summary>

`130-130`: **Correctly updated to use FONT_STYLE_FLAG instead of FONT.**

The update from `constants.FONT.ITALIC` to `constants.FONT_STYLE_FLAG.ITALIC` preserves the original functionality while adapting to the new API.

</details>
<details>
<summary>packages/html/stories/HoverStyle.stories.js (1)</summary>

`48-48`: **Correctly updated to use FONT_STYLE_FLAG instead of FONT.**

The font style value has been properly updated to use the new `FONT_STYLE_FLAG.BOLD` constant, maintaining the same functionality in the hover style logic.

</details>
<details>
<summary>packages/html/stories/EdgeCurvedAndRounded.stories.ts (2)</summary>

`94-94`: **Correctly updated to use FONT_STYLE_FLAG instead of FONT.**

The vertex style's font style property has been properly updated to use the new constant object.

---

`99-99`: **Correctly updated to use FONT_STYLE_FLAG instead of FONT.**

The edge style's font style property has been properly updated to use the new constant object.

</details>
<details>
<summary>packages/html/stories/Merge.stories.ts (2)</summary>

`74-74`: **Replaced deprecated FONT enum with FONT_STYLE_FLAG for vertex style**  
Consistently updated to use the new bitmask constant.

---

`80-80`: **Replaced deprecated FONT enum with FONT_STYLE_FLAG for edge style**  
Consistently updated to use the new bitmask constant.

</details>
<details>
<summary>packages/core/__tests__/internal/utils.test.ts (5)</summary>

`18-18`: **Replaced FONT import with FONT_STYLE_FLAG**  
Import updated to the new value object in the test.

---

`23-25`: **Updated test to use FONT_STYLE_FLAG for STRIKETHROUGH**  
Assertion now matches the new bitmask constant.

---

`28-28`: **Updated test to use FONT_STYLE_FLAG for BOLD**  
Assertion now matches the new bitmask constant.

---

`31-31`: **Updated test to use FONT_STYLE_FLAG for UNDERLINE**  
Assertion now matches the new bitmask constant.

---

`34-34`: **Updated test to use FONT_STYLE_FLAG for ITALIC**  
Assertion now matches the new bitmask constant.

</details>
<details>
<summary>packages/core/__tests__/view/mixins/CellMixin.test.ts (2)</summary>

`20-20`: **Replaced FONT import with FONT_STYLE_FLAG**  
Import updated to the new value object in the mixin test.

---

`53-53`: **Updated fontStyle flag for setCellStyleFlags**  
Using `FONT_STYLE_FLAG.UNDERLINE` to compute the combined fontStyle correctly.

</details>
<details>
<summary>CHANGELOG.md (1)</summary>

`27-27`: **Document breaking change from FONT to FONT_STYLE_FLAG**  
CHANGELOG entry clearly describes the removal of the `FONT` enum and replacement by `FONT_STYLE_FLAG`.

</details>
<details>
<summary>packages/core/src/types.ts (1)</summary>

`337-338`: **Updated documentation to reference FONT_STYLE_FLAG**  
Comment now correctly links to the new constant object for font styles.

</details>
<details>
<summary>packages/core/src/editor/Editor.ts (2)</summary>

`36-36`: **Update import to use `FONT_STYLE_FLAG`**  
Import now references the new `FONT_STYLE_FLAG` constant object instead of the removed `FONT` enum. Confirm there are no lingering `FONT` imports in this module.

---

`1143-1156`: **Use new font style flags in toggle actions**  
The action handlers for 'bold', 'italic', and 'underline' have been updated to call `toggleCellStyleFlags` with `FONT_STYLE_FLAG.BOLD`, `.ITALIC`, and `.UNDERLINE`. This change preserves the existing behavior under the new flag constants.

</details>
<details>
<summary>packages/core/src/util/styleUtils.ts (1)</summary>

`20-25`: **Replace `FONT` enum import with `FONT_STYLE_FLAG`**  
The import correctly brings in `FONT_STYLE_FLAG` from `Constants.ts`. Ensure that all references to font style flags in this file now use `FONT_STYLE_FLAG`.

</details>
<details>
<summary>packages/core/src/view/canvas/SvgCanvas2D.ts (4)</summary>

`27-27`: **Update import to use `FONT_STYLE_FLAG`**  
The import statement has been updated from the removed `FONT` enum to the new `FONT_STYLE_FLAG` constant object.

---

`475-483`: **Use `FONT_STYLE_FLAG` in alternate text rendering**  
In `getAlternateText`, the bitmask checks have been switched to use the new flags (`FONT_STYLE_FLAG.BOLD`, etc.), and map to the same SVG attributes as before.

---

`1360-1368`: **Update font style flags in CSS generation**  
The `getTextCss` method now correctly toggles `font-weight` and `font-style` based on `FONT_STYLE_FLAG`. Behavior is unchanged.

---

`1653-1661`: **Apply new font style flags in `updateFont`**  
In the `updateFont` method, bitmask checks now use `FONT_STYLE_FLAG` to set SVG attributes (`font-weight`, `font-style`, `text-decoration`). This aligns with the refactor.

</details>
<details>
<summary>packages/core/src/view/plugins/CellEditorHandler.ts (3)</summary>

`28-28`: **Consistent enum replacement with object constant.**

The import has been properly changed from `FONT` enum to the new `FONT_STYLE_FLAG` value object, aligning with the PR objective.

---

`699-700`: **Properly replaced enum with value object for bold and italic flags.**

The bitwise font style checks have been correctly updated to use `FONT_STYLE_FLAG` instead of the previous `FONT` enum.

---

`703-707`: **Correctly updated underline and strikethrough font style flag checks.**

The bitwise operations have been properly migrated to use `FONT_STYLE_FLAG` while maintaining the same functionality.

</details>
<details>
<summary>packages/core/src/view/geometry/node/TextShape.ts (3)</summary>

`26-26`: **Import correctly updated to use FONT_STYLE_FLAG.**

The import has been properly changed to use the new constant object, consistent with the PR objective.

---

`597-606`: **Font style checks properly migrated in getTextCss method.**

The bitwise checks in the CSS generation have been correctly updated to use `FONT_STYLE_FLAG` with the `matchBinaryMask` helper function, maintaining the same functionality.

---

`802-815`: **Successfully updated font style checks in updateFont method.**

The font style flag checks in the `updateFont` method have been properly migrated to use `FONT_STYLE_FLAG` constants.

</details>
<details>
<summary>packages/core/__tests__/util/styleUtils.test.ts (5)</summary>

`24-24`: **Updated import to use new FONT_STYLE_FLAG constant.**

The import has been correctly changed to use the new constant object instead of the enum.

---

`48-54`: **Test case properly updated to use FONT_STYLE_FLAG.**

The test case has been appropriately updated to use the new `FONT_STYLE_FLAG` constant object for creating styles and setting flags.

---

`55-56`: **Expectation correctly updated to use FONT_STYLE_FLAG bitwise operations.**

The test's expected result has been properly updated to use the new constants with the same bitwise logic.

---

`63-68`: **Multiple flag combination tests correctly updated.**

All test cases that combine multiple font style flags have been properly migrated to use `FONT_STYLE_FLAG`, maintaining the same test logic and expectations.

---

`146-152`: **Successfully updated setCellStyleFlags test.**

The test for `setCellStyleFlags` function has been properly updated to use the new `FONT_STYLE_FLAG` constant.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-03T20:15:46Z
user: login: tbouffard
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-04T06:47:49Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-04T06:48:09Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 0**

<details>
<summary>🧹 Nitpick comments (1)</summary><blockquote>

<details>
<summary>packages/core/src/internal/utils.ts (1)</summary><blockquote>

`68-69`: **Consider handling nullish values more explicitly**

The function now accepts `undefined` and `null` values but uses a non-null assertion (`!`) which tells TypeScript to ignore the possibility of nullish values. While this works because of JavaScript's coercion (null/undefined become 0 in bitwise operations), it can be confusing and potentially risky.


Consider explicitly handling nullish values for better clarity and type safety:

```diff
- export const matchBinaryMask = (value: number | undefined | null, mask: number) => {
-   return (value! & mask) === mask;
- };
+ export const matchBinaryMask = (value: number | undefined | null, mask: number) => {
+   // Explicitly handle nullish values - they behave like 0 in bitwise operations
+   if (value === null || value === undefined) {
+     return mask === 0;
+   }
+   return (value & mask) === mask;
+ };
```

Or using nullish coalescing:

```diff
- export const matchBinaryMask = (value: number | undefined | null, mask: number) => {
-   return (value! & mask) === mask;
- };
+ export const matchBinaryMask = (value: number | undefined | null, mask: number) => {
+   return ((value ?? 0) & mask) === mask;
+ };
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 80f24e3452b0497aadaf347f391f0f8c90aab9c4 and 55f3f6a7f2d72135c0549fdb4c53f9bd547b65b5.

</details>

<details>
<summary>📒 Files selected for processing (14)</summary>

* `packages/core/__tests__/internal/utils.test.ts` (1 hunks)
* `packages/core/__tests__/util/styleUtils.test.ts` (3 hunks)
* `packages/core/__tests__/view/mixins/CellMixin.test.ts` (2 hunks)
* `packages/core/src/editor/Editor.ts` (2 hunks)
* `packages/core/src/internal/utils.ts` (1 hunks)
* `packages/core/src/util/Constants.ts` (1 hunks)
* `packages/core/src/util/styleUtils.ts` (3 hunks)
* `packages/core/src/view/canvas/SvgCanvas2D.ts` (5 hunks)
* `packages/core/src/view/geometry/node/TextShape.ts` (3 hunks)
* `packages/core/src/view/plugins/CellEditorHandler.ts` (4 hunks)
* `packages/html/stories/EdgeCurvedAndRounded.stories.ts` (1 hunks)
* `packages/html/stories/HoverStyle.stories.js` (1 hunks)
* `packages/html/stories/Merge.stories.ts` (1 hunks)
* `packages/html/stories/SecondLabel.stories.js` (1 hunks)

</details>

<details>
<summary>🚧 Files skipped from review as they are similar to previous changes (13)</summary>

* packages/html/stories/HoverStyle.stories.js
* packages/html/stories/Merge.stories.ts
* packages/core/__tests__/view/mixins/CellMixin.test.ts
* packages/html/stories/SecondLabel.stories.js
* packages/core/__tests__/internal/utils.test.ts
* packages/core/src/view/canvas/SvgCanvas2D.ts
* packages/core/src/util/styleUtils.ts
* packages/html/stories/EdgeCurvedAndRounded.stories.ts
* packages/core/src/editor/Editor.ts
* packages/core/__tests__/util/styleUtils.test.ts
* packages/core/src/util/Constants.ts
* packages/core/src/view/plugins/CellEditorHandler.ts
* packages/core/src/view/geometry/node/TextShape.ts

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (4)</summary>

* GitHub Check: build
* GitHub Check: build (windows-2022)
* GitHub Check: build (ubuntu-22.04)
* GitHub Check: build (macos-14)

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-04T07:42:37Z

Review comments:
diff_hunk: @@ -451,12 +456,14 @@ export const getSizeForString = (
 
   // Sets the font style
   if (fontStyle !== null) {
-    matchBinaryMask(fontStyle, FONT.BOLD) && (div.style.fontWeight = 'bold');
-    matchBinaryMask(fontStyle, FONT.ITALIC) && (div.style.fontWeight = 'italic');
+    matchBinaryMask(fontStyle, FONT_STYLE_FLAG.BOLD) && (div.style.fontWeight = 'bold');
+    matchBinaryMask(fontStyle, FONT_STYLE_FLAG.ITALIC) &&
+      (div.style.fontWeight = 'italic');
 
     const txtDecor = [];
-    matchBinaryMask(fontStyle, FONT.UNDERLINE) && txtDecor.push('underline');
-    matchBinaryMask(fontStyle, FONT.STRIKETHROUGH) && txtDecor.push('line-through');
+    matchBinaryMask(fontStyle, FONT_STYLE_FLAG.UNDERLINE) && txtDecor.push('underline');
+    matchBinaryMask(fontStyle, FONT_STYLE_FLAG.STRIKETHROUGH) &&
+      txtDecor.push('line-through');
     txtDecor.length > 0 && (div.style.textDecoration = txtDecor.join(' '));
path: packages/core/src/util/styleUtils.ts
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Fix incorrect CSS property for italic styling**  
The code mistakenly assigns `'italic'` to `div.style.fontWeight`. It should set the `fontStyle` property instead.  

Apply this diff:

```diff
- matchBinaryMask(fontStyle, FONT_STYLE_FLAG.ITALIC) &&
-   (div.style.fontWeight = 'italic');
+ matchBinaryMask(fontStyle, FONT_STYLE_FLAG.ITALIC) &&
+   (div.style.fontStyle = 'italic');
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
    matchBinaryMask(fontStyle, FONT_STYLE_FLAG.BOLD) && (div.style.fontWeight = 'bold');
    matchBinaryMask(fontStyle, FONT_STYLE_FLAG.ITALIC) &&
      (div.style.fontStyle = 'italic');

    const txtDecor = [];
    matchBinaryMask(fontStyle, FONT_STYLE_FLAG.UNDERLINE) && txtDecor.push('underline');
    matchBinaryMask(fontStyle, FONT_STYLE_FLAG.STRIKETHROUGH) &&
      txtDecor.push('line-through');
    txtDecor.length > 0 && (div.style.textDecoration = txtDecor.join(' '));
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated reply by CodeRabbit -->

✅ Addressed in commits e310943 to 80f24e3

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-03T20:15:45Z
author_association: NONE
diff_hunk: @@ -451,12 +456,14 @@ export const getSizeForString = (
 
   // Sets the font style
   if (fontStyle !== null) {
-    matchBinaryMask(fontStyle, FONT.BOLD) && (div.style.fontWeight = 'bold');
-    matchBinaryMask(fontStyle, FONT.ITALIC) && (div.style.fontWeight = 'italic');
+    matchBinaryMask(fontStyle, FONT_STYLE_FLAG.BOLD) && (div.style.fontWeight = 'bold');
+    matchBinaryMask(fontStyle, FONT_STYLE_FLAG.ITALIC) &&
+      (div.style.fontWeight = 'italic');
 
     const txtDecor = [];
-    matchBinaryMask(fontStyle, FONT.UNDERLINE) && txtDecor.push('underline');
-    matchBinaryMask(fontStyle, FONT.STRIKETHROUGH) && txtDecor.push('line-through');
+    matchBinaryMask(fontStyle, FONT_STYLE_FLAG.UNDERLINE) && txtDecor.push('underline');
+    matchBinaryMask(fontStyle, FONT_STYLE_FLAG.STRIKETHROUGH) &&
+      txtDecor.push('line-through');
     txtDecor.length > 0 && (div.style.textDecoration = txtDecor.join(' '));
path: packages/core/src/util/styleUtils.ts
user: login: tbouffard
body: Good catch, your analysis is right!
I have proposed a fix in #805 
created_at: 2025-05-04T06:47:49Z
author_association: MEMBER
diff_hunk: @@ -451,12 +456,14 @@ export const getSizeForString = (
 
   // Sets the font style
   if (fontStyle !== null) {
-    matchBinaryMask(fontStyle, FONT.BOLD) && (div.style.fontWeight = 'bold');
-    matchBinaryMask(fontStyle, FONT.ITALIC) && (div.style.fontWeight = 'italic');
+    matchBinaryMask(fontStyle, FONT_STYLE_FLAG.BOLD) && (div.style.fontWeight = 'bold');
+    matchBinaryMask(fontStyle, FONT_STYLE_FLAG.ITALIC) &&
+      (div.style.fontWeight = 'italic');
 
     const txtDecor = [];
-    matchBinaryMask(fontStyle, FONT.UNDERLINE) && txtDecor.push('underline');
-    matchBinaryMask(fontStyle, FONT.STRIKETHROUGH) && txtDecor.push('line-through');
+    matchBinaryMask(fontStyle, FONT_STYLE_FLAG.UNDERLINE) && txtDecor.push('underline');
+    matchBinaryMask(fontStyle, FONT_STYLE_FLAG.STRIKETHROUGH) &&
+      txtDecor.push('line-through');
     txtDecor.length > 0 && (div.style.textDecoration = txtDecor.join(' '));
path: packages/core/src/util/styleUtils.ts
user: login: coderabbitai[bot]
body: @tbouffard Thanks for confirming! I'm glad you've already submitted a fix in PR #805. It's great to see quick action on these types of issues to improve the codebase.

🐰✨


<!-- <agentComponentType>chat-answer</agentComponentType> -->

<!-- <rawResChunk><answer>@tbouffard Thanks for confirming! I'm glad you've already submitted a fix in PR #805. It's great to see quick action on these types of issues to improve the codebase.

🐰✨</answer></rawResChunk> -->

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-05-04T06:48:09Z
author_association: NONE

PR comments:
user: login: coderabbitai[bot]
created_at: 2025-05-03T20:00:35Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- walkthrough_start -->

## Walkthrough

The changes systematically replace the `FONT` TypeScript enum with a new constant object `FONT_STYLE_MASK` for representing font style flags (BOLD, ITALIC, UNDERLINE, STRIKETHROUGH) throughout the codebase. All references, imports, documentation comments, and test cases that previously used `FONT` are updated to use `FONT_STYLE_MASK`. This update affects core source files, utility functions, rendering logic, plugins, tests, documentation, and several example story files. Additionally, the changelog is updated to document this breaking change, the `matchBinaryMask` function is enhanced to accept nullish values, and a minor build configuration adjustment is made to the Vite config.

## Changes

| File(s)                                                                                  | Change Summary                                                                                                         |
|------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------|
| `packages/core/src/util/Constants.ts`                                                    | Replaced exported `FONT` enum with `FONT_STYLE_MASK` constant object with same flag values as readonly properties.    |
| `packages/core/src/editor/Editor.ts`,<br>`packages/core/src/util/styleUtils.ts`,<br>`packages/core/src/view/geometry/node/TextShape.ts`,<br>`packages/core/src/view/plugins/CellEditorHandler.ts`,<br>`packages/core/src/view/canvas/SvgCanvas2D.ts` | Updated imports and all references from `FONT` to `FONT_STYLE_MASK` for font style bitmask operations and checks.     |
| `packages/core/src/types.ts`                                                             | Updated documentation comment for `fontStyle` property to reference `FONT_STYLE_FLAG` instead of `FONT`.              |
| `packages/core/__tests__/internal/utils.test.ts`,<br>`packages/core/__tests__/util/styleUtils.test.ts`,<br>`packages/core/__tests__/view/mixins/CellMixin.test.ts` | Updated all test imports and usages from `FONT` to `FONT_STYLE_MASK`; added tests for `matchBinaryMask` with nullish values. Test logic unchanged. |
| `packages/html/stories/EdgeCurvedAndRounded.stories.ts`,<br>`packages/html/stories/HoverStyle.stories.js`,<br>`packages/html/stories/Merge.stories.ts`,<br>`packages/html/stories/SecondLabel.stories.js` | Updated all assignments of `fontStyle` from `constants.FONT` to `constants.FONT_STYLE_MASK`.                          |
| `CHANGELOG.md`                                                                          | Added breaking change entry documenting replacement of `FONT` enum with `FONT_STYLE_FLAG` value object.               |
| `packages/ts-example-selected-features/vite.config.js`                                  | Increased Vite chunk size warning limit from 377 to 378.                                                              |
| `packages/core/src/internal/utils.ts`                                                   | Modified `matchBinaryMask` function signature to accept `number | undefined | null` for `value`; adjusted internal handling accordingly. |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant User
    participant Editor
    participant Constants
    participant Renderer

    User->>Editor: Toggle font style (e.g., bold)
    Editor->>Constants: Access FONT_STYLE_MASK.BOLD
    Editor->>Renderer: Apply style using FONT_STYLE_MASK.BOLD
    Renderer->>Constants: Use FONT_STYLE_MASK to determine rendering
```

```mermaid
sequenceDiagram
    participant TestSuite
    participant Constants

    TestSuite->>Constants: Import FONT_STYLE_MASK
    TestSuite->>TestSuite: Use FONT_STYLE_MASK in assertions and style flag combinations
```

<!-- walkthrough_end -->
<!-- internal state start -->


<!-- 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 -->

<!-- internal state end -->
<!-- finishing_touch_checkbox_start -->

<details open="true">
<summary>✨ Finishing Touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate Docstrings

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

<details>
<summary>❤️ Share</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)
- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)

</details>

<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=maxGraph/maxGraph&utm_content=804):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Generate unit testing code for this file.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai generate unit testing code for this file.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and generate unit testing code.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

### Support

Need help? Create a ticket on our [support page](https://www.coderabbit.ai/contact-us/support) for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai generate sequence diagram` to generate a sequence diagram of the changes in this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: NONE
user: login: sonarqubecloud[bot]
created_at: 2025-05-04T07:41:37Z
body: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=maxGraph_maxGraph&pullRequest=804) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=maxGraph_maxGraph&pullRequest=804&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=maxGraph_maxGraph&pullRequest=804&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=maxGraph_maxGraph&pullRequest=804&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=maxGraph_maxGraph&pullRequest=804&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=maxGraph_maxGraph&pullRequest=804&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=maxGraph_maxGraph&pullRequest=804)


author_association: NONE