mevebu 1.2.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 (167) hide show
  1. package/mevebu/app/mevebu.js +260 -208
  2. package/mevebu/app/tailwind.css +1639 -2077
  3. package/mevebu/app/tailwind.src.css +155 -1120
  4. package/mevebu/index.html +2 -2
  5. package/mevebu/libs/js/core.js +236 -96
  6. package/mevebu/libs/js/registry.js +11 -11
  7. package/mevebu/libs/js/tools/abstract-checklist.js +5 -7
  8. package/mevebu/libs/js/tools/add-subtract-days.js +9 -11
  9. package/mevebu/libs/js/tools/age-calculator.js +7 -8
  10. package/mevebu/libs/js/tools/api-request-builder.js +3 -4
  11. package/mevebu/libs/js/tools/area-converter.js +7 -9
  12. package/mevebu/libs/js/tools/aspect-ratio.js +8 -9
  13. package/mevebu/libs/js/tools/average-calculator.js +4 -5
  14. package/mevebu/libs/js/tools/base64.js +31 -11
  15. package/mevebu/libs/js/tools/binary-calculator.js +6 -7
  16. package/mevebu/libs/js/tools/border-radius.js +7 -9
  17. package/mevebu/libs/js/tools/box-shadow.js +6 -7
  18. package/mevebu/libs/js/tools/break-even.js +4 -5
  19. package/mevebu/libs/js/tools/business-days.js +7 -8
  20. package/mevebu/libs/js/tools/canonical-tag.js +5 -6
  21. package/mevebu/libs/js/tools/caption-counter.js +5 -7
  22. package/mevebu/libs/js/tools/case-converter.js +11 -12
  23. package/mevebu/libs/js/tools/case-inspector.js +14 -16
  24. package/mevebu/libs/js/tools/cash-denomination.js +7 -8
  25. package/mevebu/libs/js/tools/cash-flow.js +7 -8
  26. package/mevebu/libs/js/tools/cert-expiry.js +6 -10
  27. package/mevebu/libs/js/tools/changelog-generator.js +4 -6
  28. package/mevebu/libs/js/tools/character-counter.js +10 -13
  29. package/mevebu/libs/js/tools/checklist.js +6 -7
  30. package/mevebu/libs/js/tools/citation-number-check.js +10 -13
  31. package/mevebu/libs/js/tools/coin-flip.js +9 -11
  32. package/mevebu/libs/js/tools/color-converter.js +23 -17
  33. package/mevebu/libs/js/tools/color-palette.js +6 -8
  34. package/mevebu/libs/js/tools/color-picker.js +4 -5
  35. package/mevebu/libs/js/tools/content-calendar.js +3 -4
  36. package/mevebu/libs/js/tools/contrast-checker.js +5 -6
  37. package/mevebu/libs/js/tools/cron-builder.js +4 -6
  38. package/mevebu/libs/js/tools/css-clamp.js +5 -6
  39. package/mevebu/libs/js/tools/css-formatter.js +4 -7
  40. package/mevebu/libs/js/tools/css-grid.js +6 -7
  41. package/mevebu/libs/js/tools/css-minifier.js +5 -8
  42. package/mevebu/libs/js/tools/csv-to-json.js +2 -3
  43. package/mevebu/libs/js/tools/csv-viewer.js +9 -11
  44. package/mevebu/libs/js/tools/currency-format.js +5 -7
  45. package/mevebu/libs/js/tools/daily-planner.js +9 -10
  46. package/mevebu/libs/js/tools/data-masker.js +7 -9
  47. package/mevebu/libs/js/tools/data-storage.js +7 -9
  48. package/mevebu/libs/js/tools/data-validator.js +9 -11
  49. package/mevebu/libs/js/tools/date-difference.js +6 -7
  50. package/mevebu/libs/js/tools/date-format.js +11 -13
  51. package/mevebu/libs/js/tools/delimiter-converter.js +3 -5
  52. package/mevebu/libs/js/tools/discount-calculator.js +4 -5
  53. package/mevebu/libs/js/tools/doi-resolver.js +4 -5
  54. package/mevebu/libs/js/tools/env-formatter.js +3 -6
  55. package/mevebu/libs/js/tools/equation-formatter.js +5 -9
  56. package/mevebu/libs/js/tools/expense-split.js +6 -7
  57. package/mevebu/libs/js/tools/fake-data.js +20 -13
  58. package/mevebu/libs/js/tools/find-replace.js +3 -5
  59. package/mevebu/libs/js/tools/flashcard-creator.js +9 -10
  60. package/mevebu/libs/js/tools/font-pairing.js +6 -8
  61. package/mevebu/libs/js/tools/fraction-calculator.js +10 -13
  62. package/mevebu/libs/js/tools/git-command-builder.js +11 -22
  63. package/mevebu/libs/js/tools/gpa-calculator.js +8 -10
  64. package/mevebu/libs/js/tools/grade-calculator.js +7 -8
  65. package/mevebu/libs/js/tools/gradient-generator.js +10 -12
  66. package/mevebu/libs/js/tools/habit-tracker.js +8 -9
  67. package/mevebu/libs/js/tools/hash-generator.js +3 -5
  68. package/mevebu/libs/js/tools/hashtag-formatter.js +6 -8
  69. package/mevebu/libs/js/tools/hmac-generator.js +3 -5
  70. package/mevebu/libs/js/tools/hpp-calculator.js +4 -5
  71. package/mevebu/libs/js/tools/html-entity.js +2 -3
  72. package/mevebu/libs/js/tools/html-formatter.js +3 -6
  73. package/mevebu/libs/js/tools/http-status.js +5 -7
  74. package/mevebu/libs/js/tools/inventory-quantity.js +7 -8
  75. package/mevebu/libs/js/tools/json-formatter.js +3 -6
  76. package/mevebu/libs/js/tools/json-minifier.js +5 -8
  77. package/mevebu/libs/js/tools/json-to-csv.js +3 -5
  78. package/mevebu/libs/js/tools/json-to-typescript.js +2 -5
  79. package/mevebu/libs/js/tools/json-validator.js +2 -3
  80. package/mevebu/libs/js/tools/jwt-decoder.js +6 -7
  81. package/mevebu/libs/js/tools/jwt-expiry.js +5 -6
  82. package/mevebu/libs/js/tools/jwt-generator.js +4 -6
  83. package/mevebu/libs/js/tools/length-converter.js +7 -9
  84. package/mevebu/libs/js/tools/license-reference.js +10 -13
  85. package/mevebu/libs/js/tools/line-counter.js +5 -7
  86. package/mevebu/libs/js/tools/list-difference.js +5 -7
  87. package/mevebu/libs/js/tools/list-merger.js +4 -5
  88. package/mevebu/libs/js/tools/locale-number.js +5 -8
  89. package/mevebu/libs/js/tools/lorem-ipsum.js +4 -2
  90. package/mevebu/libs/js/tools/markdown-preview.js +5 -8
  91. package/mevebu/libs/js/tools/markdown-table.js +5 -7
  92. package/mevebu/libs/js/tools/markup-calculator.js +4 -5
  93. package/mevebu/libs/js/tools/meeting-agenda.js +8 -9
  94. package/mevebu/libs/js/tools/meta-preview.js +12 -13
  95. package/mevebu/libs/js/tools/meta-tags.js +6 -7
  96. package/mevebu/libs/js/tools/mime-types.js +5 -7
  97. package/mevebu/libs/js/tools/name-picker.js +2 -3
  98. package/mevebu/libs/js/tools/number-base-converter.js +9 -12
  99. package/mevebu/libs/js/tools/password-generator.js +9 -12
  100. package/mevebu/libs/js/tools/password-strength.js +21 -25
  101. package/mevebu/libs/js/tools/percent-to-fraction.js +6 -7
  102. package/mevebu/libs/js/tools/percentage-calculator.js +6 -9
  103. package/mevebu/libs/js/tools/percentage-change.js +4 -5
  104. package/mevebu/libs/js/tools/permutation-combination.js +9 -12
  105. package/mevebu/libs/js/tools/prefix-suffix.js +2 -3
  106. package/mevebu/libs/js/tools/priority-matrix.js +6 -10
  107. package/mevebu/libs/js/tools/profit-margin.js +4 -5
  108. package/mevebu/libs/js/tools/qr-generator.js +3 -4
  109. package/mevebu/libs/js/tools/qr-scanner.js +11 -12
  110. package/mevebu/libs/js/tools/quadratic-solver.js +7 -8
  111. package/mevebu/libs/js/tools/query-builder.js +2 -3
  112. package/mevebu/libs/js/tools/quiz-formatter.js +4 -6
  113. package/mevebu/libs/js/tools/ratio-calculator.js +7 -8
  114. package/mevebu/libs/js/tools/readability.js +12 -15
  115. package/mevebu/libs/js/tools/reading-time.js +5 -7
  116. package/mevebu/libs/js/tools/reference-completeness.js +6 -8
  117. package/mevebu/libs/js/tools/regex-tester.js +7 -10
  118. package/mevebu/libs/js/tools/remove-duplicate-lines.js +2 -3
  119. package/mevebu/libs/js/tools/remove-extra-spaces.js +3 -5
  120. package/mevebu/libs/js/tools/robots-txt.js +3 -4
  121. package/mevebu/libs/js/tools/roman-numeral-converter.js +6 -8
  122. package/mevebu/libs/js/tools/sales-commission.js +8 -9
  123. package/mevebu/libs/js/tools/scientific-calculator.js +10 -14
  124. package/mevebu/libs/js/tools/scientific-notation.js +8 -9
  125. package/mevebu/libs/js/tools/script-planner.js +8 -10
  126. package/mevebu/libs/js/tools/selling-price.js +4 -5
  127. package/mevebu/libs/js/tools/semver-helper.js +3 -5
  128. package/mevebu/libs/js/tools/sentence-length.js +9 -10
  129. package/mevebu/libs/js/tools/sequence-analyzer.js +7 -8
  130. package/mevebu/libs/js/tools/sitemap-xml.js +4 -7
  131. package/mevebu/libs/js/tools/slug-converter.js +3 -5
  132. package/mevebu/libs/js/tools/slug-preview.js +6 -7
  133. package/mevebu/libs/js/tools/sort-lines.js +3 -5
  134. package/mevebu/libs/js/tools/speed-converter.js +7 -9
  135. package/mevebu/libs/js/tools/spin-wheel.js +19 -17
  136. package/mevebu/libs/js/tools/sql-formatter.js +5 -9
  137. package/mevebu/libs/js/tools/sql-minifier.js +5 -8
  138. package/mevebu/libs/js/tools/standard-deviation.js +6 -9
  139. package/mevebu/libs/js/tools/stopwatch.js +4 -6
  140. package/mevebu/libs/js/tools/study-schedule.js +8 -9
  141. package/mevebu/libs/js/tools/subtitle-formatter.js +10 -11
  142. package/mevebu/libs/js/tools/table-to-markdown.js +8 -12
  143. package/mevebu/libs/js/tools/tax-calculator.js +4 -5
  144. package/mevebu/libs/js/tools/temperature-converter.js +8 -11
  145. package/mevebu/libs/js/tools/text-diff.js +11 -7
  146. package/mevebu/libs/js/tools/text-direction.js +3 -5
  147. package/mevebu/libs/js/tools/text-reverser.js +3 -5
  148. package/mevebu/libs/js/tools/time-duration.js +8 -9
  149. package/mevebu/libs/js/tools/timestamp-converter.js +4 -6
  150. package/mevebu/libs/js/tools/timezone-converter.js +9 -13
  151. package/mevebu/libs/js/tools/title-length.js +6 -7
  152. package/mevebu/libs/js/tools/unicode-inspector.js +4 -5
  153. package/mevebu/libs/js/tools/url-encoder.js +4 -2
  154. package/mevebu/libs/js/tools/url-inspector.js +9 -11
  155. package/mevebu/libs/js/tools/url-parser.js +12 -13
  156. package/mevebu/libs/js/tools/utf8-bytes.js +6 -9
  157. package/mevebu/libs/js/tools/uuid-generator.js +107 -15
  158. package/mevebu/libs/js/tools/version-compare.js +3 -4
  159. package/mevebu/libs/js/tools/video-duration.js +5 -7
  160. package/mevebu/libs/js/tools/volume-converter.js +7 -9
  161. package/mevebu/libs/js/tools/weekly-planner.js +8 -10
  162. package/mevebu/libs/js/tools/weight-converter.js +7 -9
  163. package/mevebu/libs/js/tools/weighted-average.js +4 -5
  164. package/mevebu/libs/js/tools/word-counter.js +10 -13
  165. package/mevebu/libs/js/tools/word-limit.js +4 -5
  166. package/mevebu/libs/js/tools/yaml-formatter.js +6 -7
  167. package/package.json +1 -1
