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
@@ -55,32 +55,30 @@ function detectDelimiter(text) {
55
55
  const escapeCell = (cell) => String(cell ?? "").replace(/\|/g, "\\|").replace(/\s*\n\s*/g, " ").trim();
56
56
 
57
57
  function tile(label) {
58
- const value = el("span", { class: "mvb-stat__v" }, "0");
59
- const node = el("div", { class: "mvb-stat" }, value, el("span", { class: "mvb-stat__k" }, label));
58
+ const value = el("span", { class: "wrap-anywhere text-xl font-bold tracking-[-0.02em] tabular-nums" }, "0");
59
+ 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));
60
60
  node.set = (text) => { value.textContent = text; };
61
61
  return node;
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": "Table data", placeholder: "Paste rows copied from a spreadsheet" });
65
+ const source = textArea({ rows: 12, "aria-label": "Table data", placeholder: "Paste rows copied from a spreadsheet" });
66
66
  const delimiter = selectInput([
67
67
  { value: "auto", label: "Auto" },
68
68
  { value: ",", label: "Comma" },
69
69
  { value: ";", label: "Semicolon" },
70
70
  { value: "\t", label: "Tab" },
71
- { value: "|", label: "Pipe" },
72
- ], { value: "auto", "aria-label": "Delimiter" });
71
+ { value: "|", label: "Pipe" }], { value: "auto", "aria-label": "Delimiter" });
73
72
  const alignment = selectInput([
74
73
  { value: "none", label: "None" },
75
74
  { value: "left", label: "Left" },
76
75
  { value: "center", label: "Center" },
77
- { value: "right", label: "Right" },
78
- ], { value: "none", "aria-label": "Alignment" });
76
+ { value: "right", label: "Right" }], { value: "none", "aria-label": "Alignment" });
79
77
 
80
78
  const result = output({ rows: 12 });
81
79
  const rowsTile = tile("Rows");
82
80
  const colsTile = tile("Columns");
83
- const stats = el("div", { class: "mvb-stats" }, rowsTile, colsTile);
81
+ const stats = el("div", { class: "grid gap-2.5 [grid-template-columns:repeat(auto-fit,minmax(120px,1fr))]" }, rowsTile, colsTile);
84
82
  const error = notice("error");
85
83
  let current = "";
86
84
 
@@ -112,8 +110,7 @@ export function mount(slot) {
112
110
  current = [
113
111
  line(header.map(escapeCell)),
114
112
  line(separator(header.length)),
115
- ...body.map((row) => line(header.map((_, i) => escapeCell(row[i])))),
116
- ].join("\n");
113
+ ...body.map((row) => line(header.map((_, i) => escapeCell(row[i]))))].join("\n");
117
114
  result.write(current);
118
115
  }
119
116
 
@@ -126,8 +123,7 @@ export function mount(slot) {
126
123
  stack(panel("Table data", source), panel("Delimiter", delimiter), panel("Alignment", alignment), error),
127
124
  stack(stats, panel("Markdown", result))
128
125
  ),
129
- foot: actions(copyButton(() => current, "Copy Markdown", "fill")),
130
- });
126
+ foot: actions(copyButton(() => current, "Copy Markdown", "fill"))});
131
127
  slot.append(card.node);
132
128
  run();
133
129
 
@@ -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
  }
@@ -23,7 +23,7 @@ export function mount(slot) {
23
23
  const taxTile = tile("Tax");
24
24
  const netTile = tile("Net");
25
25
  const grossTile = tile("Total");
26
- const stats = el("div", { class: "mvb-stats" }, taxTile, netTile, grossTile);
26
+ const stats = el("div", { class: "grid gap-2.5 [grid-template-columns:repeat(auto-fit,minmax(120px,1fr))]" }, taxTile, netTile, grossTile);
27
27
  const error = notice("error");
28
28
  const note = notice("note");
29
29
  note.write("You set the rate. No official rule is applied.");
@@ -73,8 +73,7 @@ export function mount(slot) {
73
73
  stack(panel("Amount", amount), panel("Amount type", chips), panel("Tax rate %", rate), error),
74
74
  stack(stats, note)
75
75
  ),
76
- foot: actions(copyButton(() => text, "Copy result", "fill")),
77
- });
76
+ foot: actions(copyButton(() => text, "Copy result", "fill"))});
78
77
  slot.append(card.node);
