@mathscan/math-exercise-engine 1.1.8 → 1.1.10
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 +8069 -3402
- package/dist/index.js +756 -77
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +756 -77
- package/dist/index.mjs.map +1 -1
- package/dist/style.css +253 -45
- package/package.json +1 -1
package/dist/style.css
CHANGED
|
@@ -1206,6 +1206,8 @@ th.exercise-table-cell {
|
|
|
1206
1206
|
--nl-visual-fraction-size: clamp(5.5rem, 18vw, 8rem);
|
|
1207
1207
|
/* Visual gap between end ticks and arrowheads — pure CSS, no effect on calculations */
|
|
1208
1208
|
--nl-arrow-gap: 8px;
|
|
1209
|
+
--nl-arrow-arc-color: var(--exercise-danger, #D6234D);
|
|
1210
|
+
--nl-arrow-arc-stroke-width: 1.75;
|
|
1209
1211
|
}
|
|
1210
1212
|
|
|
1211
1213
|
/* ========================================
|
|
@@ -1230,7 +1232,39 @@ th.exercise-table-cell {
|
|
|
1230
1232
|
}
|
|
1231
1233
|
|
|
1232
1234
|
/* ========================================
|
|
1233
|
-
ROW 1 —
|
|
1235
|
+
ROW 1 — OVERLAYS ATTACHED TO AXIS
|
|
1236
|
+
======================================== */
|
|
1237
|
+
|
|
1238
|
+
.number-line-overlays-row {
|
|
1239
|
+
position: relative;
|
|
1240
|
+
width: 100%;
|
|
1241
|
+
min-height: max(
|
|
1242
|
+
var(--nl-position-content-min-height),
|
|
1243
|
+
var(--nl-position-content-height),
|
|
1244
|
+
var(--nl-arrow-arcs-row-height, 0px)
|
|
1245
|
+
);
|
|
1246
|
+
margin-top: 0.1rem;
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
.number-line-overlays-row .number-line-content-row,
|
|
1250
|
+
.number-line-overlays-row .number-line-arrow-arcs-row {
|
|
1251
|
+
position: absolute;
|
|
1252
|
+
left: 0;
|
|
1253
|
+
right: 0;
|
|
1254
|
+
bottom: 0;
|
|
1255
|
+
margin-top: 0;
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
/* The two overlays share the same axis-anchored box. Position content holds the
|
|
1259
|
+
interactive inputs, so it takes pointer/paint priority over arc annotations —
|
|
1260
|
+
made explicit here rather than relying on DOM sibling order. */
|
|
1261
|
+
|
|
1262
|
+
.number-line-overlays-row .number-line-arrow-arcs-row { z-index: 0; }
|
|
1263
|
+
|
|
1264
|
+
.number-line-overlays-row .number-line-content-row { z-index: 1; }
|
|
1265
|
+
|
|
1266
|
+
/* ========================================
|
|
1267
|
+
POSITION CONTENT (inputs / labels above axis)
|
|
1234
1268
|
======================================== */
|
|
1235
1269
|
|
|
1236
1270
|
.number-line-content-row {
|
|
@@ -1255,7 +1289,64 @@ th.exercise-table-cell {
|
|
|
1255
1289
|
}
|
|
1256
1290
|
|
|
1257
1291
|
/* ========================================
|
|
1258
|
-
|
|
1292
|
+
ARROW ANNOTATION ARCS
|
|
1293
|
+
======================================== */
|
|
1294
|
+
|
|
1295
|
+
.number-line-arrow-arcs-row {
|
|
1296
|
+
position: relative;
|
|
1297
|
+
width: 100%;
|
|
1298
|
+
margin-top: 0.1rem;
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
.number-line-arrow-arc {
|
|
1302
|
+
position: absolute;
|
|
1303
|
+
height: 80px;
|
|
1304
|
+
pointer-events: none;
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
.number-line-arrow-arc-svg {
|
|
1308
|
+
position: absolute;
|
|
1309
|
+
left: 0;
|
|
1310
|
+
right: 0;
|
|
1311
|
+
bottom: 0;
|
|
1312
|
+
width: 100%;
|
|
1313
|
+
height: 64px;
|
|
1314
|
+
overflow: visible;
|
|
1315
|
+
pointer-events: none;
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1318
|
+
.number-line-arrow-arc-path {
|
|
1319
|
+
fill: none;
|
|
1320
|
+
stroke: var(--nl-arrow-arc-color);
|
|
1321
|
+
stroke-width: var(--nl-arrow-arc-stroke-width);
|
|
1322
|
+
stroke-linecap: round;
|
|
1323
|
+
stroke-linejoin: round;
|
|
1324
|
+
vector-effect: non-scaling-stroke;
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1327
|
+
.number-line-arrow-arc-head {
|
|
1328
|
+
fill: var(--nl-arrow-arc-color);
|
|
1329
|
+
stroke: none;
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1332
|
+
.number-line-arrow-arc-label {
|
|
1333
|
+
position: absolute;
|
|
1334
|
+
/* top is always set via inline style from getArrowArcLabelTop() */
|
|
1335
|
+
left: 50%;
|
|
1336
|
+
transform: translateX(-50%);
|
|
1337
|
+
color: var(--nl-arrow-arc-color);
|
|
1338
|
+
font-size: calc(var(--exercise-font-size) * 0.8);
|
|
1339
|
+
font-weight: var(--exercise-font-weight-bold, 600);
|
|
1340
|
+
line-height: 1.1;
|
|
1341
|
+
pointer-events: auto;
|
|
1342
|
+
/* Centre content horizontally; allow LayoutRenderer to size naturally */
|
|
1343
|
+
display: flex;
|
|
1344
|
+
align-items: center;
|
|
1345
|
+
justify-content: center;
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
/* ========================================
|
|
1349
|
+
ROW 3 — AXIS (track + ticks + arrows + dots)
|
|
1259
1350
|
======================================== */
|
|
1260
1351
|
|
|
1261
1352
|
.number-line-axis-row {
|
|
@@ -1264,6 +1355,10 @@ th.exercise-table-cell {
|
|
|
1264
1355
|
height: 2rem;
|
|
1265
1356
|
}
|
|
1266
1357
|
|
|
1358
|
+
.number-line-axis-row:has(.number-line-cursor-label) {
|
|
1359
|
+
height: 3rem;
|
|
1360
|
+
}
|
|
1361
|
+
|
|
1267
1362
|
/* In drag mode, chip zone is handled by .number-line--drag-mode padding-top on root */
|
|
1268
1363
|
|
|
1269
1364
|
/* Horizontal track line — spans [--nl-side-pad, 100% - --nl-side-pad] by default */
|
|
@@ -1543,7 +1638,7 @@ th.exercise-table-cell {
|
|
|
1543
1638
|
/* Axis glow on drag-over intentionally disabled — axis stays neutral */
|
|
1544
1639
|
|
|
1545
1640
|
/* ========================================
|
|
1546
|
-
ROW
|
|
1641
|
+
ROW 4 — NUMERIC TICK LABELS
|
|
1547
1642
|
======================================== */
|
|
1548
1643
|
|
|
1549
1644
|
.number-line-labels-row {
|
|
@@ -1563,7 +1658,7 @@ th.exercise-table-cell {
|
|
|
1563
1658
|
}
|
|
1564
1659
|
|
|
1565
1660
|
/* ========================================
|
|
1566
|
-
HIGHLIGHT BANDS
|
|
1661
|
+
ROW 5 — HIGHLIGHT BANDS / LABELS
|
|
1567
1662
|
======================================== */
|
|
1568
1663
|
|
|
1569
1664
|
/* A coloured band spanning [from%, to%] on the axis row */
|
|
@@ -1598,84 +1693,197 @@ th.exercise-table-cell {
|
|
|
1598
1693
|
pointer-events: none;
|
|
1599
1694
|
}
|
|
1600
1695
|
|
|
1696
|
+
/* ========================================
|
|
1697
|
+
CURSOR BALL
|
|
1698
|
+
======================================== */
|
|
1699
|
+
|
|
1700
|
+
/* Filled range between the cursor's initial value and its current position */
|
|
1701
|
+
|
|
1702
|
+
.number-line-cursor-fill {
|
|
1703
|
+
position: absolute;
|
|
1704
|
+
top: 50%;
|
|
1705
|
+
transform: translateY(-50%);
|
|
1706
|
+
height: 6px;
|
|
1707
|
+
background-color: var(--nl-cursor-color, var(--exercise-accent, #705DC0));
|
|
1708
|
+
opacity: 0.28;
|
|
1709
|
+
border-radius: 999px;
|
|
1710
|
+
pointer-events: none;
|
|
1711
|
+
z-index: 2;
|
|
1712
|
+
}
|
|
1713
|
+
|
|
1714
|
+
/* The ball rendered at the cursor's current value */
|
|
1715
|
+
|
|
1716
|
+
.number-line-cursor-ball {
|
|
1717
|
+
position: absolute;
|
|
1718
|
+
top: 50%;
|
|
1719
|
+
transform: translate(-50%, -50%);
|
|
1720
|
+
width: 16px;
|
|
1721
|
+
height: 16px;
|
|
1722
|
+
border-radius: 50%;
|
|
1723
|
+
background-color: #fff;
|
|
1724
|
+
border: 2.5px solid var(--nl-cursor-color, var(--exercise-accent, #705DC0));
|
|
1725
|
+
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.18);
|
|
1726
|
+
pointer-events: auto;
|
|
1727
|
+
cursor: grab;
|
|
1728
|
+
z-index: 4;
|
|
1729
|
+
touch-action: none; /* required for pointer capture on mobile */
|
|
1730
|
+
transition: box-shadow 0.12s ease, transform 0.12s ease;
|
|
1731
|
+
}
|
|
1732
|
+
|
|
1733
|
+
.number-line-cursor-ball:hover {
|
|
1734
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.28);
|
|
1735
|
+
transform: translate(-50%, -50%) scale(1.15);
|
|
1736
|
+
}
|
|
1737
|
+
|
|
1738
|
+
.number-line-cursor-ball--dragging {
|
|
1739
|
+
cursor: grabbing;
|
|
1740
|
+
box-shadow: 0 4px 14px rgba(0, 0, 0, 0.32);
|
|
1741
|
+
transform: translate(-50%, -50%) scale(1.25);
|
|
1742
|
+
transition: none;
|
|
1743
|
+
}
|
|
1744
|
+
|
|
1745
|
+
@keyframes number-line-cursor-jiggle {
|
|
1746
|
+
0%, 100% { transform: translate(-50%, -50%) translateX(0) rotate(0deg); }
|
|
1747
|
+
20% { transform: translate(-50%, -50%) translateX(-5px) rotate(-2deg); }
|
|
1748
|
+
40% { transform: translate(-50%, -50%) translateX(5px) rotate(2deg); }
|
|
1749
|
+
60% { transform: translate(-50%, -50%) translateX(-3px) rotate(-1deg); }
|
|
1750
|
+
80% { transform: translate(-50%, -50%) translateX(3px) rotate(1deg); }
|
|
1751
|
+
}
|
|
1752
|
+
|
|
1753
|
+
.number-line-cursor-ball--jiggle {
|
|
1754
|
+
animation: number-line-cursor-jiggle 0.55s ease-in-out 0.4s 2 both;
|
|
1755
|
+
}
|
|
1756
|
+
|
|
1757
|
+
.number-line-cursor-label {
|
|
1758
|
+
position: absolute;
|
|
1759
|
+
top: calc(50% - 0.6rem);
|
|
1760
|
+
display: block;
|
|
1761
|
+
max-width: min(8rem, 26vw);
|
|
1762
|
+
transform: translate(-50%, -100%);
|
|
1763
|
+
overflow: hidden;
|
|
1764
|
+
color: var(--nl-cursor-color, var(--exercise-text-light, #555));
|
|
1765
|
+
font-size: calc(var(--exercise-font-size) * 0.75);
|
|
1766
|
+
font-weight: var(--exercise-font-weight-bold, 600);
|
|
1767
|
+
line-height: 1.1;
|
|
1768
|
+
pointer-events: none;
|
|
1769
|
+
text-align: center;
|
|
1770
|
+
text-overflow: ellipsis;
|
|
1771
|
+
white-space: nowrap;
|
|
1772
|
+
z-index: 5;
|
|
1773
|
+
}
|
|
1774
|
+
|
|
1775
|
+
/* Validation feedback — mirrors the correct/incorrect axis colors */
|
|
1776
|
+
|
|
1777
|
+
.number-line-cursor-ball--correct,
|
|
1778
|
+
.number-line-cursor-fill--correct,
|
|
1779
|
+
.number-line-cursor-label--correct {
|
|
1780
|
+
--nl-cursor-color: #43B925;
|
|
1781
|
+
}
|
|
1782
|
+
|
|
1783
|
+
.number-line-cursor-ball--incorrect,
|
|
1784
|
+
.number-line-cursor-fill--incorrect,
|
|
1785
|
+
.number-line-cursor-label--incorrect {
|
|
1786
|
+
--nl-cursor-color: #D6234D;
|
|
1787
|
+
}
|
|
1788
|
+
|
|
1789
|
+
.number-line-cursor-ball:focus-visible {
|
|
1790
|
+
outline: 2px solid var(--nl-cursor-color, var(--exercise-accent, #705DC0));
|
|
1791
|
+
outline-offset: 3px;
|
|
1792
|
+
}
|
|
1793
|
+
|
|
1601
1794
|
/* ========================================
|
|
1602
1795
|
COUNTER UI
|
|
1796
|
+
ROW 6 — COUNTER UI
|
|
1603
1797
|
======================================== */
|
|
1604
1798
|
|
|
1605
1799
|
.number-line-counter {
|
|
1800
|
+
/* ── Color tokens (override to restyle) ── */
|
|
1801
|
+
--nl-counter-bg: #FFFFFF;
|
|
1802
|
+
--nl-counter-border-color: #FFCA76;
|
|
1803
|
+
--nl-counter-value-color: #705DC0;
|
|
1804
|
+
--nl-counter-btn-bg: #E7E2FF;
|
|
1805
|
+
--nl-counter-btn-color: #705DC0;
|
|
1806
|
+
|
|
1807
|
+
/* ── Layout ── */
|
|
1606
1808
|
display: flex;
|
|
1607
1809
|
flex-direction: column;
|
|
1608
1810
|
align-items: center;
|
|
1609
1811
|
gap: 0.25rem;
|
|
1610
|
-
margin
|
|
1812
|
+
margin: 0.5rem auto 0;
|
|
1611
1813
|
padding: 0.4rem 0.75rem;
|
|
1612
1814
|
width: fit-content;
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
border:
|
|
1617
|
-
border-radius: 8px;
|
|
1815
|
+
box-sizing: border-box;
|
|
1816
|
+
background: var(--nl-counter-bg);
|
|
1817
|
+
border: 1px solid var(--nl-counter-border-color);
|
|
1818
|
+
border-radius: 9px;
|
|
1618
1819
|
}
|
|
1619
1820
|
|
|
1620
|
-
|
|
1821
|
+
.number-line-counter-label {
|
|
1822
|
+
font-size: calc(var(--exercise-font-size) * 0.85);
|
|
1823
|
+
color: var(--exercise-text-light, #555);
|
|
1824
|
+
text-align: center;
|
|
1825
|
+
}
|
|
1621
1826
|
|
|
1622
1827
|
.number-line-counter-controls {
|
|
1623
1828
|
display: flex;
|
|
1624
1829
|
align-items: center;
|
|
1625
1830
|
justify-content: center;
|
|
1626
|
-
gap: 0.
|
|
1627
|
-
}
|
|
1628
|
-
|
|
1629
|
-
.number-line-counter-label {
|
|
1630
|
-
font-size: calc(var(--exercise-font-size) * 0.85);
|
|
1631
|
-
color: var(--exercise-text-light, #555);
|
|
1632
|
-
text-align: center;
|
|
1831
|
+
gap: 0.75rem;
|
|
1633
1832
|
}
|
|
1634
1833
|
|
|
1635
1834
|
.number-line-counter-value {
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
color: var(--exercise-text, #333);
|
|
1835
|
+
display: flex;
|
|
1836
|
+
align-items: center;
|
|
1837
|
+
justify-content: center;
|
|
1640
1838
|
min-width: 2ch;
|
|
1641
|
-
|
|
1839
|
+
font-size: calc(var(--exercise-font-size) * 2);
|
|
1840
|
+
font-weight: 700;
|
|
1841
|
+
font-variant-numeric: tabular-nums;
|
|
1642
1842
|
line-height: 1;
|
|
1843
|
+
text-align: center;
|
|
1844
|
+
color: var(--nl-counter-value-color);
|
|
1643
1845
|
}
|
|
1644
1846
|
|
|
1645
1847
|
.number-line-counter-btn {
|
|
1848
|
+
position: relative;
|
|
1646
1849
|
display: inline-flex;
|
|
1647
1850
|
align-items: center;
|
|
1648
1851
|
justify-content: center;
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
color: var(--exercise-accent, #705DC0);
|
|
1657
|
-
font-size: 1.1rem;
|
|
1658
|
-
font-weight: 500;
|
|
1659
|
-
line-height: 1;
|
|
1852
|
+
width: 48px;
|
|
1853
|
+
height: 48px;
|
|
1854
|
+
border: none;
|
|
1855
|
+
border-radius: 9px;
|
|
1856
|
+
background: var(--nl-counter-btn-bg);
|
|
1857
|
+
color: var(--nl-counter-btn-color);
|
|
1858
|
+
font-size: 0;
|
|
1660
1859
|
cursor: pointer;
|
|
1661
|
-
transition:
|
|
1860
|
+
transition: filter 0.12s ease, transform 0.12s ease;
|
|
1662
1861
|
-webkit-tap-highlight-color: transparent;
|
|
1663
1862
|
}
|
|
1664
1863
|
|
|
1665
|
-
.number-line-counter-btn:hover:not(:disabled)
|
|
1666
|
-
background: var(--exercise-accent, #705DC0);
|
|
1667
|
-
color: white;
|
|
1668
|
-
}
|
|
1864
|
+
.number-line-counter-btn:hover:not(:disabled) { filter: brightness(0.94); }
|
|
1669
1865
|
|
|
1670
|
-
.number-line-counter-btn:active:not(:disabled) {
|
|
1671
|
-
transform: scale(0.93);
|
|
1672
|
-
}
|
|
1866
|
+
.number-line-counter-btn:active:not(:disabled) { transform: scale(0.93); }
|
|
1673
1867
|
|
|
1674
|
-
.number-line-counter-btn:disabled
|
|
1675
|
-
|
|
1676
|
-
|
|
1868
|
+
.number-line-counter-btn:disabled { opacity: 0.45; cursor: not-allowed; }
|
|
1869
|
+
|
|
1870
|
+
/* ── Icon pseudo-elements (horizontal bar shared; vertical bar on + only) ── */
|
|
1871
|
+
|
|
1872
|
+
.number-line-counter-btn::before,
|
|
1873
|
+
.number-line-counter-btn::after {
|
|
1874
|
+
content: '';
|
|
1875
|
+
position: absolute;
|
|
1876
|
+
left: 50%;
|
|
1877
|
+
top: 50%;
|
|
1878
|
+
background: currentColor;
|
|
1879
|
+
border-radius: 999px;
|
|
1880
|
+
transform: translate(-50%, -50%);
|
|
1677
1881
|
}
|
|
1678
1882
|
|
|
1883
|
+
.number-line-counter-btn::before { width: 29px; height: 7px; }
|
|
1884
|
+
|
|
1885
|
+
.number-line-counter-btn[aria-label='Increase']::after { width: 7px; height: 29px; }
|
|
1886
|
+
|
|
1679
1887
|
/* ========================================
|
|
1680
1888
|
ACCESSIBILITY — FOCUS-VISIBLE RINGS (item 6)
|
|
1681
1889
|
======================================== */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mathscan/math-exercise-engine",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.10",
|
|
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",
|