@@ -32,13 +32,13 @@ function toHsl({ r, g, b }) {
32
32
 
33
33
  export function mount(slot) {
34
34
  const picker = colorInput({ value: "#f5b942", "aria-label": "Pick a color" });
35
- const hexInput = textInput({ value: "#F5B942", class: "mvb-in mvb-in--mono", "aria-label": "Hex value" });
36
- const swatch = el("div", { class: "mvb-swatch" });
35
+ const hexInput = textInput({ value: "#F5B942", mono: true, "aria-label": "Hex value" });
36
+ const swatch = el("div", { class: "h-14 rounded-r1 border border-orbit-hard bg-deck-2" });
37
37
  const error = notice("error");
38
38
  let current = "#f5b942";
39
39
 
40
40
  const readonly = (label) => {
41
- const field = textInput({ readonly: true, class: "mvb-in mvb-in--mono", "aria-label": `${label}, press to copy` });
41
+ const field = textInput({ readonly: true, mono: true, "aria-label": `${label}, press to copy` });
42
42
  const wrap = panel(label, field);
43
43
  wrap.style.cursor = "pointer";
44
44
  wrap.addEventListener("click", async () => {
@@ -82,8 +82,7 @@ export function mount(slot) {
82
82
  foot: actions(
83
83
  copyButton(() => `--color: ${current};`, "Copy CSS"),
84
84
  copyButton(() => current, "Copy hex", "fill")
85
- ),
86
- });
85
+ )});
87
86
  slot.append(card.node);
88
87
  read("#F5B942", false);
89
88
 
@@ -19,7 +19,7 @@ export function mount(slot) {
19
19
  { value: "Idea", label: "Status", onChange: (next) => { status = next; } }
20
20
  );
21
21
 
22
- const listWrap = el("div", { class: "mvb-scroll" });
22
+ const listWrap = el("div", { class: "max-h-80 overflow-auto" });
23
23
  const addButton = button("Add", { icon: "plus", kind: "fill" });
24
24
 
25
25
  const save = () => writePin(PIN, { entries });
@@ -46,7 +46,7 @@ export function mount(slot) {
46
46
  el("td", {}, entry.platform || ""),
47
47
  el("td", {}, entry.status),
48
48
  el("td", {}, removeButton(entry))));
