mevebu 1.3.0 → 1.4.0

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.
Files changed (164) hide show
  1. package/mevebu/app/mevebu.js +260 -208
  2. package/mevebu/app/tailwind.css +1635 -2079
  3. package/mevebu/app/tailwind.src.css +155 -1127
  4. package/mevebu/index.html +2 -2
  5. package/mevebu/libs/js/core.js +235 -95
  6. package/mevebu/libs/js/tools/abstract-checklist.js +5 -7
  7. package/mevebu/libs/js/tools/add-subtract-days.js +9 -11
  8. package/mevebu/libs/js/tools/age-calculator.js +7 -8
  9. package/mevebu/libs/js/tools/api-request-builder.js +3 -4
  10. package/mevebu/libs/js/tools/area-converter.js +7 -9
  11. package/mevebu/libs/js/tools/aspect-ratio.js +8 -9
  12. package/mevebu/libs/js/tools/average-calculator.js +4 -5
  13. package/mevebu/libs/js/tools/base64.js +2 -3
  14. package/mevebu/libs/js/tools/binary-calculator.js +6 -7
  15. package/mevebu/libs/js/tools/border-radius.js +7 -9
  16. package/mevebu/libs/js/tools/box-shadow.js +6 -7
  17. package/mevebu/libs/js/tools/break-even.js +4 -5
  18. package/mevebu/libs/js/tools/business-days.js +7 -8
  19. package/mevebu/libs/js/tools/canonical-tag.js +5 -6
  20. package/mevebu/libs/js/tools/caption-counter.js +5 -7
  21. package/mevebu/libs/js/tools/case-converter.js +10 -11
  22. package/mevebu/libs/js/tools/case-inspector.js +14 -16
  23. package/mevebu/libs/js/tools/cash-denomination.js +7 -8
  24. package/mevebu/libs/js/tools/cash-flow.js +7 -8
  25. package/mevebu/libs/js/tools/cert-expiry.js +5 -7
  26. package/mevebu/libs/js/tools/changelog-generator.js +4 -6
  27. package/mevebu/libs/js/tools/character-counter.js +10 -13
  28. package/mevebu/libs/js/tools/checklist.js +6 -7
  29. package/mevebu/libs/js/tools/citation-number-check.js +10 -13
  30. package/mevebu/libs/js/tools/coin-flip.js +9 -11
  31. package/mevebu/libs/js/tools/color-converter.js +6 -7
  32. package/mevebu/libs/js/tools/color-palette.js +6 -8
  33. package/mevebu/libs/js/tools/color-picker.js +4 -5
  34. package/mevebu/libs/js/tools/content-calendar.js +3 -4
  35. package/mevebu/libs/js/tools/contrast-checker.js +5 -6
  36. package/mevebu/libs/js/tools/cron-builder.js +4 -6
  37. package/mevebu/libs/js/tools/css-clamp.js +5 -6
  38. package/mevebu/libs/js/tools/css-formatter.js +4 -7
  39. package/mevebu/libs/js/tools/css-grid.js +6 -7
  40. package/mevebu/libs/js/tools/css-minifier.js +5 -8
  41. package/mevebu/libs/js/tools/csv-to-json.js +2 -3
  42. package/mevebu/libs/js/tools/csv-viewer.js +9 -11
  43. package/mevebu/libs/js/tools/currency-format.js +5 -7
  44. package/mevebu/libs/js/tools/daily-planner.js +9 -10
  45. package/mevebu/libs/js/tools/data-masker.js +6 -8
  46. package/mevebu/libs/js/tools/data-storage.js +7 -9
  47. package/mevebu/libs/js/tools/data-validator.js +9 -11
  48. package/mevebu/libs/js/tools/date-difference.js +6 -7
  49. package/mevebu/libs/js/tools/date-format.js +11 -13
  50. package/mevebu/libs/js/tools/delimiter-converter.js +3 -5
  51. package/mevebu/libs/js/tools/discount-calculator.js +4 -5
  52. package/mevebu/libs/js/tools/doi-resolver.js +4 -5
  53. package/mevebu/libs/js/tools/env-formatter.js +3 -6
  54. package/mevebu/libs/js/tools/equation-formatter.js +5 -9
  55. package/mevebu/libs/js/tools/expense-split.js +6 -7
  56. package/mevebu/libs/js/tools/fake-data.js +8 -13
  57. package/mevebu/libs/js/tools/find-replace.js +3 -5
  58. package/mevebu/libs/js/tools/flashcard-creator.js +9 -10
  59. package/mevebu/libs/js/tools/font-pairing.js +6 -8
  60. package/mevebu/libs/js/tools/fraction-calculator.js +10 -13
  61. package/mevebu/libs/js/tools/git-command-builder.js +11 -22
  62. package/mevebu/libs/js/tools/gpa-calculator.js +8 -10
  63. package/mevebu/libs/js/tools/grade-calculator.js +7 -8
  64. package/mevebu/libs/js/tools/gradient-generator.js +10 -12
  65. package/mevebu/libs/js/tools/habit-tracker.js +8 -9
  66. package/mevebu/libs/js/tools/hash-generator.js +3 -5
  67. package/mevebu/libs/js/tools/hashtag-formatter.js +6 -8
  68. package/mevebu/libs/js/tools/hmac-generator.js +3 -5
  69. package/mevebu/libs/js/tools/hpp-calculator.js +4 -5
  70. package/mevebu/libs/js/tools/html-entity.js +2 -3
  71. package/mevebu/libs/js/tools/html-formatter.js +3 -6
  72. package/mevebu/libs/js/tools/http-status.js +5 -7
  73. package/mevebu/libs/js/tools/inventory-quantity.js +7 -8
  74. package/mevebu/libs/js/tools/json-formatter.js +3 -6
  75. package/mevebu/libs/js/tools/json-minifier.js +5 -8
  76. package/mevebu/libs/js/tools/json-to-csv.js +3 -5
  77. package/mevebu/libs/js/tools/json-to-typescript.js +2 -5
  78. package/mevebu/libs/js/tools/json-validator.js +2 -3
  79. package/mevebu/libs/js/tools/jwt-decoder.js +6 -7
  80. package/mevebu/libs/js/tools/jwt-expiry.js +5 -6
  81. package/mevebu/libs/js/tools/jwt-generator.js +4 -6
  82. package/mevebu/libs/js/tools/length-converter.js +7 -9
  83. package/mevebu/libs/js/tools/license-reference.js +10 -13
  84. package/mevebu/libs/js/tools/line-counter.js +5 -7
  85. package/mevebu/libs/js/tools/list-difference.js +5 -7
  86. package/mevebu/libs/js/tools/list-merger.js +4 -5
  87. package/mevebu/libs/js/tools/locale-number.js +5 -8
  88. package/mevebu/libs/js/tools/markdown-preview.js +5 -8
  89. package/mevebu/libs/js/tools/markdown-table.js +5 -7
  90. package/mevebu/libs/js/tools/markup-calculator.js +4 -5
  91. package/mevebu/libs/js/tools/meeting-agenda.js +8 -9
  92. package/mevebu/libs/js/tools/meta-preview.js +12 -13
  93. package/mevebu/libs/js/tools/meta-tags.js +6 -7
  94. package/mevebu/libs/js/tools/mime-types.js +5 -7
  95. package/mevebu/libs/js/tools/name-picker.js +2 -3
  96. package/mevebu/libs/js/tools/number-base-converter.js +9 -12
  97. package/mevebu/libs/js/tools/password-generator.js +9 -12
  98. package/mevebu/libs/js/tools/password-strength.js +21 -25
  99. package/mevebu/libs/js/tools/percent-to-fraction.js +6 -7
  100. package/mevebu/libs/js/tools/percentage-calculator.js +6 -9
  101. package/mevebu/libs/js/tools/percentage-change.js +4 -5
  102. package/mevebu/libs/js/tools/permutation-combination.js +9 -12
  103. package/mevebu/libs/js/tools/prefix-suffix.js +2 -3
  104. package/mevebu/libs/js/tools/priority-matrix.js +6 -10
  105. package/mevebu/libs/js/tools/profit-margin.js +4 -5
  106. package/mevebu/libs/js/tools/qr-generator.js +3 -4
  107. package/mevebu/libs/js/tools/qr-scanner.js +11 -12
  108. package/mevebu/libs/js/tools/quadratic-solver.js +7 -8
  109. package/mevebu/libs/js/tools/query-builder.js +2 -3
  110. package/mevebu/libs/js/tools/quiz-formatter.js +4 -6
  111. package/mevebu/libs/js/tools/ratio-calculator.js +7 -8
  112. package/mevebu/libs/js/tools/readability.js +12 -15
  113. package/mevebu/libs/js/tools/reading-time.js +5 -7
  114. package/mevebu/libs/js/tools/reference-completeness.js +6 -8
  115. package/mevebu/libs/js/tools/regex-tester.js +7 -10
  116. package/mevebu/libs/js/tools/remove-duplicate-lines.js +2 -3
  117. package/mevebu/libs/js/tools/remove-extra-spaces.js +3 -5
  118. package/mevebu/libs/js/tools/robots-txt.js +3 -4
  119. package/mevebu/libs/js/tools/roman-numeral-converter.js +6 -8
  120. package/mevebu/libs/js/tools/sales-commission.js +8 -9
  121. package/mevebu/libs/js/tools/scientific-calculator.js +10 -14
  122. package/mevebu/libs/js/tools/scientific-notation.js +8 -9
  123. package/mevebu/libs/js/tools/script-planner.js +8 -10
  124. package/mevebu/libs/js/tools/selling-price.js +4 -5
  125. package/mevebu/libs/js/tools/semver-helper.js +3 -5
  126. package/mevebu/libs/js/tools/sentence-length.js +9 -10
  127. package/mevebu/libs/js/tools/sequence-analyzer.js +7 -8
  128. package/mevebu/libs/js/tools/sitemap-xml.js +4 -7
  129. package/mevebu/libs/js/tools/slug-converter.js +3 -5
  130. package/mevebu/libs/js/tools/slug-preview.js +6 -7
  131. package/mevebu/libs/js/tools/sort-lines.js +3 -5
  132. package/mevebu/libs/js/tools/speed-converter.js +7 -9
  133. package/mevebu/libs/js/tools/spin-wheel.js +19 -17
  134. package/mevebu/libs/js/tools/sql-formatter.js +5 -9
  135. package/mevebu/libs/js/tools/sql-minifier.js +5 -8
  136. package/mevebu/libs/js/tools/standard-deviation.js +6 -9
  137. package/mevebu/libs/js/tools/stopwatch.js +4 -6
  138. package/mevebu/libs/js/tools/study-schedule.js +8 -9
  139. package/mevebu/libs/js/tools/subtitle-formatter.js +10 -11
  140. package/mevebu/libs/js/tools/table-to-markdown.js +8 -12
  141. package/mevebu/libs/js/tools/tax-calculator.js +4 -5
  142. package/mevebu/libs/js/tools/temperature-converter.js +8 -11
  143. package/mevebu/libs/js/tools/text-diff.js +11 -7
  144. package/mevebu/libs/js/tools/text-direction.js +3 -5
  145. package/mevebu/libs/js/tools/text-reverser.js +3 -5
  146. package/mevebu/libs/js/tools/time-duration.js +8 -9
  147. package/mevebu/libs/js/tools/timestamp-converter.js +4 -6
  148. package/mevebu/libs/js/tools/timezone-converter.js +9 -13
  149. package/mevebu/libs/js/tools/title-length.js +6 -7
  150. package/mevebu/libs/js/tools/unicode-inspector.js +4 -5
  151. package/mevebu/libs/js/tools/url-inspector.js +8 -9
  152. package/mevebu/libs/js/tools/url-parser.js +12 -13
  153. package/mevebu/libs/js/tools/utf8-bytes.js +6 -9
  154. package/mevebu/libs/js/tools/uuid-generator.js +5 -9
  155. package/mevebu/libs/js/tools/version-compare.js +3 -4
  156. package/mevebu/libs/js/tools/video-duration.js +5 -7
  157. package/mevebu/libs/js/tools/volume-converter.js +7 -9
  158. package/mevebu/libs/js/tools/weekly-planner.js +8 -10
  159. package/mevebu/libs/js/tools/weight-converter.js +7 -9
  160. package/mevebu/libs/js/tools/weighted-average.js +4 -5
  161. package/mevebu/libs/js/tools/word-counter.js +10 -13
  162. package/mevebu/libs/js/tools/word-limit.js +4 -5
  163. package/mevebu/libs/js/tools/yaml-formatter.js +6 -7
  164. package/package.json +1 -1
