@mathscan/math-exercise-engine 1.2.13 → 1.2.15

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
@@ -515,6 +515,87 @@ mark {
515
515
  opacity: 1;
516
516
  }
517
517
 
518
+ /* ========================================
519
+ DROP INPUT
520
+ ======================================== */
521
+
522
+ .drop-input {
523
+ direction: ltr;
524
+ text-align: center;
525
+ }
526
+
527
+ /* Border, color and padding come from .inline-input. 5em is InlineInput's default
528
+ box. Low specificity on purpose, so a host's .inline-input size wins. */
529
+
530
+ .drop-input.inline-input,
531
+ .drop-input-sizer.inline-input {
532
+ display: inline-flex;
533
+ align-items: center;
534
+ justify-content: center;
535
+ width: 5em;
536
+ /* A placeholder longer than the box grows it, on one line. Not fit-content: its
537
+ shrink-wrapped parent gets its width from this box, so that would wrap the text. */
538
+ min-width: max-content;
539
+ }
540
+
541
+ /* Keeps a line box when the box has no text, so it has the input's height. Only
542
+ when empty: beside a placeholder it is a flex item that a host's gap pushes apart. */
543
+
544
+ .drop-input--empty:empty::before,
545
+ .drop-input-sizer:empty::before {
546
+ content: '\200b';
547
+ }
548
+
549
+ /* Not a real <input>, so ::placeholder can't style it and it would read as an
550
+ answer. Faded from the input's own color, so it suits any host without an override.
551
+ Padding is here because hosts set .inline-input padding to 0 for a centered short
552
+ value, which leaves a box-sized placeholder touching the border. */
553
+
554
+ .drop-input-placeholder {
555
+ padding: 0 0.25em;
556
+ color: var(--exercise-placeholder, color-mix(in srgb, currentColor 50%, transparent));
557
+ font-weight: normal;
558
+ }
559
+
560
+ /* Sizer and chip share one grid cell, so the cell fits whichever is bigger. */
561
+
562
+ .drop-input.drop-input--filled {
563
+ display: inline-grid;
564
+ }
565
+
566
+ .drop-input--filled > * {
567
+ grid-area: 1 / 1;
568
+ }
569
+
570
+ .drop-input-sizer {
571
+ visibility: hidden;
572
+ }
573
+
574
+ .drop-input--locked {
575
+ cursor: default;
576
+ }
577
+
578
+ /* Unresolved stored value (no matching bank option) — plain text, still clickable to clear. */
579
+
580
+ .drop-input-unresolved {
581
+ padding: 0;
582
+ border: 0;
583
+ background: transparent;
584
+ color: inherit;
585
+ font: inherit;
586
+ cursor: pointer;
587
+ }
588
+
589
+ .drop-input.widget--correct {
590
+ border-color: var(--exercise-correct, #22c55e);
591
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--exercise-correct, #22c55e) 15%, transparent);
592
+ }
593
+
594
+ .drop-input.widget--incorrect {
595
+ border-color: var(--exercise-incorrect, #ef4444);
596
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--exercise-incorrect, #ef4444) 15%, transparent);
597
+ }
598
+
518
599
  /* ========================================
519
600
  VALIDATION STATUS
520
601
  ======================================== */
@@ -636,19 +717,23 @@ mark {
636
717
  box-shadow: 0 2px 4px rgba(0, 122, 204, 0.2);
637
718
  }
638
719
 
639
- /* Bounce hint — repeats until the student drags any option */
720
+ /* A gentle lift and soft halo invite the student to pick up a chip. */
640
721
 
641
722
  @keyframes drag-option-hint {
642
- 0%, 100% { transform: translateY(0) scale(1, 1); }
643
- 15% { transform: translateY(0) scale(1.12, 0.88); } /* squash before jump */
644
- 40% { transform: translateY(-14px) scale(0.94, 1.08) rotate(-4deg); } /* stretch in the air */
645
- 60% { transform: translateY(0) scale(1.1, 0.9); } /* squash on landing */
646
- 78% { transform: translateY(-4px) scale(0.98, 1.02) rotate(2deg); }
723
+ 0%, 100% {
724
+ transform: translateY(0);
725
+ box-shadow: 0 0 0 0 transparent, 0 0 0 transparent;
726
+ }
727
+ 40%, 55% {
728
+ transform: translateY(-5px);
729
+ box-shadow:
730
+ 0 0 0 5px color-mix(in srgb, var(--exercise-accent) 16%, transparent),
731
+ 0 6px 12px color-mix(in srgb, var(--exercise-accent) 22%, transparent);
732
+ }
647
733
  }
648
734
 
649
735
  .drag-option.drag-option--hint {
650
- transform-origin: center bottom;
651
- animation: drag-option-hint 0.8s ease-in-out 0.4s 2 both;
736
+ animation: drag-option-hint 1.8s ease-in-out 0.65s 1 both;
652
737
  }
653
738
 
654
739
  @media (prefers-reduced-motion: reduce) {
@@ -669,6 +754,35 @@ button.drag-option {
669
754
  font-weight: var(--exercise-font-weight);
670
755
  }
671
756
 
757
+ /* Cards holding images or colored text: on the accent surface they could become
758
+ unreadable, so only the surface changes. Text-only cards stay plain chips.
759
+ Same specificity as button.drag-option, so a host's restyle of that rule wins
760
+ here too and all cards in a bank match. */
761
+
762
+ button.drag-option--rich {
763
+ background: var(--exercise-bg);
764
+ color: var(--exercise-text);
765
+ }
766
+
767
+ /* The mobile fixed chip width would clip an image. */
768
+
769
+ .rich-drag-option-slot button.drag-option {
770
+ width: auto;
771
+ }
772
+
773
+ .rich-option-content {
774
+ display: inline-flex;
775
+ align-items: center;
776
+ white-space: nowrap;
777
+ }
778
+
779
+ /* Text takes its color from the card it sits on. COLOR_TEXT keeps its own. */
780
+
781
+ .rich-option-content .text-item,
782
+ .rich-option-content .bold-text {
783
+ color: inherit;
784
+ }
785
+
672
786
  /* ========================================
673
787
  RADIO OPTIONS
674
788
  ======================================== */
@@ -3672,6 +3786,7 @@ button.drag-option {
3672
3786
  --met-tutorial-next-text: #2e2463;
3673
3787
  --met-tutorial-text: #2e2463;
3674
3788
  --met-tutorial-bubble-border: #e0d9ff;
3789
+ --met-tutorial-fill-bg: #fff;
3675
3790
 
3676
3791
  position: fixed;
3677
3792
  inset: 0;
@@ -3799,12 +3914,24 @@ button.drag-option {
3799
3914
  animation: met-tutorial-hop 550ms ease-in-out both;
3800
3915
  }
3801
3916
 
3917
+ .met-tutorial-hop--digit {
3918
+ display: flex;
3919
+ align-items: center;
3920
+ justify-content: center;
3921
+ width: 24px;
3922
+ height: 24px;
3923
+ margin: -12px 0 0 -12px;
3924
+ color: white;
3925
+ font-size: 18px;
3926
+ font-weight: 700;
3927
+ }
3928
+
3802
3929
  @keyframes met-tutorial-hop {
3803
3930
  from { offset-distance: 0%; }
3804
3931
  to { offset-distance: 100%; opacity: 0.2; }
3805
3932
  }
3806
3933
 
3807
- /* Dots over the real (empty) answer box — never digits. */
3934
+ /* Guided digits (or generic dots) over the real answer box. */
3808
3935
 
3809
3936
  .met-tutorial-fill {
3810
3937
  display: flex;
@@ -3812,6 +3939,8 @@ button.drag-option {
3812
3939
  justify-content: center;
3813
3940
  gap: 0.35em;
3814
3941
  border-radius: 10px;
3942
+ /* Opaque: a prefill or typed value in the real box must not show through. */
3943
+ background: var(--met-tutorial-fill-bg);
3815
3944
  pointer-events: none;
3816
3945
  font-size: 18px;
3817
3946
  font-weight: 700;
@@ -4079,12 +4208,6 @@ button.drag-option {
4079
4208
  user-select: none;
4080
4209
  }
4081
4210
 
4082
- .draggable.dragging {
4083
- /* Hide original while drag overlay follows cursor to avoid duplicate render.
4084
- visibility:hidden preserves layout space so neighbors don't shift. */
4085
- visibility: hidden;
4086
- }
4087
-
4088
4211
  /* Droppable base */
4089
4212
  .droppable {
4090
4213
  display: inline-block;
@@ -4120,6 +4243,35 @@ button.drag-option {
4120
4243
  cursor: grabbing;
4121
4244
  }
4122
4245
 
4246
+ /* Carries the theme variables to the overlay; drops the root's own card look. */
4247
+ .math-exercise-renderer.rich-drag-overlay-root {
4248
+ padding: 0;
4249
+ background: transparent;
4250
+ border-radius: 0;
4251
+ box-shadow: none;
4252
+ }
4253
+
4254
+ .rich-drag-overlay-option {
4255
+ display: inline-flex;
4256
+ align-items: center;
4257
+ justify-content: center;
4258
+ }
4259
+
4260
+ /* Same light surface as .drag-option--rich in the bank. */
4261
+ .drag-overlay-option--rich {
4262
+ background: var(--exercise-bg);
4263
+ color: var(--exercise-text);
4264
+ }
4265
+
4266
+ /* The droppable wrapper carries the inline flow (its child .drop-input keeps
4267
+ .inline-widget for the no-dnd path). drop-active styling is shared with
4268
+ .inline-input-droppable below. */
4269
+ .drop-input-droppable {
4270
+ display: inline-flex;
4271
+ align-items: center;
4272
+ vertical-align: middle;
4273
+ }
4274
+
4123
4275
  /* Note: .drag-options and .drag-option base styles live in styles/widgets.css
4124
4276
  (themed via CSS vars). Don't duplicate them here. */
4125
4277
 
@@ -4127,20 +4279,34 @@ button.drag-option {
4127
4279
  display: inline-block;
4128
4280
  }
4129
4281
 
4130
- /* Slot container - maintains space when item is dragged out */
4282
+ /* Slot container - maintains space when item is dragged out.
4283
+ Shared by the legacy chip bank and the rich DRAGGABLE_OPTIONS bank. */
4131
4284
  .drag-option-slot {
4132
4285
  display: inline-flex;
4133
4286
  min-width: 60px;
4134
4287
  min-height: 44px;
4135
4288
  }
4136
4289
 
4137
- /* Empty slot - shows where item came from */
4290
+ /* Empty slot - shows where a chip came from, from pick-up on. It is sized by an
4291
+ invisible copy of the chip; the outline draws inside so the size stays exact.
4292
+ Solid, not dashed: dashed is the look of the inputs it must not be mistaken for. */
4138
4293
  .drag-option-empty {
4139
- min-width: 60px;
4140
- min-height: 44px;
4141
- border: 2px dashed #d4c5a9;
4294
+ outline: 1px solid color-mix(in srgb, var(--exercise-accent) 30%, transparent);
4295
+ outline-offset: -1px;
4142
4296
  border-radius: 8px;
4143
- background: rgba(212, 197, 169, 0.2);
4297
+ background: #FFEECA;
4298
+ }
4299
+
4300
+ .drag-option-empty .drag-option {
4301
+ visibility: hidden;
4302
+ }
4303
+
4304
+ /* Rich cards vary in size, so the slot always matches its hidden copy, even if a
4305
+ host gives .drag-option-empty a fixed width or a border that adds to the size.
4306
+ The outline above still draws the edge. */
4307
+ .rich-drag-option-slot .drag-option-empty {
4308
+ width: auto;
4309
+ border: 0;
4144
4310
  }
4145
4311
 
4146
4312
  /* Inline input droppable */
@@ -4203,8 +4369,19 @@ button.drag-option {
4203
4369
  opacity: 0.4;
4204
4370
  }
4205
4371
 
4206
- /* When droppable is active, style the input inside */
4207
- .inline-input-droppable.drop-active .inline-input {
4372
+ /* A chip in a DROP_INPUT stacks with its sizer instead of overlaying it, so a
4373
+ large card can grow the slot. See .drop-input--filled. */
4374
+ .inline-input-chip.placed-rich-option {
4375
+ display: inline-flex;
4376
+ align-items: center;
4377
+ justify-content: center;
4378
+ padding: 4px 8px;
4379
+ }
4380
+
4381
+ /* When droppable is active, style the input inside.
4382
+ DROP_INPUT shares the inline-input affordance. */
4383
+ .inline-input-droppable.drop-active .inline-input,
4384
+ .drop-input-droppable.drop-active .drop-input {
4208
4385
  border-color: #4a90d9;
4209
4386
  background-color: rgba(74, 144, 217, 0.05);
4210
4387
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mathscan/math-exercise-engine",
3
- "version": "1.2.13",
3
+ "version": "1.2.15",
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",