49
- listWrap.replaceChildren(el("table", { class: "mvb-ttable" },
49
+ listWrap.replaceChildren(el("table", { class: "w-full border-collapse font-mono text-sm [&_th]:wrap-anywhere [&_td]:wrap-anywhere [&_th]:border-b [&_td]:border-b [&_th]:border-orbit [&_td]:border-orbit [&_th]:px-2.5 [&_td]:px-2.5 [&_th]:py-2 [&_td]:py-2 [&_th]:text-start [&_td]:text-start [&_th]:font-sans [&_th]:text-2xs [&_th]:font-semibold [&_th]:text-ink-2 [&_tbody>tr:last-child>th]:border-b-0 [&_tbody>tr:last-child>td]:border-b-0" },
50
50
  el("thead", {}, el("tr", {},
51
51
  el("th", {}, "Date"), el("th", {}, "Topic"), el("th", {}, "Platform"), el("th", {}, "Status"), el("th", {}, ""))),
52
52
  el("tbody", {}, ...rows)));
@@ -100,8 +100,7 @@ export function mount(slot) {
100
100
  downloadButton(() => csv(), "content-calendar.csv", "Save CSV", "text/csv"),
101
101
  downloadButton(() => markdown(), "content-calendar.md", "Save Markdown", "text/markdown"),
102
102
  addButton
103
- ),
104
- });
103
+ )});
105
104
  slot.append(card.node);
106
105
  render();
107
106
 
@@ -31,19 +31,19 @@ export function mount(slot) {
31
31
  const bgPicker = colorInput({ value: "#ffffff", "aria-label": "Background color" });
32
32
 
33
33
  const kvRow = (key) => {
34
- const value = el("span", { class: "mvb-kv__v" }, "-");
35
- return { v: value, node: el("div", { class: "mvb-kv__row" }, el("span", { class: "mvb-kv__k" }, key), value) };
34
+ const value = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
35
+ return { v: value, node: 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) };
36
36
  };
37
37
  const ratioRow = kvRow("Contrast ratio");
38
38
  const normalAA = kvRow("Normal text AA");
39
39
  const normalAAA = kvRow("Normal text AAA");
40
40
  const largeAA = kvRow("Large text AA");
41
41
  const largeAAA = kvRow("Large text AAA");
42
- const list = el("div", { class: "mvb-kv" }, ratioRow.node, normalAA.node, normalAAA.node, largeAA.node, largeAAA.node);
42
+ const list = el("div", { class: "grid gap-px overflow-hidden rounded-r1 border border-orbit bg-orbit" }, ratioRow.node, normalAA.node, normalAAA.node, largeAA.node, largeAAA.node);
43
43
 
44
44
  const sample = el(
45
45
  "div",
46
- { class: "mvb-preview__sample" },
46
+ { class: "w-full rounded-lg border border-orbit bg-deck px-[18px] py-4" },
47
47
  el("p", { style: "margin:0 0 8px;font-size:20px;font-weight:700" }, "Large text sample"),
48
48
  el("p", { style: "margin:0;font-size:15px" }, "Normal text sample for the contrast reading.")
49
49
  );
@@ -82,8 +82,7 @@ export function mount(slot) {
82
82
  stack(panel("Text color", textPicker), panel("Background color", bgPicker)),
83
83
  stack(sample, list)
84
84
  ),
85
- foot: actions(swap, copyButton(() => reading, "Copy result", "fill")),
86
- });
85
+ foot: actions(swap, copyButton(() => reading, "Copy result", "fill"))});
87
86
  slot.append(card.node);
88
87
  update();
89
88
 
@@ -38,8 +38,7 @@ const FIELDS = [
38
38
  ["hour", "Hour", "0-23", "*"],
39
39
  ["day", "Day of month", "1-31", "*"],
40
40
  ["month", "Month", "1-12", "*"],
41
- ["weekday", "Day of week", "0-6", "*"],
42
- ];
41
+ ["weekday", "Day of week", "0-6", "*"]];
43
42
 
44
43
  export function mount(slot) {
45
44
  const inputs = {};
@@ -49,8 +48,8 @@ export function mount(slot) {
49
48
  return panel(label, input, hint);
50
49
  });
51
50
 
52
- const expression = textInput({ readonly: true, class: "mvb-in mvb-in--mono", "aria-label": "Cron expression" });
53
- const reading = el("p", { class: "mvb-note" });
51
+ const expression = textInput({ readonly: true, mono: true, "aria-label": "Cron expression" });
52
+ const reading = el("p", { class: "border-s-[3px] border-transparent ps-2 text-sm text-ink-3" });
54
53
  const error = notice("error");