@@ -17,16 +17,16 @@ function fmt(n) {
17
17
  }
18
18
 
19
19
  const kvRow = (key, value) =>
20
- el("div", { class: "mvb-kv__row" }, el("span", { class: "mvb-kv__k" }, key), el("span", { class: "mvb-kv__v" }, value));
20
+ el("div", { class: "grid gap-3 bg-deck px-3 py-[9px] [grid-template-columns:minmax(90px,34%)_minmax(0,1fr)]" }, el("span", { class: "wrap-anywhere text-2xs font-semibold text-ink-2" }, key), el("span", { class: "wrap-anywhere font-mono text-2xs" }, value));
21
21
 
22
22
  export function mount(slot) {
23
23
  const source = textInput({ value: "12.5", "aria-label": "Percent", placeholder: "12.5" });
24
- const fraction = el("span", { class: "mvb-kv__v" }, "-");
25
- const decimal = el("span", { class: "mvb-kv__v" }, "-");
26
- const mixed = el("span", { class: "mvb-kv__v" }, "-");
24
+ const fraction = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
25
+ const decimal = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
26
+ const mixed = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
27
27
  const kv = el(
28
28
  "div",
29
- { class: "mvb-kv" },
29
+ { class: "grid gap-px overflow-hidden rounded-r1 border border-orbit bg-orbit" },
30
30
  kvRow("Fraction", fraction),
31
31
  kvRow("Decimal", decimal),
32
32
  kvRow("Mixed number", mixed)
@@ -85,8 +85,7 @@ export function mount(slot) {
85
85
  stack(panel("Percent", source), error),
86
86
  stack(panel("Result", kv))
87
87
  ),
88
- foot: actions(copyButton(() => text, "Copy fraction", "fill")),
89
- });
88
+ foot: actions(copyButton(() => text, "Copy fraction", "fill"))});
90
89
  slot.append(card.node);
