@mathscan/math-exercise-engine 1.2.4 → 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
@@ -30416,6 +30416,8 @@ export declare interface ExerciseRendererProps {
30416
30416
  soundEnabled?: boolean;
30417
30417
  /** Maps sound events → URL or callback. Omitted events stay silent. */
30418
30418
  sounds?: SoundConfig;
30419
+ /** Overrides for validation state colors. */
30420
+ theme?: ExerciseTheme;
30419
30421
  }
30420
30422
 
30421
30423
  export declare interface ExerciseRendererRef {
@@ -30429,6 +30431,22 @@ export declare interface ExerciseRendererRef {
30429
30431
  revealAnswers: () => void;
30430
30432
  }
30431
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
+
30432
30450
  export declare type ExpressionValidation = z.infer<typeof ExpressionValidationSchema>;
30433
30451
 
30434
30452
  /**
package/dist/index.js CHANGED
@@ -11918,7 +11918,7 @@ function getEventCoordinates(event) {
11918
11918
  }
11919
11919
  return null;
11920
11920
  }
11921
- const CSS = /* @__PURE__ */ Object.freeze({
11921
+ const CSS$1 = /* @__PURE__ */ Object.freeze({
11922
11922
  Translate: {
11923
11923
  toString(transform) {
11924
11924
  if (!transform) {
@@ -11948,7 +11948,7 @@ const CSS = /* @__PURE__ */ Object.freeze({
11948
11948
  if (!transform) {
11949
11949
  return;
11950
11950
  }
11951
- return [CSS.Translate.toString(transform), CSS.Scale.toString(transform)].join(" ");
11951
+ return [CSS$1.Translate.toString(transform), CSS$1.Scale.toString(transform)].join(" ");
11952
11952
  }
11953
11953
  },
11954
11954
  Transition: {
@@ -15067,7 +15067,7 @@ const PositionedOverlay = /* @__PURE__ */ React.forwardRef((_ref, ref) => {
15067
15067
  height: rect.height,
15068
15068
  top: rect.top,
15069
15069
  left: rect.left,
15070
- transform: CSS.Transform.toString(scaleAdjustedTransform),
15070
+ transform: CSS$1.Transform.toString(scaleAdjustedTransform),
15071
15071
  transformOrigin: adjustScale2 && activatorEvent ? getRelativeTransformOrigin(activatorEvent, rect) : void 0,
15072
15072
  transition: typeof transition === "function" ? transition(activatorEvent) : transition,
15073
15073
  ...style
@@ -15128,9 +15128,9 @@ const defaultKeyframeResolver = (_ref2) => {
15128
15128
  }
15129
15129
  } = _ref2;
15130
15130
  return [{
15131
- transform: CSS.Transform.toString(initial)
15131
+ transform: CSS$1.Transform.toString(initial)
15132
15132
  }, {
15133
- transform: CSS.Transform.toString(final)
15133
+ transform: CSS$1.Transform.toString(final)
15134
15134
  }];
15135
15135
  };
15136
15136
  const defaultDropAnimationConfiguration = {
@@ -15563,7 +15563,7 @@ const Draggable = ({
15563
15563
  disabled
15564
15564
  });
15565
15565
  const style = {
15566
- transform: CSS.Translate.toString(transform),
15566
+ transform: CSS$1.Translate.toString(transform),
15567
15567
  cursor: disabled ? "default" : "grab",
15568
15568
  touchAction: "none"
15569
15569
  };
@@ -17923,7 +17923,7 @@ const CHART_TYPE_COMPONENTS = {
17923
17923
  const Chart = (props) => {
17924
17924
  const ChartComponent = CHART_TYPE_COMPONENTS[props.item.chartType];
17925
17925
  if (!ChartComponent) {
17926
- 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.` }) });
17927
17927
  }
17928
17928
  return /* @__PURE__ */ jsxRuntime.jsx(ChartComponent, { ...props });
17929
17929
  };
@@ -20843,6 +20843,15 @@ const TOOL_ASPECT_RATIO = {
20843
20843
  "set-square": SET_SQUARE_ASPECT_RATIO
20844
20844
  };
20845
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
+ }
20846
20855
  function buildInitialValues(json) {
20847
20856
  const initial = {};
20848
20857
  for (const [id, widget] of Object.entries(json.widgets)) {
@@ -20915,7 +20924,8 @@ const ExerciseRenderer = React.forwardRef(({
20915
20924
  showSubmitButton = false,
20916
20925
  className = "",
20917
20926
  soundEnabled = true,
20918
- sounds
20927
+ sounds,
20928
+ theme
20919
20929
  }, ref) => {
20920
20930
  const initialValuesSignature = React.useMemo(() => buildInitialValuesSignature(json), [json]);
20921
20931
  const [values, setValues] = React.useState(() => buildInitialValues(json));
@@ -20929,6 +20939,15 @@ const ExerciseRenderer = React.forwardRef(({
20929
20939
  const rootRef = React.useRef(null);
20930
20940
  const [resetKey, setResetKey] = React.useState(0);
20931
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]);
20932
20951
  const playSound = useSoundPlayer(soundEnabled, sounds);
20933
20952
  const externallyLocked = useExerciseLocked();
20934
20953
  React.useLayoutEffect(() => {
@@ -21185,7 +21204,7 @@ const ExerciseRenderer = React.forwardRef(({
21185
21204
  if (setSquareWidget) add2("set-square-1", "set-square", Math.round(getSize(setSquareWidget) * 160));
21186
21205
  return { tools: list, toolsMinHeight: maxH };
21187
21206
  }, [rulerWidget, protractorWidget, setSquareWidget]);
21188
- 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: [
21189
21208
  /* @__PURE__ */ jsxRuntime.jsxs(
21190
21209
  "div",
21191
21210
  {