55
54
 
56
55
  function run() {
@@ -80,8 +79,7 @@ export function mount(slot) {
80
79
  stack(...controls, error),
81
80
  stack(panel("Expression", expression), panel("Reading", reading))
82
81
  ),
83
- foot: actions(reset, copyButton(() => expression.value, "Copy expression", "fill")),
84
- });
82
+ foot: actions(reset, copyButton(() => expression.value, "Copy expression", "fill"))});
85
83
  slot.append(card.node);
86
84
  run();
87
85
 
@@ -3,11 +3,11 @@ import { el, bench, stack, rangeInput, output, copyButton, actions, toolcard } f
3
3
 
4
4
  function slider(label, { min, max, step, value, unit }) {
5
5
  const input = rangeInput({ min, max, step, value, "aria-label": label });
6
- const val = el("span", { class: "mvb-slider__val" }, `${value}${unit}`);
6
+ const val = el("span", { class: "text-end font-mono text-2xs text-ink-2" }, `${value}${unit}`);
7
7
  const node = el(
8
8
  "div",
9
- { class: "mvb-slider" },
10
- el("span", { class: "mvb-slider__label" }, label),
9
+ { class: "grid items-center gap-2.5 [grid-template-columns:88px_minmax(0,1fr)_62px] max-[45rem]:[grid-template-columns:minmax(0,1fr)_62px]" },
10
+ el("span", { class: "text-2xs font-semibold text-ink-2 max-[45rem]:col-span-2" }, label),
11
11
  input,
12
12
  val
13
13
  );
@@ -24,7 +24,7 @@ export function mount(slot) {
24
24
 
25
25
  const sample = el(
26
26
  "div",
27
- { class: "mvb-preview__sample" },
27
+ { class: "w-full rounded-lg border border-orbit bg-deck px-[18px] py-4" },
28
28
  el("p", { style: "margin:0" }, "The quick brown fox jumps over the lazy dog.")
29
29
  );
30
30
  const code = output();
@@ -55,8 +55,7 @@ export function mount(slot) {
55
55
  stack(min, preferred, max),
56
56
  stack(sample, code)
57
57
  ),
58
- foot: actions(copyButton(() => `font-size: ${clampValue()};`, "Copy CSS", "fill")),
59
- });
58
+ foot: actions(copyButton(() => `font-size: ${clampValue()};`, "Copy CSS", "fill"))});
60
59
  slot.append(card.node);
61
60
  update();
62
61
 
@@ -44,17 +44,15 @@ function format(css, size) {
44
44
  }
45
45
  const rest = clean(buffer);
46
46
  if (rest) push(rest);
47
- return lines.join("\n").replace(/\n{3,}/g, "\n\n").trim();
47
+ return lines.join("\n").replace(/\n{3}/g, "\n\n").trim();
48
48
  }
49
49
 
50
50
  export function mount(slot) {
51
51
  const source = textArea({
52
52
  rows: 12,
53
- class: "mvb-in mvb-in--area mvb-in--mono",
54
53
  "aria-label": "CSS",
55
- value: ".card{color:red;padding:10px 12px}.card:hover{color:blue}",
56
- });
57
- const result = textArea({ rows: 12, readonly: true, class: "mvb-in mvb-in--area mvb-in--mono", "aria-label": "Formatted CSS" });
54
+ value: ".card{color:red;padding:10px 12px}.card:hover{color:blue}"});
55
+ const result = textArea({ rows: 12, readonly: true, "aria-label": "Formatted CSS" });
58
56
  let size = "2";
59
57
 
60
58
  const chips = chipRow(
@@ -73,8 +71,7 @@ export function mount(slot) {
73
71
  stack(panel("Indent", chips), panel("CSS", source)),
74
72
  stack(panel("Formatted", result))
75
73
  ),
76
- foot: actions(copyButton(() => result.value, "Copy CSS", "fill")),
77
- });
74
+ foot: actions(copyButton(() => result.value, "Copy CSS", "fill"))});
78
75
  slot.append(card.node);
79
76
  run();
80
77
 
@@ -1,15 +1,15 @@
1
1
  // Lay out a CSS grid: columns, rows and gaps with a live preview.
2
2
  import { el, bench, stack, panel, numberInput, rangeInput, output, copyButton, actions, toolcard } from "../core.js";
3
3
 
4
- const CELL = "min-height:32px;background:var(--mvb-nebula);border:1px solid var(--mvb-comet);border-radius:6px";
4
+ const CELL = "min-height:32px;background:var(--jade-soft);border:1px solid var(--jade);border-radius:6px";
5
5
 
6
6
  function slider(label, value) {
7
7
  const input = rangeInput({ min: 0, max: 48, value, "aria-label": label });
8
- const val = el("span", { class: "mvb-slider__val" }, `${value}px`);
8
+ const val = el("span", { class: "text-end font-mono text-2xs text-ink-2" }, `${value}px`);
9
9
  const node = el(
10
10
  "div",
11
- { class: "mvb-slider" },
12
- el("span", { class: "mvb-slider__label" }, label),
11
+ { class: "grid items-center gap-2.5 [grid-template-columns:88px_minmax(0,1fr)_62px] max-[45rem]:[grid-template-columns:minmax(0,1fr)_62px]" },
12
+ el("span", { class: "text-2xs font-semibold text-ink-2 max-[45rem]:col-span-2" }, label),
13
13
  input,
14
14
  val
15
15
  );
@@ -62,10 +62,9 @@ export function mount(slot) {
62
62
  const card = toolcard({
63
63
  body: bench(
64
64
  stack(panel("Columns", columns), panel("Rows", rows), columnGap, rowGap),
65
- stack(el("div", { class: "mvb-preview" }, grid), code)
65
+ stack(el("div", { class: "grid min-h-40 place-items-center overflow-hidden rounded-r1 border border-orbit bg-deck-2 p-[22px]" }, grid), code)
66
66
  ),
67
- foot: actions(copyButton(() => code.textContent, "Copy CSS", "fill")),
68
- });
67
+ foot: actions(copyButton(() => code.textContent, "Copy CSS", "fill"))});
69
68
  slot.append(card.node);
70
69
  update();
71
70
 
