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
|
@@ -6,12 +6,11 @@ const PARTS = [
|
|
|
6
6
|
{ value: "aim", label: "Aim" },
|
|
7
7
|
{ value: "method", label: "Method" },
|
|
8
8
|
{ value: "results", label: "Results" },
|
|
9
|
-
{ value: "conclusion", label: "Conclusion" }
|
|
10
|
-
];
|
|
9
|
+
{ value: "conclusion", label: "Conclusion" }];
|
|
11
10
|
|
|
12
11
|
function tile(label) {
|
|
13
|
-
const value = el("span", { class: "
|
|
14
|
-
const node = el("div", { class: "
|
|
12
|
+
const value = el("span", { class: "wrap-anywhere text-xl font-bold tracking-[-0.02em] tabular-nums" }, "0");
|
|
13
|
+
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));
|
|
15
14
|
node.set = (text) => { value.textContent = text; };
|
|
16
15
|
return node;
|
|
17
16
|
}
|
|
@@ -23,7 +22,7 @@ export function mount(slot) {
|
|
|
23
22
|
const parts = toggleRow(PARTS.map((part) => ({ ...part, checked: false })));
|
|
24
23
|
const wordsTile = tile("Words");
|
|
25
24
|
const tickedTile = tile("Ticked");
|
|
26
|
-
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, tickedTile);
|
|
27
26
|
const note = notice("note");
|
|
28
27
|
note.write("This checks structure only, not quality.");
|
|
29
28
|
let summary = "";
|
|
@@ -43,8 +42,7 @@ export function mount(slot) {
|
|
|
43
42
|
stack(panel("Abstract", source), note),
|
|
44
43
|
stack(stats, panel("Parts found", parts))
|
|
45
44
|
),
|
|
46
|
-
foot: actions(copyButton(() => summary, "Copy checklist", "fill"))
|
|
47
|
-
});
|
|
45
|
+
foot: actions(copyButton(() => summary, "Copy checklist", "fill"))});
|
|
48
46
|
slot.append(card.node);
|
|
49
47
|
run();
|
|
50
48
|
|
|
@@ -5,8 +5,7 @@ const UNITS = [
|
|
|
5
5
|
{ value: "days", label: "Days" },
|
|
6
6
|
{ value: "weeks", label: "Weeks" },
|
|
7
7
|
{ value: "months", label: "Months" },
|
|
8
|
-
{ value: "years", label: "Years" }
|
|
9
|
-
];
|
|
8
|
+
{ value: "years", label: "Years" }];
|
|
10
9
|
|
|
11
10
|
const pad = (n) => String(n).padStart(2, "0");
|
|
12
11
|
|
|
@@ -24,7 +23,7 @@ function parseDate(value) {
|
|
|
24
23
|
const iso = (date) => `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;
|
|
25
24
|
|
|
26
25
|
const kvRow = (key, value) =>
|
|
27
|
-
el("div", { class: "
|
|
26
|
+
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));
|
|
28
27
|
|
|
29
28
|
export function mount(slot) {
|
|
30
29
|
const date = textInput({ type: "date", value: "2026-01-01", "aria-label": "Start date" });
|
|
@@ -35,12 +34,12 @@ export function mount(slot) {
|
|
|
35
34
|
[{ value: "add", label: "Add" }, { value: "subtract", label: "Subtract" }],
|
|
36
35
|
{ value: "add", label: "Direction", onChange: (value) => { mode = value; run(); } }
|
|
37
36
|
);
|
|
38
|
-
const result = el("span", { class: "
|
|
39
|
-
const tile = el("div", { class: "
|
|
40
|
-
const stats = el("div", { class: "
|
|
41
|
-
const isoOut = el("span", { class: "
|
|
42
|
-
const weekday = el("span", { class: "
|
|
43
|
-
const kv = el("div", { class: "
|
|
37
|
+
const result = el("span", { class: "wrap-anywhere text-xl font-bold tracking-[-0.02em] tabular-nums" }, "-");
|
|
38
|
+
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" }, "Result"));
|
|
39
|
+
const stats = el("div", { class: "grid gap-2.5 [grid-template-columns:repeat(auto-fit,minmax(120px,1fr))]" }, tile);
|
|
40
|
+
const isoOut = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
|
|
41
|
+
const weekday = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
|
|
42
|
+
const kv = el("div", { class: "grid gap-px overflow-hidden rounded-r1 border border-orbit bg-orbit" }, kvRow("ISO date", isoOut), kvRow("Weekday", weekday));
|
|
44
43
|
const error = notice("error");
|
|
45
44
|
let text = "";
|
|
46
45
|
|
|
@@ -94,8 +93,7 @@ export function mount(slot) {
|
|
|
94
93
|
),
|
|
95
94
|
stack(stats, panel("Result", kv))
|
|
96
95
|
),
|
|
97
|
-
foot: actions(copyButton(() => text, "Copy result", "fill"))
|
|
98
|
-
});
|
|
96
|
+
foot: actions(copyButton(() => text, "Copy result", "fill"))});
|
|
99
97
|
slot.append(card.node);
|
|
100
98
|
run();
|
|
101
99
|
|
|
@@ -32,20 +32,20 @@ function calendarDiff(from, to) {
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
const kvRow = (key, value) =>
|
|
35
|
-
el("div", { class: "
|
|
35
|
+
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
36
|
|
|
37
37
|
export function mount(slot) {
|
|
38
38
|
const today = new Date();
|
|
39
39
|
const birth = textInput({ type: "date", value: "1990-01-01", "aria-label": "Birth date" });
|
|
40
40
|
const at = textInput({ type: "date", value: `${today.getFullYear()}-${pad(today.getMonth() + 1)}-${pad(today.getDate())}`, "aria-label": "Age at date" });
|
|
41
41
|
|
|
42
|
-
const age = el("span", { class: "
|
|
43
|
-
const totalDays = el("span", { class: "
|
|
44
|
-
const nextDate = el("span", { class: "
|
|
45
|
-
const nextIn = el("span", { class: "
|
|
42
|
+
const age = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
|
|
43
|
+
const totalDays = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
|
|
44
|
+
const nextDate = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
|
|
45
|
+
const nextIn = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
|
|
46
46
|
const kv = el(
|
|
47
47
|
"div",
|
|
48
|
-
{ class: "
|
|
48
|
+
{ class: "grid gap-px overflow-hidden rounded-r1 border border-orbit bg-orbit" },
|
|
49
49
|
kvRow("Age", age),
|
|
50
50
|
kvRow("Total days", totalDays),
|
|
51
51
|
kvRow("Next birthday", nextDate),
|
|
@@ -97,8 +97,7 @@ export function mount(slot) {
|
|
|
97
97
|
stack(panel("Birth date", birth), panel("Age at", at), error),
|
|
98
98
|
stack(panel("Result", kv))
|
|
99
99
|
),
|
|
100
|
-
foot: actions(copyButton(() => text, "Copy age", "fill"))
|
|
101
|
-
});
|
|
100
|
+
foot: actions(copyButton(() => text, "Copy age", "fill"))});
|
|
102
101
|
slot.append(card.node);
|
|
103
102
|
run();
|
|
104
103
|
|
|
@@ -36,8 +36,8 @@ function build(method, url, headers, body) {
|
|
|
36
36
|
export function mount(slot) {
|
|
37
37
|
const methodSelect = selectInput(METHODS, { value: "GET", "aria-label": "Method" });
|
|
38
38
|
const urlInput = textInput({ value: "https://api.example.com/users", "aria-label": "URL", spellcheck: "false" });
|
|
39
|
-
const headerInput = textArea({ rows: 5,
|
|
40
|
-
const bodyInput = textArea({ rows: 6,
|
|
39
|
+
const headerInput = textArea({ rows: 5, "aria-label": "Headers", placeholder: "Content-Type: application/json", value: "Accept: application/json" });
|
|
40
|
+
const bodyInput = textArea({ rows: 6, "aria-label": "Body", placeholder: '{"name":"Ada"}', value: '{\n "name": "Ada"\n}' });
|
|
41
41
|
const result = output({ rows: 14 });
|
|
42
42
|
const hint = notice("note");
|
|
43
43
|
|
|
@@ -65,8 +65,7 @@ export function mount(slot) {
|
|
|
65
65
|
),
|
|
66
66
|
stack(panel("fetch", result))
|
|
67
67
|
),
|
|
68
|
-
foot: actions(copyButton(() => result.textContent, "Copy snippet", "fill"))
|
|
69
|
-
});
|
|
68
|
+
foot: actions(copyButton(() => result.textContent, "Copy snippet", "fill"))});
|
|
70
69
|
slot.append(card.node);
|
|
71
70
|
run();
|
|
72
71
|
|
|
@@ -10,8 +10,7 @@ const UNITS = [
|
|
|
10
10
|
{ value: "in2", label: "Square inch", factor: 0.00064516 },
|
|
11
11
|
{ value: "ft2", label: "Square foot", factor: 0.09290304 },
|
|
12
12
|
{ value: "yd2", label: "Square yard", factor: 0.83612736 },
|
|
13
|
-
{ value: "acre", label: "Acre", factor: 4046.8564224 }
|
|
14
|
-
];
|
|
13
|
+
{ value: "acre", label: "Acre", factor: 4046.8564224 }];
|
|
15
14
|
|
|
16
15
|
const byId = Object.fromEntries(UNITS.map((unit) => [unit.value, unit]));
|
|
17
16
|
const options = UNITS.map(({ value, label }) => ({ value, label }));
|
|
@@ -28,11 +27,11 @@ export function mount(slot) {
|
|
|
28
27
|
const value = numberInput({ value: "1", step: "any", "aria-label": "Value" });
|
|
29
28
|
const from = selectInput(options, { value: "m2", "aria-label": "From unit" });
|
|
30
29
|
const to = selectInput(options, { value: "ft2", "aria-label": "To unit" });
|
|
31
|
-
const result = el("span", { class: "
|
|
32
|
-
const stats = el("div", { class: "
|
|
33
|
-
const factor = el("p", { class: "
|
|
30
|
+
const result = el("span", { class: "wrap-anywhere text-xl font-bold tracking-[-0.02em] tabular-nums" }, "-");
|
|
31
|
+
const stats = el("div", { class: "grid gap-2.5 [grid-template-columns:repeat(auto-fit,minmax(120px,1fr))]" }, el("div", { class: "grid gap-0.5 rounded-r1 border border-orbit bg-deck-2 px-3.5 py-3" }, el("span", { class: "text-2xs font-semibold text-ink-3" }, "Result"), result));
|
|
32
|
+
const factor = el("p", { class: "border-s-[3px] border-transparent ps-2 text-sm text-ink-3" });
|
|
34
33
|
const error = notice("error");
|
|
35
|
-
const scroll = el("div", { class: "
|
|
34
|
+
const scroll = el("div", { class: "max-h-80 overflow-auto" });
|
|
36
35
|
let text = "";
|
|
37
36
|
|
|
38
37
|
function run() {
|
|
@@ -52,7 +51,7 @@ export function mount(slot) {
|
|
|
52
51
|
const out = (n * a.factor) / b.factor;
|
|
53
52
|
result.textContent = `${fmt(out)} ${b.value}`;
|
|
54
53
|
factor.textContent = `1 ${a.value} = ${fmt(a.factor / b.factor)} ${b.value}`;
|
|
55
|
-
scroll.replaceChildren(el("table", { class: "
|
|
54
|
+
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" },
|
|
56
55
|
el("thead", {}, el("tr", {}, el("th", {}, "Unit"), el("th", {}, "Value"))),
|
|
57
56
|
el("tbody", {}, ...UNITS.map((unit) => el("tr", {},
|
|
58
57
|
el("td", {}, unit.label),
|
|
@@ -76,8 +75,7 @@ export function mount(slot) {
|
|
|
76
75
|
stack(panel("Value", value), panel("From", from), panel("To", to), error),
|
|
77
76
|
stack(panel("Result", stats), factor, panel("All units", scroll))
|
|
78
77
|
),
|
|
79
|
-
foot: actions(swap, copyButton(() => text, "Copy result", "fill"))
|
|
80
|
-
});
|
|
78
|
+
foot: actions(swap, copyButton(() => text, "Copy result", "fill"))});
|
|
81
79
|
slot.append(card.node);
|
|
82
80
|
run();
|
|
83
81
|
|
|
@@ -8,18 +8,18 @@ export function mount(slot) {
|
|
|
8
8
|
const width = numberInput({ value: 1920, min: "1", "aria-label": "Width" });
|
|
9
9
|
const height = numberInput({ value: 1080, min: "1", "aria-label": "Height" });
|
|
10
10
|
|
|
11
|
-
const ratioValue = el("span", { class: "
|
|
12
|
-
const decimalValue = el("span", { class: "
|
|
11
|
+
const ratioValue = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
|
|
12
|
+
const decimalValue = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
|
|
13
13
|
const list = el(
|
|
14
14
|
"div",
|
|
15
|
-
{ class: "
|
|
16
|
-
el("div", { class: "
|
|
17
|
-
el("div", { class: "
|
|
15
|
+
{ class: "grid gap-px overflow-hidden rounded-r1 border border-orbit bg-orbit" },
|
|
16
|
+
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" }, "Ratio"), ratioValue),
|
|
17
|
+
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" }, "Decimal"), decimalValue)
|
|
18
18
|
);
|
|
19
19
|
|
|
20
20
|
const newWidth = numberInput({ value: 800, min: "1", "aria-label": "New width" });
|
|
21
21
|
const newHeight = numberInput({ value: 450, min: "1", "aria-label": "New height" });
|
|
22
|
-
const box = el("div", { class: "
|
|
22
|
+
const box = el("div", { class: "grid h-[120px] w-[min(200px,100%)] place-items-center bg-jade text-2xs text-on-jade" });
|
|
23
23
|
const code = output();
|
|
24
24
|
let ratioText = "16 / 9";
|
|
25
25
|
|
|
@@ -68,10 +68,9 @@ export function mount(slot) {
|
|
|
68
68
|
const card = toolcard({
|
|
69
69
|
body: bench(
|
|
70
70
|
stack(panel("Width", width), panel("Height", height), list),
|
|
71
|
-
stack(el("div", { class: "
|
|
71
|
+
stack(el("div", { class: "grid min-h-40 place-items-center overflow-hidden rounded-r1 border border-orbit bg-deck-2 p-[22px]" }, box), panel("New width", newWidth), panel("New height", newHeight), code)
|
|
72
72
|
),
|
|
73
|
-
foot: actions(copyButton(() => `aspect-ratio: ${ratioText};`, "Copy CSS", "fill"))
|
|
74
|
-
});
|
|
73
|
+
foot: actions(copyButton(() => `aspect-ratio: ${ratioText};`, "Copy CSS", "fill"))});
|
|
75
74
|
slot.append(card.node);
|
|
76
75
|
update();
|
|
77
76
|
|
|
@@ -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
|
}
|
|
@@ -25,7 +25,7 @@ export function mount(slot) {
|
|
|
25
25
|
const mode = tile("Mode");
|
|
26
26
|
const count = tile("Count");
|
|
27
27
|
const sum = tile("Sum");
|
|
28
|
-
const stats = el("div", { class: "
|
|
28
|
+
const stats = el("div", { class: "grid gap-2.5 [grid-template-columns:repeat(auto-fit,minmax(120px,1fr))]" }, mean, median, mode, count, sum);
|
|
29
29
|
const error = notice("error");
|
|
30
30
|
let text = "";
|
|
31
31
|
|
|
@@ -72,8 +72,7 @@ export function mount(slot) {
|
|
|
72
72
|
stack(panel("Numbers", source), error),
|
|
73
73
|
stack(stats)
|
|
74
74
|
),
|
|
75
|
-
foot: actions(copyButton(() => text, "Copy summary", "fill"))
|
|
76
|
-
});
|
|
75
|
+
foot: actions(copyButton(() => text, "Copy summary", "fill"))});
|
|
77
76
|
slot.append(card.node);
|
|
78
77
|
run();
|
|
79
78
|
|
|
@@ -1,21 +1,40 @@
|
|
|
1
1
|
// Text to Base64 and back, live in both directions. Emoji and other non-Latin
|
|
2
2
|
// text survive the round trip because the bytes are UTF-8.
|
|
3
|
-
import { bench, stack, panel, textArea, notice, copyButton, actions, toolcard } from "../core.js";
|
|
3
|
+
import { checkbox, bench, stack, panel, textArea, notice, copyButton, actions, toolcard } from "../core.js";
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
// btoa and atob take a binary string. A long input is converted in chunks,
|
|
6
|
+
// because spreading a whole document into String.fromCharCode overflows the
|
|
7
|
+
// call stack.
|
|
8
|
+
const CHUNK = 0x8000;
|
|
9
|
+
|
|
10
|
+
function encode(text, urlSafe) {
|
|
11
|
+
const bytes = new TextEncoder().encode(text);
|
|
12
|
+
let binary = "";
|
|
13
|
+
for (let at = 0; at < bytes.length; at += CHUNK) {
|
|
14
|
+
binary += String.fromCharCode(...bytes.subarray(at, at + CHUNK));
|
|
15
|
+
}
|
|
16
|
+
const base = btoa(binary);
|
|
17
|
+
return urlSafe ? base.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "") : base;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function decode(text) {
|
|
21
|
+
// Both alphabets are accepted and the padding goes back on: the URL-safe form
|
|
22
|
+
// drops its padding, and a JWT segment turns up that way.
|
|
23
|
+
const base = text.replace(/\s+/g, "").replace(/-/g, "+").replace(/_/g, "/");
|
|
24
|
+
const padded = base.padEnd(base.length + ((4 - (base.length % 4)) % 4), "=");
|
|
25
|
+
return new TextDecoder().decode(Uint8Array.from(atob(padded), (char) => char.charCodeAt(0)));
|
|
26
|
+
}
|
|
9
27
|
|
|
10
28
|
export function mount(slot) {
|
|
11
29
|
const plain = textArea({ rows: 7, value: "hello mevebu", "aria-label": "Plain text" });
|
|
12
|
-
const coded = textArea({ rows: 7,
|
|
30
|
+
const coded = textArea({ rows: 7, "aria-label": "Base64" });
|
|
31
|
+
const urlSafeBox = checkbox("URL-safe, no padding", { checked: false });
|
|
13
32
|
const error = notice("error");
|
|
14
33
|
let guard = false;
|
|
15
34
|
|
|
16
35
|
function refresh() {
|
|
17
36
|
try {
|
|
18
|
-
coded.value = plain.value ? encode(plain.value) : "";
|
|
37
|
+
coded.value = plain.value ? encode(plain.value, urlSafeBox.input.checked) : "";
|
|
19
38
|
error.write("");
|
|
20
39
|
} catch {
|
|
21
40
|
error.write("That text could not be encoded.");
|
|
@@ -42,20 +61,21 @@ export function mount(slot) {
|
|
|
42
61
|
}
|
|
43
62
|
});
|
|
44
63
|
|
|
64
|
+
urlSafeBox.input.addEventListener("change", refresh);
|
|
65
|
+
|
|
45
66
|
const card = toolcard({
|
|
46
67
|
body: bench(
|
|
47
68
|
stack(panel("Text", plain)),
|
|
48
|
-
stack(panel("Base64", coded), error)
|
|
69
|
+
stack(panel("Base64", coded), urlSafeBox, error)
|
|
49
70
|
),
|
|
50
71
|
foot: actions(
|
|
51
72
|
copyButton(() => plain.value, "Copy text"),
|
|
52
73
|
copyButton(() => coded.value, "Copy Base64", "fill"),
|
|
53
|
-
)
|
|
54
|
-
});
|
|
74
|
+
)});
|
|
55
75
|
slot.append(card.node);
|
|
56
76
|
refresh();
|
|
57
77
|
|
|
58
78
|
return {};
|
|
59
79
|
}
|
|
60
80
|
|
|
61
|
-
export default { mount };
|
|
81
|
+
export default { mount };
|
|
@@ -8,11 +8,11 @@ function parse(text) {
|
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
export function mount(slot) {
|
|
11
|
-
const first = textInput({ value: "1011",
|
|
12
|
-
const second = textInput({ value: "0110",
|
|
13
|
-
const result = el("span", { class: "
|
|
14
|
-
const stats = el("div", { class: "
|
|
15
|
-
const decimal = el("p", { class: "
|
|
11
|
+
const first = textInput({ value: "1011", mono: true, "aria-label": "First number" });
|
|
12
|
+
const second = textInput({ value: "0110", mono: true, "aria-label": "Second number" });
|
|
13
|
+
const result = el("span", { class: "wrap-anywhere text-xl font-bold tracking-[-0.02em] tabular-nums" }, "-");
|
|
14
|
+
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" }, "Binary"), result));
|
|
15
|
+
const decimal = el("p", { class: "border-s-[3px] border-transparent ps-2 text-sm text-ink-3" });
|
|
16
16
|
const error = notice("error");
|
|
17
17
|
let mode = "add";
|
|
18
18
|
let text = "";
|
|
@@ -47,8 +47,7 @@ export function mount(slot) {
|
|
|
47
47
|
stack(panel("First number", first), panel("Second number", second), panel("Operation", chips), error),
|
|
48
48
|
stack(panel("Result", stats), decimal)
|
|
49
49
|
),
|
|
50
|
-
foot: actions(copyButton(() => text, "Copy result", "fill"))
|
|
51
|
-
});
|
|
50
|
+
foot: actions(copyButton(() => text, "Copy result", "fill"))});
|
|
52
51
|
slot.append(card.node);
|
|
53
52
|
run();
|
|
54
53
|
|
|
@@ -5,11 +5,11 @@ const KEYS = ["tl", "tr", "br", "bl"];
|
|
|
5
5
|
|
|
6
6
|
function slider(label, value) {
|
|
7
7
|
const input = rangeInput({ min: 0, max: 100, value, "aria-label": label });
|
|
8
|
-
const val = el("span", { class: "
|
|
8
|
+
const val = el("span", { class: "text-end font-mono text-2xs text-ink-2" }, `${value}px`);
|
|
9
9
|
const node = el(
|
|
10
10
|
"div",
|
|
11
|
-
{ class: "
|
|
12
|
-
el("span", { class: "
|
|
11
|
+
{ class: "grid items-center gap-2.5 [grid-template-columns:88px_minmax(0,1fr)_62px] max-[45rem]:[grid-template-columns:minmax(0,1fr)_62px]" },
|
|
12
|
+
el("span", { class: "text-2xs font-semibold text-ink-2 max-[45rem]:col-span-2" }, label),
|
|
13
13
|
input,
|
|
14
14
|
val
|
|
15
15
|
);
|
|
@@ -27,11 +27,10 @@ export function mount(slot) {
|
|
|
27
27
|
tl: slider("Top left", corners.tl),
|
|
28
28
|
tr: slider("Top right", corners.tr),
|
|
29
29
|
br: slider("Bottom right", corners.br),
|
|
30
|
-
bl: slider("Bottom left", corners.bl)
|
|
31
|
-
};
|
|
30
|
+
bl: slider("Bottom left", corners.bl)};
|
|
32
31
|
|
|
33
32
|
const linkSwitch = switchControl("Link corners", { checked: true });
|
|
34
|
-
const previewBox = el("div", { class: "
|
|
33
|
+
const previewBox = el("div", { class: "grid h-[120px] w-[min(200px,100%)] place-items-center bg-jade text-2xs text-on-jade" });
|
|
35
34
|
const code = output();
|
|
36
35
|
|
|
37
36
|
function css() {
|
|
@@ -75,10 +74,9 @@ export function mount(slot) {
|
|
|
75
74
|
const card = toolcard({
|
|
76
75
|
body: bench(
|
|
77
76
|
stack(linkSwitch, sliders.tl, sliders.tr, sliders.br, sliders.bl),
|
|
78
|
-
stack(el("div", { class: "
|
|
77
|
+
stack(el("div", { class: "grid min-h-40 place-items-center overflow-hidden rounded-r1 border border-orbit bg-deck-2 p-[22px]" }, previewBox), code)
|
|
79
78
|
),
|
|
80
|
-
foot: actions(copyButton(css, "Copy CSS", "fill"))
|
|
81
|
-
});
|
|
79
|
+
foot: actions(copyButton(css, "Copy CSS", "fill"))});
|
|
82
80
|
slot.append(card.node);
|
|
83
81
|
update();
|
|
84
82
|
|
|
@@ -9,11 +9,11 @@ function hexToRgb(hex) {
|
|
|
9
9
|
|
|
10
10
|
function slider(label, { min, max, value, unit = "px" }) {
|
|
11
11
|
const input = rangeInput({ min, max, value, "aria-label": label });
|
|
12
|
-
const val = el("span", { class: "
|
|
12
|
+
const val = el("span", { class: "text-end font-mono text-2xs text-ink-2" }, `${value}${unit}`);
|
|
13
13
|
const node = el(
|
|
14
14
|
"div",
|
|
15
|
-
{ class: "
|
|
16
|
-
el("span", { class: "
|
|
15
|
+
{ class: "grid items-center gap-2.5 [grid-template-columns:88px_minmax(0,1fr)_62px] max-[45rem]:[grid-template-columns:minmax(0,1fr)_62px]" },
|
|
16
|
+
el("span", { class: "text-2xs font-semibold text-ink-2 max-[45rem]:col-span-2" }, label),
|
|
17
17
|
input,
|
|
18
18
|
val
|
|
19
19
|
);
|
|
@@ -31,7 +31,7 @@ export function mount(slot) {
|
|
|
31
31
|
const opacity = slider("Opacity", { min: 0, max: 100, value: 35, unit: "%" });
|
|
32
32
|
const picker = colorInput({ value: "#1b1b1f", "aria-label": "Shadow color" });
|
|
33
33
|
const inset = switchControl("Inset", {});
|
|
34
|
-
const previewBox = el("div", { class: "
|
|
34
|
+
const previewBox = el("div", { class: "grid h-[120px] w-[min(200px,100%)] place-items-center bg-jade text-2xs text-on-jade" });
|
|
35
35
|
const code = output();
|
|
36
36
|
|
|
37
37
|
function css() {
|
|
@@ -58,10 +58,9 @@ export function mount(slot) {
|
|
|
58
58
|
const card = toolcard({
|
|
59
59
|
body: bench(
|
|
60
60
|
stack(x, y, blur, spread, opacity, panel("Color", picker), inset),
|
|
61
|
-
stack(el("div", { class: "
|
|
61
|
+
stack(el("div", { class: "grid min-h-40 place-items-center overflow-hidden rounded-r1 border border-orbit bg-deck-2 p-[22px]" }, previewBox), code)
|
|
62
62
|
),
|
|
63
|
-
foot: actions(copyButton(() => `box-shadow: ${css()};`, "Copy CSS", "fill"))
|
|
64
|
-
});
|
|
63
|
+
foot: actions(copyButton(() => `box-shadow: ${css()};`, "Copy CSS", "fill"))});
|
|
65
64
|
slot.append(card.node);
|
|
66
65
|
update();
|
|
67
66
|
|
|
@@ -6,8 +6,8 @@ function fmt(n) {
|
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
function tile(label) {
|
|
9
|
-
const value = el("span", { class: "
|
|
10
|
-
const node = el("div", { class: "
|
|
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
|
}
|
|
@@ -19,7 +19,7 @@ export function mount(slot) {
|
|
|
19
19
|
const unitsTile = tile("Break-even units");
|
|
20
20
|
const revenueTile = tile("Revenue");
|
|
21
21
|
const marginTile = tile("Contribution");
|
|
22
|
-
const stats = el("div", { class: "
|
|
22
|
+
const stats = el("div", { class: "grid gap-2.5 [grid-template-columns:repeat(auto-fit,minmax(120px,1fr))]" }, unitsTile, revenueTile, marginTile);
|
|
23
23
|
const error = notice("error");
|
|
24
24
|
let text = "";
|
|
25
25
|
|
|
@@ -57,8 +57,7 @@ export function mount(slot) {
|
|
|
57
57
|
stack(panel("Fixed costs", fixed), panel("Price per unit", price), panel("Variable cost per unit", variable), error),
|
|
58
58
|
stack(stats)
|
|
59
59
|
),
|
|
60
|
-
foot: actions(copyButton(() => text, "Copy result", "fill"))
|
|
61
|
-
});
|
|
60
|
+
foot: actions(copyButton(() => text, "Copy result", "fill"))});
|
|
62
61
|
slot.append(card.node);
|
|
63
62
|
run();
|
|
64
63
|
|
|
@@ -20,7 +20,7 @@ function parseDate(value) {
|
|
|
20
20
|
const iso = (date) => `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;
|
|
21
21
|
|
|
22
22
|
const kvRow = (key, value) =>
|
|
23
|
-
el("div", { class: "
|
|
23
|
+
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));
|
|
24
24
|
|
|
25
25
|
export function mount(slot) {
|
|
26
26
|
const start = textInput({ type: "date", value: "2026-01-01", "aria-label": "Start date" });
|
|
@@ -29,13 +29,13 @@ export function mount(slot) {
|
|
|
29
29
|
const weekdays = toggleRow(DAY_LABELS.map((label, index) => ({ value: index, label, checked: index >= 1 && index <= 5 })));
|
|
30
30
|
const holidays = textArea({ rows: 4, placeholder: "2026-01-01", "aria-label": "Holidays, one per line" });
|
|
31
31
|
|
|
32
|
-
const working = el("span", { class: "
|
|
33
|
-
const calendar = el("span", { class: "
|
|
34
|
-
const weekend = el("span", { class: "
|
|
35
|
-
const holidayCount = el("span", { class: "
|
|
32
|
+
const working = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
|
|
33
|
+
const calendar = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
|
|
34
|
+
const weekend = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
|
|
35
|
+
const holidayCount = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
|
|
36
36
|
const kv = el(
|
|
37
37
|
"div",
|
|
38
|
-
{ class: "
|
|
38
|
+
{ class: "grid gap-px overflow-hidden rounded-r1 border border-orbit bg-orbit" },
|
|
39
39
|
kvRow("Working days", working),
|
|
40
40
|
kvRow("Calendar days", calendar),
|
|
41
41
|
kvRow("Weekend days", weekend),
|
|
@@ -109,8 +109,7 @@ export function mount(slot) {
|
|
|
109
109
|
),
|
|
110
110
|
stack(panel("Holidays", holidays), panel("Count", kv))
|
|
111
111
|
),
|
|
112
|
-
foot: actions(copyButton(() => text, "Copy result", "fill"))
|
|
113
|
-
});
|
|
112
|
+
foot: actions(copyButton(() => text, "Copy result", "fill"))});
|
|
114
113
|
slot.append(card.node);
|
|
115
114
|
run();
|
|
116
115
|
|
|
@@ -6,15 +6,15 @@ const esc = (value) =>
|
|
|
6
6
|
String(value).replace(/[&<>"']/g, (char) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }[char]));
|
|
7
7
|
|
|
8
8
|
const kvRow = (key, value) =>
|
|
9
|
-
el("div", { class: "
|
|
9
|
+
el("div", { class: "grid gap-3 bg-deck px-3 py-[9px] [grid-template-columns:minmax(90px,34%)_minmax(0,1fr)]" }, el("span", { class: "wrap-anywhere text-2xs font-semibold text-ink-2" }, key), el("span", { class: "wrap-anywhere font-mono text-2xs" }, value));
|
|
10
10
|
|
|
11
11
|
export function mount(slot) {
|
|
12
12
|
const source = textInput({ value: "https://example.com/blog/post/?utm_source=news#comments", "aria-label": "Page URL" });
|
|
13
13
|
const result = output({ rows: 2 });
|
|
14
14
|
const error = notice("error");
|
|
15
|
-
const normalized = el("span", { class: "
|
|
16
|
-
const length = el("span", { class: "
|
|
17
|
-
const kv = el("div", { class: "
|
|
15
|
+
const normalized = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
|
|
16
|
+
const length = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
|
|
17
|
+
const kv = el("div", { class: "grid gap-px overflow-hidden rounded-r1 border border-orbit bg-orbit" }, kvRow("Canonical URL", normalized), kvRow("Length", length));
|
|
18
18
|
let canonical = "";
|
|
19
19
|
|
|
20
20
|
function clear() {
|
|
@@ -52,8 +52,7 @@ export function mount(slot) {
|
|
|
52
52
|
foot: actions(
|
|
53
53
|
copyButton(() => canonical, "Copy URL"),
|
|
54
54
|
copyButton(() => result.textContent, "Copy tag", "fill")
|
|
55
|
-
)
|
|
56
|
-
});
|
|
55
|
+
)});
|
|
57
56
|
slot.append(card.node);
|
|
58
57
|
run();
|
|
59
58
|
|
|
@@ -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
|
}
|
|
@@ -16,7 +16,7 @@ export function mount(slot) {
|
|
|
16
16
|
const wordsTile = tile("Words");
|
|
17
17
|
const linesTile = tile("Lines");
|
|
18
18
|
const leftTile = tile("Left");
|
|
19
|
-
const stats = el("div", { class: "
|
|
19
|
+
const stats = el("div", { class: "grid gap-2.5 [grid-template-columns:repeat(auto-fit,minmax(120px,1fr))]" }, charsTile, wordsTile, linesTile, leftTile);
|
|
20
20
|
const note = notice("note");
|
|
21
21
|
|
|
22
22
|
let summary = "";
|
|
@@ -43,8 +43,7 @@ export function mount(slot) {
|
|
|
43
43
|
`${chars} characters`,
|
|
44
44
|
`${words} words`,
|
|
45
45
|
`${lines} lines`,
|
|
46
|
-
cap > 0 ? `${cap - chars} left of ${cap}` : "No limit set"
|
|
47
|
-
].join("\n");
|
|
46
|
+
cap > 0 ? `${cap - chars} left of ${cap}` : "No limit set"].join("\n");
|
|
48
47
|
}
|
|
49
48
|
|
|
50
49
|
source.addEventListener("input", run);
|
|
@@ -55,8 +54,7 @@ export function mount(slot) {
|
|
|
55
54
|
stack(panel("Caption", source), panel("Character limit", limit)),
|
|
56
55
|
stack(stats, note)
|
|
57
56
|
),
|
|
58
|
-
foot: actions(copyButton(() => summary, "Copy summary", "fill"))
|
|
59
|
-
});
|
|
57
|
+
foot: actions(copyButton(() => summary, "Copy summary", "fill"))});
|
|
60
58
|
slot.append(card.node);
|
|
61
59
|
run();
|
|
62
60
|
|