79
78
  run();
80
79
 
@@ -4,8 +4,7 @@ import { el, bench, stack, panel, numberInput, selectInput, notice, copyButton,
4
4
  const SCALES = [
5
5
  { value: "c", label: "Celsius" },
6
6
  { value: "f", label: "Fahrenheit" },
7
- { value: "k", label: "Kelvin" },
8
- ];
7
+ { value: "k", label: "Kelvin" }];
9
8
 
10
9
  const byId = Object.fromEntries(SCALES.map((scale) => [scale.value, scale]));
11
10
  const options = SCALES.map(({ value, label }) => ({ value, label }));
@@ -19,8 +18,7 @@ const FORMULAS = {
19
18
  "f-c": "C = (F - 32) x 5/9",
20
19
  "f-k": "K = (F - 32) x 5/9 + 273.15",
21
20
  "k-c": "C = K - 273.15",
22
- "k-f": "F = (K - 273.15) x 9/5 + 32",
23
- };
21
+ "k-f": "F = (K - 273.15) x 9/5 + 32"};
24
22
 
25
23
  function fmt(value) {
26
24
  if (!Number.isFinite(value)) return "-";
@@ -34,11 +32,11 @@ export function mount(slot) {
34
32
  const value = numberInput({ value: "0", step: "any", "aria-label": "Value" });
35
33
  const from = selectInput(options, { value: "c", "aria-label": "From scale" });
36
34
  const to = selectInput(options, { value: "f", "aria-label": "To scale" });
37
- const result = el("span", { class: "mvb-stat__v" }, "-");
38
- const stats = el("div", { class: "mvb-stats" }, el("div", { class: "mvb-stat" }, el("span", { class: "mvb-stat__k" }, "Result"), result));
39
- const formula = el("p", { class: "mvb-note" });
35
+ const result = el("span", { class: "wrap-anywhere text-xl font-bold tracking-[-0.02em] tabular-nums" }, "-");
36
+ 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));
37
+ const formula = el("p", { class: "border-s-[3px] border-transparent ps-2 text-sm text-ink-3" });
40
38
  const error = notice("error");
41
- const scroll = el("div", { class: "mvb-scroll" });
39
+ const scroll = el("div", { class: "max-h-80 overflow-auto" });
42
40
  let text = "";
43
41
 