91
90
  run();
92
91
 
@@ -9,17 +9,16 @@ function fmt(n) {
9
9
  export function mount(slot) {
10
10
  const modes = [
11
11
  { value: "of", label: "Percent of a value" },
12
- { value: "is", label: "Value as a percent" },
13
- ];
12
+ { value: "is", label: "Value as a percent" }];
14
13
  const labelA = el("span", {}, "Percent");
15
14
  const labelB = el("span", {}, "Value");
16
15
  const a = numberInput({ value: "15", "aria-label": "First value" });
17
16
  const b = numberInput({ value: "200", "aria-label": "Second value" });
18
- const result = el("span", { class: "mvb-stat__v" }, "-");
17
+ const result = el("span", { class: "wrap-anywhere text-xl font-bold tracking-[-0.02em] tabular-nums" }, "-");
19
18
  const stats = el(
20
19
  "div",
21
- { class: "mvb-stats" },
22
- el("div", { class: "mvb-stat" }, result, el("span", { class: "mvb-stat__k" }, "Result"))
20
+ { class: "grid gap-2.5 [grid-template-columns:repeat(auto-fit,minmax(120px,1fr))]" },
21
+ el("div", { class: "grid gap-0.5 rounded-r1 border border-orbit bg-deck-2 px-3.5 py-3" }, result, el("span", { class: "text-2xs font-semibold text-ink-3" }, "Result"))
23
22
  );
24
23
  const error = notice("error");
25
24
  let mode = "of";
@@ -63,8 +62,7 @@ export function mount(slot) {
63
62
  mode = value;
64
63
  relabel();
65
64
  run();
66
- },
67
- });
65
+ }});
68
66
 
69
67
  a.addEventListener("input", run);
70
68
  b.addEventListener("input", run);
@@ -74,8 +72,7 @@ export function mount(slot) {
74
72
  stack(panel("Mode", chips), panel(labelA, a), panel(labelB, b), error),
75
73
  stack(stats)
76
74
  ),
77
- foot: actions(copyButton(() => text, "Copy result", "fill")),
78
- });
75
+ foot: actions(copyButton(() => text, "Copy result", "fill"))});
79
76
  slot.append(card.node);
80
77
  relabel();
81
78
  run();
@@ -6,8 +6,8 @@ function fmt(n) {
6
6
  }
7
7
 
8
8
  function tile(label) {
9
- const value = el("span", { class: "mvb-stat__v" }, "-");
10
- const node = el("div", { class: "mvb-stat" }, value, el("span", { class: "mvb-stat__k" }, label));
9
+ const value = el("span", { class: "wrap-anywhere text-xl font-bold tracking-[-0.02em] tabular-nums" }, "-");
10
+ const node = el("div", { class: "grid gap-0.5 rounded-r1 border border-orbit bg-deck-2 px-3.5 py-3" }, value, el("span", { class: "text-2xs font-semibold text-ink-3" }, label));
11
11
  node.set = (text) => { value.textContent = text; };
12
12
  return node;
13
13
  }
@@ -18,7 +18,7 @@ export function mount(slot) {
18
18
  const change = tile("Change");
19
19
  const difference = tile("Difference");
20
20
  const direction = tile("Direction");
21
- const stats = el("div", { class: "mvb-stats" }, change, difference, direction);
21
+ const stats = el("div", { class: "grid gap-2.5 [grid-template-columns:repeat(auto-fit,minmax(120px,1fr))]" }, change, difference, direction);
22
22
  const error = notice("error");
23
23
  let text = "";
24
24
 
@@ -59,8 +59,7 @@ export function mount(slot) {
59
59
  stack(panel("Starting value", start), panel("Ending value", end), error),
60
60
  stack(stats)
61
61
  ),
62
- foot: actions(copyButton(() => text, "Copy result", "fill")),
63
- });
62
+ foot: actions(copyButton(() => text, "Copy result", "fill"))});
64
63
  slot.append(card.node);
65
64
  run();
66
65
 
@@ -6,8 +6,8 @@ function fmt(n) {
6
6
  }
7
7
 
