mr-md 2.0.0-beta → 2.1.0-beta

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.
@@ -56,11 +56,12 @@ function mdToHtml(md: string): { html: string; title: string; headings: { id: st
56
56
  });
57
57
 
58
58
  const headings: { id: string; text: string; level: number }[] = [];
59
+ const idPrefix = Math.random().toString(36).substring(2, 6);
59
60
  let headingIdCounter = 0;
60
61
 
61
62
  const renderer = new marked.Renderer();
62
63
  renderer.heading = ({ tokens, depth, text }) => {
63
- const id = `bk-heading-${headingIdCounter++}`;
64
+ const id = `bk-heading-${idPrefix}-${headingIdCounter++}`;
64
65
  if (depth === 1 || depth === 2) {
65
66
  const plainText = text.replace(/<[^>]+>/g, "");
66
67
  headings.push({ id, text: plainText, level: depth });
@@ -161,24 +162,63 @@ function renderSimulationControls(
161
162
  const props = block.props ?? {};
162
163
  const keys = Object.keys(block.tunables ?? props).filter((key) => {
163
164
  const value = props[key];
164
- return typeof value === "number" || typeof value === "boolean";
165
+ return typeof value === "number" || typeof value === "boolean" || typeof value === "string";
165
166
  });
166
167
 
167
168
  if (!keys.length || block.controls === "observe") return "";
168
169
 
170
+ const controls = keys.map((key) => {
171
+ const value = props[key];
172
+ const control = block.tunables?.[key] ?? {};
173
+ let type = control.type;
174
+ if (!type) {
175
+ if (typeof value === "boolean") type = "boolean";
176
+ else if (typeof value === "string") type = "text";
177
+ else type = "range";
178
+ }
179
+ const label = escHtml(control.label ?? key.replace(/([A-Z])/g, " $1"));
180
+ return { key, value, control, type, label };
181
+ });
182
+
183
+ const ranges = controls.filter((c) => c.type === "range");
184
+ const booleans = controls.filter((c) => c.type === "boolean");
185
+ const others = controls.filter((c) => c.type === "text" || c.type === "number");
186
+
187
+ const sortedControls = [...ranges, ...others, ...booleans];
188
+
189
+ let firstBooleanRendered = false;
190
+
169
191
  return `<div class="bk-sim-controls" aria-label="Simulation controls">
170
- ${keys
171
- .map((key) => {
172
- const value = props[key];
173
- const control = block.tunables?.[key] ?? {};
174
- const label = escHtml(control.label ?? key.replace(/([A-Z])/g, " $1"));
175
- if (typeof value === "boolean") {
176
- return `<label class="bk-sim-toggle">
192
+ ${sortedControls
193
+ .map(({ key, value, control, type, label }) => {
194
+ if (type === "boolean") {
195
+ const isFirst = !firstBooleanRendered;
196
+ firstBooleanRendered = true;
197
+ const extraClass = isFirst ? " bk-sim-toggle--first" : "";
198
+ return `<label class="bk-sim-toggle${extraClass}">
177
199
  <input type="checkbox" data-bk-prop="${escAttr(key)}" ${value ? "checked" : ""}>
178
200
  <span>${label}</span>
179
201
  </label>`;
180
202
  }
181
203
 
204
+ if (type === "text") {
205
+ return `<label class="bk-sim-text">
206
+ <span>${label}</span>
207
+ <input type="text" data-bk-prop="${escAttr(key)}" value="${escAttr(String(value))}">
208
+ </label>`;
209
+ }
210
+
211
+ if (type === "number") {
212
+ const min = control.min ?? "";
213
+ const max = control.max ?? "";
214
+ const step = control.step ?? "any";
215
+ return `<label class="bk-sim-number">
216
+ <span>${label}</span>
217
+ <input type="number" data-bk-prop="${escAttr(key)}" min="${min}" max="${max}" step="${step}" value="${value}">
218
+ </label>`;
219
+ }
220
+
221
+ // type === "range"
182
222
  const min = control.min ?? Math.min(0, Number(value));
183
223
  const max = control.max ?? Math.max(10, Number(value) * 2);
184
224
  const step = control.step ?? 1;
@@ -45,16 +45,25 @@ function resolveContent(
45
45
  }
46
46
  }
47
47
 
48
+ const baseDir = path.resolve(options.contentBase ?? ".");
49
+ if (!filePath.startsWith(baseDir) && options.strict !== false) {
50
+ throw new Error(`Security Error: Path traversal attempt outside contentBase: ${filePath}`);
51
+ }
52
+
48
53
  if (fs.existsSync(filePath)) {
49
54
  const stat = fs.statSync(filePath);
50
55
  if (stat.isFile()) {
51
56
  if (expectedType === "js" && (filePath.endsWith(".js") || filePath.endsWith(".ts") || filePath.endsWith(".jsx") || filePath.endsWith(".tsx"))) {
52
- const out = spawnSync("bun", ["build", "--target=browser", filePath]);
53
- if (out.status === 0) {
54
- return out.stdout.toString("utf-8");
57
+ if (!filePath.match(/^[a-zA-Z0-9_\-./\\]+$/)) {
58
+ console.warn(`\n ⚠ Invalid characters in file path for bun build: ${filePath}`);
55
59
  } else {
56
- console.warn(`\n ⚠ Bun build failed for ${filePath}:\n${out.stderr.toString("utf-8")}`);
57
- // fallback to reading raw
60
+ const out = spawnSync("bun", ["build", "--target=browser", filePath]);
61
+ if (out.status === 0) {
62
+ return out.stdout.toString("utf-8");
63
+ } else {
64
+ console.warn(`\n ⚠ Bun build failed for ${filePath}:\n${out.stderr.toString("utf-8")}`);
65
+ // fallback to reading raw
66
+ }
58
67
  }
59
68
  }
60
69
  return fs.readFileSync(filePath, "utf-8");
@@ -840,7 +840,7 @@ hr.bk-divider {
840
840
  color: var(--text-code);
841
841
  }
842
842
  .hljs {
843
- background: transparent !important;
843
+ background: transparent;
844
844
  }
845
845
 
846
846
  .bk-copy-btn {
@@ -939,8 +939,8 @@ hr.bk-divider {
939
939
  .bk-object--maximized .bk-code-block {
940
940
  flex-grow: 1;
941
941
  min-height: 0;
942
- height: auto !important;
943
- max-height: none !important;
942
+ height: auto;
943
+ max-height: none;
944
944
  }
945
945
  .bk-object--maximized .bk-embed-frame,
946
946
  .bk-object--maximized .bk-media {
@@ -948,19 +948,19 @@ hr.bk-divider {
948
948
  min-height: 0;
949
949
  min-width: 0;
950
950
  margin: auto;
951
- height: 100% !important;
952
- width: auto !important;
953
- max-width: 100% !important;
954
- max-height: 100% !important;
951
+ height: 100%;
952
+ width: auto;
953
+ max-width: 100%;
954
+ max-height: 100%;
955
955
  }
956
956
  .bk-object--maximized .bk-caption {
957
957
  margin-top: auto;
958
958
  }
959
959
  .bk-object--maximized .bk-embed-interactive iframe {
960
- pointer-events: auto !important;
960
+ pointer-events: auto;
961
961
  }
962
962
  .bk-object--maximized .bk-embed-overlay {
963
- display: none !important;
963
+ display: none;
964
964
  }
965
965
  .bk-object--blue {
966
966
  --object-accent: var(--blue);
@@ -1040,44 +1040,205 @@ hr.bk-divider {
1040
1040
  pointer-events: none;
1041
1041
  }
1042
1042
  .bk-sim-controls {
1043
- display: grid;
1044
- grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
1045
- gap: 10px 14px;
1046
- padding: 14px 16px;
1043
+ display: flex;
1044
+ flex-wrap: wrap;
1045
+ gap: 16px 20px;
1046
+ padding: 12px 16px;
1047
1047
  border-bottom: 1px solid var(--line);
1048
1048
  background: var(--panel);
1049
1049
  }
1050
1050
  .bk-sim-toggle,
1051
- .bk-sim-range {
1052
- display: grid;
1053
- gap: 6px;
1051
+ .bk-sim-text,
1052
+ .bk-sim-number {
1053
+ flex: 0 0 auto;
1054
+ min-width: 120px;
1055
+ display: flex;
1056
+ align-items: center;
1057
+ gap: 8px;
1054
1058
  color: var(--muted);
1055
- font-size: 12px;
1059
+ font-size: 13px;
1056
1060
  font-weight: 650;
1057
1061
  }
1058
- .bk-sim-toggle {
1059
- grid-template-columns: auto 1fr;
1060
- align-items: center;
1062
+ .bk-sim-toggle--first {
1063
+ margin-left: auto;
1061
1064
  }
1062
1065
  .bk-sim-range {
1063
- grid-template-columns: 1fr auto;
1066
+ flex: 1 1 240px;
1067
+ max-width: 400px;
1068
+ display: flex;
1064
1069
  align-items: center;
1070
+ gap: 12px;
1071
+ color: var(--muted);
1072
+ font-size: 13px;
1073
+ font-weight: 650;
1065
1074
  }
1066
- .bk-sim-range input {
1067
- grid-column: 1 / -1;
1068
- width: 100%;
1069
- accent-color: var(--accent);
1075
+ .bk-sim-range output {
1076
+ min-width: 4ch;
1077
+ text-align: right;
1078
+ font-variant-numeric: tabular-nums;
1079
+ }
1080
+ .bk-sim-range input[type="range"] {
1081
+ -webkit-appearance: none;
1082
+ appearance: none;
1083
+ flex-grow: 1;
1084
+ width: auto;
1085
+ height: 6px;
1086
+ border-radius: 4px;
1087
+ background: var(--line-strong);
1088
+ outline: none;
1089
+ margin: 0;
1090
+ transition: background 0.2s;
1091
+ cursor: pointer;
1070
1092
  }
1071
- .bk-sim-toggle input {
1072
- accent-color: var(--accent);
1093
+
1094
+ .bk-sim-range input[type="range"]:hover {
1095
+ background: var(--faint);
1096
+ }
1097
+
1098
+ .bk-sim-range input[type="range"]::-webkit-slider-thumb {
1099
+ -webkit-appearance: none;
1100
+ appearance: none;
1101
+ width: 16px;
1102
+ height: 16px;
1103
+ border-radius: 50%;
1104
+ background: var(--paper);
1105
+ border: 2px solid var(--accent);
1106
+ box-shadow: 0 2px 5px rgba(0,0,0,0.2);
1107
+ transition: transform 0.2s, box-shadow 0.2s;
1108
+ }
1109
+
1110
+ .bk-sim-range input[type="range"]::-moz-range-thumb {
1111
+ width: 16px;
1112
+ height: 16px;
1113
+ border-radius: 50%;
1114
+ background: var(--paper);
1115
+ border: 2px solid var(--accent);
1116
+ box-shadow: 0 2px 5px rgba(0,0,0,0.2);
1117
+ transition: transform 0.2s, box-shadow 0.2s;
1118
+ }
1119
+
1120
+ .bk-sim-range input[type="range"]:active::-webkit-slider-thumb {
1121
+ transform: scale(1.25);
1122
+ box-shadow: 0 0 0 4px var(--accent-soft);
1073
1123
  }
1074
- .bk-sim-toggle input,
1075
- .bk-sim-range input {
1124
+ .bk-sim-range input[type="range"]:active::-moz-range-thumb {
1125
+ transform: scale(1.25);
1126
+ box-shadow: 0 0 0 4px var(--accent-soft);
1127
+ }
1128
+
1129
+ .bk-sim-toggle input[type="checkbox"] {
1130
+ -webkit-appearance: none;
1131
+ appearance: none;
1132
+ width: 36px;
1133
+ height: 20px;
1134
+ border-radius: 10px;
1135
+ background: var(--line-strong);
1136
+ position: relative;
1076
1137
  cursor: pointer;
1138
+ outline: none;
1139
+ transition: background 0.3s;
1140
+ margin: 0;
1141
+ }
1142
+
1143
+ .bk-sim-toggle input[type="checkbox"]::after {
1144
+ content: "";
1145
+ position: absolute;
1146
+ top: 2px;
1147
+ left: 2px;
1148
+ width: 16px;
1149
+ height: 16px;
1150
+ border-radius: 50%;
1151
+ background: var(--paper);
1152
+ box-shadow: 0 2px 4px rgba(0,0,0,0.2);
1153
+ transition: transform 0.3s cubic-bezier(0.4, 0.0, 0.2, 1);
1077
1154
  }
1155
+
1156
+ .bk-sim-toggle input[type="checkbox"]:checked {
1157
+ background: var(--accent);
1158
+ }
1159
+
1160
+ .bk-sim-toggle input[type="checkbox"]:checked::after {
1161
+ transform: translateX(16px);
1162
+ }
1163
+
1078
1164
  .bk-sim-range output {
1079
1165
  color: var(--ink);
1080
1166
  font-variant-numeric: tabular-nums;
1167
+ background: var(--paper);
1168
+ padding: 2px 6px;
1169
+ border-radius: 4px;
1170
+ font-size: 11px;
1171
+ border: 1px solid var(--line);
1172
+ }
1173
+
1174
+ .bk-sim-text, .bk-sim-number {
1175
+ display: flex;
1176
+ flex-direction: column;
1177
+ gap: 4px;
1178
+ color: var(--muted);
1179
+ font-size: 12px;
1180
+ font-weight: 650;
1181
+ }
1182
+
1183
+ .bk-sim-text input, .bk-sim-number input {
1184
+ width: 100%;
1185
+ padding: 6px 8px;
1186
+ font-size: 13px;
1187
+ border: 1px solid var(--line-strong);
1188
+ border-radius: 6px;
1189
+ background: var(--paper);
1190
+ color: var(--ink);
1191
+ outline: none;
1192
+ transition: border-color 0.2s, box-shadow 0.2s;
1193
+ box-sizing: border-box;
1194
+ }
1195
+
1196
+ .bk-sim-text input:focus, .bk-sim-number input:focus {
1197
+ border-color: var(--accent);
1198
+ box-shadow: 0 0 0 3px var(--accent-soft);
1199
+ }
1200
+
1201
+ /* UI Reactivity for Simulation Controls */
1202
+
1203
+ [data-ui="neo"] .bk-sim-controls {
1204
+ border-bottom: 2px solid var(--ink);
1205
+ background: var(--paper);
1206
+ }
1207
+ [data-ui="neo"] .bk-sim-text input, [data-ui="neo"] .bk-sim-number input {
1208
+ border: 2px solid var(--ink);
1209
+ border-radius: 0;
1210
+ box-shadow: 2px 2px 0 var(--ink);
1211
+ }
1212
+ [data-ui="neo"] .bk-sim-text input:focus, [data-ui="neo"] .bk-sim-number input:focus {
1213
+ box-shadow: 4px 4px 0 var(--accent);
1214
+ }
1215
+ [data-ui="neo"] .bk-sim-toggle input[type="checkbox"] {
1216
+ border-radius: 0;
1217
+ border: 2px solid var(--ink);
1218
+ }
1219
+ [data-ui="neo"] .bk-sim-toggle input[type="checkbox"]::after {
1220
+ border-radius: 0;
1221
+ border: 2px solid var(--ink);
1222
+ top: -2px; left: -2px;
1223
+ }
1224
+ [data-ui="neo"] .bk-sim-range input[type="range"]::-webkit-slider-thumb {
1225
+ border-radius: 0;
1226
+ border: 2px solid var(--ink);
1227
+ box-shadow: 2px 2px 0 var(--ink);
1228
+ }
1229
+
1230
+ [data-ui="playful"] .bk-sim-controls {
1231
+ border-bottom: 2px dashed var(--line-strong);
1232
+ padding: 12px 14px;
1233
+ }
1234
+ [data-ui="playful"] .bk-sim-text input, [data-ui="playful"] .bk-sim-number input {
1235
+ border-radius: 12px;
1236
+ background: var(--faint);
1237
+ border-color: transparent;
1238
+ }
1239
+ [data-ui="playful"] .bk-sim-text input:focus, [data-ui="playful"] .bk-sim-number input:focus {
1240
+ background: var(--paper);
1241
+ border-color: var(--accent);
1081
1242
  }
1082
1243
  .bk-media {
1083
1244
  width: 100%;
@@ -1358,7 +1519,7 @@ hr.bk-divider {
1358
1519
  padding-right: 20px;
1359
1520
  }
1360
1521
  .bk-columns {
1361
- grid-template-columns: 1fr !important;
1522
+ grid-template-columns: 1fr;
1362
1523
  }
1363
1524
  }
1364
1525
 
@@ -1379,7 +1540,7 @@ html[data-ui="neo"] {
1379
1540
  /* NO COLOR OVERRIDES FOR NEO! Let palettes do their job. */
1380
1541
 
1381
1542
  html[data-ui="neo"] * {
1382
- border-radius: 0 !important;
1543
+ border-radius: 0;
1383
1544
  }
1384
1545
 
1385
1546
  html[data-ui="neo"] .bk-callout,
@@ -1391,20 +1552,20 @@ html[data-ui="neo"] .bk-segmented-control,
1391
1552
  html[data-ui="neo"] .bk-icon-btn,
1392
1553
  html[data-ui="neo"] .bk-sidebar-expand,
1393
1554
  html[data-ui="neo"] .bk-brand-mark {
1394
- border-width: 2px !important;
1395
- border-style: solid !important;
1396
- border-color: var(--line-strong, var(--line)) !important;
1555
+ border-width: 2px;
1556
+ border-style: solid;
1557
+ border-color: var(--line-strong, var(--line));
1397
1558
  }
1398
1559
 
1399
1560
  html[data-ui="neo"] .bk-sidebar {
1400
- border-top: none !important;
1401
- border-bottom: none !important;
1402
- border-left: none !important;
1403
- border-right: 2px solid var(--line-strong, var(--line)) !important;
1561
+ border-top: none;
1562
+ border-bottom: none;
1563
+ border-left: none;
1564
+ border-right: 2px solid var(--line-strong, var(--line));
1404
1565
  }
1405
1566
 
1406
1567
  html[data-ui="neo"] blockquote {
1407
- border-left-width: 6px !important;
1568
+ border-left-width: 6px;
1408
1569
  }
1409
1570
 
1410
1571
  html[data-ui="neo"] .bk-callout,
@@ -1413,8 +1574,8 @@ html[data-ui="neo"] .bk-theme-panel,
1413
1574
  html[data-ui="neo"] .bk-lesson-card,
1414
1575
  html[data-ui="neo"] .bk-sidebar-expand,
1415
1576
  html[data-ui="neo"] .bk-icon-btn {
1416
- box-shadow: 4px 4px 0px 0px var(--ink) !important;
1417
- transition: all 0.15s ease-out !important;
1577
+ box-shadow: 4px 4px 0px 0px var(--ink);
1578
+ transition: all 0.15s ease-out;
1418
1579
  }
1419
1580
 
1420
1581
  html[data-ui="neo"][data-theme="dark"] .bk-callout,
@@ -1423,7 +1584,7 @@ html[data-ui="neo"][data-theme="dark"] .bk-theme-panel,
1423
1584
  html[data-ui="neo"][data-theme="dark"] .bk-lesson-card,
1424
1585
  html[data-ui="neo"][data-theme="dark"] .bk-sidebar-expand,
1425
1586
  html[data-ui="neo"][data-theme="dark"] .bk-icon-btn {
1426
- box-shadow: 4px 4px 0px 0px var(--accent) !important;
1587
+ box-shadow: 4px 4px 0px 0px var(--accent);
1427
1588
  }
1428
1589
 
1429
1590
  html[data-ui="neo"] .bk-lesson-card {
@@ -1431,12 +1592,12 @@ html[data-ui="neo"] .bk-lesson-card {
1431
1592
  }
1432
1593
 
1433
1594
  html[data-ui="neo"] .bk-lesson-card:hover {
1434
- transform: translate(-2px, -2px) !important;
1435
- box-shadow: 6px 6px 0px 0px var(--ink) !important;
1595
+ transform: translate(-2px, -2px);
1596
+ box-shadow: 6px 6px 0px 0px var(--ink);
1436
1597
  }
1437
1598
 
1438
1599
  html[data-ui="neo"][data-theme="dark"] .bk-lesson-card:hover {
1439
- box-shadow: 6px 6px 0px 0px var(--accent) !important;
1600
+ box-shadow: 6px 6px 0px 0px var(--accent);
1440
1601
  }
1441
1602
 
1442
1603
 
@@ -1452,14 +1613,14 @@ html[data-ui="playful"] .bk-lesson-card,
1452
1613
  html[data-ui="playful"] .bk-object,
1453
1614
  html[data-ui="playful"] .bk-path-terminal,
1454
1615
  html[data-ui="playful"] .bk-theme-panel {
1455
- border-radius: 24px !important;
1456
- border-width: 3px !important;
1457
- border-style: solid !important;
1458
- border-color: var(--line) !important;
1459
- box-shadow: 0 6px 0 0 var(--line), 0 16px 30px rgba(0,0,0,0.06) !important;
1460
- transition: transform 0.2s cubic-bezier(0.34, 1.56, 0.64, 1), box-shadow 0.2s cubic-bezier(0.34, 1.56, 0.64, 1) !important;
1616
+ border-radius: 24px;
1617
+ border-width: 3px;
1618
+ border-style: solid;
1619
+ border-color: var(--line);
1620
+ box-shadow: 0 6px 0 0 var(--line), 0 16px 30px rgba(0,0,0,0.06);
1621
+ transition: transform 0.2s cubic-bezier(0.34, 1.56, 0.64, 1), box-shadow 0.2s cubic-bezier(0.34, 1.56, 0.64, 1);
1461
1622
  overflow: hidden;
1462
- background: var(--paper) !important;
1623
+ background: var(--paper);
1463
1624
  }
1464
1625
 
1465
1626
  html[data-ui="playful"][data-theme="dark"] .bk-callout,
@@ -1467,56 +1628,56 @@ html[data-ui="playful"][data-theme="dark"] .bk-lesson-card,
1467
1628
  html[data-ui="playful"][data-theme="dark"] .bk-object,
1468
1629
  html[data-ui="playful"][data-theme="dark"] .bk-path-terminal,
1469
1630
  html[data-ui="playful"][data-theme="dark"] .bk-theme-panel {
1470
- box-shadow: 0 6px 0 0 var(--line), 0 16px 30px rgba(0,0,0,0.3) !important;
1631
+ box-shadow: 0 6px 0 0 var(--line), 0 16px 30px rgba(0,0,0,0.3);
1471
1632
  }
1472
1633
 
1473
1634
  @media (hover: hover) and (pointer: fine) {
1474
1635
  html[data-ui="playful"] .bk-lesson-card:hover,
1475
1636
  html[data-ui="playful"] .bk-icon-btn:hover {
1476
- transform: translateY(2px) !important;
1477
- box-shadow: 0 4px 0 0 var(--line), 0 8px 20px rgba(0,0,0,0.1) !important;
1637
+ transform: translateY(2px);
1638
+ box-shadow: 0 4px 0 0 var(--line), 0 8px 20px rgba(0,0,0,0.1);
1478
1639
  }
1479
1640
 
1480
1641
  html[data-ui="playful"][data-theme="dark"] .bk-lesson-card:hover,
1481
1642
  html[data-ui="playful"][data-theme="dark"] .bk-icon-btn:hover {
1482
- box-shadow: 0 4px 0 0 var(--line), 0 8px 20px rgba(0,0,0,0.4) !important;
1643
+ box-shadow: 0 4px 0 0 var(--line), 0 8px 20px rgba(0,0,0,0.4);
1483
1644
  }
1484
1645
  }
1485
1646
 
1486
1647
 
1487
1648
  html[data-ui="playful"] .bk-lesson-card:active,
1488
1649
  html[data-ui="playful"] .bk-icon-btn:active {
1489
- transform: translateY(6px) !important;
1490
- box-shadow: 0 0px 0 0 var(--line) !important;
1650
+ transform: translateY(6px);
1651
+ box-shadow: 0 0px 0 0 var(--line);
1491
1652
  }
1492
1653
 
1493
1654
  html[data-ui="playful"] .bk-object-header {
1494
- border-bottom: 3px solid var(--line) !important;
1495
- padding: 16px 20px !important;
1655
+ border-bottom: 3px solid var(--line);
1656
+ padding: 16px 20px;
1496
1657
  }
1497
1658
 
1498
1659
  html[data-ui="playful"] .bk-sidebar {
1499
- border-top-right-radius: 32px !important;
1500
- border-bottom-right-radius: 32px !important;
1501
- border-right: 3px solid var(--line) !important;
1502
- box-shadow: 4px 0 24px rgba(0,0,0,0.05) !important;
1660
+ border-top-right-radius: 32px;
1661
+ border-bottom-right-radius: 32px;
1662
+ border-right: 3px solid var(--line);
1663
+ box-shadow: 4px 0 24px rgba(0,0,0,0.05);
1503
1664
  }
1504
1665
 
1505
1666
  html[data-ui="playful"][data-theme="dark"] .bk-sidebar {
1506
- box-shadow: 4px 0 24px rgba(0,0,0,0.3) !important;
1667
+ box-shadow: 4px 0 24px rgba(0,0,0,0.3);
1507
1668
  }
1508
1669
 
1509
1670
  html[data-ui="playful"] .bk-segment-btn {
1510
- border-radius: 16px !important;
1511
- font-weight: 800 !important;
1671
+ border-radius: 16px;
1672
+ font-weight: 800;
1512
1673
  }
1513
1674
 
1514
1675
  html[data-ui="playful"] .bk-icon-btn {
1515
- border-radius: 50% !important;
1516
- border: 3px solid var(--line) !important;
1517
- box-shadow: 0 4px 0 0 var(--line) !important;
1518
- transition: transform 0.2s cubic-bezier(0.34, 1.56, 0.64, 1), box-shadow 0.2s cubic-bezier(0.34, 1.56, 0.64, 1) !important;
1519
- background: var(--paper) !important;
1676
+ border-radius: 50%;
1677
+ border: 3px solid var(--line);
1678
+ box-shadow: 0 4px 0 0 var(--line);
1679
+ transition: transform 0.2s cubic-bezier(0.34, 1.56, 0.64, 1), box-shadow 0.2s cubic-bezier(0.34, 1.56, 0.64, 1);
1680
+ background: var(--paper);
1520
1681
  }
1521
1682
 
1522
1683
  html[data-ui="playful"] .bk-hero h1 {
@@ -1530,14 +1691,14 @@ html[data-ui="playful"] .bk-lesson-card h2 {
1530
1691
  }
1531
1692
 
1532
1693
  html[data-ui="playful"] .bk-timeline-node {
1533
- border-radius: 50% !important;
1534
- border-width: 3px !important;
1535
- box-shadow: 0 3px 0 0 var(--line) !important;
1694
+ border-radius: 50%;
1695
+ border-width: 3px;
1696
+ box-shadow: 0 3px 0 0 var(--line);
1536
1697
  }
1537
1698
 
1538
1699
  html[data-ui="playful"] blockquote {
1539
- border-left: 6px solid var(--accent) !important;
1700
+ border-left: 6px solid var(--accent);
1540
1701
  border-radius: 12px;
1541
- background: var(--accent-soft) !important;
1542
- padding: 16px 20px !important;
1702
+ background: var(--accent-soft);
1703
+ padding: 16px 20px;
1543
1704
  }
package/src/types.ts CHANGED
@@ -199,7 +199,7 @@ export interface BuildOptions {
199
199
  /** Light or dark mode. Default: `'auto'` */
200
200
  theme?: "light" | "dark" | "auto";
201
201
  /** Visual color palette of the generated page. Default: `'ink'` */
202
- palette?: "ink" | "field" | "ember";
202
+ palette?: "ink" | "field" | "ember" | "elixir" | "trunk" | "lava";
203
203
  /**
204
204
  * Structural layout styling.
205
205
  * - `standard`: clean, rounded standard aesthetic
@@ -217,6 +217,8 @@ export interface BuildOptions {
217
217
  preset?: LessonPreset;
218
218
  /** If true, throws errors on missing files and invalid blocks to prevent silent failures. Default: `true` */
219
219
  strict?: boolean;
220
+ /** If true, inlines CSS and JS into a single HTML file. If false, outputs assets to an `assets/` folder. Default: `true` */
221
+ standalone?: boolean;
220
222
  }
221
223
 
222
224
  /** Configuration for simulation blocks */
@@ -236,6 +238,7 @@ export interface SimulationConfig extends SimulationOptions {
236
238
 
237
239
  /** Represents a single interactive control in a simulation (e.g., a slider) */
238
240
  export interface SimulationControl {
241
+ type?: "range" | "number" | "text" | "boolean";
239
242
  label?: string;
240
243
  min?: number;
241
244
  max?: number;