44
42
  function run() {
@@ -59,7 +57,7 @@ export function mount(slot) {
59
57
  const out = fromCelsius(celsius, b.value);
60
58
  result.textContent = `${fmt(out)} ${b.value === "k" ? "K" : `°${b.value.toUpperCase()}`}`;
61
59
  formula.textContent = a.value === b.value ? "" : FORMULAS[`${a.value}-${b.value}`];
62
- scroll.replaceChildren(el("table", { class: "mvb-ttable" },
60
+ 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" },
63
61
  el("thead", {}, el("tr", {}, el("th", {}, "Scale"), el("th", {}, "Value"))),
64
62
  el("tbody", {}, ...SCALES.map((scale) => el("tr", {},
65
63
  el("td", {}, scale.label),
@@ -83,8 +81,7 @@ export function mount(slot) {
83
81
  stack(panel("Value", value), panel("From", from), panel("To", to), error),
84
82
  stack(panel("Result", stats), formula, panel("All scales", scroll))
85
83
  ),
86
- foot: actions(swap, copyButton(() => text, "Copy result", "fill")),
87
- });
84
+ foot: actions(swap, copyButton(() => text, "Copy result", "fill"))});
88
85
  slot.append(card.node);
89
86
  run();
90
87
 
@@ -8,9 +8,8 @@ export function mount(slot) {
8
8
  const right = textArea({ rows: 9, "aria-label": "Second text", placeholder: "Second text" });
9
9
  const options = toggleRow([
10
10
  { value: "ignore", label: "Ignore case", checked: false },
11
- { value: "spaces", label: "Ignore spaces", checked: false },
12
- ]);
13
- const diffBox = el("div", { class: "mvb-diff" });
11
+ { value: "spaces", label: "Ignore spaces", checked: false }]);
12
+ const diffBox = el("div", { class: "grid gap-px overflow-hidden rounded-r1 border border-orbit bg-orbit" });
14
13
  const note = notice("note");
15
14
  let rows = [];
16
15
 
@@ -66,8 +65,14 @@ export function mount(slot) {
66
65
  const removed = rows.filter(([type]) => type === "del").length;
67
66
  diffBox.replaceChildren(...rows.map(([type, raw]) => el(
68
67
  "div",
69
- { class: `mvb-diff__row${type === "add" ? " mvb-diff__row--add" : type === "del" ? " mvb-diff__row--del" : ""}` },
70
- el("span", { class: "mvb-diff__sign" }, type === "add" ? "+" : type === "del" ? "-" : " "),
68
+ {
69
+ // One row skin. The add/del tint and the sign colour hang off the data
70
+ // attribute, so a row never carries two competing background utilities.
71
+ class:
72
+ "grid bg-deck font-mono text-2xs [grid-template-columns:34px_minmax(0,1fr)] [&>span]:wrap-anywhere [&>span]:px-2.5 [&>span]:py-[3px] [&>span]:whitespace-pre-wrap data-[kind=add]:bg-[var(--add)] data-[kind=add]:[&>span:first-child]:text-jade data-[kind=del]:bg-[var(--del)] data-[kind=del]:[&>span:first-child]:text-danger",
73
+ dataset: { kind: type },
74
+ },
75
+ el("span", { class: "border-e border-orbit text-center text-ink-3 select-none" }, type === "add" ? "+" : type === "del" ? "-" : " "),
71
76
  el("span", {}, raw)
72
77
  )));
73
78
  const counts = !added && !removed
@@ -93,8 +98,7 @@ export function mount(slot) {
93
98
  stack(panel("First text", left), panel("Second text", right), options, note),
94
99
  stack(panel("Difference", diffBox))
95
100
  ),
96
- foot: actions(swapButton, copyButton(diffText, "Copy diff", "fill")),
97
- });
101
+ foot: actions(swapButton, copyButton(diffText, "Copy diff", "fill"))});
98
102
  slot.append(card.node);
99
103
  run();
100
104
 
@@ -14,7 +14,7 @@ function detect(text) {
14
14
 
15
15
  export function mount(slot) {
16
16
  const source = textArea({ rows: 6, "aria-label": "Text", placeholder: "Type or paste text" });
17
- const sample = el("div", { class: "mvb-preview__sample" }, "Sample text");
17
+ const sample = el("div", { class: "w-full rounded-lg border border-orbit bg-deck px-[18px] py-4" }, "Sample text");
18
18
  const code = output();
19
19
 
20
20
  let mode = "auto";
@@ -22,8 +22,7 @@ export function mount(slot) {
22
22
  [
23
23
  { value: "auto", label: "Auto" },
24
24
  { value: "ltr", label: "Left to right" },
25
- { value: "rtl", label: "Right to left" },
26
- ],
25
+ { value: "rtl", label: "Right to left" }],
27
26
  { value: "auto", label: "Direction", onChange: (next) => { mode = next; run(); } }
28
27
  );
29
28
 
@@ -44,8 +43,7 @@ export function mount(slot) {
44
43
  stack(panel("Text", source), panel("Direction", dirChips)),
45
44
  stack(panel("Preview", sample), panel("Attribute", code))
46
45
  ),
47
- foot: actions(copyButton(() => snippet, "Copy attribute", "fill")),
48
- });
46
+ foot: actions(copyButton(() => snippet, "Copy attribute", "fill"))});
49
47
  slot.append(card.node);
50
48
  run();
51
49
 
@@ -15,15 +15,14 @@ const reverseLines = (text) => text ? text.split("\n").reverse().join("\n") : ""
15
15
 