@@ -4,7 +4,7 @@ import { el, bench, stack, panel, textArea, switchControl, copyButton, actions,
4
4
  const bytes = (text) => new TextEncoder().encode(text).length;
5
5
 
6
6
  const tile = (key, value) =>
7
- el("div", { class: "mvb-stat" }, el("span", { class: "mvb-stat__v" }, value), el("span", { class: "mvb-stat__k" }, key));
7
+ el("div", { class: "grid gap-0.5 rounded-r1 border border-orbit bg-deck-2 px-3.5 py-3" }, el("span", { class: "wrap-anywhere text-xl font-bold tracking-[-0.02em] tabular-nums" }, value), el("span", { class: "text-2xs font-semibold text-ink-3" }, key));
8
8
 
9
9
  function minify(css, stripComments) {
10
10
  let out = "";
@@ -55,12 +55,10 @@ function minify(css, stripComments) {
55
55
  export function mount(slot) {
56
56
  const source = textArea({
57
57
  rows: 12,
58
- class: "mvb-in mvb-in--area mvb-in--mono",
59
58
  "aria-label": "CSS",
60
- value: "/* card */\n.card {\n color: red;\n padding: 10px 12px;\n}",
61
- });
62
- const result = textArea({ rows: 12, readonly: true, class: "mvb-in mvb-in--area mvb-in--mono", "aria-label": "Minified CSS" });
63
- const stats = el("div", { class: "mvb-stats" });
59
+ value: "/* card */\n.card {\n color: red;\n padding: 10px 12px;\n}"});
60
+ const result = textArea({ rows: 12, readonly: true, "aria-label": "Minified CSS" });
61
+ const stats = el("div", { class: "grid gap-2.5 [grid-template-columns:repeat(auto-fit,minmax(120px,1fr))]" });
64
62
  let stripComments = true;
65
63
 
66
64
  const comments = switchControl("Remove comments", { checked: true });
@@ -87,8 +85,7 @@ export function mount(slot) {
87
85
  stack(panel("Options", comments), panel("CSS", source)),
88
86
  stack(panel("Minified", result), stats)
89
87
  ),
90
- foot: actions(copyButton(() => result.value, "Copy CSS", "fill")),
91
- });
88
+ foot: actions(copyButton(() => result.value, "Copy CSS", "fill"))});
92
89
  slot.append(card.node);
93
90
  run();
94
91
 
@@ -62,7 +62,7 @@ function coerce(value) {
62
62
  }
63
63
 
64
64
  export function mount(slot) {
65
- const source = textArea({ rows: 12, class: "mvb-in mvb-in--area mvb-in--mono", "aria-label": "CSV", placeholder: "name,role\nAda,engineer" });
65
+ const source = textArea({ rows: 12, "aria-label": "CSV", placeholder: "name,role\nAda,engineer" });
66
66
  const typedBox = checkbox("Typed values", { checked: true });
67
67
  const result = output({ rows: 12 });
68
68
  const error = notice("error");
@@ -103,8 +103,7 @@ export function mount(slot) {
103
103
  stack(panel("CSV", source), panel(null, typedBox), error),
104
104
  stack(panel("JSON", result))
105
105
  ),
106
- foot: actions(copyButton(() => current, "Copy JSON", "fill")),
107
- });
106
+ foot: actions(copyButton(() => current, "Copy JSON", "fill"))});
108
107
  slot.append(card.node);
109
108
  run();
110
109
 
@@ -53,27 +53,26 @@ function detectDelimiter(text) {
53
53
  }
54
54
 
55
55
  function tile(label) {
56
- const value = el("span", { class: "mvb-stat__v" }, "0");
57
- const node = el("div", { class: "mvb-stat" }, value, el("span", { class: "mvb-stat__k" }, label));
56
+ const value = el("span", { class: "wrap-anywhere text-xl font-bold tracking-[-0.02em] tabular-nums" }, "0");
57
+ 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));
58
58
  node.set = (text) => { value.textContent = text; };
59
59
  return node;
60
60
  }
61
61
 
62
62
  export function mount(slot) {
63
- const source = textArea({ rows: 12, class: "mvb-in mvb-in--area mvb-in--mono", "aria-label": "CSV", placeholder: "Paste CSV here" });
63
+ const source = textArea({ rows: 12, "aria-label": "CSV", placeholder: "Paste CSV here" });
64
64
  const delimiter = selectInput([
65
65
  { value: "auto", label: "Auto" },
66
66
  { value: ",", label: "Comma" },
67
67
  { value: ";", label: "Semicolon" },
68
68
  { value: "\t", label: "Tab" },
69
- { value: "|", label: "Pipe" },
70
- ], { value: "auto", "aria-label": "Delimiter" });
69
+ { value: "|", label: "Pipe" }], { value: "auto", "aria-label": "Delimiter" });
71
70
  const file = fileInput({ accept: ".csv,.tsv,.txt,text/csv", "aria-label": "CSV file" });
72
71
 
73
- const tableWrap = el("div", { class: "mvb-scroll" });
72
+ const tableWrap = el("div", { class: "max-h-80 overflow-auto" });
74
73
  const rowsTile = tile("Rows");
75
74
  const colsTile = tile("Columns");
76
- const stats = el("div", { class: "mvb-stats" }, rowsTile, colsTile);
75
+ const stats = el("div", { class: "grid gap-2.5 [grid-template-columns:repeat(auto-fit,minmax(120px,1fr))]" }, rowsTile, colsTile);
77
76
  const error = notice("error");
78
77
  let current = [];
79
78
 
@@ -82,7 +81,7 @@ export function mount(slot) {
82
81
  const text = source.value;
83
82
  if (!text.trim()) {
84
83
  current = [];
85
- tableWrap.replaceChildren(el("p", { class: "mvb-note" }, "Paste CSV or choose a file."));
84
+ tableWrap.replaceChildren(el("p", { class: "border-s-[3px] border-transparent ps-2 text-sm text-ink-3" }, "Paste CSV or choose a file."));
86
85
  rowsTile.set("0");
87
86
  colsTile.set("0");
88
87
  return;
@@ -97,7 +96,7 @@ export function mount(slot) {
97
96
  }
98
97
  rowsTile.set(String(body.length));
99
98
  colsTile.set(String(header.length));
100
- tableWrap.replaceChildren(el("table", { class: "mvb-ttable" },
99
+ tableWrap.replaceChildren(el("table", { class: "w-full border-collapse font-mono text-sm [&_th]:wrap-anywhere [&_td]:wrap-anywhere [&_th]:border-b [&_td]:border-b [&_th]:border-orbit [&_td]:border-orbit [&_th]:px-2.5 [&_td]:px-2.5 [&_th]:py-2 [&_td]:py-2 [&_th]:text-start [&_td]:text-start [&_th]:font-sans [&_th]:text-2xs [&_th]:font-semibold [&_th]:text-ink-2 [&_tbody>tr:last-child>th]:border-b-0 [&_tbody>tr:last-child>td]:border-b-0" },
101
100
  el("thead", {}, el("tr", {}, ...header.map((cell, i) => el("th", {}, cell || `Column ${i + 1}`)))),
102
101
  el("tbody", {}, ...body.map((row) => el("tr", {}, ...header.map((_, i) => el("td", {}, row[i] ?? "")))))
103
102
  ));
@@ -121,8 +120,7 @@ export function mount(slot) {
121
120
  stack(panel("CSV", source), panel("Delimiter", delimiter), panel("CSV file", file), error),
122
121
  stack(stats, panel("Table", tableWrap))
123
122
  ),
124
- foot: actions(clearButton, copyButton(() => current.map((row) => row.join("\t")).join("\n"), "Copy table", "fill")),
125
- });
123
+ foot: actions(clearButton, copyButton(() => current.map((row) => row.join("\t")).join("\n"), "Copy table", "fill"))});
126
124
  slot.append(card.node);
