@mathscan/math-exercise-engine 1.1.0 → 1.1.2

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/style.css CHANGED
@@ -421,10 +421,6 @@ mark {
421
421
  DRAG OPTIONS
422
422
  ======================================== */
423
423
 
424
- .drag-options-container {
425
- /* Container for drag options widget */
426
- }
427
-
428
424
  .drag-options {
429
425
  display: flex;
430
426
  justify-content: center;
@@ -737,10 +733,10 @@ th.exercise-table-cell {
737
733
  --visual-fraction-svg-line-width: 0.5px;
738
734
  --visual-fraction-gap: var(--visual-fraction-line-width);
739
735
  --visual-fraction-border-radius: 0;
740
- --visual-fraction-border-color: #705DC0;
736
+ --visual-fraction-border-color: #705dc0;
741
737
  --visual-fraction-empty-bg: var(--exercise-bg, #fff);
742
- --visual-fraction-hover-color: #E6E0FF;
743
- --visual-fraction-part-color: #AE9CFF;
738
+ --visual-fraction-hover-color: #e6e0ff;
739
+ --visual-fraction-part-color: #ae9cff;
744
740
  --visual-fraction-part-text-color: #fff;
745
741
  --visual-fraction-label-color: var(--exercise-text-light, #555);
746
742
  display: inline-flex;
@@ -778,18 +774,21 @@ th.exercise-table-cell {
778
774
  min-height: 0;
779
775
  background: var(--visual-fraction-empty-bg);
780
776
  color: var(--exercise-text, #333);
781
- transition: background 0.2s ease, border-color 0.2s ease, filter 0.2s ease;
777
+ transition:
778
+ background 0.2s ease,
779
+ border-color 0.2s ease,
780
+ filter 0.2s ease;
782
781
  }
783
782
 
784
- .visual-fraction-widget__part[role="button"] {
783
+ .visual-fraction-widget__part[role='button'] {
785
784
  cursor: pointer;
786
785
  }
787
786
 
788
- .visual-fraction-widget__part[role="button"]:hover {
787
+ .visual-fraction-widget__part[role='button']:hover {
789
788
  background: var(--visual-fraction-hover-color);
790
789
  }
791
790
 
792
- .visual-fraction-widget__part[role="button"]:focus-visible {
791
+ .visual-fraction-widget__part[role='button']:focus-visible {
793
792
  outline: 2px solid var(--exercise-border-focus, var(--exercise-primary, #007acc));
794
793
  outline-offset: -2px;
795
794
  }
@@ -800,8 +799,8 @@ th.exercise-table-cell {
800
799
  color: var(--visual-fraction-part-text-color);
801
800
  }
802
801
 
803
- .visual-fraction-widget__part--filled[role="button"]:hover,
804
- .visual-fraction-widget__part--answer[role="button"]:hover {
802
+ .visual-fraction-widget__part--filled[role='button']:hover,
803
+ .visual-fraction-widget__part--answer[role='button']:hover {
805
804
  background: var(--visual-fraction-part-color);
806
805
  color: var(--visual-fraction-part-text-color);
807
806
  }
@@ -812,26 +811,28 @@ th.exercise-table-cell {
812
811
  stroke-width: var(--visual-fraction-svg-line-width);
813
812
  stroke-linejoin: round;
814
813
  stroke-linecap: round;
815
- transition: fill 0.2s ease, filter 0.2s ease;
814
+ transition:
815
+ fill 0.2s ease,
816
+ filter 0.2s ease;
816
817
  }
817
818
 
818
- .visual-fraction-widget__sector[role="button"] {
819
+ .visual-fraction-widget__sector[role='button'] {
819
820
  cursor: pointer;
820
821
  }
821
822
 
822
- .visual-fraction-widget__sector[role="button"]:hover {
823
+ .visual-fraction-widget__sector[role='button']:hover {
823
824
  fill: var(--visual-fraction-hover-color);
824
825
  }
825
826
 
826
- .visual-fraction-widget__sector[role="button"]:focus-visible {
827
+ .visual-fraction-widget__sector[role='button']:focus-visible {
827
828
  outline: none;
828
829
  filter: drop-shadow(0 0 0.18rem var(--exercise-border-focus, var(--exercise-primary, #007acc)));
829
830
  }
830
831
 
831
832
  .visual-fraction-widget__sector.visual-fraction-widget__part--filled,
832
833
  .visual-fraction-widget__sector.visual-fraction-widget__part--answer,
833
- .visual-fraction-widget__sector.visual-fraction-widget__part--filled[role="button"]:hover,
834
- .visual-fraction-widget__sector.visual-fraction-widget__part--answer[role="button"]:hover {
834
+ .visual-fraction-widget__sector.visual-fraction-widget__part--filled[role='button']:hover,
835
+ .visual-fraction-widget__sector.visual-fraction-widget__part--answer[role='button']:hover {
835
836
  fill: var(--visual-fraction-part-color);
836
837
  }
837
838
 
@@ -896,9 +897,9 @@ th.exercise-table-cell {
896
897
  justify-content: center;
897
898
  min-height: clamp(72px, 14vw, 96px);
898
899
  padding: clamp(8px, 2vw, 12px);
899
- border: 2px solid #FFCA76;
900
+ border: 2px solid #ffca76;
900
901
  border-radius: clamp(12px, 3vw, 17px);
901
- background: #FFFFFF;
902
+ background: #ffffff;
902
903
  color: #333333;
903
904
  text-align: center;
904
905
  cursor: pointer;
@@ -921,21 +922,21 @@ th.exercise-table-cell {
921
922
  } */
922
923
 
923
924
  .matcher-card--selected {
924
- background: color-mix(in srgb, var(--matcher-card-color) 12%, #FFFFFF);
925
+ background: color-mix(in srgb, var(--matcher-card-color) 12%, #ffffff);
925
926
  border-color: var(--matcher-card-color);
926
927
  box-shadow: 0 4px 0 color-mix(in srgb, var(--matcher-card-color) 30%, transparent);
927
928
  transform: scale(1.04);
928
929
  }
929
930
 
930
931
  .widget--correct .matcher-card--selected {
931
- border-color: #43B925;
932
- background: #F2FFEE;
933
- box-shadow: 0 4px 0 rgba(67, 185, 37, 0.30);
932
+ border-color: #43b925;
933
+ background: #f2ffee;
934
+ box-shadow: 0 4px 0 rgba(67, 185, 37, 0.3);
934
935
  }
935
936
 
936
937
  .widget--incorrect .matcher-card--selected {
937
- border-color: #D6234D;
938
- background: #FFF0F7;
938
+ border-color: #d6234d;
939
+ background: #fff0f7;
939
940
  box-shadow: 0 4px 0 rgba(214, 35, 77, 0.25);
940
941
  }
941
942
 
@@ -950,6 +951,109 @@ th.exercise-table-cell {
950
951
  opacity: 0.5;
951
952
  } */
952
953
 
954
+ /* ========================================
955
+ PAIR MATCHER WIDGET
956
+ ======================================== */
957
+
958
+ .pair-matcher-widget {
959
+ --pm-orange: #FFCA76; --pm-purple: #705DC0;
960
+ --pm-selected-bg: #E7E2FF; --pm-selected-border: #AE9CFF;
961
+ --pm-card-width: 163px; --pm-card-height: 82px; --pm-card-radius: 12px; --pm-card-border-width: 1px;
962
+ --pm-dot-size: 17px; --pm-dot-border-width: 2px;
963
+ --pm-line-color: var(--pm-orange); --pm-line-color-correct: #43B925; --pm-line-color-incorrect: #D6234D; --pm-line-width: 4px;
964
+ position: relative; display: flex; flex-direction: column;
965
+ gap: 52px; width: max-content; max-width: 100%; margin-inline: auto; padding: 0;
966
+ }
967
+
968
+ .pair-matcher-row {
969
+ position: relative; z-index: 2; display: grid;
970
+ grid-template-columns: repeat(var(--pm-cols, 3), minmax(0, var(--pm-card-width)));
971
+ justify-content: center; gap: 18px; align-items: stretch; min-width: 0;
972
+ }
973
+
974
+ .pair-matcher-cell { display: flex; flex-direction: column; align-items: center; min-width: 0; }
975
+
976
+ .pair-matcher-card {
977
+ box-sizing: border-box; display: flex; align-items: center; justify-content: center;
978
+ width: 100%; min-width: 0; max-width: 100%; align-self: stretch; flex: 1 1 auto;
979
+ min-height: var(--pm-card-height); height: auto; padding: 6px 8px;
980
+ border: var(--pm-card-border-width) solid var(--pm-orange); border-radius: var(--pm-card-radius);
981
+ background: #fff; color: var(--pm-purple);
982
+ font: 500 1rem / 1.2 var(--exercise-font-family, inherit); letter-spacing: 0;
983
+ text-align: center; word-break: normal; overflow-wrap: break-word;
984
+ white-space: normal; hyphens: auto; overflow: visible; cursor: pointer;
985
+ transition: background 0.35s ease, border-color 0.35s ease, transform 0.2s ease;
986
+ }
987
+
988
+ .pair-matcher-card:not([aria-disabled="true"]):active { transform: scale(0.98); }
989
+
990
+ .pair-matcher-card[aria-disabled="true"] { cursor: default; }
991
+
992
+ .pair-matcher-card--pending, .pair-matcher-card--flash { border-color: var(--pm-selected-border); background: var(--pm-selected-bg); }
993
+
994
+ .pair-matcher-card--correct { border-color: #43B925; background: #F2FFEE; color: #43B925; }
995
+
996
+ .pair-matcher-card--incorrect { border-color: #D6234D; background: #FFF0F7; color: #D6234D; }
997
+
998
+ .pair-matcher-dot {
999
+ position: relative; z-index: 3; flex-shrink: 0;
1000
+ width: var(--pm-dot-size); height: var(--pm-dot-size);
1001
+ border: var(--pm-dot-border-width) solid var(--pm-orange); border-radius: 50%; background: #fff;
1002
+ }
1003
+
1004
+ .pair-matcher-dot--south { margin-top: calc(var(--pm-dot-size) / -2); }
1005
+
1006
+ .pair-matcher-dot--north { margin-bottom: calc(var(--pm-dot-size) / -2); }
1007
+
1008
+ .pair-matcher-dot--active { background: var(--pm-orange); }
1009
+
1010
+ .pair-matcher-dot--pending { border-color: var(--pm-selected-border); background: #fff; }
1011
+
1012
+ .pair-matcher-dot--correct { border-color: #43B925; background: #43B925; }
1013
+
1014
+ .pair-matcher-dot--incorrect { border-color: #D6234D; background: #D6234D; }
1015
+
1016
+ .pair-matcher-lines {
1017
+ position: absolute; inset: 0; z-index: 1;
1018
+ width: 100%; height: 100%; overflow: visible; pointer-events: none;
1019
+ }
1020
+
1021
+ .pair-matcher-line { stroke-dasharray: 1; animation: pair-matcher-line-draw 0.5s ease both; }
1022
+
1023
+ @keyframes pair-matcher-line-draw { from { stroke-dashoffset: 1; } to { stroke-dashoffset: 0; } }
1024
+
1025
+ .pair-matcher-card .text-item,
1026
+ .pair-matcher-card .question-text,
1027
+ .pair-matcher-card .group {
1028
+ margin: 0; min-width: 0; max-width: 100%;
1029
+ color: inherit; font: inherit;
1030
+ overflow-wrap: break-word; word-break: normal; white-space: normal;
1031
+ }
1032
+
1033
+ .pair-matcher-card:not(.pair-matcher-card--rtl) .text-item,
1034
+ .pair-matcher-card:not(.pair-matcher-card--rtl) .question-text,
1035
+ .pair-matcher-card:not(.pair-matcher-card--rtl) .group { overflow-wrap: break-word; }
1036
+
1037
+ .pair-matcher-card--rtl { direction: rtl; unicode-bidi: plaintext; }
1038
+
1039
+ .pair-matcher-card-content {
1040
+ display: block; width: 100%; min-width: 0; max-width: 100%; text-align: inherit; white-space: normal;
1041
+ }
1042
+
1043
+ .pair-matcher-card-content .space { display: inline; width: auto; }
1044
+
1045
+ .pair-matcher-card .text-item-arabic,
1046
+ .pair-matcher-card .question-text-arabic {
1047
+ direction: rtl; font-size: 0.72rem; line-height: 1.15;
1048
+ overflow-wrap: normal; word-break: normal; hyphens: manual;
1049
+ }
1050
+
1051
+ @media (max-width: 420px) {
1052
+ .pair-matcher-widget { gap: 46px; }
1053
+ .pair-matcher-row { grid-template-columns: repeat(var(--pm-cols, 3), minmax(0, 1fr)); gap: 12px; }
1054
+ .pair-matcher-card { min-height: 58px; padding: 6px 7px; font-size: 0.92rem; }
1055
+ }
1056
+
953
1057
  /**
954
1058
  * Number Line Styles
955
1059
  *
@@ -1544,7 +1648,9 @@ th.exercise-table-cell {
1544
1648
  }
1545
1649
 
1546
1650
  .draggable.dragging {
1547
- opacity: 0.5;
1651
+ /* Hide original while drag overlay follows cursor to avoid duplicate render.
1652
+ visibility:hidden preserves layout space so neighbors don't shift. */
1653
+ visibility: hidden;
1548
1654
  }
1549
1655
 
1550
1656
  /* Droppable base */
@@ -1564,53 +1670,31 @@ th.exercise-table-cell {
1564
1670
  outline-offset: 2px;
1565
1671
  }
1566
1672
 
1567
- /* Drag overlay - the item following cursor */
1673
+ /* Drag overlay - the item following cursor. Matches the rendered button look,
1674
+ which is the `button.drag-option` legacy rule in styles/widgets.css
1675
+ (accent background + white text, no border).
1676
+ NOTE: @dnd-kit DragOverlay renders in a portal at document.body, outside
1677
+ the .math-exercise-renderer scope — so CSS vars must include fallbacks. */
1568
1678
  .drag-overlay-option {
1569
- padding: 8px 16px;
1570
- background: #4a90d9;
1679
+ padding: 10px 16px;
1680
+ border: none;
1681
+ border-radius: var(--exercise-border-radius, 0.25rem);
1682
+ background: var(--exercise-accent, #705DC0);
1571
1683
  color: white;
1572
- border-radius: 6px;
1573
- font-weight: 500;
1574
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
1684
+ font-size: var(--exercise-font-size, clamp(0.875rem, 2vw, 1.1rem));
1685
+ font-weight: var(--exercise-font-weight, 500);
1686
+ min-width: var(--exercise-button-width, clamp(3rem, 6vw, 4rem));
1687
+ text-align: center;
1575
1688
  cursor: grabbing;
1576
1689
  }
1577
1690
 
1578
- /* Drag options container */
1579
- .drag-options {
1580
- display: flex;
1581
- flex-wrap: wrap;
1582
- gap: 8px;
1583
- /* padding: 12px; */
1584
- /* background: #f5f5f5; */
1585
- border-radius: 8px;
1586
- justify-content: center;
1587
- }
1691
+ /* Note: .drag-options and .drag-option base styles live in styles/widgets.css
1692
+ (themed via CSS vars). Don't duplicate them here. */
1588
1693
 
1589
1694
  .drag-option-wrapper {
1590
1695
  display: inline-block;
1591
1696
  }
1592
1697
 
1593
- .drag-option {
1594
- padding: 10px 20px;
1595
- border: 2px solid #ddd;
1596
- border-radius: 8px;
1597
- background: white;
1598
- font-size: 16px;
1599
- font-weight: 500;
1600
- cursor: grab;
1601
- transition: all 0.15s ease;
1602
- }
1603
-
1604
- .drag-option:hover {
1605
- border-color: #4a90d9;
1606
- background: #f0f7ff;
1607
- }
1608
-
1609
- .drag-option.selected {
1610
- border-color: #4a90d9;
1611
- background: #e3f0ff;
1612
- }
1613
-
1614
1698
  /* Slot container - maintains space when item is dragged out */
1615
1699
  .drag-option-slot {
1616
1700
  display: inline-flex;
@@ -1676,17 +1760,6 @@ th.exercise-table-cell {
1676
1760
  background-color: rgba(74, 144, 217, 0.05);
1677
1761
  }
1678
1762
 
1679
- /* Drag options container at bottom */
1680
- .drag-options-container {
1681
- /* margin-top: 20px;
1682
- padding-top: 16px;
1683
- border-top: 1px solid #eee; */
1684
- }
1685
-
1686
- /* ============================================
1687
- FUTURE WIDGETS - Placeholder styles
1688
- ============================================ */
1689
-
1690
1763
  /* ============================================
1691
1764
  DRAGGABLE TOOLS (Ruler, Protractor, etc.)
1692
1765
  ============================================ */
@@ -1747,4 +1820,4 @@ th.exercise-table-cell {
1747
1820
  .math-exercise-renderer {
1748
1821
  position: relative;
1749
1822
  min-height: 200px;
1750
- }
1823
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mathscan/math-exercise-engine",
3
- "version": "1.1.0",
3
+ "version": "1.1.2",
4
4
  "description": "A DSL parser and validator for math exercises - transforms text-based exercise definitions into structured AST and JSON",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",