16
16
  export function mount(slot) {
17
17
  const source = textArea({ rows: 10, "aria-label": "Text", placeholder: "Type or paste text" });
18
- const result = textArea({ rows: 10, readonly: true, class: "mvb-in mvb-in--area mvb-in--mono", "aria-label": "Result" });
18
+ const result = textArea({ rows: 10, readonly: true, "aria-label": "Result" });
19
19
 
20
20
  let mode = "chars";
21
21
  const chips = chipRow(
22
22
  [
23
23
  { value: "chars", label: "Characters" },
24
24
  { value: "words", label: "Words" },
25
- { value: "lines", label: "Lines" },
26
- ],
25
+ { value: "lines", label: "Lines" }],
27
26
  { value: "chars", label: "Reverse by", onChange: (value) => { mode = value; run(); } }
28
27
  );
29
28
 
@@ -40,8 +39,7 @@ export function mount(slot) {
40
39
  stack(panel("Text", source), panel("Reverse by", chips)),
41
40
  stack(panel("Result", result))
42
41
  ),
43
- foot: actions(copyButton(() => result.value, "Copy result", "fill")),
44
- });
42
+ foot: actions(copyButton(() => result.value, "Copy result", "fill"))});
45
43
  slot.append(card.node);
46
44
  run();
47
45
 
@@ -11,19 +11,19 @@ function parseTime(value) {
11
11
  }
12
12
 
13
13
  const kvRow = (key, value) =>
14
- el("div", { class: "mvb-kv__row" }, el("span", { class: "mvb-kv__k" }, key), el("span", { class: "mvb-kv__v" }, value));
14
+ 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));
15
15
 
16
16
  export function mount(slot) {
17
17
  const start = textInput({ type: "time", value: "09:00", "aria-label": "Start time" });
18
18
  const end = textInput({ type: "time", value: "17:30", "aria-label": "End time" });
19
19
  const breakMin = numberInput({ value: "30", min: "0", "aria-label": "Break in minutes" });
20
20
 
21
- const result = el("span", { class: "mvb-stat__v" }, "-");
22
- const tile = el("div", { class: "mvb-stat" }, result, el("span", { class: "mvb-stat__k" }, "Duration"));
23
- const stats = el("div", { class: "mvb-stats" }, tile);
24
- const minutesOut = el("span", { class: "mvb-kv__v" }, "-");
25
- const decimalOut = el("span", { class: "mvb-kv__v" }, "-");
26
- const kv = el("div", { class: "mvb-kv" }, kvRow("Total minutes", minutesOut), kvRow("Decimal hours", decimalOut));
21
+ const result = el("span", { class: "wrap-anywhere text-xl font-bold tracking-[-0.02em] tabular-nums" }, "-");
22
+ const tile = 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" }, "Duration"));
23
+ const stats = el("div", { class: "grid gap-2.5 [grid-template-columns:repeat(auto-fit,minmax(120px,1fr))]" }, tile);
24
+ const minutesOut = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
25
+ const decimalOut = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
26
+ const kv = el("div", { class: "grid gap-px overflow-hidden rounded-r1 border border-orbit bg-orbit" }, kvRow("Total minutes", minutesOut), kvRow("Decimal hours", decimalOut));
27
27
  const error = notice("error");
28
28
  let text = "";
29
29
 
@@ -72,8 +72,7 @@ export function mount(slot) {
72
72
  stack(panel("Start", start), panel("End", end), panel("Break in minutes", breakMin), error),
73
73
  stack(stats, panel("Breakdown", kv))
74
74
  ),
75
- foot: actions(copyButton(() => text, "Copy result", "fill")),
76
- });
75
+ foot: actions(copyButton(() => text, "Copy result", "fill"))});
77
76
  slot.append(card.node);
78
77
  run();
79
78
 