127
125
  run();
128
126
 
@@ -12,8 +12,7 @@ const LOCALES = [
12
12
  { value: "id-ID", label: "Indonesian" },
13
13
  { value: "hi-IN", label: "Hindi" },
14
14
  { value: "ja-JP", label: "Japanese" },
15
- { value: "pt-BR", label: "Portuguese (Brazil)" },
16
- ];
15
+ { value: "pt-BR", label: "Portuguese (Brazil)" }];
17
16
 
18
17
  function format(locale, currency, value) {
19
18
  try {
@@ -28,9 +27,9 @@ export function mount(slot) {
28
27
  const currency = selectInput(CURRENCIES, { value: "USD", "aria-label": "Currency" });
29
28
  const locale = selectInput(LOCALES, { value: "en-US", "aria-label": "Locale" });
30
29
 
31
- const amount = el("span", { class: "mvb-stat__v" }, "-");
32
- const readout = el("div", { class: "mvb-stats" },
33
- el("div", { class: "mvb-stat" }, amount, el("span", { class: "mvb-stat__k" }, "Formatted")));
30
+ const amount = el("span", { class: "wrap-anywhere text-xl font-bold tracking-[-0.02em] tabular-nums" }, "-");
31
+ const readout = el("div", { class: "grid gap-2.5 [grid-template-columns:repeat(auto-fit,minmax(120px,1fr))]" },
32
+ el("div", { class: "grid gap-0.5 rounded-r1 border border-orbit bg-deck-2 px-3.5 py-3" }, amount, el("span", { class: "text-2xs font-semibold text-ink-3" }, "Formatted")));
34
33
 
35
34
  const note = notice("note");
36
35
  note.write("The amount is formatted, not converted. No exchange rate is applied.");
@@ -58,8 +57,7 @@ export function mount(slot) {
58
57
  ),
59
58
  stack(readout, note)
60
59
  ),
61
- foot: actions(copyButton(() => formatted, "Copy amount", "fill")),
62
- });
60
+ foot: actions(copyButton(() => formatted, "Copy amount", "fill"))});
63
61
  slot.append(card.node);
64
62
  run();
65
63
 
@@ -21,20 +21,20 @@ export function mount(slot) {
21
21
  let list = Array.isArray(saved) ? saved : [];
22
22
 
23
23
  const title = textInput({ placeholder: "Activity", "aria-label": "Activity" });
24
- const start = el("input", { class: "mvb-in", type: "time", value: "09:00", "aria-label": "Start time" });
25
- const end = el("input", { class: "mvb-in", type: "time", value: "10:00", "aria-label": "End time" });
24
+ const start = el("input", { class: "min-w-0 rounded-r1 border border-orbit bg-deck text-ink transition-[border-color,box-shadow] duration-150 placeholder:text-ink-3 hover:border-orbit-hard focus:border-jade focus:outline-none focus:ring-3 focus:ring-jade-soft w-full min-h-[var(--ctl)] px-3 py-2.5 text-base", type: "time", value: "09:00", "aria-label": "Start time" });
25
+ const end = el("input", { class: "min-w-0 rounded-r1 border border-orbit bg-deck text-ink transition-[border-color,box-shadow] duration-150 placeholder:text-ink-3 hover:border-orbit-hard focus:border-jade focus:outline-none focus:ring-3 focus:ring-jade-soft w-full min-h-[var(--ctl)] px-3 py-2.5 text-base", type: "time", value: "10:00", "aria-label": "End time" });
26
26
 
27
- const listWrap = el("div", { class: "mvb-scroll" });
28
- const stats = el("div", { class: "mvb-stats" });
27
+ const listWrap = el("div", { class: "max-h-80 overflow-auto" });
28
+ const stats = el("div", { class: "grid gap-2.5 [grid-template-columns:repeat(auto-fit,minmax(120px,1fr))]" });
29
29
  const overlapNote = notice("error");
30
30
  const error = notice("error");
31
31
 
32
32
  const addButton = button("Add", { icon: "plus", kind: "fill" });
33
33
  const clearButton = button("Clear day", { icon: "trash" });
34
34
 
35
- const tile = (key, value) => el("div", { class: "mvb-stat" },
36
- el("span", { class: "mvb-stat__v" }, String(value)),
37
- el("span", { class: "mvb-stat__k" }, key));
35
+ const tile = (key, value) => el("div", { class: "grid gap-0.5 rounded-r1 border border-orbit bg-deck-2 px-3.5 py-3" },
36
+ el("span", { class: "wrap-anywhere text-xl font-bold tracking-[-0.02em] tabular-nums" }, String(value)),
37
+ el("span", { class: "text-2xs font-semibold text-ink-3" }, key));
38
38
 
39
39
  const save = () => writePin(PIN, list);
40
40
 
@@ -69,7 +69,7 @@ export function mount(slot) {
69
69
  el("td", {}, flagged.has(item.id) ? "Overlap" : ""),
70
70
  el("td", {}, removeButton(item))
71
71
  ));
