mevebu 0.3.0 → 0.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 (178) hide show
  1. package/README.md +24 -8
  2. package/mevebu/app/mevebu.js +3 -3
  3. package/mevebu/libs/css/tools.css +103 -1
  4. package/mevebu/libs/js/core.js +1 -1
  5. package/mevebu/libs/js/icons.js +115 -1
  6. package/mevebu/libs/js/registry.js +3102 -0
  7. package/mevebu/libs/js/tools/abstract-checklist.js +54 -0
  8. package/mevebu/libs/js/tools/add-subtract-days.js +105 -0
  9. package/mevebu/libs/js/tools/age-calculator.js +108 -0
  10. package/mevebu/libs/js/tools/apa-reference.js +98 -0
  11. package/mevebu/libs/js/tools/api-request-builder.js +76 -0
  12. package/mevebu/libs/js/tools/area-converter.js +87 -0
  13. package/mevebu/libs/js/tools/aspect-ratio.js +81 -0
  14. package/mevebu/libs/js/tools/average-calculator.js +83 -0
  15. package/mevebu/libs/js/tools/bibliography-sorter.js +43 -0
  16. package/mevebu/libs/js/tools/bibtex-generator.js +98 -0
  17. package/mevebu/libs/js/tools/binary-calculator.js +58 -0
  18. package/mevebu/libs/js/tools/border-radius.js +88 -0
  19. package/mevebu/libs/js/tools/box-shadow.js +71 -0
  20. package/mevebu/libs/js/tools/branch-name.js +44 -0
  21. package/mevebu/libs/js/tools/break-even.js +68 -0
  22. package/mevebu/libs/js/tools/business-days.js +120 -0
  23. package/mevebu/libs/js/tools/canonical-tag.js +63 -0
  24. package/mevebu/libs/js/tools/caption-counter.js +66 -0
  25. package/mevebu/libs/js/tools/case-inspector.js +114 -0
  26. package/mevebu/libs/js/tools/cash-denomination.js +74 -0
  27. package/mevebu/libs/js/tools/cash-flow.js +124 -0
  28. package/mevebu/libs/js/tools/cert-expiry.js +83 -0
  29. package/mevebu/libs/js/tools/changelog-generator.js +48 -0
  30. package/mevebu/libs/js/tools/character-counter.js +80 -0
  31. package/mevebu/libs/js/tools/checklist.js +86 -0
  32. package/mevebu/libs/js/tools/citation-metadata.js +89 -0
  33. package/mevebu/libs/js/tools/citation-number-check.js +107 -0
  34. package/mevebu/libs/js/tools/color-palette.js +110 -0
  35. package/mevebu/libs/js/tools/color-picker.js +93 -0
  36. package/mevebu/libs/js/tools/commit-message.js +62 -0
  37. package/mevebu/libs/js/tools/content-calendar.js +111 -0
  38. package/mevebu/libs/js/tools/contrast-checker.js +93 -0
  39. package/mevebu/libs/js/tools/countdown-timer.js +174 -0
  40. package/mevebu/libs/js/tools/cron-builder.js +91 -0
  41. package/mevebu/libs/js/tools/css-clamp.js +66 -0
  42. package/mevebu/libs/js/tools/css-formatter.js +84 -0
  43. package/mevebu/libs/js/tools/css-grid.js +75 -0
  44. package/mevebu/libs/js/tools/css-minifier.js +98 -0
  45. package/mevebu/libs/js/tools/csv-to-json.js +114 -0
  46. package/mevebu/libs/js/tools/csv-viewer.js +132 -0
  47. package/mevebu/libs/js/tools/currency-format.js +69 -0
  48. package/mevebu/libs/js/tools/daily-planner.js +124 -0
  49. package/mevebu/libs/js/tools/data-masker.js +71 -0
  50. package/mevebu/libs/js/tools/data-storage.js +89 -0
  51. package/mevebu/libs/js/tools/data-validator.js +111 -0
  52. package/mevebu/libs/js/tools/date-difference.js +100 -0
  53. package/mevebu/libs/js/tools/date-format.js +107 -0
  54. package/mevebu/libs/js/tools/delimiter-converter.js +109 -0
  55. package/mevebu/libs/js/tools/discount-calculator.js +83 -0
  56. package/mevebu/libs/js/tools/doi-resolver.js +67 -0
  57. package/mevebu/libs/js/tools/env-formatter.js +72 -0
  58. package/mevebu/libs/js/tools/equation-formatter.js +71 -0
  59. package/mevebu/libs/js/tools/expense-split.js +90 -0
  60. package/mevebu/libs/js/tools/favicon-markup.js +53 -0
  61. package/mevebu/libs/js/tools/find-replace.js +88 -0
  62. package/mevebu/libs/js/tools/flashcard-creator.js +117 -0
  63. package/mevebu/libs/js/tools/font-pairing.js +88 -0
  64. package/mevebu/libs/js/tools/fraction-calculator.js +130 -0
  65. package/mevebu/libs/js/tools/git-command-builder.js +100 -0
  66. package/mevebu/libs/js/tools/gitignore-generator.js +60 -0
  67. package/mevebu/libs/js/tools/gpa-calculator.js +100 -0
  68. package/mevebu/libs/js/tools/grade-calculator.js +101 -0
  69. package/mevebu/libs/js/tools/gradient-generator.js +99 -0
  70. package/mevebu/libs/js/tools/habit-tracker.js +120 -0
  71. package/mevebu/libs/js/tools/harvard-citation.js +90 -0
  72. package/mevebu/libs/js/tools/hashtag-formatter.js +62 -0
  73. package/mevebu/libs/js/tools/heading-formatter.js +61 -0
  74. package/mevebu/libs/js/tools/hmac-generator.js +63 -0
  75. package/mevebu/libs/js/tools/hpp-calculator.js +70 -0
  76. package/mevebu/libs/js/tools/html-entity.js +49 -0
  77. package/mevebu/libs/js/tools/html-formatter.js +72 -0
  78. package/mevebu/libs/js/tools/html-to-text.js +47 -0
  79. package/mevebu/libs/js/tools/http-status.js +63 -0
  80. package/mevebu/libs/js/tools/ieee-reference.js +102 -0
  81. package/mevebu/libs/js/tools/intext-citation.js +75 -0
  82. package/mevebu/libs/js/tools/inventory-quantity.js +103 -0
  83. package/mevebu/libs/js/tools/invoice-number.js +64 -0
  84. package/mevebu/libs/js/tools/json-escape.js +76 -0
  85. package/mevebu/libs/js/tools/json-formatter.js +51 -0
  86. package/mevebu/libs/js/tools/json-minifier.js +58 -0
  87. package/mevebu/libs/js/tools/json-to-csv.js +72 -0
  88. package/mevebu/libs/js/tools/json-to-typescript.js +102 -0
  89. package/mevebu/libs/js/tools/json-validator.js +49 -0
  90. package/mevebu/libs/js/tools/jwt-decoder.js +74 -0
  91. package/mevebu/libs/js/tools/jwt-expiry.js +97 -0
  92. package/mevebu/libs/js/tools/length-converter.js +86 -0
  93. package/mevebu/libs/js/tools/license-reference.js +58 -0
  94. package/mevebu/libs/js/tools/line-break-formatter.js +48 -0
  95. package/mevebu/libs/js/tools/line-counter.js +55 -0
  96. package/mevebu/libs/js/tools/list-difference.js +86 -0
  97. package/mevebu/libs/js/tools/list-merger.js +67 -0
  98. package/mevebu/libs/js/tools/locale-number.js +79 -0
  99. package/mevebu/libs/js/tools/markdown-preview.js +143 -0
  100. package/mevebu/libs/js/tools/markdown-table.js +111 -0
  101. package/mevebu/libs/js/tools/markdown-to-text.js +48 -0
  102. package/mevebu/libs/js/tools/markup-calculator.js +69 -0
  103. package/mevebu/libs/js/tools/meeting-agenda.js +137 -0
  104. package/mevebu/libs/js/tools/meta-preview.js +51 -0
  105. package/mevebu/libs/js/tools/meta-tags.js +62 -0
  106. package/mevebu/libs/js/tools/mime-types.js +54 -0
  107. package/mevebu/libs/js/tools/mla-citation.js +91 -0
  108. package/mevebu/libs/js/tools/number-base-converter.js +97 -0
  109. package/mevebu/libs/js/tools/open-graph.js +62 -0
  110. package/mevebu/libs/js/tools/password-strength.js +118 -0
  111. package/mevebu/libs/js/tools/percent-to-fraction.js +96 -0
  112. package/mevebu/libs/js/tools/percentage-calculator.js +86 -0
  113. package/mevebu/libs/js/tools/percentage-change.js +70 -0
  114. package/mevebu/libs/js/tools/permutation-combination.js +106 -0
  115. package/mevebu/libs/js/tools/prefix-suffix.js +46 -0
  116. package/mevebu/libs/js/tools/priority-matrix.js +100 -0
  117. package/mevebu/libs/js/tools/profit-margin.js +69 -0
  118. package/mevebu/libs/js/tools/quadratic-solver.js +93 -0
  119. package/mevebu/libs/js/tools/query-builder.js +71 -0
  120. package/mevebu/libs/js/tools/quiz-formatter.js +70 -0
  121. package/mevebu/libs/js/tools/quotation-formatter.js +77 -0
  122. package/mevebu/libs/js/tools/ratio-calculator.js +103 -0
  123. package/mevebu/libs/js/tools/readability.js +112 -0
  124. package/mevebu/libs/js/tools/reading-time.js +76 -0
  125. package/mevebu/libs/js/tools/readme-generator.js +50 -0
  126. package/mevebu/libs/js/tools/reference-completeness.js +62 -0
  127. package/mevebu/libs/js/tools/regex-tester.js +86 -0
  128. package/mevebu/libs/js/tools/remove-duplicate-lines.js +46 -0
  129. package/mevebu/libs/js/tools/remove-extra-spaces.js +41 -0
  130. package/mevebu/libs/js/tools/robots-txt.js +60 -0
  131. package/mevebu/libs/js/tools/roman-numeral-converter.js +106 -0
  132. package/mevebu/libs/js/tools/sales-commission.js +96 -0
  133. package/mevebu/libs/js/tools/scientific-calculator.js +247 -0
  134. package/mevebu/libs/js/tools/scientific-notation.js +83 -0
  135. package/mevebu/libs/js/tools/script-planner.js +72 -0
  136. package/mevebu/libs/js/tools/secure-token.js +60 -0
  137. package/mevebu/libs/js/tools/selling-price.js +74 -0
  138. package/mevebu/libs/js/tools/semver-helper.js +54 -0
  139. package/mevebu/libs/js/tools/sentence-length.js +80 -0
  140. package/mevebu/libs/js/tools/sequence-analyzer.js +90 -0
  141. package/mevebu/libs/js/tools/sitemap-xml.js +82 -0
  142. package/mevebu/libs/js/tools/slug-converter.js +48 -0
  143. package/mevebu/libs/js/tools/slug-preview.js +83 -0
  144. package/mevebu/libs/js/tools/sort-lines.js +54 -0
  145. package/mevebu/libs/js/tools/speed-converter.js +83 -0
  146. package/mevebu/libs/js/tools/sql-formatter.js +78 -0
  147. package/mevebu/libs/js/tools/sql-minifier.js +102 -0
  148. package/mevebu/libs/js/tools/standard-deviation.js +96 -0
  149. package/mevebu/libs/js/tools/stopwatch.js +133 -0
  150. package/mevebu/libs/js/tools/study-schedule.js +88 -0
  151. package/mevebu/libs/js/tools/subtitle-formatter.js +74 -0
  152. package/mevebu/libs/js/tools/table-to-markdown.js +137 -0
  153. package/mevebu/libs/js/tools/tax-calculator.js +84 -0
  154. package/mevebu/libs/js/tools/temperature-converter.js +94 -0
  155. package/mevebu/libs/js/tools/text-diff.js +104 -0
  156. package/mevebu/libs/js/tools/text-direction.js +55 -0
  157. package/mevebu/libs/js/tools/text-reverser.js +51 -0
  158. package/mevebu/libs/js/tools/time-duration.js +83 -0
  159. package/mevebu/libs/js/tools/time-format-converter.js +86 -0
  160. package/mevebu/libs/js/tools/timestamp-converter.js +85 -0
  161. package/mevebu/libs/js/tools/timezone-converter.js +163 -0
  162. package/mevebu/libs/js/tools/title-length.js +67 -0
  163. package/mevebu/libs/js/tools/unicode-escape.js +80 -0
  164. package/mevebu/libs/js/tools/unicode-inspector.js +58 -0
  165. package/mevebu/libs/js/tools/url-inspector.js +98 -0
  166. package/mevebu/libs/js/tools/url-parser.js +83 -0
  167. package/mevebu/libs/js/tools/utf8-bytes.js +67 -0
  168. package/mevebu/libs/js/tools/version-compare.js +70 -0
  169. package/mevebu/libs/js/tools/video-duration.js +72 -0
  170. package/mevebu/libs/js/tools/volume-converter.js +88 -0
  171. package/mevebu/libs/js/tools/weekly-planner.js +101 -0
  172. package/mevebu/libs/js/tools/weight-converter.js +85 -0
  173. package/mevebu/libs/js/tools/weighted-average.js +86 -0
  174. package/mevebu/libs/js/tools/word-counter.js +90 -0
  175. package/mevebu/libs/js/tools/word-limit.js +60 -0
  176. package/mevebu/libs/js/tools/xml-formatter.js +85 -0
  177. package/mevebu/libs/js/tools/yaml-formatter.js +71 -0
  178. package/package.json +1 -1