@@ -6,8 +6,7 @@ function relative(date) {
6
6
  const abs = Math.abs(diff);
7
7
  const units = [
8
8
  ["year", 31536000000], ["month", 2592000000], ["day", 86400000],
9
- ["hour", 3600000], ["minute", 60000], ["second", 1000],
10
- ];
9
+ ["hour", 3600000], ["minute", 60000], ["second", 1000]];
11
10
  for (const [unit, ms] of units) {
12
11
  if (abs >= ms) {
13
12
  const value = Math.round(abs / ms);
@@ -32,11 +31,11 @@ function parse(text) {
32
31
  }
33
32
 
34
33
  const row = (key, value) =>
35
- el("div", { class: "mvb-kv__row" }, el("span", { class: "mvb-kv__k" }, key), el("span", { class: "mvb-kv__v" }, value));
34
+ 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));
36
35
 
37
36
  export function mount(slot) {
38
37
  const source = textInput({ value: "1700000000", "aria-label": "Timestamp or date", placeholder: "1700000000 or 2026-01-01T00:00:00Z" });
39
- const kv = el("div", { class: "mvb-kv" });
38
+ const kv = el("div", { class: "grid gap-px overflow-hidden rounded-r1 border border-orbit bg-orbit" });
40
39
  const error = notice("error");
41
40
  let iso = "";
42
41
 
@@ -74,8 +73,7 @@ export function mount(slot) {
74
73
  stack(panel("Timestamp or date", source), error),
75
74
  stack(panel("Reading", kv))
76
75
  ),
77
- foot: actions(nowButton, copyButton(() => iso, "Copy ISO 8601", "fill")),
78
- });
76
+ foot: actions(nowButton, copyButton(() => iso, "Copy ISO 8601", "fill"))});
79
77
  slot.append(card.node);
80
78
  run();
81
79
 
@@ -18,8 +18,7 @@ const FALLBACK_ZONES = [
18
18
  "Asia/Shanghai",
19
19
  "Asia/Tokyo",
20
20
  "Asia/Singapore",
21
- "Australia/Sydney",
22
- ];
21
+ "Australia/Sydney"];
23
22
 
24
23
  function zoneList() {
25
24
  let zones = FALLBACK_ZONES;
@@ -68,8 +67,7 @@ function offsetMs(timeZone, date) {
68
67
  day: "2-digit",
69
68
  hour: "2-digit",
70
69
  minute: "2-digit",
71
- second: "2-digit",
72
- });
70
+ second: "2-digit"});
73
71
  for (const part of format.formatToParts(date)) parts[part.type] = part.value;
74
72
  const hour = parts.hour === "24" ? 0 : Number(parts.hour);
75
73
  const asUTC = Date.UTC(Number(parts.year), Number(parts.month) - 1, Number(parts.day), hour, Number(parts.minute), Number(parts.second));
@@ -85,7 +83,7 @@ function wallToInstant(day, time, timeZone) {
85
83
  const pad = (n) => String(n).padStart(2, "0");
86
84
 
87
85
  const kvRow = (key, value) =>
88
- el("div", { class: "mvb-kv__row" }, el("span", { class: "mvb-kv__k" }, key), el("span", { class: "mvb-kv__v" }, value));
86
+ 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));
89
87
 