72
- listWrap.replaceChildren(el("table", { class: "mvb-ttable" },
72
+ listWrap.replaceChildren(el("table", { class: "w-full border-collapse font-mono text-sm [&_th]:wrap-anywhere [&_td]:wrap-anywhere [&_th]:border-b [&_td]:border-b [&_th]:border-orbit [&_td]:border-orbit [&_th]:px-2.5 [&_td]:px-2.5 [&_th]:py-2 [&_td]:py-2 [&_th]:text-start [&_td]:text-start [&_th]:font-sans [&_th]:text-2xs [&_th]:font-semibold [&_th]:text-ink-2 [&_tbody>tr:last-child>th]:border-b-0 [&_tbody>tr:last-child>td]:border-b-0" },
73
73
  el("thead", {}, el("tr", {},
74
74
  el("th", {}, "Start"), el("th", {}, "End"), el("th", {}, "Activity"),
75
75
  el("th", {}, "Length"), el("th", {}, "Note"), el("th", {}, ""))),
@@ -113,8 +113,7 @@ export function mount(slot) {
113
113
  ),
114
114
  stack(stats, panel("Day", listWrap), overlapNote)
115
115
  ),
116
- foot: actions(clearButton, addButton),
117
- });
116
+ foot: actions(clearButton, addButton)});
118
117
  slot.append(card.node);
119
118
  render();
120
119
 
@@ -1,9 +1,9 @@
1
1
  // Masks emails, card numbers and phone numbers in pasted text.
2
2
  import { el, textArea, output, toggleRow, copyButton, bench, stack, panel, actions, toolcard } from "../core.js";
3
3
 
4
- const EMAIL = /[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/g;
5
- const CARD = /\b\d[\d -]{11,}\d\b/g;
6
- const PHONE = /\b\+?\d[\d ().-]{5,}\d\b/g;
4
+ const EMAIL = /[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2}/g;
5
+ const CARD = /\b\d[\d -]{11}\d\b/g;
6
+ const PHONE = /\b\+?\d[\d ().-]{5}\d\b/g;
7
7
 
8
8
  function maskDigits(value, keep) {
9
9
  const digits = value.replace(/\D/g, "");
@@ -28,8 +28,7 @@ export function mount(slot) {
28
28
  const kinds = toggleRow([
29
29
  { value: "email", label: "Email" },
30
30
  { value: "card", label: "Card number" },
31
- { value: "phone", label: "Phone number" },
32
- ]);
31
+ { value: "phone", label: "Phone number" }]);
33
32
 
34
33
  function run() {
35
34
  const on = kinds.values();
@@ -55,13 +54,12 @@ export function mount(slot) {
55
54
  body: bench(
56
55
  stack(
57
56
  panel("Values", source),
58
- panel("Mask", kinds),
59
- el("p", { class: "mvb-note" }, "Masking runs in this tab. Check the result before sharing.")
57
+ panel("What to mask", kinds),
58
+ el("p", { class: "border-s-[3px] border-transparent ps-2 text-sm text-ink-3" }, "Pattern matching can miss a value, so check the result before sharing.")
60
59
  ),
61
60
  stack(panel("Masked", result))
62
61
  ),
63
- foot: actions(copyButton(() => result.textContent, "Copy masked", "fill")),
64
- });
62
+ foot: actions(copyButton(() => result.textContent, "Copy masked", "fill"))});
65
63
  slot.append(card.node);
66
64
  run();
67
65
 
@@ -10,8 +10,7 @@ const UNITS = [
10
10
  { value: "KiB", label: "Kibibyte", factor: 1024 },
11
11
  { value: "MiB", label: "Mebibyte", factor: 1048576 },
12
12
  { value: "GiB", label: "Gibibyte", factor: 1073741824 },
13
- { value: "TiB", label: "Tebibyte", factor: 1099511627776 },
14
- ];
13
+ { value: "TiB", label: "Tebibyte", factor: 1099511627776 }];
15
14
 
16
15
  const byId = Object.fromEntries(UNITS.map((unit) => [unit.value, unit]));
17
16
  const options = UNITS.map(({ value, label }) => ({ value, label }));