8
8
  function tile(label) {
9
- const value = el("span", { class: "mvb-stat__v" }, "-");
10
- const node = el("div", { class: "mvb-stat" }, value, el("span", { class: "mvb-stat__k" }, label));
9
+ const value = el("span", { class: "wrap-anywhere text-xl font-bold tracking-[-0.02em] tabular-nums" }, "-");
10
+ const node = el("div", { class: "grid gap-0.5 rounded-r1 border border-orbit bg-deck-2 px-3.5 py-3" }, value, el("span", { class: "text-2xs font-semibold text-ink-3" }, label));
11
11
  node.set = (text) => { value.textContent = text; };
12
12
  return node;
13
13
  }
@@ -16,12 +16,12 @@ export function mount(slot) {
16
16
  const nInput = numberInput({ value: "5", min: "0", step: "1", "aria-label": "n" });
17
17
  const rInput = numberInput({ value: "3", min: "0", step: "1", "aria-label": "r" });
18
18
  const count = tile("Result");
19
- const stats = el("div", { class: "mvb-stats" }, count);
20
- const formulaRow = el("span", { class: "mvb-kv__v" }, "-");
19
+ const stats = el("div", { class: "grid gap-2.5 [grid-template-columns:repeat(auto-fit,minmax(120px,1fr))]" }, count);
20
+ const formulaRow = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
21
21
  const kv = el(
22
22
  "div",
23
- { class: "mvb-kv" },
24
- el("div", { class: "mvb-kv__row" }, el("span", { class: "mvb-kv__k" }, "Formula"), formulaRow)
23
+ { class: "grid gap-px overflow-hidden rounded-r1 border border-orbit bg-orbit" },
24
+ el("div", { class: "grid gap-3 bg-deck px-3 py-[9px] [grid-template-columns:minmax(90px,34%)_minmax(0,1fr)]" }, el("span", { class: "wrap-anywhere text-2xs font-semibold text-ink-2" }, "Formula"), formulaRow)
25
25
  );
26
26
  const error = notice("error");
27
27
  let mode = "npr";
@@ -75,16 +75,14 @@ export function mount(slot) {
75
75
  const chips = chipRow(
76
76
  [
77
77
  { value: "npr", label: "Permutation nPr" },
78
- { value: "ncr", label: "Combination nCr" },
79
- ],
78
+ { value: "ncr", label: "Combination nCr" }],
80
79
  {
81
80
  value: mode,
82
81
  label: "Mode",
83
82
  onChange: (value) => {
84
83
  mode = value;
85
84
  run();
86
- },
87
- }
85
+ }}
88
86
  );
89
87
 
90
88
  nInput.addEventListener("input", run);
@@ -95,8 +93,7 @@ export function mount(slot) {
95
93
  stack(panel("n", nInput), panel("r", rInput), panel("Mode", chips), error),
96
94
  stack(stats, panel("Working", kv))
97
95
  ),
98
- foot: actions(copyButton(() => text, "Copy result", "fill")),
99
- });
96
+ foot: actions(copyButton(() => text, "Copy result", "fill"))});
100
97
  slot.append(card.node);
101
98
  run();
102
99
 
@@ -6,7 +6,7 @@ export function mount(slot) {
6
6
  const prefixField = textInput({ placeholder: "Prefix", "aria-label": "Prefix" });
7
7
  const suffixField = textInput({ placeholder: "Suffix", "aria-label": "Suffix" });
8
8
  const numbering = checkbox("Numbering", { checked: false });
9
- const result = textArea({ rows: 11, readonly: true, class: "mvb-in mvb-in--area mvb-in--mono", "aria-label": "Result" });
9
+ const result = textArea({ rows: 11, readonly: true, "aria-label": "Result" });
10
10
 
11
11
  function run() {
12
12
  const text = source.value;
@@ -35,8 +35,7 @@ export function mount(slot) {
35
35
  ),
36
36
  stack(panel("Result", result))
37
37
  ),
38
- foot: actions(copyButton(() => result.value, "Copy result", "fill")),
39
- });
38
+ foot: actions(copyButton(() => result.value, "Copy result", "fill"))});
40
39
  slot.append(card.node);
41
40
  run();
42
41
 
@@ -6,8 +6,7 @@ const QUADRANTS = [
6
6
  { id: "do", name: "Do now" },
7
7
  { id: "schedule", name: "Schedule" },
8
8
  { id: "delegate", name: "Delegate" },
9
- { id: "drop", name: "Drop" },
10
- ];
9
+ { id: "drop", name: "Drop" }];
11
10
 
12
11
  export function mount(slot) {
13
12
  const PIN = "priority-matrix";
@@ -17,8 +16,7 @@ export function mount(slot) {
17
16
  const task = textInput({ placeholder: "Task", "aria-label": "Task" });
18
17
  const quadrant = selectInput(QUADRANTS.map((q) => ({ value: q.id, label: q.name })), {
19
18
  value: "do",
20
- "aria-label": "Quadrant",
21
- });
19
+ "aria-label": "Quadrant"});
22
20
 
23
21
  const matrix = stack();
24
22
 
@@ -39,8 +37,7 @@ export function mount(slot) {
39
37
  const text = el("span", {}, item.text);
40
38
  const move = selectInput(QUADRANTS.map((q) => ({ value: q.id, label: q.name })), {
41
39
  value: item.q,
42
- "aria-label": `Move ${item.text}`,
43
- });
40
+ "aria-label": `Move ${item.text}`});
44
41
  move.addEventListener("change", () => {
45
42
  item.q = move.value;
46
43
  save();
@@ -52,12 +49,12 @@ export function mount(slot) {
52
49
  save();
53
50
  render();
54
51
  });
55
- return el("div", { class: "mvb-row" }, text, move, remove);
52
+ return el("div", { class: "flex min-w-0 flex-wrap items-center gap-2" }, text, move, remove);
56
53
  }
57
54
 
58
55
  function quadrantBox(q) {
59
56
  const items = list.filter((item) => item.q === q.id);
60
- const body = items.length ? stack(...items.map(taskRow)) : el("span", { class: "mvb-note" }, "Empty");
57
+ const body = items.length ? stack(...items.map(taskRow)) : el("span", { class: "border-s-[3px] border-transparent ps-2 text-sm text-ink-3" }, "Empty");
61
58
  return panel(`${q.name} (${items.length})`, body);
62
59
  }