@@ -0,0 +1,90 @@
1
+ // Live word, character, sentence and line counts, plus an optional word limit
2
+ // and a reading time estimate.
3
+ import { el, bench, stack, panel, textArea, numberInput, notice, button, copyButton, actions, toolcard } from "../core.js";
4
+
5
+ const WPM = 200;
6
+
7
+ function tile(label) {
8
+ const value = el("span", { class: "mvb-stat__v" }, "0");
9
+ const node = el("div", { class: "mvb-stat" }, value, el("span", { class: "mvb-stat__k" }, label));
10
+ node.set = (text) => { value.textContent = text; };
11
+ return node;
12
+ }
13
+
14
+ const countSentences = (text) => text.trim()
15
+ ? text.trim().split(/[.!?\u2026]+/).filter((part) => part.trim()).length
16
+ : 0;
17
+
18
+ export function mount(slot) {
19
+ const source = textArea({ rows: 10, "aria-label": "Text", placeholder: "Type or paste text" });
20
+ const limitField = numberInput({ min: "0", placeholder: "No limit", "aria-label": "Word limit" });
21
+
22
+ const wordsTile = tile("Words");
23
+ const charsTile = tile("Characters");
24
+ const sentencesTile = tile("Sentences");
25
+ const linesTile = tile("Lines");
26
+ const readingTile = tile("Reading time");
27
+ const stats = el("div", { class: "mvb-stats" }, wordsTile, charsTile, sentencesTile, linesTile, readingTile);
28
+
29
+ const meter = el("div", { class: "mvb-meter", role: "img", "aria-label": "Word limit" }, el("div", { class: "mvb-meter__fill" }));
30
+ const limitNote = notice("note");
31
+ const fill = meter.querySelector(".mvb-meter__fill");
32
+
33
+ let counts = { words: 0, chars: 0, sentences: 0, lines: 0 };
34
+
35
+ function summary() {
36
+ return [
37
+ `${counts.words} words`,
38
+ `${counts.chars} characters`,
39
+ `${counts.sentences} sentences`,
40
+ `${counts.lines} lines`,
41
+ ].join("\n");
42
+ }
43
+
44
+ function run() {
45
+ const text = source.value;
46
+ const words = text.trim() ? text.trim().split(/\s+/).length : 0;
47
+ counts = {
48
+ words,
49
+ chars: text.length,
50
+ sentences: countSentences(text),
51
+ lines: text ? text.split("\n").length : 0,
52
+ };
53
+ wordsTile.set(String(counts.words));
54
+ charsTile.set(String(counts.chars));
55
+ sentencesTile.set(String(counts.sentences));
56
+ linesTile.set(String(counts.lines));
57
+ readingTile.set(counts.words ? `${Math.max(1, Math.round(counts.words / WPM))} min` : "0 min");
58
+
59
+ const limit = parseInt(limitField.value, 10) || 0;
60
+ if (limit > 0) {
61
+ const ratio = words / limit;
62
+ fill.style.width = `${Math.min(100, ratio * 100)}%`;
63
+ fill.className = `mvb-meter__fill ${ratio > 1 ? "mvb-weak" : ratio > 0.8 ? "mvb-ok" : "mvb-strong"}`;
64
+ limitNote.write(ratio > 1 ? `${words - limit} over the limit.` : `${limit - words} words left.`);
65
+ } else {
66
+ fill.style.width = "0%";
67
+ fill.className = "mvb-meter__fill";
68
+ limitNote.write("");
69
+ }
70
+ }
71
+
72
+ const clearButton = button("Clear", { icon: "trash" });
73
+ clearButton.addEventListener("click", () => { source.value = ""; run(); });
74
+ limitField.addEventListener("input", run);
75
+ source.addEventListener("input", run);
76
+
77
+ const card = toolcard({
78
+ body: bench(
79
+ stack(panel("Text", source), panel("Word limit", limitField)),
80
+ stack(stats, meter, limitNote)
81
+ ),
82
+ foot: actions(clearButton, copyButton(summary, "Copy summary", "fill")),
83
+ });
84
+ slot.append(card.node);
85
+ run();
86
+
87
+ return {};
88
+ }
89
+
90
+ export default { mount };
@@ -0,0 +1,60 @@
1
+ // Checks text against a minimum and maximum word count.
2
+ import { el, bench, stack, panel, textArea, numberInput, notice, copyButton, actions, toolcard } from "../core.js";
3
+
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));
7
+ node.set = (text) => { value.textContent = text; };
8
+ return node;
9
+ }
10
+
11
+ const countWords = (text) => (text.trim() ? text.trim().split(/\s+/).length : 0);
12
+
13
+ export function mount(slot) {
14
+ const source = textArea({ rows: 12, "aria-label": "Text", placeholder: "Paste your text" });
15
+ const min = numberInput({ value: "150", min: "0", "aria-label": "Minimum words" });
16
+ const max = numberInput({ value: "250", min: "0", "aria-label": "Maximum words" });
17
+ const wordsTile = tile("Words");
18
+ const minTile = tile("To minimum");
19
+ const maxTile = tile("To maximum");
20
+ const stats = el("div", { class: "mvb-stats" }, wordsTile, minTile, maxTile);
21
+ const status = notice("note");
22
+ const note = notice("note");
23
+ note.write("Each hyphenated word and each number counts as one word.");
24
+ let summary = "";
25
+
26
+ function run() {
27
+ const words = countWords(source.value);
28
+ const lo = Number(min.value);
29
+ const hi = Number(max.value);
30
+ const hasLo = min.value.trim() !== "" && Number.isFinite(lo);
31
+ const hasHi = max.value.trim() !== "" && Number.isFinite(hi);
32
+
33
+ wordsTile.set(String(words));
34
+ minTile.set(hasLo ? String(Math.max(0, lo - words)) : "-");
35
+ maxTile.set(hasHi ? String(Math.max(0, hi - words)) : "-");
36
+
37
+ if (hasLo && words < lo) status.write(`${lo - words} words under the minimum.`);
38
+ else if (hasHi && words > hi) status.write(`${words - hi} words over the maximum.`);
39
+ else if (hasLo && hasHi) status.write("Within the range.");
40
+ else status.write("");
41
+
42
+ summary = `${words} words`;
43
+ }
44
+
45
+ for (const input of [source, min, max]) input.addEventListener("input", run);
46
+
47
+ const card = toolcard({
48
+ body: bench(
49
+ stack(panel("Text", source), panel("Minimum", min), panel("Maximum", max), note),
50
+ stack(stats, status)
51
+ ),
52
+ foot: actions(copyButton(() => summary, "Copy count", "fill")),
53
+ });
54
+ slot.append(card.node);
55
+ run();
56
+
57
+ return {};
58
+ }
59
+
60
+ export default { mount };
@@ -0,0 +1,85 @@
1
+ // Reindents an XML document by nesting and reports tags that do not match.
2
+ import { bench, stack, panel, textArea, output, notice, chipRow, copyButton, actions, toolcard } from "../core.js";
3
+
4
+ const TOKEN = /<!--[\s\S]*?-->|<!\[CDATA\[[\s\S]*?\]\]>|<\?[\s\S]*?\?>|<\/?[^>]+>|[^<]+/g;
5
+
6
+ const tagName = (token) => {
7
+ const match = token.match(/^<\/?\s*([^\s/>]+)/);
8
+ return match ? match[1] : "";
9
+ };
10
+
11
+ function format(source, unit) {
12
+ const tokens = source.match(TOKEN) ?? [];
13
+ const lines = [];
14
+ const open = [];
15
+ const problems = [];
16
+ let depth = 0;
17
+
18
+ for (const raw of tokens) {
19
+ const token = raw.trim();
20
+ if (!token) continue;
21
+
22
+ if (token.startsWith("<!--") || token.startsWith("<![CDATA[") || token.startsWith("<?") || token.startsWith("<!")) {
23
+ lines.push(unit.repeat(depth) + token);
24
+ } else if (token.startsWith("</")) {
25
+ const closing = tagName(token);
26
+ const expected = open.pop();
27
+ if (expected !== closing) problems.push(`Closing </${closing}> does not match <${expected ?? "?"}>.`);
28
+ depth = Math.max(0, depth - 1);
29
+ lines.push(unit.repeat(depth) + token);
30
+ } else if (token.endsWith("/>")) {
31
+ lines.push(unit.repeat(depth) + token);
32
+ } else if (token.startsWith("<")) {
33
+ lines.push(unit.repeat(depth) + token);
34
+ open.push(tagName(token));
35
+ depth++;
36
+ } else {
37
+ lines.push(unit.repeat(depth) + token);
38
+ }
39
+ }
40
+
41
+ if (open.length) problems.push(`Unclosed: ${open.map((name) => `<${name}>`).join(", ")}.`);
42
+ return { text: lines.join("\n"), problems };
43
+ }
44
+
45
+ export function mount(slot) {
46
+ const source = textArea({ rows: 9, value: "<note><to>you</to><body>hi</body></note>", "aria-label": "XML" });
47
+ const result = output({ value: "", rows: 9 });
48
+ const error = notice("error");
49
+ let unit = " ";
50
+ let text = "";
51
+
52
+ const chips = chipRow(
53
+ [{ value: " ", label: "2 spaces" }, { value: " ", label: "4 spaces" }],
54
+ { value: " ", label: "Indent", onChange: (value) => { unit = value; run(); } }
55
+ );
56
+
57
+ function run() {
58
+ if (!source.value) {
59
+ text = "";
60
+ result.write("");
61
+ error.write("");
62
+ return;
63
+ }
64
+ const formatted = format(source.value, unit);
65
+ text = formatted.text;
66
+ result.write(text);
67
+ error.write(formatted.problems.join(" "));
68
+ }
69
+
70
+ source.addEventListener("input", run);
71
+
72
+ const card = toolcard({
73
+ body: bench(
74
+ stack(panel("XML", source), panel("Indent", chips)),
75
+ stack(panel("Formatted", result), error)
76
+ ),
77
+ foot: actions(copyButton(() => text, "Copy XML", "fill")),
78
+ });
79
+ slot.append(card.node);
80
+ run();
81
+
82
+ return {};
83
+ }
84
+
85
+ export default { mount };
@@ -0,0 +1,71 @@
1
+ // Checks a small YAML subset for indentation and key spacing problems.
2
+ import { el, bench, stack, panel, textArea, copyButton, actions, toolcard } from "../core.js";
3
+
4
+ function check(source) {
5
+ const problems = [];
6
+ const lines = source.split(/\r?\n/);
7
+ let step = 0;
8
+
9
+ lines.forEach((raw, index) => {
10
+ const line = index + 1;
11
+ if (!raw.trim()) return;
12
+
13
+ const lead = raw.match(/^[ \t]*/)[0];
14
+ if (lead.includes("\t")) {
15
+ problems.push({ line, message: "Tab used for indentation." });
16
+ } else if (lead.length) {
17
+ if (step === 0) step = lead.length;
18
+ else if (lead.length % step !== 0) problems.push({ line, message: `Indentation is not a multiple of ${step}.` });
19
+ }
20
+
21
+ const body = raw.trim().replace(/^-\s+/, "");
22
+ if (/^[a-z][a-z0-9+.-]*:\/\//i.test(body)) return;
23
+ const match = body.match(/^([^:#]+?)(\s*):(\s*)(.*)$/);
24
+ if (!match) return;
25
+ if (match[2]) problems.push({ line, message: "Remove the space before the colon." });
26
+ if (match[4] && !match[3]) problems.push({ line, message: "Add a space after the colon." });
27
+ });
28
+
29
+ return problems;
30
+ }
31
+
32
+ export function mount(slot) {
33
+ const source = textArea({ rows: 11, value: "name: mevebu\ntools:\n - unicode\n - json", "aria-label": "YAML" });
34
+ const scroll = el("div", { class: "mvb-scroll" });
35
+ const count = el("span", { class: "mvb-stat__v" }, "0");
36
+ const stats = el("div", { class: "mvb-stats" }, el("div", { class: "mvb-stat" }, count, el("span", { class: "mvb-stat__k" }, "Problems")));
37
+ let problems = [];
38
+
39
+ function run() {
40
+ problems = check(source.value);
41
+ count.textContent = String(problems.length);
42
+ if (!problems.length) {
43
+ scroll.replaceChildren(el("p", { class: "mvb-note" }, "No problems."));
44
+ return;
45
+ }
46
+ scroll.replaceChildren(el(
47
+ "table",
48
+ { class: "mvb-ttable" },
49
+ el("thead", {}, el("tr", {}, el("th", {}, "Line"), el("th", {}, "Problem"))),
50
+ el("tbody", {}, ...problems.map((entry) => el("tr", {},
51
+ el("th", {}, String(entry.line)),
52
+ el("td", {}, entry.message))))
53
+ ));
54
+ }
55
+
56
+ source.addEventListener("input", run);
57
+
58
+ const card = toolcard({
59
+ body: bench(
60
+ stack(panel("YAML", source)),
61
+ stack(stats, panel("Problems", scroll))
62
+ ),
63
+ foot: actions(copyButton(() => problems.map((entry) => `${entry.line}: ${entry.message}`).join("\n"), "Copy problems", "fill")),
64
+ });
65
+ slot.append(card.node);
66
+ run();
67
+
68
+ return {};
69
+ }
70
+
71
+ export default { mount };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mevebu",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Small tools that run in your browser. No accounts, no uploads, no tracking.",
5
5
  "license": "MIT",
6
6
  "type": "module",