@@ -28,13 +27,13 @@ export function mount(slot) {
28
27
  const value = numberInput({ value: "1", step: "any", "aria-label": "Value" });
29
28
  const from = selectInput(options, { value: "MB", "aria-label": "From unit" });
30
29
  const to = selectInput(options, { value: "MiB", "aria-label": "To unit" });
31
- const result = el("span", { class: "mvb-stat__v" }, "-");
32
- const stats = el("div", { class: "mvb-stats" }, el("div", { class: "mvb-stat" }, el("span", { class: "mvb-stat__k" }, "Result"), result));
33
- const factor = el("p", { class: "mvb-note" });
30
+ const result = el("span", { class: "wrap-anywhere text-xl font-bold tracking-[-0.02em] tabular-nums" }, "-");
31
+ const stats = el("div", { class: "grid gap-2.5 [grid-template-columns:repeat(auto-fit,minmax(120px,1fr))]" }, el("div", { class: "grid gap-0.5 rounded-r1 border border-orbit bg-deck-2 px-3.5 py-3" }, el("span", { class: "text-2xs font-semibold text-ink-3" }, "Result"), result));
32
+ const factor = el("p", { class: "border-s-[3px] border-transparent ps-2 text-sm text-ink-3" });
34
33
  const scaleNote = notice("note");
35
34
  scaleNote.write("KB is 1000 bytes, KiB is 1024 bytes.");
36
35
  const error = notice("error");
37
- const scroll = el("div", { class: "mvb-scroll" });
36
+ const scroll = el("div", { class: "max-h-80 overflow-auto" });
38
37
  let text = "";
39
38
 
40
39
  function run() {
@@ -54,7 +53,7 @@ export function mount(slot) {
54
53
  const out = (n * a.factor) / b.factor;
55
54
  result.textContent = `${fmt(out)} ${b.value}`;
56
55
  factor.textContent = `1 ${a.value} = ${fmt(a.factor / b.factor)} ${b.value}`;
57
- scroll.replaceChildren(el("table", { class: "mvb-ttable" },
56
+ scroll.replaceChildren(el("table", { class: "w-full border-collapse font-mono text-sm [&_th]:wrap-anywhere [&_td]:wrap-anywhere [&_th]:border-b [&_td]:border-b [&_th]:border-orbit [&_td]:border-orbit [&_th]:px-2.5 [&_td]:px-2.5 [&_th]:py-2 [&_td]:py-2 [&_th]:text-start [&_td]:text-start [&_th]:font-sans [&_th]:text-2xs [&_th]:font-semibold [&_th]:text-ink-2 [&_tbody>tr:last-child>th]:border-b-0 [&_tbody>tr:last-child>td]:border-b-0" },
58
57
  el("thead", {}, el("tr", {}, el("th", {}, "Unit"), el("th", {}, "Value"))),
59
58
  el("tbody", {}, ...UNITS.map((unit) => el("tr", {},
60
59
  el("td", {}, unit.label),
@@ -78,8 +77,7 @@ export function mount(slot) {
78
77
  stack(panel("Value", value), panel("From", from), panel("To", to), scaleNote, error),
79
78
  stack(panel("Result", stats), factor, panel("All units", scroll))
80
79
  ),
81
- foot: actions(swap, copyButton(() => text, "Copy result", "fill")),
82
- });
80
+ foot: actions(swap, copyButton(() => text, "Copy result", "fill"))});
83
81
  slot.append(card.node);
84
82
  run();
85
83
 
@@ -2,8 +2,8 @@
2
2
  import { el, bench, stack, panel, textArea, selectInput, textInput, notice, copyButton, actions, toolcard } from "../core.js";
3
3
 
4
4
  function tile(label) {
5
- const value = el("span", { class: "mvb-stat__v" }, "0");
6
- const node = el("div", { class: "mvb-stat" }, value, el("span", { class: "mvb-stat__k" }, label));
5
+ const value = el("span", { class: "wrap-anywhere text-xl font-bold tracking-[-0.02em] tabular-nums" }, "0");
6
+ 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));
7
7
  node.set = (text) => { value.textContent = text; };
8
8
  return node;
9
9
  }
@@ -17,16 +17,15 @@ export function mount(slot) {
17
17
  { value: "number", label: "Number" },
18
18
  { value: "minlength", label: "Minimum length" },
19
19
  { value: "maxlength", label: "Maximum length" },
20
- { value: "regex", label: "Regex" },
21
- ], { value: "email", "aria-label": "Rule" });
20
+ { value: "regex", label: "Regex" }], { value: "email", "aria-label": "Rule" });
22
21
  const setting = textInput({ "aria-label": "Rule setting", placeholder: "Number" });
23
22
  const settingField = panel("Setting", setting);
24
23
 
25
- const tableWrap = el("div", { class: "mvb-scroll" });
24
+ const tableWrap = el("div", { class: "max-h-80 overflow-auto" });
26
25
  const totalTile = tile("Total");
27
26
  const validTile = tile("Valid");
28
27
  const invalidTile = tile("Invalid");
29
- const stats = el("div", { class: "mvb-stats" }, totalTile, validTile, invalidTile);
28
+ const stats = el("div", { class: "grid gap-2.5 [grid-template-columns:repeat(auto-fit,minmax(120px,1fr))]" }, totalTile, validTile, invalidTile);
30
29
  const error = notice("error");
31
30
  let failing = [];
32
31
 
@@ -72,11 +71,11 @@ export function mount(slot) {
72
71
  failing = failures;
73
72
 
74
73
  if (!values.length) {
75
- tableWrap.replaceChildren(el("p", { class: "mvb-note" }, "Paste one value per line."));
74
+ tableWrap.replaceChildren(el("p", { class: "border-s-[3px] border-transparent ps-2 text-sm text-ink-3" }, "Paste one value per line."));
76
75
  } else if (!failures.length) {
77
- tableWrap.replaceChildren(el("p", { class: "mvb-note" }, "Every value passes."));
76
+ tableWrap.replaceChildren(el("p", { class: "border-s-[3px] border-transparent ps-2 text-sm text-ink-3" }, "Every value passes."));
78
77
  } else {
79
- tableWrap.replaceChildren(el("table", { class: "mvb-ttable" },
78
+ tableWrap.replaceChildren(el("table", { class: "w-full border-collapse font-mono text-sm [&_th]:wrap-anywhere [&_td]:wrap-anywhere [&_th]:border-b [&_td]:border-b [&_th]:border-orbit [&_td]:border-orbit [&_th]:px-2.5 [&_td]:px-2.5 [&_th]:py-2 [&_td]:py-2 [&_th]:text-start [&_td]:text-start [&_th]:font-sans [&_th]:text-2xs [&_th]:font-semibold [&_th]:text-ink-2 [&_tbody>tr:last-child>th]:border-b-0 [&_tbody>tr:last-child>td]:border-b-0" },
80
79
  el("thead", {}, el("tr", {}, el("th", {}, "#"), el("th", {}, "Value"), el("th", {}, "Reason"))),
81
80
  el("tbody", {}, ...failures.map((row) => el("tr", {},
82
81
  el("th", {}, String(row.line)),
@@ -100,8 +99,7 @@ export function mount(slot) {
100
99
  stack(panel("Values", source), panel("Rule", rule), settingField, error),
101
100
  stack(stats, panel("Failed rows", tableWrap))
102
101
  ),
103
- foot: actions(copyButton(() => failing.map((row) => row.value).join("\n"), "Copy failing values", "fill")),
104
- });
102
+ foot: actions(copyButton(() => failing.map((row) => row.value).join("\n"), "Copy failing values", "fill"))});
105
103
  slot.append(card.node);
106
104
  run();
107
105
 
@@ -30,18 +30,18 @@ function calendarDiff(from, to) {
30
30
  }
31
31
 
32
32
  const kvRow = (key, value) =>
33
- el("div", { class: "mvb-kv__row" }, el("span", { class: "mvb-kv__k" }, key), el("span", { class: "mvb-kv__v" }, value));
33
+ 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));
34
34
 
35
35
  export function mount(slot) {
36
36
  const start = textInput({ type: "date", value: "2026-01-01", "aria-label": "Start date" });
37
37
  const end = textInput({ type: "date", value: "2026-12-31", "aria-label": "End date" });
38
38
  const includeEnd = checkbox("Include the end date", { checked: false });
39
- const days = el("span", { class: "mvb-kv__v" }, "-");
40
- const weeks = el("span", { class: "mvb-kv__v" }, "-");
41
- const calendar = el("span", { class: "mvb-kv__v" }, "-");
39
+ const days = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
40
+ const weeks = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
41
+ const calendar = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
42
42
  const kv = el(
43
43
  "div",
44
- { class: "mvb-kv" },
44
+ { class: "grid gap-px overflow-hidden rounded-r1 border border-orbit bg-orbit" },
45
45
  kvRow("Total days", days),
46
46
  kvRow("Weeks", weeks),
47
47
  kvRow("Calendar gap", calendar)
@@ -89,8 +89,7 @@ export function mount(slot) {
89
89
  stack(panel("Start", start), panel("End", end), includeEnd, error),
90
90
  stack(panel("Difference", kv))
91
91
  ),
92
- foot: actions(copyButton(() => text, "Copy result", "fill")),
93
- });
92
+ foot: actions(copyButton(() => text, "Copy result", "fill"))});
94
93
  slot.append(card.node);
95
94
  run();
96
95