63
60
 
@@ -89,8 +86,7 @@ export function mount(slot) {
89
86
  stack(panel("Task", task), panel("Quadrant", quadrant)),
90
87
  matrix
91
88
  ),
92
- foot: actions(copyMatrix, addButton),
93
- });
89
+ foot: actions(copyMatrix, addButton)});
94
90
  slot.append(card.node);
95
91
  render();
96
92
 
@@ -6,8 +6,8 @@ function fmt(n) {
6
6
  }
7
7
 
8
8
  function tile(label) {
9
- const value = el("span", { class: "mvb-stat__v" }, "-");
10
- const node = el("div", { class: "mvb-stat" }, value, el("span", { class: "mvb-stat__k" }, label));
9
+ const value = el("span", { class: "wrap-anywhere text-xl font-bold tracking-[-0.02em] tabular-nums" }, "-");
10
+ const node = el("div", { class: "grid gap-0.5 rounded-r1 border border-orbit bg-deck-2 px-3.5 py-3" }, value, el("span", { class: "text-2xs font-semibold text-ink-3" }, label));
11
11
  node.set = (text) => { value.textContent = text; };
12
12
  return node;
13
13
  }
@@ -18,7 +18,7 @@ export function mount(slot) {
18
18
  const profit = tile("Profit");
19
19
  const margin = tile("Margin");
20
20
  const markup = tile("Markup");
21
- const stats = el("div", { class: "mvb-stats" }, profit, margin, markup);
21
+ const stats = el("div", { class: "grid gap-2.5 [grid-template-columns:repeat(auto-fit,minmax(120px,1fr))]" }, profit, margin, markup);
22
22
  const error = notice("error");
23
23
  let text = "";
24
24
 
@@ -58,8 +58,7 @@ export function mount(slot) {
58
58
  stack(panel("Cost", cost), panel("Selling price", price), error),
59
59
  stack(stats)
60
60
  ),
61
- foot: actions(copyButton(() => text, "Copy result", "fill")),
62
- });
61
+ foot: actions(copyButton(() => text, "Copy result", "fill"))});
63
62
  slot.append(card.node);
64
63
  run();
65
64
 
@@ -6,8 +6,8 @@ import qrcode from "../vendor/qrcode-generator.mjs";
6
6
  export function mount(slot) {
7
7
  const content = textArea({ rows: 4, value: "https://mevebu.app", "aria-label": "Text or link", placeholder: "Text or link" });
8
8
  const sizeField = numberInput({ value: "256", min: "120", max: "1024", step: "8", "aria-label": "Size in pixels" });
9
- const canvas = el("canvas", { class: "mvb-qrcode", role: "img", "aria-label": "QR code" });
10
- const reading = el("p", { class: "mvb-note" });
9
+ const canvas = el("canvas", { class: "h-auto w-[min(300px,100%)] [image-rendering:pixelated]", role: "img", "aria-label": "QR code" });
10
+ const reading = el("p", { class: "border-s-[3px] border-transparent ps-2 text-sm text-ink-3" });
11
11
  const error = notice("error");
12
12
  let svg = "";
13
13
 
@@ -86,8 +86,7 @@ export function mount(slot) {
86
86
  stack(panel("Text or link", content), panel("Size in pixels", sizeField), error),
87
87
  stack(panel("Preview", canvas), reading)
88
88
  ),
89
- foot: actions(copyButton, svgButton, pngButton),
90
- });
89
+ foot: actions(copyButton, svgButton, pngButton)});
91
90
  slot.append(card.node);
92
91
  render();
93
92
 
@@ -19,9 +19,9 @@ function loadJsQR() {
19
19
  }
20
20
 