90
88
  export function mount(slot) {
91
89
  const zones = zoneList();
@@ -95,10 +93,10 @@ export function mount(slot) {
95
93
  const fromZone = selectInput(zones, { value: Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC", "aria-label": "Source zone" });
96
94
  const toZone = selectInput(zones, { value: "UTC", "aria-label": "Target zone" });
97
95
 
98
- const target = el("span", { class: "mvb-kv__v" }, "-");
99
- const utc = el("span", { class: "mvb-kv__v" }, "-");
100
- const diff = el("span", { class: "mvb-kv__v" }, "-");
101
- const kv = el("div", { class: "mvb-kv" }, kvRow("Target time", target), kvRow("UTC", utc), kvRow("Offset", diff));
96
+ const target = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
97
+ const utc = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
98
+ const diff = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
99
+ const kv = el("div", { class: "grid gap-px overflow-hidden rounded-r1 border border-orbit bg-orbit" }, kvRow("Target time", target), kvRow("UTC", utc), kvRow("Offset", diff));
102
100
  const error = notice("error");
103
101
  let text = "";
104
102
 
@@ -127,8 +125,7 @@ export function mount(slot) {
127
125
  month: "short",
128
126
  year: "numeric",
129
127
  hour: "2-digit",
130
- minute: "2-digit",
131
- }).format(instant);
128
+ minute: "2-digit"}).format(instant);
132
129
  const utcText = `${instant.getUTCFullYear()}-${pad(instant.getUTCMonth() + 1)}-${pad(instant.getUTCDate())} ${pad(instant.getUTCHours())}:${pad(instant.getUTCMinutes())} UTC`;
133
130
  const deltaHours = (offsetMs(toZone.value, instant) - offsetMs(fromZone.value, instant)) / 3600000;
134
131
  const sign = deltaHours > 0 ? "+" : deltaHours < 0 ? "-" : "";
@@ -152,8 +149,7 @@ export function mount(slot) {
152
149
  stack(panel("Date", date), panel("Time", time), panel("From", fromZone), panel("To", toZone), error),
153
150
  stack(panel("Result", kv))
154
151
  ),
155
- foot: actions(copyButton(() => text, "Copy result", "fill")),
156
- });
152
+ foot: actions(copyButton(() => text, "Copy result", "fill"))});
157
153
  slot.append(card.node);
158
154
  run();
159
155
 
@@ -4,8 +4,8 @@ import { el, blank, bench, stack, panel, textArea, numberInput, copyButton, acti
4
4
  const countWords = (text) => (text.trim() ? text.trim().split(/\s+/).length : 0);
5
5
 
6
6
  function tile(label) {
7
- const value = el("span", { class: "mvb-stat__v" }, "0");
8
- const node = el("div", { class: "mvb-stat" }, value, el("span", { class: "mvb-stat__k" }, label));
7
+ const value = el("span", { class: "wrap-anywhere text-xl font-bold tracking-[-0.02em] tabular-nums" }, "0");
8
+ 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));
9
9
  node.set = (text) => { value.textContent = text; };
10
10
  return node;
11
11
  }
@@ -17,9 +17,9 @@ export function mount(slot) {
17
17
  const titlesTile = tile("Titles");
18
18
  const overTile = tile("Over the limit");
19
19
  const longestTile = tile("Longest");
20
- const stats = el("div", { class: "mvb-stats" }, titlesTile, overTile, longestTile);
20
+ const stats = el("div", { class: "grid gap-2.5 [grid-template-columns:repeat(auto-fit,minmax(120px,1fr))]" }, titlesTile, overTile, longestTile);
21
21
 
22
- const listWrap = el("div", { class: "mvb-scroll" });
22
+ const listWrap = el("div", { class: "max-h-80 overflow-auto" });
23
23
  let listText = "";
24
24
 
25
25
  function run() {
@@ -42,7 +42,7 @@ export function mount(slot) {
42
42
  el("th", {}, item.title),
43
43
  el("td", {}, cap > 0 && item.chars > cap ? `${item.chars} over` : String(item.chars)),
44
44
  el("td", {}, String(item.words))));
45
- listWrap.replaceChildren(el("table", { class: "mvb-ttable" },
45
+ 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" },
46
46
  el("thead", {}, el("tr", {}, el("th", {}, "Title"), el("th", {}, "Characters"), el("th", {}, "Words"))),
47
47
  el("tbody", {}, ...rows)));
48
48
  listText = items.map((item) => `${item.chars} characters, ${item.words} words ${item.title}`).join("\n");
@@ -56,8 +56,7 @@ export function mount(slot) {
56
56
  stack(panel("Titles", source), panel("Character limit", limit)),
57
57
  stack(stats, panel("By length", listWrap))
58
58
  ),
59
- foot: actions(copyButton(() => listText, "Copy list", "fill")),
60
- });
59
+ foot: actions(copyButton(() => listText, "Copy list", "fill"))});
61
60
  slot.append(card.node);
62
61
  run();
63
62
 
