@mathscan/math-exercise-engine 1.2.3 → 1.2.5

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.ts CHANGED
@@ -6239,6 +6239,7 @@ export declare function compileWithAST(dsl: string): {
6239
6239
  backgroundColor?: string | undefined;
6240
6240
  }[];
6241
6241
  cols: number;
6242
+ borderColor: string;
6242
6243
  } | {
6243
6244
  type: "VerticalOperationBlock";
6244
6245
  items: ({
@@ -7402,6 +7403,7 @@ export declare const ContentBlockSchema: z.ZodUnion<[z.ZodObject<{
7402
7403
  }>, z.ZodObject<{
7403
7404
  type: z.ZodLiteral<"TableBlock">;
7404
7405
  cols: z.ZodNumber;
7406
+ borderColor: z.ZodDefault<z.ZodEffects<z.ZodString, string, string>>;
7405
7407
  rows: z.ZodArray<z.ZodObject<{
7406
7408
  type: z.ZodLiteral<"TableRow">;
7407
7409
  backgroundColor: z.ZodOptional<z.ZodString>;
@@ -8095,6 +8097,7 @@ export declare const ContentBlockSchema: z.ZodUnion<[z.ZodObject<{
8095
8097
  backgroundColor?: string | undefined;
8096
8098
  }[];
8097
8099
  cols: number;
8100
+ borderColor: string;
8098
8101
  }, {
8099
8102
  type: "TableBlock";
8100
8103
  rows: {
@@ -8186,6 +8189,7 @@ export declare const ContentBlockSchema: z.ZodUnion<[z.ZodObject<{
8186
8189
  backgroundColor?: string | undefined;
8187
8190
  }[];
8188
8191
  cols: number;
8192
+ borderColor?: string | undefined;
8189
8193
  }>, z.ZodObject<{
8190
8194
  type: z.ZodLiteral<"RadioOptions">;
8191
8195
  name: z.ZodString;
@@ -16281,6 +16285,9 @@ export declare function createVopRow(cells: VopCell[], op?: VopOperator): VopRow
16281
16285
 
16282
16286
  export declare function createVopSeparator(): VopSeparator;
16283
16287
 
16288
+ /** Default color used for table borders and internal separators. */
16289
+ export declare const DEFAULT_TABLE_BORDER_COLOR = "#7c6cf2";
16290
+
16284
16291
  export declare type DisplayInlineNode = z.infer<typeof DisplayInlineNodeSchema>;
16285
16292
 
16286
16293
  export declare const DisplayInlineNodeSchema: z.ZodUnion<[z.ZodObject<{
@@ -16995,6 +17002,7 @@ export declare const ExerciseDocumentSchema: z.ZodEffects<z.ZodObject<{
16995
17002
  }>, z.ZodObject<{
16996
17003
  type: z.ZodLiteral<"TableBlock">;
16997
17004
  cols: z.ZodNumber;
17005
+ borderColor: z.ZodDefault<z.ZodEffects<z.ZodString, string, string>>;
16998
17006
  rows: z.ZodArray<z.ZodObject<{
16999
17007
  type: z.ZodLiteral<"TableRow">;
17000
17008
  backgroundColor: z.ZodOptional<z.ZodString>;
@@ -17688,6 +17696,7 @@ export declare const ExerciseDocumentSchema: z.ZodEffects<z.ZodObject<{
17688
17696
  backgroundColor?: string | undefined;
17689
17697
  }[];
17690
17698
  cols: number;
17699
+ borderColor: string;
17691
17700
  }, {
17692
17701
  type: "TableBlock";
17693
17702
  rows: {
@@ -17779,6 +17788,7 @@ export declare const ExerciseDocumentSchema: z.ZodEffects<z.ZodObject<{
17779
17788
  backgroundColor?: string | undefined;
17780
17789
  }[];
17781
17790
  cols: number;
17791
+ borderColor?: string | undefined;
17782
17792
  }>, z.ZodObject<{
17783
17793
  type: z.ZodLiteral<"RadioOptions">;
17784
17794
  name: z.ZodString;
@@ -26123,6 +26133,7 @@ export declare const ExerciseDocumentSchema: z.ZodEffects<z.ZodObject<{
26123
26133
  backgroundColor?: string | undefined;
26124
26134
  }[];
26125
26135
  cols: number;
26136
+ borderColor: string;
26126
26137
  } | {
26127
26138
  type: "VerticalOperationBlock";
26128
26139
  items: ({
@@ -27119,6 +27130,7 @@ export declare const ExerciseDocumentSchema: z.ZodEffects<z.ZodObject<{
27119
27130
  backgroundColor?: string | undefined;
27120
27131
  }[];
27121
27132
  cols: number;
27133
+ borderColor?: string | undefined;
27122
27134
  } | {
27123
27135
  type: "VerticalOperationBlock";
27124
27136
  items: ({
@@ -28115,6 +28127,7 @@ export declare const ExerciseDocumentSchema: z.ZodEffects<z.ZodObject<{
28115
28127
  backgroundColor?: string | undefined;
28116
28128
  }[];
28117
28129
  cols: number;
28130
+ borderColor: string;
28118
28131
  } | {
28119
28132
  type: "VerticalOperationBlock";
28120
28133
  items: ({
@@ -29111,6 +29124,7 @@ export declare const ExerciseDocumentSchema: z.ZodEffects<z.ZodObject<{
29111
29124
  backgroundColor?: string | undefined;
29112
29125
  }[];
29113
29126
  cols: number;
29127
+ borderColor?: string | undefined;
29114
29128
  } | {
29115
29129
  type: "VerticalOperationBlock";
29116
29130
  items: ({
@@ -30402,6 +30416,8 @@ export declare interface ExerciseRendererProps {
30402
30416
  soundEnabled?: boolean;
30403
30417
  /** Maps sound events → URL or callback. Omitted events stay silent. */
30404
30418
  sounds?: SoundConfig;
30419
+ /** Overrides for validation state colors. */
30420
+ theme?: ExerciseTheme;
30405
30421
  }
30406
30422
 
30407
30423
  export declare interface ExerciseRendererRef {
@@ -30415,6 +30431,22 @@ export declare interface ExerciseRendererRef {
30415
30431
  revealAnswers: () => void;
30416
30432
  }
30417
30433
 
30434
+ /**
30435
+ * Validation state colors, applied as inline CSS variables on the renderer root.
30436
+ * Inline beats any stylesheet, so this is the reliable way for a host to recolor
30437
+ * per attempt (e.g. orange on the 1st wrong answer, red on the 2nd) — a
30438
+ * `--exercise-incorrect` set on a wrapper is shadowed by variables.css.
30439
+ * Background tints derive from these via color-mix. Omitted keys keep the defaults.
30440
+ * Values must be valid CSS colors: an invalid one does NOT fall back to the
30441
+ * default, it leaves the affected styles uncolored (dev builds log a warning).
30442
+ * Change the color when a retry starts, not in onSubmit — otherwise the result
30443
+ * still on screen recolors immediately.
30444
+ */
30445
+ export declare interface ExerciseTheme {
30446
+ correct?: string;
30447
+ incorrect?: string;
30448
+ }
30449
+
30418
30450
  export declare type ExpressionValidation = z.infer<typeof ExpressionValidationSchema>;
30419
30451
 
30420
30452
  /**
@@ -38876,6 +38908,7 @@ export declare type TableBlock = z.infer<typeof TableBlockSchema>;
38876
38908
  declare const TableBlockSchema: z.ZodObject<{
38877
38909
  type: z.ZodLiteral<"TableBlock">;
38878
38910
  cols: z.ZodNumber;
38911
+ borderColor: z.ZodDefault<z.ZodEffects<z.ZodString, string, string>>;
38879
38912
  rows: z.ZodArray<z.ZodObject<{
38880
38913
  type: z.ZodLiteral<"TableRow">;
38881
38914
  backgroundColor: z.ZodOptional<z.ZodString>;
@@ -39569,6 +39602,7 @@ declare const TableBlockSchema: z.ZodObject<{
39569
39602
  backgroundColor?: string | undefined;
39570
39603
  }[];
39571
39604
  cols: number;
39605
+ borderColor: string;
39572
39606
  }, {
39573
39607
  type: "TableBlock";
39574
39608
  rows: {
@@ -39660,6 +39694,7 @@ declare const TableBlockSchema: z.ZodObject<{
39660
39694
  backgroundColor?: string | undefined;
39661
39695
  }[];
39662
39696
  cols: number;
39697
+ borderColor?: string | undefined;
39663
39698
  }>;
39664
39699
 
39665
39700
  export declare type TableCell = z.infer<typeof TableCellSchema>;
@@ -40120,6 +40155,7 @@ export declare const TableItemSchema: z.ZodObject<{
40120
40155
  id: z.ZodString;
40121
40156
  type: z.ZodLiteral<"table">;
40122
40157
  cols: z.ZodNumber;
40158
+ borderColor: z.ZodDefault<z.ZodEffects<z.ZodString, string, string>>;
40123
40159
  rows: z.ZodArray<z.ZodObject<{
40124
40160
  id: z.ZodString;
40125
40161
  type: z.ZodLiteral<"table-row">;
@@ -40187,6 +40223,7 @@ export declare const TableItemSchema: z.ZodObject<{
40187
40223
  backgroundColor?: string | undefined;
40188
40224
  }[];
40189
40225
  cols: number;
40226
+ borderColor: string;
40190
40227
  id: string;
40191
40228
  }, {
40192
40229
  type: "table";
@@ -40205,6 +40242,7 @@ export declare const TableItemSchema: z.ZodObject<{
40205
40242
  }[];
40206
40243
  cols: number;
40207
40244
  id: string;
40245
+ borderColor?: string | undefined;
40208
40246
  }>;
40209
40247
 
40210
40248
  export declare type TableRow = z.infer<typeof TableRowSchema>;
package/dist/index.js CHANGED
@@ -267,6 +267,10 @@ const HEX_COLOR_REGEX = /^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/;
267
267
  const isNumberLineHighlightColor = (value) => NUMBER_LINE_HIGHLIGHT_COLORS.includes(value) || HEX_COLOR_REGEX.test(value);
268
268
  const highlightColorMessage = (block) => `${block}: color must be one of ${NUMBER_LINE_HIGHLIGHT_COLORS.join(", ")} or a hex value (e.g. "#3B82F6")`;
269
269
  const NUMBER_LINE_DRAG_MODES = ["axis", "inputs"];
270
+ const DEFAULT_TABLE_BORDER_COLOR = "#7c6cf2";
271
+ const isTableBorderColor = (value) => value.toLowerCase() === "transparent" || HEX_COLOR_REGEX.test(value);
272
+ const TABLE_BORDER_COLOR_ERROR = 'TABLE: bordercolor must be "transparent" or a hex value (e.g. "#E67E22" or "#F00")';
273
+ const TableBorderColorSchema = zod.z.string({ message: TABLE_BORDER_COLOR_ERROR }).refine(isTableBorderColor, { message: TABLE_BORDER_COLOR_ERROR }).default(DEFAULT_TABLE_BORDER_COLOR);
270
274
  function getFractionKey(widgetId, part) {
271
275
  return `${widgetId}__${part}`;
272
276
  }
@@ -2536,12 +2540,14 @@ function createTableRow(cells, backgroundColor) {
2536
2540
  const TableBlockSchema = zod.z.object({
2537
2541
  type: zod.z.literal(AST_TYPES.TABLE_BLOCK),
2538
2542
  cols: zod.z.number().int().positive(),
2543
+ borderColor: TableBorderColorSchema,
2539
2544
  rows: zod.z.array(TableRowSchema).min(1)
2540
2545
  });
2541
- function createTableBlock(cols, rows) {
2546
+ function createTableBlock(cols, rows, borderColor) {
2542
2547
  return TableBlockSchema.parse({
2543
2548
  type: AST_TYPES.TABLE_BLOCK,
2544
2549
  cols,
2550
+ borderColor,
2545
2551
  rows
2546
2552
  });
2547
2553
  }
@@ -3486,8 +3492,10 @@ class NaturalDSLVisitor extends BaseCstVisitor {
3486
3492
  table(ctx) {
3487
3493
  const params = this.visit(ctx.parameterList);
3488
3494
  const cols = params.cols;
3495
+ const borderColorParam = params.bordercolor;
3496
+ const borderColor = borderColorParam === void 0 ? void 0 : String(borderColorParam);
3489
3497
  const rows = ctx.row.map((r) => this.visit(r));
3490
- return createTableBlock(cols, rows);
3498
+ return createTableBlock(cols, rows, borderColor);
3491
3499
  }
3492
3500
  row(ctx) {
3493
3501
  const params = ctx.parameterList ? this.visit(ctx.parameterList) : {};
@@ -4282,6 +4290,7 @@ const TableItemSchema = zod.z.object({
4282
4290
  id: zod.z.string(),
4283
4291
  type: zod.z.literal(JSON_TYPES.TABLE),
4284
4292
  cols: zod.z.number().int().positive(),
4293
+ borderColor: TableBorderColorSchema,
4285
4294
  rows: zod.z.array(TableRowItemSchema)
4286
4295
  });
4287
4296
  const NumberLinePositionItemSchema = zod.z.object({
@@ -10866,6 +10875,7 @@ function transformTable(table, idGenerator, inlineDragIds) {
10866
10875
  id: idGenerator.generate(JSON_TYPES.TABLE),
10867
10876
  type: JSON_TYPES.TABLE,
10868
10877
  cols: table.cols,
10878
+ borderColor: table.borderColor,
10869
10879
  rows
10870
10880
  };
10871
10881
  }
@@ -11908,7 +11918,7 @@ function getEventCoordinates(event) {
11908
11918
  }
11909
11919
  return null;
11910
11920
  }
11911
- const CSS = /* @__PURE__ */ Object.freeze({
11921
+ const CSS$1 = /* @__PURE__ */ Object.freeze({
11912
11922
  Translate: {
11913
11923
  toString(transform) {
11914
11924
  if (!transform) {
@@ -11938,7 +11948,7 @@ const CSS = /* @__PURE__ */ Object.freeze({
11938
11948
  if (!transform) {
11939
11949
  return;
11940
11950
  }
11941
- return [CSS.Translate.toString(transform), CSS.Scale.toString(transform)].join(" ");
11951
+ return [CSS$1.Translate.toString(transform), CSS$1.Scale.toString(transform)].join(" ");
11942
11952
  }
11943
11953
  },
11944
11954
  Transition: {
@@ -15057,7 +15067,7 @@ const PositionedOverlay = /* @__PURE__ */ React.forwardRef((_ref, ref) => {
15057
15067
  height: rect.height,
15058
15068
  top: rect.top,
15059
15069
  left: rect.left,
15060
- transform: CSS.Transform.toString(scaleAdjustedTransform),
15070
+ transform: CSS$1.Transform.toString(scaleAdjustedTransform),
15061
15071
  transformOrigin: adjustScale2 && activatorEvent ? getRelativeTransformOrigin(activatorEvent, rect) : void 0,
15062
15072
  transition: typeof transition === "function" ? transition(activatorEvent) : transition,
15063
15073
  ...style
@@ -15118,9 +15128,9 @@ const defaultKeyframeResolver = (_ref2) => {
15118
15128
  }
15119
15129
  } = _ref2;
15120
15130
  return [{
15121
- transform: CSS.Transform.toString(initial)
15131
+ transform: CSS$1.Transform.toString(initial)
15122
15132
  }, {
15123
- transform: CSS.Transform.toString(final)
15133
+ transform: CSS$1.Transform.toString(final)
15124
15134
  }];
15125
15135
  };
15126
15136
  const defaultDropAnimationConfiguration = {
@@ -15553,7 +15563,7 @@ const Draggable = ({
15553
15563
  disabled
15554
15564
  });
15555
15565
  const style = {
15556
- transform: CSS.Translate.toString(transform),
15566
+ transform: CSS$1.Translate.toString(transform),
15557
15567
  cursor: disabled ? "default" : "grab",
15558
15568
  touchAction: "none"
15559
15569
  };
@@ -17913,7 +17923,7 @@ const CHART_TYPE_COMPONENTS = {
17913
17923
  const Chart = (props) => {
17914
17924
  const ChartComponent = CHART_TYPE_COMPONENTS[props.item.chartType];
17915
17925
  if (!ChartComponent) {
17916
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "chart chart--unsupported", children: /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "var(--exercise-incorrect, #ef4444)", fontSize: "0.85rem" }, children: `Chart type "${props.item.chartType}" is not yet supported.` }) });
17926
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "chart chart--unsupported", children: /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "var(--exercise-error, #ef4444)", fontSize: "0.85rem" }, children: `Chart type "${props.item.chartType}" is not yet supported.` }) });
17917
17927
  }
17918
17928
  return /* @__PURE__ */ jsxRuntime.jsx(ChartComponent, { ...props });
17919
17929
  };
@@ -20627,7 +20637,10 @@ const LayoutRenderer = ({
20627
20637
  )) });
20628
20638
  case JSON_TYPES.TABLE: {
20629
20639
  const totalRows = item.rows.length;
20630
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "exercise-table-container", children: /* @__PURE__ */ jsxRuntime.jsx("table", { className: "exercise-table", children: /* @__PURE__ */ jsxRuntime.jsx("tbody", { children: item.rows.map((row, rowIndex) => /* @__PURE__ */ jsxRuntime.jsx("tr", { className: "exercise-table-row", style: { backgroundColor: row.backgroundColor }, children: row.cells.map((cell) => {
20640
+ const tableStyle = {
20641
+ "--exercise-table-border-color": item.borderColor ?? DEFAULT_TABLE_BORDER_COLOR
20642
+ };
20643
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "exercise-table-container", style: tableStyle, children: /* @__PURE__ */ jsxRuntime.jsx("table", { className: "exercise-table", children: /* @__PURE__ */ jsxRuntime.jsx("tbody", { children: item.rows.map((row, rowIndex) => /* @__PURE__ */ jsxRuntime.jsx("tr", { className: "exercise-table-row", style: { backgroundColor: row.backgroundColor }, children: row.cells.map((cell) => {
20631
20644
  const rowspan = cell.rowspan || 1;
20632
20645
  const isLastRow = rowIndex + rowspan - 1 >= totalRows - 1;
20633
20646
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -20830,6 +20843,15 @@ const TOOL_ASPECT_RATIO = {
20830
20843
  "set-square": SET_SQUARE_ASPECT_RATIO
20831
20844
  };
20832
20845
  const FLIP_BTN_EXTRA = 44;
20846
+ function warnOnInvalidThemeColors(theme) {
20847
+ if (process.env.NODE_ENV === "production" || !theme) return;
20848
+ if (typeof CSS === "undefined" || typeof CSS.supports !== "function") return;
20849
+ for (const [key2, value] of Object.entries(theme)) {
20850
+ if (value && !CSS.supports("color", value)) {
20851
+ console.warn(`[ExerciseRenderer] theme.${key2} is not a valid CSS color: "${value}"`);
20852
+ }
20853
+ }
20854
+ }
20833
20855
  function buildInitialValues(json) {
20834
20856
  const initial = {};
20835
20857
  for (const [id, widget] of Object.entries(json.widgets)) {
@@ -20902,7 +20924,8 @@ const ExerciseRenderer = React.forwardRef(({
20902
20924
  showSubmitButton = false,
20903
20925
  className = "",
20904
20926
  soundEnabled = true,
20905
- sounds
20927
+ sounds,
20928
+ theme
20906
20929
  }, ref) => {
20907
20930
  const initialValuesSignature = React.useMemo(() => buildInitialValuesSignature(json), [json]);
20908
20931
  const [values, setValues] = React.useState(() => buildInitialValues(json));
@@ -20916,6 +20939,15 @@ const ExerciseRenderer = React.forwardRef(({
20916
20939
  const rootRef = React.useRef(null);
20917
20940
  const [resetKey, setResetKey] = React.useState(0);
20918
20941
  const [revealed, setRevealed] = React.useState(false);
20942
+ const themeStyle = React.useMemo(() => {
20943
+ const style = {};
20944
+ if (theme?.correct) style["--exercise-correct"] = theme.correct;
20945
+ if (theme?.incorrect) style["--exercise-incorrect"] = theme.incorrect;
20946
+ return Object.keys(style).length > 0 ? style : void 0;
20947
+ }, [theme?.correct, theme?.incorrect]);
20948
+ React.useEffect(() => {
20949
+ warnOnInvalidThemeColors({ correct: theme?.correct, incorrect: theme?.incorrect });
20950
+ }, [theme?.correct, theme?.incorrect]);
20919
20951
  const playSound = useSoundPlayer(soundEnabled, sounds);
20920
20952
  const externallyLocked = useExerciseLocked();
20921
20953
  React.useLayoutEffect(() => {
@@ -21172,7 +21204,7 @@ const ExerciseRenderer = React.forwardRef(({
21172
21204
  if (setSquareWidget) add2("set-square-1", "set-square", Math.round(getSize(setSquareWidget) * 160));
21173
21205
  return { tools: list, toolsMinHeight: maxH };
21174
21206
  }, [rulerWidget, protractorWidget, setSquareWidget]);
21175
- return /* @__PURE__ */ jsxRuntime.jsx(SoundPlayerProvider, { play: playSound, children: /* @__PURE__ */ jsxRuntime.jsx(ExerciseLockProvider, { locked, children: /* @__PURE__ */ jsxRuntime.jsx(ExerciseDndProvider, { droppedSources, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: rootRef, className: `math-exercise-renderer ${revealed ? "revealed" : ""} ${locked ? "locked" : ""} ${className}`, children: [
21207
+ return /* @__PURE__ */ jsxRuntime.jsx(SoundPlayerProvider, { play: playSound, children: /* @__PURE__ */ jsxRuntime.jsx(ExerciseLockProvider, { locked, children: /* @__PURE__ */ jsxRuntime.jsx(ExerciseDndProvider, { droppedSources, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: rootRef, className: `math-exercise-renderer ${revealed ? "revealed" : ""} ${locked ? "locked" : ""} ${className}`, style: themeStyle, children: [
21176
21208
  /* @__PURE__ */ jsxRuntime.jsxs(
21177
21209
  "div",
21178
21210
  {
@@ -21333,6 +21365,7 @@ exports.ClockItemSchema = ClockItemSchema;
21333
21365
  exports.ColorTextInlineSchema = ColorTextInlineSchema;
21334
21366
  exports.ColorTextSchema = ColorTextSchema;
21335
21367
  exports.ContentBlockSchema = ContentBlockSchema;
21368
+ exports.DEFAULT_TABLE_BORDER_COLOR = DEFAULT_TABLE_BORDER_COLOR;
21336
21369
  exports.DisplayInlineNodeSchema = DisplayInlineNodeSchema;
21337
21370
  exports.DragOptionsJSONSchema = DragOptionsJSONSchema;
21338
21371
  exports.DragOptionsToolSchema = DragOptionsToolSchema;