21
21
  export function mount(slot) {
22
- const video = el("video", { class: "mvb-qrview", playsinline: true, muted: true, hidden: true });
22
+ const video = el("video", { class: "w-[min(420px,100%)] rounded-r2 border border-orbit-hard bg-black", playsinline: true, muted: true, hidden: true });
23
23
  const fileField = el("input", { type: "file", accept: "image/*", hidden: true, "aria-label": "Image file" });
24
- const status = el("p", { class: "mvb-note" });
24
+ const status = el("p", { class: "border-s-[3px] border-transparent ps-2 text-sm text-ink-3" });
25
25
  const found = output({ rows: 3 });
26
26
  const error = notice("error");
27
27
 
@@ -32,15 +32,15 @@ export function mount(slot) {
32
32
  const paint = scratch.getContext("2d", { willReadFrequently: true });
33
33
 
34
34
  const startButton = button("Start camera", { icon: "camera", kind: "fill" });
35
- const stopButton = el("button", { class: "mvb-btn mvb-btn--plain", type: "button", hidden: true }, icon("video-off"), el("span", {}, "Stop"));
35
+ const stopButton = el("button", { class: "inline-flex min-h-[var(--ctl)] cursor-pointer items-center justify-center gap-2 rounded-r1 border px-4 text-sm font-semibold whitespace-nowrap no-underline transition-[background-color,border-color,color,transform] duration-150 active:scale-[0.97] disabled:cursor-not-allowed disabled:opacity-45 border-orbit bg-deck text-ink hover:border-orbit-hard hover:bg-deck-2", type: "button", hidden: true }, icon("video-off"), el("span", {}, "Stop"));
36
36
  const uploadButton = button("Choose an image", { icon: "upload" });
37
37
  const copyButton = button("Copy", { icon: "copy" });
38
- const openWrap = el("span", { class: "mvb-row" });
38
+ const openWrap = el("span", { class: "flex min-w-0 flex-wrap items-center gap-2" });
39
39
 
40
40
  const drop = el(
41
41
  "div",
42
- { class: "mvb-drop", role: "button", tabindex: "0", "aria-label": "Drop an image here or choose one" },
43
- icon("image-up", "mvb-ico mvb-ico--lg"),
42
+ { class: "grid cursor-pointer justify-items-center gap-2 rounded-r2 border border-dashed border-orbit-hard px-4 py-7 text-sm text-ink-3 transition-colors duration-150 hover:border-jade hover:bg-jade-soft data-[drag]:border-jade data-[drag]:bg-jade-soft", role: "button", tabindex: "0", "aria-label": "Drop an image here or choose one" },
43
+ icon("image-up", "size-[26px] flex-none"),
44
44
  el("p", {}, "Drop an image here, or choose one.")
45
45
  );
46
46
 
@@ -50,7 +50,7 @@ export function mount(slot) {
50
50
  error.write("");
51
51
  openWrap.replaceChildren();
52
52
  if (/^https?:\/\//i.test(text.trim())) {
53
- openWrap.append(el("a", { class: "mvb-btn mvb-btn--plain", href: text.trim(), target: "_blank", rel: "noopener" },
53
+ openWrap.append(el("a", { class: "inline-flex min-h-[var(--ctl)] cursor-pointer items-center justify-center gap-2 rounded-r1 border px-4 text-sm font-semibold whitespace-nowrap no-underline transition-[background-color,border-color,color,transform] duration-150 active:scale-[0.97] disabled:cursor-not-allowed disabled:opacity-45 border-orbit bg-deck text-ink hover:border-orbit-hard hover:bg-deck-2", href: text.trim(), target: "_blank", rel: "noopener" },
54
54
  icon("external-link"), el("span", {}, "Open link")));
55
55
  }
56
56
  toast("Found");
@@ -159,12 +159,12 @@ export function mount(slot) {
159
159
  });
160
160
  drop.addEventListener("dragover", (event) => {
161
161
  event.preventDefault();
162
- drop.classList.add("mvb-drag");
162
+ drop.dataset.drag = "";
163
163
  });
164
- drop.addEventListener("dragleave", () => drop.classList.remove("mvb-drag"));
164
+ drop.addEventListener("dragleave", () => delete drop.dataset.drag);
165
165
  drop.addEventListener("drop", (event) => {
166
166
  event.preventDefault();
167
- drop.classList.remove("mvb-drag");
167
+ delete drop.dataset.drag;
168
168
  if (event.dataTransfer.files[0]) scanFile(event.dataTransfer.files[0]);
169
169
  });
170
170
  copyButton.addEventListener("click", async () => {
@@ -180,8 +180,7 @@ export function mount(slot) {
180
180
  stack(video, drop, fileField, status, error),
181
181
  panel("Decoded", found)
182
182
  ),
183
- foot: actions(openWrap, copyButton),
184
- });
183
+ foot: actions(openWrap, copyButton)});
185
184
  slot.append(card.node);
186
185
 
187
186
  return { unmount: stop };
@@ -6,19 +6,19 @@ function fmt(n) {
6
6
  }
7
7
 
8
8
  const kvRow = (key, value) =>
9
- el("div", { class: "mvb-kv__row" }, el("span", { class: "mvb-kv__k" }, key), el("span", { class: "mvb-kv__v" }, value));
9
+ el("div", { class: "grid gap-3 bg-deck px-3 py-[9px] [grid-template-columns:minmax(90px,34%)_minmax(0,1fr)]" }, el("span", { class: "wrap-anywhere text-2xs font-semibold text-ink-2" }, key), el("span", { class: "wrap-anywhere font-mono text-2xs" }, value));
10
10
 
11
11
  export function mount(slot) {
12
12
  const a = numberInput({ value: "1", "aria-label": "Coefficient a" });
13
13
  const b = numberInput({ value: "-3", "aria-label": "Coefficient b" });
14
14
  const c = numberInput({ value: "2", "aria-label": "Coefficient c" });
15
- const discriminant = el("span", { class: "mvb-kv__v" }, "-");
16
- const root1 = el("span", { class: "mvb-kv__v" }, "-");
17
- const root2 = el("span", { class: "mvb-kv__v" }, "-");
18
- const nature = el("span", { class: "mvb-kv__v" }, "-");
15
+ const discriminant = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
16
+ const root1 = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
17
+ const root2 = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
18
+ const nature = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
19
19
  const kv = el(
20
20
  "div",
21
- { class: "mvb-kv" },
21
+ { class: "grid gap-px overflow-hidden rounded-r1 border border-orbit bg-orbit" },
22
22
  kvRow("Discriminant", discriminant),
23
23
  kvRow("Root 1", root1),
24
24
  kvRow("Root 2", root2),
@@ -82,8 +82,7 @@ export function mount(slot) {
82
82
  stack(panel("a", a), panel("b", b), panel("c", c), error),
83
83
  stack(panel("Solution", kv))
84
84
  ),
85
- foot: actions(copyButton(() => text, "Copy roots", "fill")),
86
- });
85
+ foot: actions(copyButton(() => text, "Copy roots", "fill"))});
87
86
  slot.append(card.node);
88
87
  run();
89
88
 
@@ -13,7 +13,7 @@ const isUrl = (value) => {
13
13
 
14
14
  export function mount(slot) {
15
15
  const base = textInput({ value: "https://example.com/search", "aria-label": "Base URL" });
16
- const params = textArea({ rows: 8, class: "mvb-in mvb-in--area mvb-in--mono", value: "q=hello world\npage=2", placeholder: "q=hello world\npage=2", "aria-label": "Parameters" });
16
+ const params = textArea({ rows: 8, value: "q=hello world\npage=2", placeholder: "q=hello world\npage=2", "aria-label": "Parameters" });
17
17
  const encode = checkbox("Encode values", { checked: true });
18
18
  const result = output({ rows: 4 });
19
19
  const error = notice("error");
@@ -60,8 +60,7 @@ export function mount(slot) {
60
60
  stack(panel("Base URL", base), panel("Parameters", params), encode, error),
61
61
  stack(panel("URL", result))
62
62
  ),
63
- foot: actions(copyButton(() => result.textContent, "Copy URL", "fill")),
64
- });
63
+ foot: actions(copyButton(() => result.textContent, "Copy URL", "fill"))});
65
64
  slot.append(card.node);
66
65
  build();
67
66
 
@@ -23,10 +23,9 @@ export function mount(slot) {
23
23
  const source = textArea({
24
24
  rows: 14,
25
25
  "aria-label": "Questions",
26
- placeholder: "Question one\n- First option\n- Second option\n* The correct option\n\nQuestion two\n- ...",
27
- });
28
- const quiz = textArea({ rows: 12, readonly: true, class: "mvb-in mvb-in--area mvb-in--mono", "aria-label": "Quiz" });
29
- const key = textArea({ rows: 12, readonly: true, class: "mvb-in mvb-in--area mvb-in--mono", "aria-label": "Answer key" });
26
+ placeholder: "Question one\n- First option\n- Second option\n* The correct option\n\nQuestion two\n- ..."});
27
+ const quiz = textArea({ rows: 12, readonly: true, "aria-label": "Quiz" });
28
+ const key = textArea({ rows: 12, readonly: true, "aria-label": "Answer key" });
30
29
  const error = notice("error");