@@ -11,18 +11,18 @@ function describe(char) {
11
11
 
12
12
  export function mount(slot) {
13
13
  const source = textArea({ rows: 8, value: "mevebu", "aria-label": "Text" });
14
- const scroll = el("div", { class: "mvb-scroll" });
14
+ const scroll = el("div", { class: "max-h-80 overflow-auto" });
15
15
  let rows = [];
16
16
 
17
17
  function run() {
18
18
  rows = Array.from(source.value).map(describe);
19
19
  if (!rows.length) {
20
- scroll.replaceChildren(el("p", { class: "mvb-note" }, "Type or paste text."));
20
+ scroll.replaceChildren(el("p", { class: "border-s-[3px] border-transparent ps-2 text-sm text-ink-3" }, "Type or paste text."));
21
21
  return;
22
22
  }
23
23
  scroll.replaceChildren(el(
24
24
  "table",
25
- { class: "mvb-ttable" },
25
+ { 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" },
26
26
  el("thead", {}, el("tr", {},
27
27
  el("th", {}, "Character"),
28
28
  el("th", {}, "Code point"),
@@ -47,8 +47,7 @@ export function mount(slot) {
47
47
  () => rows.map((entry) => `${entry.char}\t${entry.point}\t${entry.decimal}\t${entry.bytes}`).join("\n"),
48
48
  "Copy table",
49
49
  "fill"
50
- )),
51
- });
50
+ ))});
52
51
  slot.append(card.node);
53
52
  run();
54
53
 
@@ -1,10 +1,12 @@
1
1
  // Percent-encoding for a single value, following encodeURIComponent. The
2
2
  // swap button moves the result back into the input for a second round.
3
- import { bench, stack, panel, textArea, notice, chipRow, button, copyButton, actions, toolcard } from "../core.js";
3
+ import { bench, stack, panel, textArea, output, notice, chipRow, button, copyButton, actions, toolcard } from "../core.js";
4
4
 
5
5
  export function mount(slot) {
6
6
  const source = textArea({ rows: 5, value: "q=hello world&lang=id", "aria-label": "Input" });
7
- const result = textArea({ rows: 5, readonly: true, class: "mvb-in mvb-in--area mvb-in--mono", "aria-label": "Result" });
7
+ // The encoded text is read, not typed, so it sits on the recessed surface.
8
+ const result = output({ tag: "textarea", rows: 5 });
9
+ result.setAttribute("aria-label", "Result");
8
10
  const error = notice("error");
9
11
  let mode = "encode";
10
12
 
@@ -4,23 +4,23 @@ import { el, textInput, notice, copyButton, bench, stack, panel, actions, toolca
4
4
  const IPV4 = /^\d{1,3}(\.\d{1,3}){3}$/;
5
5
 
6
6
  const kvRow = (key, value) =>
7
- el("div", { class: "mvb-kv__row" }, el("span", { class: "mvb-kv__k" }, key), el("span", { class: "mvb-kv__v" }, value));
7
+ 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));
8
8
 
9
9
  export function mount(slot) {
10
10
  const source = textInput({ value: "https://example.com/login?next=/home", "aria-label": "URL" });
11
- const scheme = el("span", { class: "mvb-kv__v" }, "-");
12
- const host = el("span", { class: "mvb-kv__v" }, "-");
13
- const port = el("span", { class: "mvb-kv__v" }, "-");
14
- const path = el("span", { class: "mvb-kv__v" }, "-");
11
+ const scheme = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
12
+ const host = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
13
+ const port = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
14
+ const path = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
15
15
  const kv = el(
16
16
  "div",
17
- { class: "mvb-kv" },
17
+ { class: "grid gap-px overflow-hidden rounded-r1 border border-orbit bg-orbit" },
18
18
  kvRow("Scheme", scheme),
19
19
  kvRow("Host", host),
20
20
  kvRow("Port", port),
21
21
  kvRow("Path", path)
22
22
  );
23
- const flags = el("div", { class: "mvb-stack" });
23
+ const flags = el("div", { class: "grid content-start gap-4" });
24
24
  const error = notice("error");
25
25
  let summary = "";
26
26
 
@@ -82,13 +82,11 @@ export function mount(slot) {
82
82
  body: bench(
83
83
  stack(
84
84
  panel("URL", source),
85
- error,
86
- el("p", { class: "mvb-note" }, "The link is read in this tab. Nothing is opened or requested.")
85
+ error
87
86
  ),
88
87
  stack(kv, panel("Checks", flags))
89
88
  ),
90
- foot: actions(copyButton(() => summary, "Copy host", "fill")),
91
- });
89
+ foot: actions(copyButton(() => summary, "Copy host", "fill"))});
92
90
  slot.append(card.node);
