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.
- package/mevebu/app/mevebu.js +260 -208
- package/mevebu/app/tailwind.css +1639 -2077
- package/mevebu/app/tailwind.src.css +155 -1120
- package/mevebu/index.html +2 -2
- package/mevebu/libs/js/core.js +236 -96
- package/mevebu/libs/js/registry.js +11 -11
- package/mevebu/libs/js/tools/abstract-checklist.js +5 -7
- package/mevebu/libs/js/tools/add-subtract-days.js +9 -11
- package/mevebu/libs/js/tools/age-calculator.js +7 -8
- package/mevebu/libs/js/tools/api-request-builder.js +3 -4
- package/mevebu/libs/js/tools/area-converter.js +7 -9
- package/mevebu/libs/js/tools/aspect-ratio.js +8 -9
- package/mevebu/libs/js/tools/average-calculator.js +4 -5
- package/mevebu/libs/js/tools/base64.js +31 -11
- package/mevebu/libs/js/tools/binary-calculator.js +6 -7
- package/mevebu/libs/js/tools/border-radius.js +7 -9
- package/mevebu/libs/js/tools/box-shadow.js +6 -7
- package/mevebu/libs/js/tools/break-even.js +4 -5
- package/mevebu/libs/js/tools/business-days.js +7 -8
- package/mevebu/libs/js/tools/canonical-tag.js +5 -6
- package/mevebu/libs/js/tools/caption-counter.js +5 -7
- package/mevebu/libs/js/tools/case-converter.js +11 -12
- package/mevebu/libs/js/tools/case-inspector.js +14 -16
- package/mevebu/libs/js/tools/cash-denomination.js +7 -8
- package/mevebu/libs/js/tools/cash-flow.js +7 -8
- package/mevebu/libs/js/tools/cert-expiry.js +6 -10
- package/mevebu/libs/js/tools/changelog-generator.js +4 -6
- package/mevebu/libs/js/tools/character-counter.js +10 -13
- package/mevebu/libs/js/tools/checklist.js +6 -7
- package/mevebu/libs/js/tools/citation-number-check.js +10 -13
- package/mevebu/libs/js/tools/coin-flip.js +9 -11
- package/mevebu/libs/js/tools/color-converter.js +23 -17
- package/mevebu/libs/js/tools/color-palette.js +6 -8
- package/mevebu/libs/js/tools/color-picker.js +4 -5
- package/mevebu/libs/js/tools/content-calendar.js +3 -4
- package/mevebu/libs/js/tools/contrast-checker.js +5 -6
- package/mevebu/libs/js/tools/cron-builder.js +4 -6
- package/mevebu/libs/js/tools/css-clamp.js +5 -6
- package/mevebu/libs/js/tools/css-formatter.js +4 -7
- package/mevebu/libs/js/tools/css-grid.js +6 -7
- package/mevebu/libs/js/tools/css-minifier.js +5 -8
- package/mevebu/libs/js/tools/csv-to-json.js +2 -3
- package/mevebu/libs/js/tools/csv-viewer.js +9 -11
- package/mevebu/libs/js/tools/currency-format.js +5 -7
- package/mevebu/libs/js/tools/daily-planner.js +9 -10
- package/mevebu/libs/js/tools/data-masker.js +7 -9
- package/mevebu/libs/js/tools/data-storage.js +7 -9
- package/mevebu/libs/js/tools/data-validator.js +9 -11
- package/mevebu/libs/js/tools/date-difference.js +6 -7
- package/mevebu/libs/js/tools/date-format.js +11 -13
- package/mevebu/libs/js/tools/delimiter-converter.js +3 -5
- package/mevebu/libs/js/tools/discount-calculator.js +4 -5
- package/mevebu/libs/js/tools/doi-resolver.js +4 -5
- package/mevebu/libs/js/tools/env-formatter.js +3 -6
- package/mevebu/libs/js/tools/equation-formatter.js +5 -9
- package/mevebu/libs/js/tools/expense-split.js +6 -7
- package/mevebu/libs/js/tools/fake-data.js +20 -13
- package/mevebu/libs/js/tools/find-replace.js +3 -5
- package/mevebu/libs/js/tools/flashcard-creator.js +9 -10
- package/mevebu/libs/js/tools/font-pairing.js +6 -8
- package/mevebu/libs/js/tools/fraction-calculator.js +10 -13
- package/mevebu/libs/js/tools/git-command-builder.js +11 -22
- package/mevebu/libs/js/tools/gpa-calculator.js +8 -10
- package/mevebu/libs/js/tools/grade-calculator.js +7 -8
- package/mevebu/libs/js/tools/gradient-generator.js +10 -12
- package/mevebu/libs/js/tools/habit-tracker.js +8 -9
- package/mevebu/libs/js/tools/hash-generator.js +3 -5
- package/mevebu/libs/js/tools/hashtag-formatter.js +6 -8
- package/mevebu/libs/js/tools/hmac-generator.js +3 -5
- package/mevebu/libs/js/tools/hpp-calculator.js +4 -5
- package/mevebu/libs/js/tools/html-entity.js +2 -3
- package/mevebu/libs/js/tools/html-formatter.js +3 -6
- package/mevebu/libs/js/tools/http-status.js +5 -7
- package/mevebu/libs/js/tools/inventory-quantity.js +7 -8
- package/mevebu/libs/js/tools/json-formatter.js +3 -6
- package/mevebu/libs/js/tools/json-minifier.js +5 -8
- package/mevebu/libs/js/tools/json-to-csv.js +3 -5
- package/mevebu/libs/js/tools/json-to-typescript.js +2 -5
- package/mevebu/libs/js/tools/json-validator.js +2 -3
- package/mevebu/libs/js/tools/jwt-decoder.js +6 -7
- package/mevebu/libs/js/tools/jwt-expiry.js +5 -6
- package/mevebu/libs/js/tools/jwt-generator.js +4 -6
- package/mevebu/libs/js/tools/length-converter.js +7 -9
- package/mevebu/libs/js/tools/license-reference.js +10 -13
- package/mevebu/libs/js/tools/line-counter.js +5 -7
- package/mevebu/libs/js/tools/list-difference.js +5 -7
- package/mevebu/libs/js/tools/list-merger.js +4 -5
- package/mevebu/libs/js/tools/locale-number.js +5 -8
- package/mevebu/libs/js/tools/lorem-ipsum.js +4 -2
- package/mevebu/libs/js/tools/markdown-preview.js +5 -8
- package/mevebu/libs/js/tools/markdown-table.js +5 -7
- package/mevebu/libs/js/tools/markup-calculator.js +4 -5
- package/mevebu/libs/js/tools/meeting-agenda.js +8 -9
- package/mevebu/libs/js/tools/meta-preview.js +12 -13
- package/mevebu/libs/js/tools/meta-tags.js +6 -7
- package/mevebu/libs/js/tools/mime-types.js +5 -7
- package/mevebu/libs/js/tools/name-picker.js +2 -3
- package/mevebu/libs/js/tools/number-base-converter.js +9 -12
- package/mevebu/libs/js/tools/password-generator.js +9 -12
- package/mevebu/libs/js/tools/password-strength.js +21 -25
- package/mevebu/libs/js/tools/percent-to-fraction.js +6 -7
- package/mevebu/libs/js/tools/percentage-calculator.js +6 -9
- package/mevebu/libs/js/tools/percentage-change.js +4 -5
- package/mevebu/libs/js/tools/permutation-combination.js +9 -12
- package/mevebu/libs/js/tools/prefix-suffix.js +2 -3
- package/mevebu/libs/js/tools/priority-matrix.js +6 -10
- package/mevebu/libs/js/tools/profit-margin.js +4 -5
- package/mevebu/libs/js/tools/qr-generator.js +3 -4
- package/mevebu/libs/js/tools/qr-scanner.js +11 -12
- package/mevebu/libs/js/tools/quadratic-solver.js +7 -8
- package/mevebu/libs/js/tools/query-builder.js +2 -3
- package/mevebu/libs/js/tools/quiz-formatter.js +4 -6
- package/mevebu/libs/js/tools/ratio-calculator.js +7 -8
- package/mevebu/libs/js/tools/readability.js +12 -15
- package/mevebu/libs/js/tools/reading-time.js +5 -7
- package/mevebu/libs/js/tools/reference-completeness.js +6 -8
- package/mevebu/libs/js/tools/regex-tester.js +7 -10
- package/mevebu/libs/js/tools/remove-duplicate-lines.js +2 -3
- package/mevebu/libs/js/tools/remove-extra-spaces.js +3 -5
- package/mevebu/libs/js/tools/robots-txt.js +3 -4
- package/mevebu/libs/js/tools/roman-numeral-converter.js +6 -8
- package/mevebu/libs/js/tools/sales-commission.js +8 -9
- package/mevebu/libs/js/tools/scientific-calculator.js +10 -14
- package/mevebu/libs/js/tools/scientific-notation.js +8 -9
- package/mevebu/libs/js/tools/script-planner.js +8 -10
- package/mevebu/libs/js/tools/selling-price.js +4 -5
- package/mevebu/libs/js/tools/semver-helper.js +3 -5
- package/mevebu/libs/js/tools/sentence-length.js +9 -10
- package/mevebu/libs/js/tools/sequence-analyzer.js +7 -8
- package/mevebu/libs/js/tools/sitemap-xml.js +4 -7
- package/mevebu/libs/js/tools/slug-converter.js +3 -5
- package/mevebu/libs/js/tools/slug-preview.js +6 -7
- package/mevebu/libs/js/tools/sort-lines.js +3 -5
- package/mevebu/libs/js/tools/speed-converter.js +7 -9
- package/mevebu/libs/js/tools/spin-wheel.js +19 -17
- package/mevebu/libs/js/tools/sql-formatter.js +5 -9
- package/mevebu/libs/js/tools/sql-minifier.js +5 -8
- package/mevebu/libs/js/tools/standard-deviation.js +6 -9
- package/mevebu/libs/js/tools/stopwatch.js +4 -6
- package/mevebu/libs/js/tools/study-schedule.js +8 -9
- package/mevebu/libs/js/tools/subtitle-formatter.js +10 -11
- package/mevebu/libs/js/tools/table-to-markdown.js +8 -12
- package/mevebu/libs/js/tools/tax-calculator.js +4 -5
- package/mevebu/libs/js/tools/temperature-converter.js +8 -11
- package/mevebu/libs/js/tools/text-diff.js +11 -7
- package/mevebu/libs/js/tools/text-direction.js +3 -5
- package/mevebu/libs/js/tools/text-reverser.js +3 -5
- package/mevebu/libs/js/tools/time-duration.js +8 -9
- package/mevebu/libs/js/tools/timestamp-converter.js +4 -6
- package/mevebu/libs/js/tools/timezone-converter.js +9 -13
- package/mevebu/libs/js/tools/title-length.js +6 -7
- package/mevebu/libs/js/tools/unicode-inspector.js +4 -5
- package/mevebu/libs/js/tools/url-encoder.js +4 -2
- package/mevebu/libs/js/tools/url-inspector.js +9 -11
- package/mevebu/libs/js/tools/url-parser.js +12 -13
- package/mevebu/libs/js/tools/utf8-bytes.js +6 -9
- package/mevebu/libs/js/tools/uuid-generator.js +107 -15
- package/mevebu/libs/js/tools/version-compare.js +3 -4
- package/mevebu/libs/js/tools/video-duration.js +5 -7
- package/mevebu/libs/js/tools/volume-converter.js +7 -9
- package/mevebu/libs/js/tools/weekly-planner.js +8 -10
- package/mevebu/libs/js/tools/weight-converter.js +7 -9
- package/mevebu/libs/js/tools/weighted-average.js +4 -5
- package/mevebu/libs/js/tools/word-counter.js +10 -13
- package/mevebu/libs/js/tools/word-limit.js +4 -5
- package/mevebu/libs/js/tools/yaml-formatter.js +6 -7
- package/package.json +1 -1
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
import { el, bench, stack, panel, textArea, copyButton, actions, toolcard } from "../core.js";
|
|
3
3
|
|
|
4
4
|
function tile(label) {
|
|
5
|
-
const value = el("span", { class: "
|
|
6
|
-
const node = el("div", { class: "
|
|
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
|
}
|
|
@@ -22,7 +22,7 @@ export function mount(slot) {
|
|
|
22
22
|
const pointTile = tile("Code points");
|
|
23
23
|
const byteTile = tile("UTF-8 bytes");
|
|
24
24
|
const unitTile = tile("UTF-16 units");
|
|
25
|
-
const stats = el("div", { class: "
|
|
25
|
+
const stats = el("div", { class: "grid gap-2.5 [grid-template-columns:repeat(auto-fit,minmax(120px,1fr))]" }, characterTile, pointTile, byteTile, unitTile);
|
|
26
26
|
|
|
27
27
|
let counts = { characters: 0, points: 0, bytes: 0, units: 0 };
|
|
28
28
|
|
|
@@ -31,8 +31,7 @@ export function mount(slot) {
|
|
|
31
31
|
`${counts.characters} characters`,
|
|
32
32
|
`${counts.points} code points`,
|
|
33
33
|
`${counts.bytes} UTF-8 bytes`,
|
|
34
|
-
`${counts.units} UTF-16 units
|
|
35
|
-
].join("\n");
|
|
34
|
+
`${counts.units} UTF-16 units`].join("\n");
|
|
36
35
|
}
|
|
37
36
|
|
|
38
37
|
function run() {
|
|
@@ -41,8 +40,7 @@ export function mount(slot) {
|
|
|
41
40
|
characters: graphemes(text),
|
|
42
41
|
points: Array.from(text).length,
|
|
43
42
|
bytes: new TextEncoder().encode(text).length,
|
|
44
|
-
units: text.length
|
|
45
|
-
};
|
|
43
|
+
units: text.length};
|
|
46
44
|
characterTile.set(String(counts.characters));
|
|
47
45
|
pointTile.set(String(counts.points));
|
|
48
46
|
byteTile.set(String(counts.bytes));
|
|
@@ -56,8 +54,7 @@ export function mount(slot) {
|
|
|
56
54
|
stack(panel("Text", source)),
|
|
57
55
|
stack(stats)
|
|
58
56
|
),
|
|
59
|
-
foot: actions(copyButton(summary, "Copy counts", "fill"))
|
|
60
|
-
});
|
|
57
|
+
foot: actions(copyButton(summary, "Copy counts", "fill"))});
|
|
61
58
|
slot.append(card.node);
|
|
62
59
|
run();
|
|
63
60
|
|
|
@@ -1,34 +1,126 @@
|
|
|
1
|
-
//
|
|
2
|
-
|
|
1
|
+
// UUIDs in the versions that reach a schema: v4 and v7 are random, v7 sorts by
|
|
2
|
+
// time, v1 and v6 are the legacy timestamp pair, and Nil and Max are the two
|
|
3
|
+
// sentinels a test suite looks for.
|
|
4
|
+
import { el, toast, copyText, download, numberInput, checkbox, chipRow, button, bench, stack, panel, output, readPin, writePin, actions, toolcard } from "../core.js";
|
|
3
5
|
|
|
4
|
-
|
|
6
|
+
// 100 ns ticks between 1582-10-15 and 1970-01-01, the epoch a v1 stamp counts from.
|
|
7
|
+
const GREGORIAN_OFFSET = 122192928000000000n;
|
|
8
|
+
|
|
9
|
+
const VERSIONS = [
|
|
10
|
+
{ value: "v4", label: "v4" },
|
|
11
|
+
{ value: "v7", label: "v7" },
|
|
12
|
+
{ value: "v1", label: "v1" },
|
|
13
|
+
{ value: "v6", label: "v6" },
|
|
14
|
+
{ value: "nil", label: "Nil" },
|
|
15
|
+
{ value: "max", label: "Max" }];
|
|
16
|
+
|
|
17
|
+
const toHex = (bytes) => [...bytes].map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
18
|
+
const group = (hex) => `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
19
|
+
const randomHex = (bytes) => toHex(crypto.getRandomValues(new Uint8Array(bytes)));
|
|
20
|
+
|
|
21
|
+
function v4() {
|
|
5
22
|
if (crypto.randomUUID) return crypto.randomUUID();
|
|
6
23
|
const bytes = crypto.getRandomValues(new Uint8Array(16));
|
|
7
24
|
bytes[6] = (bytes[6] & 0x0f) | 0x40;
|
|
8
25
|
bytes[8] = (bytes[8] & 0x3f) | 0x80;
|
|
9
|
-
|
|
10
|
-
|
|
26
|
+
return group(toHex(bytes));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
let lastStamp = 0;
|
|
30
|
+
let counter = 0;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* v7 puts a 48 bit millisecond stamp in front of random bits, so string order
|
|
34
|
+
* matches time order. A generated batch lands several UUIDs in the same
|
|
35
|
+
* millisecond, where the random tail alone would shuffle them, so the 12 bit
|
|
36
|
+
* field behind the stamp steps like a counter for the rest of that millisecond.
|
|
37
|
+
* It starts from a random value each millisecond to keep the tail unguessable.
|
|
38
|
+
*/
|
|
39
|
+
function v7() {
|
|
40
|
+
const bytes = crypto.getRandomValues(new Uint8Array(16));
|
|
41
|
+
// A clock that steps backwards would break the order as well, so never step back.
|
|
42
|
+
let ms = Math.max(Date.now(), lastStamp);
|
|
43
|
+
counter = counter + 1;
|
|
44
|
+
if (ms !== lastStamp || counter > 0x0fff) {
|
|
45
|
+
// A new millisecond, or more than 4095 in one: start over, and move the stamp
|
|
46
|
+
// on rather than hand out two UUIDs with the same timestamp and tail.
|
|
47
|
+
if (ms === lastStamp) ms += 1;
|
|
48
|
+
counter = (bytes[6] << 4) | bytes[7];
|
|
49
|
+
}
|
|
50
|
+
lastStamp = ms;
|
|
51
|
+
for (let at = 5; at >= 0; at--) {
|
|
52
|
+
bytes[at] = ms % 256;
|
|
53
|
+
ms = Math.floor(ms / 256);
|
|
54
|
+
}
|
|
55
|
+
bytes[6] = 0x70 | (counter >> 8);
|
|
56
|
+
bytes[7] = counter & 0xff;
|
|
57
|
+
bytes[8] = (bytes[8] & 0x3f) | 0x80;
|
|
58
|
+
return group(toHex(bytes));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** The 60 bit timestamp v1 and v6 store, as 15 hex characters, most significant first. */
|
|
62
|
+
const stamp = () => (BigInt(Date.now()) * 10000n + GREGORIAN_OFFSET).toString(16).padStart(15, "0");
|
|
63
|
+
|
|
64
|
+
/** A node id nobody owns, so a generated v1 cannot point at real hardware. */
|
|
65
|
+
const nodeHex = () => (parseInt(randomHex(1), 16) | 0x01).toString(16).padStart(2, "0") + randomHex(5);
|
|
66
|
+
|
|
67
|
+
const sequenceHex = () => ((parseInt(randomHex(2), 16) & 0x3fff) | 0x8000).toString(16).padStart(4, "0");
|
|
68
|
+
|
|
69
|
+
/** v1 and v6 share every field; only the order of the timestamp differs. */
|
|
70
|
+
function timed(version) {
|
|
71
|
+
const ticks = stamp();
|
|
72
|
+
const sequence = sequenceHex();
|
|
73
|
+
const node = nodeHex();
|
|
74
|
+
// v1 stores the stamp low, mid, high, which is why it never sorts. v6 puts the
|
|
75
|
+
// high half first so the string order matches the time order.
|
|
76
|
+
return version === 1
|
|
77
|
+
? `${ticks.slice(7)}-${ticks.slice(3, 7)}-1${ticks.slice(0, 3)}-${sequence}-${node}`
|
|
78
|
+
: `${ticks.slice(0, 8)}-${ticks.slice(8, 12)}-6${ticks.slice(12)}-${sequence}-${node}`;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function make(version) {
|
|
82
|
+
if (version === "nil") return "00000000-0000-0000-0000-000000000000";
|
|
83
|
+
if (version === "max") return "ffffffff-ffff-ffff-ffff-ffffffffffff";
|
|
84
|
+
if (version === "v7") return v7();
|
|
85
|
+
if (version === "v1") return timed(1);
|
|
86
|
+
if (version === "v6") return timed(6);
|
|
87
|
+
return v4();
|
|
11
88
|
}
|
|
12
89
|
|
|
13
90
|
export function mount(slot) {
|
|
14
91
|
const PIN = "uuid-generator";
|
|
15
|
-
const saved = readPin(PIN, { count: "5", caps: false });
|
|
92
|
+
const saved = readPin(PIN, { count: "5", caps: false, noHyphens: false, version: "v4" });
|
|
16
93
|
|
|
17
94
|
const countField = numberInput({ value: saved.count ?? "5", min: "1", max: "1000", "aria-label": "How many" });
|
|
18
95
|
const capsBox = checkbox("Uppercase", { checked: Boolean(saved.caps) });
|
|
96
|
+
const hyphensBox = checkbox("No hyphens", { checked: saved.noHyphens === true });
|
|
19
97
|
const result = output({ rows: 8 });
|
|
98
|
+
let version = VERSIONS.some((item) => item.value === saved.version) ? saved.version : "v4";
|
|
20
99
|
let current = [];
|
|
21
100
|
|
|
101
|
+
const versions = chipRow(VERSIONS, {
|
|
102
|
+
value: version,
|
|
103
|
+
label: "Version",
|
|
104
|
+
onChange: (value) => {
|
|
105
|
+
version = value;
|
|
106
|
+
generate();
|
|
107
|
+
}});
|
|
108
|
+
|
|
22
109
|
function totalCount() {
|
|
23
110
|
return Math.max(1, Math.min(1000, parseInt(countField.value || "1", 10) || 1));
|
|
24
111
|
}
|
|
25
112
|
|
|
26
113
|
function generate() {
|
|
27
114
|
const total = totalCount();
|
|
28
|
-
current = Array.from({ length: total },
|
|
115
|
+
current = Array.from({ length: total }, () => make(version));
|
|
29
116
|
if (capsBox.input.checked) current = current.map((value) => value.toUpperCase());
|
|
117
|
+
if (hyphensBox.input.checked) current = current.map((value) => value.replace(/-/g, ""));
|
|
30
118
|
result.write(current.join("\n"));
|
|
31
|
-
writePin(PIN, {
|
|
119
|
+
writePin(PIN, {
|
|
120
|
+
count: String(total),
|
|
121
|
+
caps: capsBox.input.checked,
|
|
122
|
+
noHyphens: hyphensBox.input.checked,
|
|
123
|
+
version});
|
|
32
124
|
}
|
|
33
125
|
|
|
34
126
|
const generateButton = button("Generate", { icon: "refresh-cw", kind: "fill" });
|
|
@@ -44,9 +136,10 @@ export function mount(slot) {
|
|
|
44
136
|
countField.value = String(totalCount());
|
|
45
137
|
});
|
|
46
138
|
capsBox.input.addEventListener("change", generate);
|
|
139
|
+
hyphensBox.input.addEventListener("change", generate);
|
|
47
140
|
copyButton.addEventListener("click", async () => {
|
|
48
141
|
if (!current.length) return toast("Generate first, then copy");
|
|
49
|
-
toast((await copyText(
|
|
142
|
+
toast((await copyText(current.join("\n"))) ? `${current.length} copied` : "Copy failed");
|
|
50
143
|
});
|
|
51
144
|
jsonButton.addEventListener("click", async () => {
|
|
52
145
|
if (!current.length) return toast("Generate first, then copy");
|
|
@@ -55,20 +148,19 @@ export function mount(slot) {
|
|
|
55
148
|
const saveButton = button("Save .txt", { icon: "download" });
|
|
56
149
|
saveButton.addEventListener("click", () => {
|
|
57
150
|
if (!current.length) return toast("Generate first");
|
|
58
|
-
download("uuids.txt",
|
|
151
|
+
download("uuids.txt", current.join("\n"));
|
|
59
152
|
});
|
|
60
153
|
|
|
61
154
|
const card = toolcard({
|
|
62
155
|
body: bench(
|
|
63
|
-
stack(panel("How many", countField), capsBox),
|
|
64
|
-
|
|
156
|
+
stack(panel("Version", versions), panel("How many", countField), el("div", { class: "flex flex-wrap gap-1.5" }, capsBox, hyphensBox)),
|
|
157
|
+
result
|
|
65
158
|
),
|
|
66
|
-
foot: actions(copyButton, jsonButton, saveButton, generateButton)
|
|
67
|
-
});
|
|
159
|
+
foot: actions(copyButton, jsonButton, saveButton, generateButton)});
|
|
68
160
|
slot.append(card.node);
|
|
69
161
|
generate();
|
|
70
162
|
|
|
71
163
|
return {};
|
|
72
164
|
}
|
|
73
165
|
|
|
74
|
-
export default { mount };
|
|
166
|
+
export default { mount };
|
|
@@ -8,8 +8,8 @@ function parse(text) {
|
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
export function mount(slot) {
|
|
11
|
-
const left = textInput({ value: "1.2.0",
|
|
12
|
-
const right = textInput({ value: "1.1.5",
|
|
11
|
+
const left = textInput({ value: "1.2.0", mono: true, "aria-label": "First version" });
|
|
12
|
+
const right = textInput({ value: "1.1.5", mono: true, "aria-label": "Second version" });
|
|
13
13
|
const error = notice("error");
|
|
14
14
|
const out = output({ rows: 8 });
|
|
15
15
|
let text = "";
|
|
@@ -59,8 +59,7 @@ export function mount(slot) {
|
|
|
59
59
|
stack(panel("First version", left), panel("Second version", right), error),
|
|
60
60
|
stack(panel("Comparison", out))
|
|
61
61
|
),
|
|
62
|
-
foot: actions(copyButton(() => text, "Copy comparison", "fill"))
|
|
63
|
-
});
|
|
62
|
+
foot: actions(copyButton(() => text, "Copy comparison", "fill"))});
|
|
64
63
|
slot.append(card.node);
|
|
65
64
|
run();
|
|
66
65
|
|
|
@@ -7,8 +7,8 @@ const clock = (seconds) => {
|
|
|
7
7
|
};
|
|
8
8
|
|
|
9
9
|
function tile(label) {
|
|
10
|
-
const value = el("span", { class: "
|
|
11
|
-
const node = el("div", { class: "
|
|
10
|
+
const value = el("span", { class: "wrap-anywhere text-xl font-bold tracking-[-0.02em] tabular-nums" }, "0:00");
|
|
11
|
+
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));
|
|
12
12
|
node.set = (text) => { value.textContent = text; };
|
|
13
13
|
return node;
|
|
14
14
|
}
|
|
@@ -22,7 +22,7 @@ export function mount(slot) {
|
|
|
22
22
|
const wordsTile = tile("Words");
|
|
23
23
|
const speakTile = tile("Speaking");
|
|
24
24
|
const totalTile = tile("With pauses");
|
|
25
|
-
const stats = el("div", { class: "
|
|
25
|
+
const stats = el("div", { class: "grid gap-2.5 [grid-template-columns:repeat(auto-fit,minmax(120px,1fr))]" }, wordsTile, speakTile, totalTile);
|
|
26
26
|
const note = notice("note");
|
|
27
27
|
note.write("Speaking rates vary by person and script.");
|
|
28
28
|
|
|
@@ -42,8 +42,7 @@ export function mount(slot) {
|
|
|
42
42
|
summary = [
|
|
43
43
|
`${words} words`,
|
|
44
44
|
`Speaking ${clock(speaking)} at ${wpm} words per minute`,
|
|
45
|
-
`Total ${clock(total)} with ${pauseSeconds} seconds of pause per minute
|
|
46
|
-
].join("\n");
|
|
45
|
+
`Total ${clock(total)} with ${pauseSeconds} seconds of pause per minute`].join("\n");
|
|
47
46
|
}
|
|
48
47
|
|
|
49
48
|
source.addEventListener("input", run);
|
|
@@ -61,8 +60,7 @@ export function mount(slot) {
|
|
|
61
60
|
),
|
|
62
61
|
stack(stats, note)
|
|
63
62
|
),
|
|
64
|
-
foot: actions(copyButton(() => summary, "Copy estimate", "fill"))
|
|
65
|
-
});
|
|
63
|
+
foot: actions(copyButton(() => summary, "Copy estimate", "fill"))});
|
|
66
64
|
slot.append(card.node);
|
|
67
65
|
run();
|
|
68
66
|
|
|
@@ -11,8 +11,7 @@ const UNITS = [
|
|
|
11
11
|
{ value: "cup", label: "Cup", factor: 0.2365882365 },
|
|
12
12
|
{ value: "pt", label: "Pint", factor: 0.473176473 },
|
|
13
13
|
{ value: "qt", label: "Quart", factor: 0.946352946 },
|
|
14
|
-
{ value: "gal", label: "Gallon", factor: 3.785411784 }
|
|
15
|
-
];
|
|
14
|
+
{ value: "gal", label: "Gallon", factor: 3.785411784 }];
|
|
16
15
|
|
|
17
16
|
const byId = Object.fromEntries(UNITS.map((unit) => [unit.value, unit]));
|
|
18
17
|
const options = UNITS.map(({ value, label }) => ({ value, label }));
|
|
@@ -29,11 +28,11 @@ export function mount(slot) {
|
|
|
29
28
|
const value = numberInput({ value: "1", step: "any", "aria-label": "Value" });
|
|
30
29
|
const from = selectInput(options, { value: "l", "aria-label": "From unit" });
|
|
31
30
|
const to = selectInput(options, { value: "gal", "aria-label": "To unit" });
|
|
32
|
-
const result = el("span", { class: "
|
|
33
|
-
const stats = el("div", { class: "
|
|
34
|
-
const factor = el("p", { class: "
|
|
31
|
+
const result = el("span", { class: "wrap-anywhere text-xl font-bold tracking-[-0.02em] tabular-nums" }, "-");
|
|
32
|
+
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));
|
|
33
|
+
const factor = el("p", { class: "border-s-[3px] border-transparent ps-2 text-sm text-ink-3" });
|
|
35
34
|
const error = notice("error");
|
|
36
|
-
const scroll = el("div", { class: "
|
|
35
|
+
const scroll = el("div", { class: "max-h-80 overflow-auto" });
|
|
37
36
|
let text = "";
|
|
38
37
|
|
|
39
38
|
function run() {
|
|
@@ -53,7 +52,7 @@ export function mount(slot) {
|
|
|
53
52
|
const out = (n * a.factor) / b.factor;
|
|
54
53
|
result.textContent = `${fmt(out)} ${b.value}`;
|
|
55
54
|
factor.textContent = `1 ${a.value} = ${fmt(a.factor / b.factor)} ${b.value}`;
|
|
56
|
-
scroll.replaceChildren(el("table", { class: "
|
|
55
|
+
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" },
|
|
57
56
|
el("thead", {}, el("tr", {}, el("th", {}, "Unit"), el("th", {}, "Value"))),
|
|
58
57
|
el("tbody", {}, ...UNITS.map((unit) => el("tr", {},
|
|
59
58
|
el("td", {}, unit.label),
|
|
@@ -77,8 +76,7 @@ export function mount(slot) {
|
|
|
77
76
|
stack(panel("Value", value), panel("From", from), panel("To", to), error),
|
|
78
77
|
stack(panel("Result", stats), factor, panel("All units", scroll))
|
|
79
78
|
),
|
|
80
|
-
foot: actions(swap, copyButton(() => text, "Copy result", "fill"))
|
|
81
|
-
});
|
|
79
|
+
foot: actions(swap, copyButton(() => text, "Copy result", "fill"))});
|
|
82
80
|
slot.append(card.node);
|
|
83
81
|
run();
|
|
84
82
|
|
|
@@ -11,8 +11,8 @@ export function mount(slot) {
|
|
|
11
11
|
let day = Number.isInteger(saved.day) && saved.day >= 0 && saved.day < 7 ? saved.day : 0;
|
|
12
12
|
|
|
13
13
|
const entry = textInput({ placeholder: "New item", "aria-label": "New item" });
|
|
14
|
-
const listWrap = el("div", { class: "
|
|
15
|
-
const stats = el("div", { class: "
|
|
14
|
+
const listWrap = el("div", { class: "max-h-80 overflow-auto" });
|
|
15
|
+
const stats = el("div", { class: "grid gap-2.5 [grid-template-columns:repeat(auto-fit,minmax(120px,1fr))]" });
|
|
16
16
|
|
|
17
17
|
const dayChips = chipRow(DAYS.map((label, value) => ({ value, label })), {
|
|
18
18
|
value: day,
|
|
@@ -21,15 +21,14 @@ export function mount(slot) {
|
|
|
21
21
|
day = next;
|
|
22
22
|
save();
|
|
23
23
|
render();
|
|
24
|
-
}
|
|
25
|
-
});
|
|
24
|
+
}});
|
|
26
25
|
|
|
27
26
|
const addButton = button("Add", { icon: "plus", kind: "fill" });
|
|
28
27
|
const copyWeek = copyButton(() => weekText(), "Copy week");
|
|
29
28
|
|
|
30
|
-
const tile = (key, value) => el("div", { class: "
|
|
31
|
-
el("span", { class: "
|
|
32
|
-
el("span", { class: "
|
|
29
|
+
const tile = (key, value) => el("div", { class: "grid gap-0.5 rounded-r1 border border-orbit bg-deck-2 px-3.5 py-3" },
|
|
30
|
+
el("span", { class: "wrap-anywhere text-xl font-bold tracking-[-0.02em] tabular-nums" }, String(value)),
|
|
31
|
+
el("span", { class: "text-2xs font-semibold text-ink-3" }, key));
|
|
33
32
|
|
|
34
33
|
const save = () => writePin(PIN, { day, days });
|
|
35
34
|
|
|
@@ -53,7 +52,7 @@ export function mount(slot) {
|
|
|
53
52
|
save();
|
|
54
53
|
render();
|
|
55
54
|
});
|
|
56
|
-
return el("div", { class: "
|
|
55
|
+
return el("div", { class: "flex min-w-0 flex-wrap items-center gap-2" }, box, remove);
|
|
57
56
|
}
|
|
58
57
|
|
|
59
58
|
function renderStats() {
|
|
@@ -90,8 +89,7 @@ export function mount(slot) {
|
|
|
90
89
|
stack(panel("Day", dayChips), panel("New item", entry)),
|
|
91
90
|
stack(stats, panel("Items", listWrap))
|
|
92
91
|
),
|
|
93
|
-
foot: actions(copyWeek, addButton)
|
|
94
|
-
});
|
|
92
|
+
foot: actions(copyWeek, addButton)});
|
|
95
93
|
slot.append(card.node);
|
|
96
94
|
render();
|
|
97
95
|
|
|
@@ -8,8 +8,7 @@ const UNITS = [
|
|
|
8
8
|
{ value: "t", label: "Tonne", factor: 1000000 },
|
|
9
9
|
{ value: "oz", label: "Ounce", factor: 28.349523125 },
|
|
10
10
|
{ value: "lb", label: "Pound", factor: 453.59237 },
|
|
11
|
-
{ value: "st", label: "Stone", factor: 6350.29318 }
|
|
12
|
-
];
|
|
11
|
+
{ value: "st", label: "Stone", factor: 6350.29318 }];
|
|
13
12
|
|
|
14
13
|
const byId = Object.fromEntries(UNITS.map((unit) => [unit.value, unit]));
|
|
15
14
|
const options = UNITS.map(({ value, label }) => ({ value, label }));
|
|
@@ -26,11 +25,11 @@ export function mount(slot) {
|
|
|
26
25
|
const value = numberInput({ value: "1", step: "any", "aria-label": "Value" });
|
|
27
26
|
const from = selectInput(options, { value: "kg", "aria-label": "From unit" });
|
|
28
27
|
const to = selectInput(options, { value: "lb", "aria-label": "To unit" });
|
|
29
|
-
const result = el("span", { class: "
|
|
30
|
-
const stats = el("div", { class: "
|
|
31
|
-
const factor = el("p", { class: "
|
|
28
|
+
const result = el("span", { class: "wrap-anywhere text-xl font-bold tracking-[-0.02em] tabular-nums" }, "-");
|
|
29
|
+
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));
|
|
30
|
+
const factor = el("p", { class: "border-s-[3px] border-transparent ps-2 text-sm text-ink-3" });
|
|
32
31
|
const error = notice("error");
|
|
33
|
-
const scroll = el("div", { class: "
|
|
32
|
+
const scroll = el("div", { class: "max-h-80 overflow-auto" });
|
|
34
33
|
let text = "";
|
|
35
34
|
|
|
36
35
|
function run() {
|
|
@@ -50,7 +49,7 @@ export function mount(slot) {
|
|
|
50
49
|
const out = (n * a.factor) / b.factor;
|
|
51
50
|
result.textContent = `${fmt(out)} ${b.value}`;
|
|
52
51
|
factor.textContent = `1 ${a.value} = ${fmt(a.factor / b.factor)} ${b.value}`;
|
|
53
|
-
scroll.replaceChildren(el("table", { class: "
|
|
52
|
+
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" },
|
|
54
53
|
el("thead", {}, el("tr", {}, el("th", {}, "Unit"), el("th", {}, "Value"))),
|
|
55
54
|
el("tbody", {}, ...UNITS.map((unit) => el("tr", {},
|
|
56
55
|
el("td", {}, unit.label),
|
|
@@ -74,8 +73,7 @@ export function mount(slot) {
|
|
|
74
73
|
stack(panel("Value", value), panel("From", from), panel("To", to), error),
|
|
75
74
|
stack(panel("Result", stats), factor, panel("All units", scroll))
|
|
76
75
|
),
|
|
77
|
-
foot: actions(swap, copyButton(() => text, "Copy result", "fill"))
|
|
78
|
-
});
|
|
76
|
+
foot: actions(swap, copyButton(() => text, "Copy result", "fill"))});
|
|
79
77
|
slot.append(card.node);
|
|
80
78
|
run();
|
|
81
79
|
|
|
@@ -12,8 +12,8 @@ function parseNumbers(text) {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
function tile(label) {
|
|
15
|
-
const value = el("span", { class: "
|
|
16
|
-
const node = el("div", { class: "
|
|
15
|
+
const value = el("span", { class: "wrap-anywhere text-xl font-bold tracking-[-0.02em] tabular-nums" }, "-");
|
|
16
|
+
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));
|
|
17
17
|
node.set = (text) => { value.textContent = text; };
|
|
18
18
|
return node;
|
|
19
19
|
}
|
|
@@ -24,7 +24,7 @@ export function mount(slot) {
|
|
|
24
24
|
const weighted = tile("Weighted average");
|
|
25
25
|
const totalWeight = tile("Total weight");
|
|
26
26
|
const count = tile("Count");
|
|
27
|
-
const stats = el("div", { class: "
|
|
27
|
+
const stats = el("div", { class: "grid gap-2.5 [grid-template-columns:repeat(auto-fit,minmax(120px,1fr))]" }, weighted, totalWeight, count);
|
|
28
28
|
const error = notice("error");
|
|
29
29
|
let text = "";
|
|
30
30
|
|
|
@@ -75,8 +75,7 @@ export function mount(slot) {
|
|
|
75
75
|
stack(panel("Values", values), panel("Weights", weights), error),
|
|
76
76
|
stack(stats)
|
|
77
77
|
),
|
|
78
|
-
foot: actions(copyButton(() => text, "Copy summary", "fill"))
|
|
79
|
-
});
|
|
78
|
+
foot: actions(copyButton(() => text, "Copy summary", "fill"))});
|
|
80
79
|
slot.append(card.node);
|
|
81
80
|
run();
|
|
82
81
|
|
|
@@ -5,8 +5,8 @@ import { el, bench, stack, panel, textArea, numberInput, notice, button, copyBut
|
|
|
5
5
|
const WPM = 200;
|
|
6
6
|
|
|
7
7
|
function tile(label) {
|
|
8
|
-
const value = el("span", { class: "
|
|
9
|
-
const node = el("div", { class: "
|
|
8
|
+
const value = el("span", { class: "wrap-anywhere text-xl font-bold tracking-[-0.02em] tabular-nums" }, "0");
|
|
9
|
+
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));
|
|
10
10
|
node.set = (text) => { value.textContent = text; };
|
|
11
11
|
return node;
|
|
12
12
|
}
|
|
@@ -24,11 +24,11 @@ export function mount(slot) {
|
|
|
24
24
|
const sentencesTile = tile("Sentences");
|
|
25
25
|
const linesTile = tile("Lines");
|
|
26
26
|
const readingTile = tile("Reading time");
|
|
27
|
-
const stats = el("div", { class: "
|
|
27
|
+
const stats = el("div", { class: "grid gap-2.5 [grid-template-columns:repeat(auto-fit,minmax(120px,1fr))]" }, wordsTile, charsTile, sentencesTile, linesTile, readingTile);
|
|
28
28
|
|
|
29
|
-
const meter = el("div", { class: "
|
|
29
|
+
const meter = el("div", { class: "h-2 overflow-hidden rounded-full bg-deck-2", role: "img", "aria-label": "Word limit" }, el("div", { class: "h-full w-0 rounded-full bg-orbit-hard transition-[width,background-color] duration-150 data-[level=weak]:bg-danger data-[level=ok]:bg-star data-[level=strong]:bg-jade" }));
|
|
30
30
|
const limitNote = notice("note");
|
|
31
|
-
const fill = meter.
|
|
31
|
+
const fill = meter.firstElementChild;
|
|
32
32
|
|
|
33
33
|
let counts = { words: 0, chars: 0, sentences: 0, lines: 0 };
|
|
34
34
|
|
|
@@ -37,8 +37,7 @@ export function mount(slot) {
|
|
|
37
37
|
`${counts.words} words`,
|
|
38
38
|
`${counts.chars} characters`,
|
|
39
39
|
`${counts.sentences} sentences`,
|
|
40
|
-
`${counts.lines} lines
|
|
41
|
-
].join("\n");
|
|
40
|
+
`${counts.lines} lines`].join("\n");
|
|
42
41
|
}
|
|
43
42
|
|
|
44
43
|
function run() {
|
|
@@ -48,8 +47,7 @@ export function mount(slot) {
|
|
|
48
47
|
words,
|
|
49
48
|
chars: text.length,
|
|
50
49
|
sentences: countSentences(text),
|
|
51
|
-
lines: text ? text.split("\n").length : 0
|
|
52
|
-
};
|
|
50
|
+
lines: text ? text.split("\n").length : 0};
|
|
53
51
|
wordsTile.set(String(counts.words));
|
|
54
52
|
charsTile.set(String(counts.chars));
|
|
55
53
|
sentencesTile.set(String(counts.sentences));
|
|
@@ -60,11 +58,11 @@ export function mount(slot) {
|
|
|
60
58
|
if (limit > 0) {
|
|
61
59
|
const ratio = words / limit;
|
|
62
60
|
fill.style.width = `${Math.min(100, ratio * 100)}%`;
|
|
63
|
-
fill.
|
|
61
|
+
fill.dataset.level = ratio > 1 ? "weak" : ratio > 0.8 ? "ok" : "strong";
|
|
64
62
|
limitNote.write(ratio > 1 ? `${words - limit} over the limit.` : `${limit - words} words left.`);
|
|
65
63
|
} else {
|
|
66
64
|
fill.style.width = "0%";
|
|
67
|
-
fill.
|
|
65
|
+
delete fill.dataset.level;
|
|
68
66
|
limitNote.write("");
|
|
69
67
|
}
|
|
70
68
|
}
|
|
@@ -79,8 +77,7 @@ export function mount(slot) {
|
|
|
79
77
|
stack(panel("Text", source), panel("Word limit", limitField)),
|
|
80
78
|
stack(stats, meter, limitNote)
|
|
81
79
|
),
|
|
82
|
-
foot: actions(clearButton, copyButton(summary, "Copy summary", "fill"))
|
|
83
|
-
});
|
|
80
|
+
foot: actions(clearButton, copyButton(summary, "Copy summary", "fill"))});
|
|
84
81
|
slot.append(card.node);
|
|
85
82
|
run();
|
|
86
83
|
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
import { el, bench, stack, panel, textArea, numberInput, notice, copyButton, actions, toolcard } from "../core.js";
|
|
3
3
|
|
|
4
4
|
function tile(label) {
|
|
5
|
-
const value = el("span", { class: "
|
|
6
|
-
const node = el("div", { class: "
|
|
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,7 +17,7 @@ export function mount(slot) {
|
|
|
17
17
|
const wordsTile = tile("Words");
|
|
18
18
|
const minTile = tile("To minimum");
|
|
19
19
|
const maxTile = tile("To maximum");
|
|
20
|
-
const stats = el("div", { class: "
|
|
20
|
+
const stats = el("div", { class: "grid gap-2.5 [grid-template-columns:repeat(auto-fit,minmax(120px,1fr))]" }, wordsTile, minTile, maxTile);
|
|
21
21
|
const status = notice("note");
|
|
22
22
|
const note = notice("note");
|
|
23
23
|
note.write("Each hyphenated word and each number counts as one word.");
|
|
@@ -49,8 +49,7 @@ export function mount(slot) {
|
|
|
49
49
|
stack(panel("Text", source), panel("Minimum", min), panel("Maximum", max), note),
|
|
50
50
|
stack(stats, status)
|
|
51
51
|
),
|
|
52
|
-
foot: actions(copyButton(() => summary, "Copy count", "fill"))
|
|
53
|
-
});
|
|
52
|
+
foot: actions(copyButton(() => summary, "Copy count", "fill"))});
|
|
54
53
|
slot.append(card.node);
|
|
55
54
|
run();
|
|
56
55
|
|
|
@@ -31,21 +31,21 @@ function check(source) {
|
|
|
31
31
|
|
|
32
32
|
export function mount(slot) {
|
|
33
33
|
const source = textArea({ rows: 11, value: "name: mevebu\ntools:\n - unicode\n - json", "aria-label": "YAML" });
|
|
34
|
-
const scroll = el("div", { class: "
|
|
35
|
-
const count = el("span", { class: "
|
|
36
|
-
const stats = el("div", { class: "
|
|
34
|
+
const scroll = el("div", { class: "max-h-80 overflow-auto" });
|
|
35
|
+
const count = el("span", { class: "wrap-anywhere text-xl font-bold tracking-[-0.02em] tabular-nums" }, "0");
|
|
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" }, count, el("span", { class: "text-2xs font-semibold text-ink-3" }, "Problems")));
|
|
37
37
|
let problems = [];
|
|
38
38
|
|
|
39
39
|
function run() {
|
|
40
40
|
problems = check(source.value);
|
|
41
41
|
count.textContent = String(problems.length);
|
|
42
42
|
if (!problems.length) {
|
|
43
|
-
scroll.replaceChildren(el("p", { class: "
|
|
43
|
+
scroll.replaceChildren(el("p", { class: "border-s-[3px] border-transparent ps-2 text-sm text-ink-3" }, "No problems."));
|
|
44
44
|
return;
|
|
45
45
|
}
|
|
46
46
|
scroll.replaceChildren(el(
|
|
47
47
|
"table",
|
|
48
|
-
{ class: "
|
|
48
|
+
{ 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" },
|
|
49
49
|
el("thead", {}, el("tr", {}, el("th", {}, "Line"), el("th", {}, "Problem"))),
|
|
50
50
|
el("tbody", {}, ...problems.map((entry) => el("tr", {},
|
|
51
51
|
el("th", {}, String(entry.line)),
|
|
@@ -60,8 +60,7 @@ export function mount(slot) {
|
|
|
60
60
|
stack(panel("YAML", source)),
|
|
61
61
|
stack(stats, panel("Problems", scroll))
|
|
62
62
|
),
|
|
63
|
-
foot: actions(copyButton(() => problems.map((entry) => `${entry.line}: ${entry.message}`).join("\n"), "Copy problems", "fill"))
|
|
64
|
-
});
|
|
63
|
+
foot: actions(copyButton(() => problems.map((entry) => `${entry.line}: ${entry.message}`).join("\n"), "Copy problems", "fill"))});
|
|
65
64
|
slot.append(card.node);
|
|
66
65
|
run();
|
|
67
66
|
|