31
30
 
32
31
  function render() {
@@ -59,8 +58,7 @@ export function mount(slot) {
59
58
  foot: actions(
60
59
  copyButton(() => key.value, "Copy key"),
61
60
  copyButton(() => quiz.value, "Copy quiz", "fill")
62
- ),
63
- });
61
+ )});
64
62
  slot.append(card.node);
65
63
  render();
66
64
 
@@ -17,7 +17,7 @@ function fmt(n) {
17
17
  }
18
18
 
19
19
  const kvRow = (key, value) =>
20
- el("div", { class: "mvb-kv__row" }, el("span", { class: "mvb-kv__k" }, key), el("span", { class: "mvb-kv__v" }, value));
20
+ el("div", { class: "grid gap-3 bg-deck px-3 py-[9px] [grid-template-columns:minmax(90px,34%)_minmax(0,1fr)]" }, el("span", { class: "wrap-anywhere text-2xs font-semibold text-ink-2" }, key), el("span", { class: "wrap-anywhere font-mono text-2xs" }, value));
21
21
 
22
22
  export function mount(slot) {
23
23
  const ratioInput = textInput({ value: "12:18:24", "aria-label": "Ratio" });
@@ -25,12 +25,12 @@ export function mount(slot) {
25
25
  const b = numberInput({ placeholder: "B", "aria-label": "Second term" });
26
26
  const c = numberInput({ placeholder: "C", "aria-label": "Third term" });
27
27
  const d = numberInput({ placeholder: "D", "aria-label": "Fourth term" });
28
- const simplified = el("span", { class: "mvb-kv__v" }, "-");
29
- const decimal = el("span", { class: "mvb-kv__v" }, "-");
30
- const missing = el("span", { class: "mvb-kv__v" }, "-");
28
+ const simplified = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
29
+ const decimal = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
30
+ const missing = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
31
31
  const kv = el(
32
32
  "div",
33
- { class: "mvb-kv" },
33
+ { class: "grid gap-px overflow-hidden rounded-r1 border border-orbit bg-orbit" },
34
34
  kvRow("Simplified ratio", simplified),
35
35
  kvRow("Decimal", decimal),
36
36
  kvRow("Missing term", missing)
@@ -87,13 +87,12 @@ export function mount(slot) {
87
87
  body: bench(
88
88
  stack(
89
89
  panel("Ratio", ratioInput),
90
- panel("Proportion", el("div", { class: "mvb-row" }, a, b, c, d)),
90
+ panel("Proportion", el("div", { class: "flex min-w-0 flex-wrap items-center gap-2" }, a, b, c, d)),
91
91
  error
92
92
  ),
93
93
  stack(panel("Result", kv))
94
94
  ),
95
- foot: actions(copyButton(() => text, "Copy result", "fill")),
96
- });
95
+ foot: actions(copyButton(() => text, "Copy result", "fill"))});
97
96
  slot.append(card.node);
98
97
  run();
99
98
 
@@ -27,15 +27,15 @@ function syllablesOf(word) {
27
27
  }
28
28
 
29
29
  function tile(label, initial = "-") {
30
- const value = el("span", { class: "mvb-stat__v" }, initial);
31
- const node = el("div", { class: "mvb-stat" }, value, el("span", { class: "mvb-stat__k" }, label));
30
+ const value = el("span", { class: "wrap-anywhere text-xl font-bold tracking-[-0.02em] tabular-nums" }, initial);
31
+ const node = el("div", { class: "grid gap-0.5 rounded-r1 border border-orbit bg-deck-2 px-3.5 py-3" }, value, el("span", { class: "text-2xs font-semibold text-ink-3" }, label));
32
32
  node.set = (text) => { value.textContent = text; };
33
33
  return node;
34
34
  }
35
35
 
36
- const kvRow = (key, value) => el("div", { class: "mvb-kv__row" },
37
- el("span", { class: "mvb-kv__k" }, key),
38
- el("span", { class: "mvb-kv__v" }, value));
36
+ const kvRow = (key, value) => el("div", { class: "grid gap-3 bg-deck px-3 py-[9px] [grid-template-columns:minmax(90px,34%)_minmax(0,1fr)]" },
37
+ el("span", { class: "wrap-anywhere text-2xs font-semibold text-ink-2" }, key),
38
+ el("span", { class: "wrap-anywhere font-mono text-2xs" }, value));
39
39
 
40
40
  export function mount(slot) {
41
41
  const source = textArea({ rows: 12, "aria-label": "Text", placeholder: "Type or paste text" });
@@ -43,14 +43,13 @@ export function mount(slot) {
43
43
  const easeTile = tile("Reading ease");
44
44
  const gradeTile = tile("Grade level");
45
45
  const fogTile = tile("Gunning Fog");
46
- const stats = el("div", { class: "mvb-stats" }, easeTile, gradeTile, fogTile);
46
+ const stats = el("div", { class: "grid gap-2.5 [grid-template-columns:repeat(auto-fit,minmax(120px,1fr))]" }, easeTile, gradeTile, fogTile);
47
47
 
48
48
  const counts = {
49
- words: el("span", { class: "mvb-kv__v" }, "0"),
50
- sentences: el("span", { class: "mvb-kv__v" }, "0"),
51
- syllables: el("span", { class: "mvb-kv__v" }, "0"),
52
- };
53
- const countList = el("div", { class: "mvb-kv" },
49
+ words: el("span", { class: "wrap-anywhere font-mono text-2xs" }, "0"),
50
+ sentences: el("span", { class: "wrap-anywhere font-mono text-2xs" }, "0"),
51
+ syllables: el("span", { class: "wrap-anywhere font-mono text-2xs" }, "0")};
52
+ const countList = el("div", { class: "grid gap-px overflow-hidden rounded-r1 border border-orbit bg-orbit" },
54
53
  kvRow("Words", counts.words),
55
54
  kvRow("Sentences", counts.sentences),
56
55
  kvRow("Syllables", counts.syllables));
@@ -90,8 +89,7 @@ export function mount(slot) {
90
89
  `Reading ease ${ease.toFixed(1)}`,
91
90
  `Grade level ${grade.toFixed(1)}`,
92
91
  `Gunning Fog ${fog.toFixed(1)}`,
93
- `${words.length} words, ${sentences.length} sentences, ${syllableCount} syllables`,
94
- ].join("\n");
92
+ `${words.length} words, ${sentences.length} sentences, ${syllableCount} syllables`].join("\n");
95
93
  }