93
91
  run();
94
92
 
@@ -2,32 +2,32 @@
2
2
  import { el, bench, stack, panel, textInput, notice, copyButton, actions, toolcard } from "../core.js";
3
3
 
4
4
  const kvRow = (key, value) =>
5
- el("div", { class: "mvb-kv__row" }, el("span", { class: "mvb-kv__k" }, key), el("span", { class: "mvb-kv__v" }, value));
5
+ 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));
6
6
 
7
7
  export function mount(slot) {
8
8
  const source = textInput({ value: "https://example.com:8443/blog/post?utm_source=news&page=2#top", "aria-label": "URL" });
9
9
  const error = notice("error");
10
10
 
11
- const protocol = el("span", { class: "mvb-kv__v" }, "-");
12
- const host = el("span", { class: "mvb-kv__v" }, "-");
13
- const port = el("span", { class: "mvb-kv__v" }, "-");
14
- const path = el("span", { class: "mvb-kv__v" }, "-");
15
- const hash = el("span", { class: "mvb-kv__v" }, "-");
11
+ const protocol = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
12
+ const host = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
13
+ const port = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
14
+ const path = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
15
+ const hash = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
16
16
  const kv = el(
17
17
  "div",
18
- { class: "mvb-kv" },
18
+ { class: "grid gap-px overflow-hidden rounded-r1 border border-orbit bg-orbit" },
19
19
  kvRow("Protocol", protocol),
20
20
  kvRow("Host", host),
21
21
  kvRow("Port", port),
22
22
  kvRow("Path", path),
23
23
  kvRow("Hash", hash)
24
24
  );
25
- const tableWrap = el("div", { class: "mvb-scroll" });
25
+ const tableWrap = el("div", { class: "max-h-80 overflow-auto" });
26
26
  let paramsText = "";
27
27
 
28
28
  function clear() {
29
29
  for (const node of [protocol, host, port, path, hash]) node.textContent = "-";
30
- tableWrap.replaceChildren(el("p", { class: "mvb-note" }, "No query parameters."));
30
+ tableWrap.replaceChildren(el("p", { class: "border-s-[3px] border-transparent ps-2 text-sm text-ink-3" }, "No query parameters."));
31
31
  paramsText = "";
32
32
  }
33
33
 
@@ -49,13 +49,13 @@ export function mount(slot) {
49
49
  hash.textContent = url.hash || "-";
50
50
  const params = [...url.searchParams];
51
51
  if (!params.length) {
52
- tableWrap.replaceChildren(el("p", { class: "mvb-note" }, "No query parameters."));
52
+ tableWrap.replaceChildren(el("p", { class: "border-s-[3px] border-transparent ps-2 text-sm text-ink-3" }, "No query parameters."));
53
53
  paramsText = "";
54
54
  return;
55
55
  }
56
56
  tableWrap.replaceChildren(el(
57
57
  "table",
58
- { class: "mvb-ttable" },
58
+ { 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" },
59
59
  el("thead", {}, el("tr", {}, el("th", {}, "Key"), el("th", {}, "Value"))),
60
60
  el("tbody", {}, ...params.map(([key, value]) => el("tr", {}, el("td", {}, key), el("td", {}, value))))
61
61
  ));
@@ -72,8 +72,7 @@ export function mount(slot) {
72
72
  foot: actions(
73
73
  copyButton(() => source.value.trim(), "Copy URL"),
74
74
  copyButton(() => paramsText, "Copy params", "fill")
75
- ),
76
- });
75
+ )});
77
76
  slot.append(card.node);
78
77
  run();
79
78