96
94
 
97
95
  source.addEventListener("input", run);
@@ -101,8 +99,7 @@ export function mount(slot) {
101
99
  stack(panel("Text", source)),
102
100
  stack(stats, countList, note)
103
101
  ),
104
- foot: actions(copyButton(() => summary, "Copy scores", "fill")),
105
- });
102
+ foot: actions(copyButton(() => summary, "Copy scores", "fill"))});
106
103
  slot.append(card.node);
107
104
  run();
108
105
 
@@ -23,8 +23,8 @@ const clock = (minutes) => {
23
23
  };
24
24
 
25
25
  function tile(label) {
26
- const value = el("span", { class: "mvb-stat__v" }, "0:00");
27
- const node = el("div", { class: "mvb-stat" }, value, el("span", { class: "mvb-stat__k" }, label));
26
+ const value = el("span", { class: "wrap-anywhere text-xl font-bold tracking-[-0.02em] tabular-nums" }, "0:00");
27
+ const node = el("div", { class: "grid gap-0.5 rounded-r1 border border-orbit bg-deck-2 px-3.5 py-3" }, value, el("span", { class: "text-2xs font-semibold text-ink-3" }, label));
28
28
  node.set = (text) => { value.textContent = text; };
29
29
  return node;
30
30
  }
@@ -36,7 +36,7 @@ export function mount(slot) {
36
36
  const wordsTile = tile("Words");
37
37
  const readTile = tile("Reading time");
38
38
  const speakTile = tile("Speaking time");
39
- const stats = el("div", { class: "mvb-stats" }, wordsTile, readTile, speakTile);
39
+ const stats = el("div", { class: "grid gap-2.5 [grid-template-columns:repeat(auto-fit,minmax(120px,1fr))]" }, wordsTile, readTile, speakTile);
40
40
  const note = notice("note");
41
41
  note.write("Reading and speaking speeds vary by person and text.");
42
42
 
@@ -53,8 +53,7 @@ export function mount(slot) {
53
53
  summary = [
54
54
  `${words} words`,
55
55
  `Reading ${words ? clock(readMinutes) : "0:00"} at ${wpm} words per minute`,
56
- `Speaking ${words ? clock(speakMinutes) : "0:00"} at 130 words per minute`,
57
- ].join("\n");
56
+ `Speaking ${words ? clock(speakMinutes) : "0:00"} at 130 words per minute`].join("\n");
58
57
  }
59
58
 
60
59
  source.addEventListener("input", run);
@@ -65,8 +64,7 @@ export function mount(slot) {
65
64
  stack(panel("Text", source), panel("Reading speed", speed, "Words per minute")),
66
65
  stack(stats, note)
67
66
  ),
68
- foot: actions(copyButton(() => summary, "Copy estimate", "fill")),
69
- });
67
+ foot: actions(copyButton(() => summary, "Copy estimate", "fill"))});
70
68
  slot.append(card.node);
71
69
  run();
72
70
 
@@ -4,8 +4,8 @@ import { el, bench, stack, panel, textArea, notice, copyButton, actions, toolcar
4
4
  const PARTS = ["Author", "Year", "Title", "Source"];
5
5
 
6
6
  function kvRow(key) {
7
- const value = el("span", { class: "mvb-kv__v" }, "-");
8
- const row = el("div", { class: "mvb-kv__row" }, el("span", { class: "mvb-kv__k" }, key), value);
7
+ const value = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
8
+ const row = el("div", { class: "grid gap-3 bg-deck px-3 py-[9px] [grid-template-columns:minmax(90px,34%)_minmax(0,1fr)]" }, el("span", { class: "wrap-anywhere text-2xs font-semibold text-ink-2" }, key), value);
9
9
  return { row, value };
10
10
  }
11
11
 
@@ -14,15 +14,14 @@ function check(text) {
14
14
  return {
15
15
  Author: /^[A-Z][A-Za-z'’.-]+,/.test(t) || /\b(and|&)\s+[A-Z]/.test(t),
16
16
  Year: /\b(1[5-9]\d{2}|20\d{2})\b/.test(t),
17
- Title: /["“][^"”]{2,}["”]/.test(t) || t.replace(/^[^.]*\./, "").trim().length > 8,
18
- Source: /https?:\/\/|www\.|\bIn\b|\b(Journal|Press|Publishing|Publishers|University|vol\.|pp\.|ed\.)\b/i.test(t),
19
- };
17
+ Title: /["“][^"”]{2}["”]/.test(t) || t.replace(/^[^.]*\./, "").trim().length > 8,
18
+ Source: /https?:\/\/|www\.|\bIn\b|\b(Journal|Press|Publishing|Publishers|University|vol\.|pp\.|ed\.)\b/i.test(t)};
20
19
  }
21
20
 
22
21
  export function mount(slot) {
23
22
  const source = textArea({ rows: 6, "aria-label": "Reference", placeholder: "Paste one reference" });
24
23
  const rows = {};
25
- const kv = el("div", { class: "mvb-kv" });
24
+ const kv = el("div", { class: "grid gap-px overflow-hidden rounded-r1 border border-orbit bg-orbit" });
26
25
  for (const key of PARTS) {
27
26
  rows[key] = kvRow(key);
28
27
  kv.append(rows[key].row);
@@ -51,8 +50,7 @@ export function mount(slot) {
51
50
  stack(panel("Reference", source), note),
52
51
  stack(panel("Parts", kv), status)
53
52
  ),
54
- foot: actions(copyButton(() => summary, "Copy check", "fill")),
55
- });
53
+ foot: actions(copyButton(() => summary, "Copy check", "fill"))});
56
54
  slot.append(card.node);
57
55
  run();
58
56