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
|
@@ -8,15 +8,15 @@ const splitSentences = (text) => text.trim()
|
|
|
8
8
|
const countWords = (text) => text.trim() ? text.trim().split(/\s+/).length : 0;
|
|
9
9
|
|
|
10
10
|
function tile(label) {
|
|
11
|
-
const value = el("span", { class: "
|
|
12
|
-
const node = el("div", { class: "
|
|
11
|
+
const value = el("span", { class: "wrap-anywhere text-xl font-bold tracking-[-0.02em] tabular-nums" }, "0");
|
|
12
|
+
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));
|
|
13
13
|
node.set = (text) => { value.textContent = text; };
|
|
14
14
|
return node;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
const kvRow = (key, value) => el("div", { class: "
|
|
18
|
-
el("span", { class: "
|
|
19
|
-
el("span", { class: "
|
|
17
|
+
const kvRow = (key, value) => el("div", { class: "grid gap-3 bg-deck px-3 py-[9px] [grid-template-columns:minmax(90px,34%)_minmax(0,1fr)]" },
|
|
18
|
+
el("span", { class: "wrap-anywhere text-2xs font-semibold text-ink-2" }, key),
|
|
19
|
+
el("span", { class: "wrap-anywhere font-mono text-2xs" }, value));
|
|
20
20
|
|
|
21
21
|
export function mount(slot) {
|
|
22
22
|
const source = textArea({ rows: 12, "aria-label": "Text", placeholder: "Type or paste text" });
|
|
@@ -26,9 +26,9 @@ export function mount(slot) {
|
|
|
26
26
|
const shortestTile = tile("Shortest");
|
|
27
27
|
const longestTile = tile("Longest");
|
|
28
28
|
const averageTile = tile("Average");
|
|
29
|
-
const stats = el("div", { class: "
|
|
29
|
+
const stats = el("div", { class: "grid gap-2.5 [grid-template-columns:repeat(auto-fit,minmax(120px,1fr))]" }, sentencesTile, shortestTile, longestTile, averageTile);
|
|
30
30
|
|
|
31
|
-
const listWrap = el("div", { class: "
|
|
31
|
+
const listWrap = el("div", { class: "max-h-80 overflow-auto" });
|
|
32
32
|
const note = notice("note");
|
|
33
33
|
|
|
34
34
|
let longText = "";
|
|
@@ -56,7 +56,7 @@ export function mount(slot) {
|
|
|
56
56
|
if (!long.length) {
|
|
57
57
|
listWrap.replaceChildren(blank("text-align-start", "No sentence runs past the threshold."));
|
|
58
58
|
} else {
|
|
59
|
-
listWrap.replaceChildren(el("div", { class: "
|
|
59
|
+
listWrap.replaceChildren(el("div", { class: "grid gap-px overflow-hidden rounded-r1 border border-orbit bg-orbit" },
|
|
60
60
|
...long.map((sentence) => kvRow(`${countWords(sentence)} words`, sentence))));
|
|
61
61
|
}
|
|
62
62
|
}
|
|
@@ -69,8 +69,7 @@ export function mount(slot) {
|
|
|
69
69
|
stack(panel("Text", source), panel("Flag past", threshold, "Words")),
|
|
70
70
|
stack(stats, note, panel("Long sentences", listWrap))
|
|
71
71
|
),
|
|
72
|
-
foot: actions(copyButton(() => longText, "Copy long sentences", "fill"))
|
|
73
|
-
});
|
|
72
|
+
foot: actions(copyButton(() => longText, "Copy long sentences", "fill"))});
|
|
74
73
|
slot.append(card.node);
|
|
75
74
|
run();
|
|
76
75
|
|
|
@@ -12,17 +12,17 @@ function parseNumbers(text) {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
const kvRow = (key, value) =>
|
|
15
|
-
el("div", { class: "
|
|
15
|
+
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));
|
|
16
16
|
|
|
17
17
|
export function mount(slot) {
|
|
18
18
|
const source = textArea({ rows: 6, value: "2, 5, 8, 11", "aria-label": "Numbers", placeholder: "2, 5, 8, 11" });
|
|
19
19
|
const error = notice("error");
|
|
20
20
|
|
|
21
|
-
const terms = el("span", { class: "
|
|
22
|
-
const differences = el("span", { class: "
|
|
23
|
-
const pattern = el("span", { class: "
|
|
24
|
-
const next = el("span", { class: "
|
|
25
|
-
const kv = el("div", { class: "
|
|
21
|
+
const terms = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
|
|
22
|
+
const differences = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
|
|
23
|
+
const pattern = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
|
|
24
|
+
const next = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
|
|
25
|
+
const kv = el("div", { class: "grid gap-px overflow-hidden rounded-r1 border border-orbit bg-orbit" },
|
|
26
26
|
kvRow("Terms", terms),
|
|
27
27
|
kvRow("Differences", differences),
|
|
28
28
|
kvRow("Pattern", pattern),
|
|
@@ -79,8 +79,7 @@ export function mount(slot) {
|
|
|
79
79
|
stack(panel("Numbers", source), error),
|
|
80
80
|
stack(panel("Analysis", kv))
|
|
81
81
|
),
|
|
82
|
-
foot: actions(copyButton(() => text, "Copy result", "fill"))
|
|
83
|
-
});
|
|
82
|
+
foot: actions(copyButton(() => text, "Copy result", "fill"))});
|
|
84
83
|
slot.append(card.node);
|
|
85
84
|
run();
|
|
86
85
|
|
|
@@ -15,7 +15,7 @@ const isUrl = (value) => {
|
|
|
15
15
|
|
|
16
16
|
export function mount(slot) {
|
|
17
17
|
const base = textInput({ value: "https://example.com", "aria-label": "Base URL" });
|
|
18
|
-
const pages = textArea({ rows: 8,
|
|
18
|
+
const pages = textArea({ rows: 8, value: "/\n/about", placeholder: "/\n/about\n/blog", "aria-label": "Pages" });
|
|
19
19
|
const freq = selectInput(["always", "hourly", "daily", "weekly", "monthly", "yearly", "never"], { value: "weekly", "aria-label": "Change frequency" });
|
|
20
20
|
const priority = numberInput({ value: "0.8", min: "0", max: "1", step: "0.1", "aria-label": "Priority" });
|
|
21
21
|
const result = output({ rows: 14 });
|
|
@@ -43,15 +43,13 @@ export function mount(slot) {
|
|
|
43
43
|
` <lastmod>${today}</lastmod>`,
|
|
44
44
|
` <changefreq>${esc(freq.value)}</changefreq>`,
|
|
45
45
|
` <priority>${esc(priority.value || "0.5")}</priority>`,
|
|
46
|
-
" </url>"
|
|
47
|
-
].join("\n");
|
|
46
|
+
" </url>"].join("\n");
|
|
48
47
|
});
|
|
49
48
|
result.write([
|
|
50
49
|
'<?xml version="1.0" encoding="UTF-8"?>',
|
|
51
50
|
'<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">',
|
|
52
51
|
...entries,
|
|
53
|
-
"</urlset>"
|
|
54
|
-
].join("\n"));
|
|
52
|
+
"</urlset>"].join("\n"));
|
|
55
53
|
}
|
|
56
54
|
|
|
57
55
|
for (const input of [base, pages, priority]) input.addEventListener("input", build);
|
|
@@ -71,8 +69,7 @@ export function mount(slot) {
|
|
|
71
69
|
foot: actions(
|
|
72
70
|
downloadButton(() => result.textContent, "sitemap.xml", "Save XML", "application/xml"),
|
|
73
71
|
copyButton(() => result.textContent, "Copy XML", "fill")
|
|
74
|
-
)
|
|
75
|
-
});
|
|
72
|
+
)});
|
|
76
73
|
slot.append(card.node);
|
|
77
74
|
build();
|
|
78
75
|
|
|
@@ -14,15 +14,14 @@ function slugify(text, sep) {
|
|
|
14
14
|
|
|
15
15
|
export function mount(slot) {
|
|
16
16
|
const source = textInput({ value: "Hello mevebu world", placeholder: "Title", "aria-label": "Title" });
|
|
17
|
-
const result = textInput({ readonly: true,
|
|
17
|
+
const result = textInput({ readonly: true, mono: true, "aria-label": "Slug" });
|
|
18
18
|
|
|
19
19
|
let sep = "-";
|
|
20
20
|
const chips = chipRow(
|
|
21
21
|
[
|
|
22
22
|
{ value: "-", label: "Hyphen" },
|
|
23
23
|
{ value: "_", label: "Underscore" },
|
|
24
|
-
{ value: ".", label: "Dot" },
|
|
25
|
-
],
|
|
24
|
+
{ value: ".", label: "Dot" }],
|
|
26
25
|
{ value: "-", label: "Separator", onChange: (value) => { sep = value; run(); } }
|
|
27
26
|
);
|
|
28
27
|
|
|
@@ -37,8 +36,7 @@ export function mount(slot) {
|
|
|
37
36
|
stack(panel("Title", source), panel("Separator", chips)),
|
|
38
37
|
stack(panel("Slug", result))
|
|
39
38
|
),
|
|
40
|
-
foot: actions(copyButton(() => result.value, "Copy slug", "fill"))
|
|
41
|
-
});
|
|
39
|
+
foot: actions(copyButton(() => result.value, "Copy slug", "fill"))});
|
|
42
40
|
slot.append(card.node);
|
|
43
41
|
run();
|
|
44
42
|
|
|
@@ -20,19 +20,19 @@ function slugify(text) {
|
|
|
20
20
|
}
|
|
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 title = textInput({ value: "Best running shoes", "aria-label": "Title" });
|
|
27
27
|
const base = textInput({ value: "https://example.com", "aria-label": "Base URL" });
|
|
28
28
|
const error = notice("error");
|
|
29
29
|
|
|
30
|
-
const slugValue = el("span", { class: "
|
|
31
|
-
const urlValue = el("span", { class: "
|
|
32
|
-
const lengthValue = el("span", { class: "
|
|
30
|
+
const slugValue = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
|
|
31
|
+
const urlValue = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
|
|
32
|
+
const lengthValue = el("span", { class: "wrap-anywhere font-mono text-2xs" }, "-");
|
|
33
33
|
const kv = el(
|
|
34
34
|
"div",
|
|
35
|
-
{ class: "
|
|
35
|
+
{ class: "grid gap-px overflow-hidden rounded-r1 border border-orbit bg-orbit" },
|
|
36
36
|
kvRow("Slug", slugValue),
|
|
37
37
|
kvRow("URL", urlValue),
|
|
38
38
|
kvRow("Length", lengthValue)
|
|
@@ -72,8 +72,7 @@ export function mount(slot) {
|
|
|
72
72
|
foot: actions(
|
|
73
73
|
copyButton(() => urlValue.textContent === "-" ? "" : urlValue.textContent, "Copy URL"),
|
|
74
74
|
copyButton(() => slugValue.textContent === "-" ? "" : slugValue.textContent, "Copy slug", "fill")
|
|
75
|
-
)
|
|
76
|
-
});
|
|
75
|
+
)});
|
|
77
76
|
slot.append(card.node);
|
|
78
77
|
run();
|
|
79
78
|
|
|
@@ -3,7 +3,7 @@ import { bench, stack, panel, textArea, chipRow, toggleRow, copyButton, actions,
|
|
|
3
3
|
|
|
4
4
|
export function mount(slot) {
|
|
5
5
|
const source = textArea({ rows: 12, "aria-label": "Text", placeholder: "Paste your list, one entry per line" });
|
|
6
|
-
const result = textArea({ rows: 12, readonly: true,
|
|
6
|
+
const result = textArea({ rows: 12, readonly: true, "aria-label": "Result" });
|
|
7
7
|
|
|
8
8
|
let direction = "asc";
|
|
9
9
|
const dirChips = chipRow(
|
|
@@ -12,8 +12,7 @@ export function mount(slot) {
|
|
|
12
12
|
);
|
|
13
13
|
const options = toggleRow([
|
|
14
14
|
{ value: "ignore", label: "Ignore case", checked: false },
|
|
15
|
-
{ value: "numeric", label: "Numeric", checked: false }
|
|
16
|
-
]);
|
|
15
|
+
{ value: "numeric", label: "Numeric", checked: false }]);
|
|
17
16
|
|
|
18
17
|
function run() {
|
|
19
18
|
const text = source.value;
|
|
@@ -43,8 +42,7 @@ export function mount(slot) {
|
|
|
43
42
|
stack(panel("Direction", dirChips), options),
|
|
44
43
|
stack(panel("Result", result))
|
|
45
44
|
),
|
|
46
|
-
foot: actions(copyButton(() => result.value, "Copy result", "fill"))
|
|
47
|
-
});
|
|
45
|
+
foot: actions(copyButton(() => result.value, "Copy result", "fill"))});
|
|
48
46
|
slot.append(card.node);
|
|
49
47
|
run();
|
|
50
48
|
|
|
@@ -6,8 +6,7 @@ const UNITS = [
|
|
|
6
6
|
{ value: "kmh", label: "Kilometer per hour", factor: 0.2777777777777778 },
|
|
7
7
|
{ value: "mph", label: "Mile per hour", factor: 0.44704 },
|
|
8
8
|
{ value: "fps", label: "Foot per second", factor: 0.3048 },
|
|
9
|
-
{ value: "kn", label: "Knot", factor: 0.5144444444444445 }
|
|
10
|
-
];
|
|
9
|
+
{ value: "kn", label: "Knot", factor: 0.5144444444444445 }];
|
|
11
10
|
|
|
12
11
|
const byId = Object.fromEntries(UNITS.map((unit) => [unit.value, unit]));
|
|
13
12
|
const options = UNITS.map(({ value, label }) => ({ value, label }));
|
|
@@ -24,11 +23,11 @@ export function mount(slot) {
|
|
|
24
23
|
const value = numberInput({ value: "1", step: "any", "aria-label": "Value" });
|
|
25
24
|
const from = selectInput(options, { value: "kmh", "aria-label": "From unit" });
|
|
26
25
|
const to = selectInput(options, { value: "mph", "aria-label": "To unit" });
|
|
27
|
-
const result = el("span", { class: "
|
|
28
|
-
const stats = el("div", { class: "
|
|
29
|
-
const factor = el("p", { class: "
|
|
26
|
+
const result = el("span", { class: "wrap-anywhere text-xl font-bold tracking-[-0.02em] tabular-nums" }, "-");
|
|
27
|
+
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));
|
|
28
|
+
const factor = el("p", { class: "border-s-[3px] border-transparent ps-2 text-sm text-ink-3" });
|
|
30
29
|
const error = notice("error");
|
|
31
|
-
const scroll = el("div", { class: "
|
|
30
|
+
const scroll = el("div", { class: "max-h-80 overflow-auto" });
|
|
32
31
|
let text = "";
|
|
33
32
|
|
|
34
33
|
function run() {
|
|
@@ -48,7 +47,7 @@ export function mount(slot) {
|
|
|
48
47
|
const out = (n * a.factor) / b.factor;
|
|
49
48
|
result.textContent = `${fmt(out)} ${b.value}`;
|
|
50
49
|
factor.textContent = `1 ${a.value} = ${fmt(a.factor / b.factor)} ${b.value}`;
|
|
51
|
-
scroll.replaceChildren(el("table", { class: "
|
|
50
|
+
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" },
|
|
52
51
|
el("thead", {}, el("tr", {}, el("th", {}, "Unit"), el("th", {}, "Value"))),
|
|
53
52
|
el("tbody", {}, ...UNITS.map((unit) => el("tr", {},
|
|
54
53
|
el("td", {}, unit.label),
|
|
@@ -72,8 +71,7 @@ export function mount(slot) {
|
|
|
72
71
|
stack(panel("Value", value), panel("From", from), panel("To", to), error),
|
|
73
72
|
stack(panel("Result", stats), factor, panel("All units", scroll))
|
|
74
73
|
),
|
|
75
|
-
foot: actions(swap, copyButton(() => text, "Copy result", "fill"))
|
|
76
|
-
});
|
|
74
|
+
foot: actions(swap, copyButton(() => text, "Copy result", "fill"))});
|
|
77
75
|
slot.append(card.node);
|
|
78
76
|
run();
|
|
79
77
|
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
// spin, driven by the browser random source, so every option has equal odds.
|
|
3
3
|
import { el, toast, copyButton, randomInt, bench, stage, stack, panel, textArea, button, readPin, writePin, toolcard, actions } from "../core.js";
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
// The slice palette. A green ramp that alternates dark and light, so two
|
|
6
|
+
// neighbouring slices never read as one wedge and every label stays legible.
|
|
7
|
+
const SEGMENTS = ["#0f6b4a", "#e0f0e7", "#0a5138", "#bfcac1", "#35805f", "#1d3b2c", "#8fd0b3", "#0b2a1e"];
|
|
6
8
|
|
|
7
9
|
/**
|
|
8
10
|
* The slice label, in whichever ink reads better on that fill. Every fill in the
|
|
@@ -12,7 +14,7 @@ const SEGMENTS = ["#2456D6", "#151D2E", "#5F8BFF", "#4B5871", "#1A43AD", "#69748
|
|
|
12
14
|
function textOn(hex) {
|
|
13
15
|
const linear = (value) => (value <= 0.03928 ? value / 12.92 : ((value + 0.055) / 1.055) ** 2.4);
|
|
14
16
|
const [r, g, b] = [1, 3, 5].map((at) => linear(parseInt(hex.slice(at, at + 2), 16) / 255));
|
|
15
|
-
return 0.2126 * r + 0.7152 * g + 0.0722 * b > 0.2056 ? "#
|
|
17
|
+
return 0.2126 * r + 0.7152 * g + 0.0722 * b > 0.2056 ? "#101a15" : "#FFFFFF";
|
|
16
18
|
}
|
|
17
19
|
|
|
18
20
|
function entriesFrom(text) {
|
|
@@ -24,15 +26,15 @@ export function mount(slot) {
|
|
|
24
26
|
const saved = readPin(PIN, { entries: "Ana\nBudi\nCitra\nDimas\nElena\nFajar" });
|
|
25
27
|
|
|
26
28
|
const listField = textArea({ value: saved.entries ?? "", "aria-label": "Options, one per line", placeholder: "One option per line" });
|
|
27
|
-
const canvas = el("canvas", { class: "
|
|
29
|
+
const canvas = el("canvas", { class: "h-auto w-[min(320px,100%)]", width: 360, height: 360, role: "img", "aria-label": "The wheel" });
|
|
28
30
|
const context = canvas.getContext("2d");
|
|
29
|
-
const historyBox = el("div", { class: "
|
|
31
|
+
const historyBox = el("div", { class: "flex max-w-[340px] flex-wrap justify-center gap-1.5", role: "list" });
|
|
30
32
|
|
|
31
33
|
const spinButton = button("Spin", { icon: "rotate-cw", kind: "fill" });
|
|
32
34
|
const clearButton = button("Clear past picks", { icon: "trash", kind: "quiet" });
|
|
33
35
|
const copyWinner = copyButton(() => winner, "Copy winner");
|
|
34
36
|
|
|
35
|
-
const status = el("div", { class: "
|
|
37
|
+
const status = el("div", { class: "text-balance text-xl font-bold tracking-[-0.01em]", "aria-live": "polite" }, "Add options, then spin.");
|
|
36
38
|
|
|
37
39
|
let rotation = 0;
|
|
38
40
|
let busy = false;
|
|
@@ -45,15 +47,17 @@ export function mount(slot) {
|
|
|
45
47
|
const center = size / 2;
|
|
46
48
|
const radius = center - 12;
|
|
47
49
|
const root = getComputedStyle(document.documentElement);
|
|
48
|
-
const ground = root.getPropertyValue("--
|
|
49
|
-
const orbit = root.getPropertyValue("--
|
|
50
|
-
const faint = root.getPropertyValue("--
|
|
51
|
-
const
|
|
50
|
+
const ground = root.getPropertyValue("--void").trim() || "#fff";
|
|
51
|
+
const orbit = root.getPropertyValue("--orbit-hard").trim() || "#888";
|
|
52
|
+
const faint = root.getPropertyValue("--ink-3").trim() || "#888";
|
|
53
|
+
const jade = root.getPropertyValue("--jade").trim() || "#0f6b4a";
|
|
54
|
+
// Canvas text is outside the stylesheet, so it reads the same stack the page uses.
|
|
55
|
+
const stack = root.getPropertyValue("--sans").trim() || "system-ui, sans-serif";
|
|
52
56
|
|
|
53
57
|
context.clearRect(0, 0, size, size);
|
|
54
58
|
if (entries.length < 2) {
|
|
55
59
|
context.fillStyle = faint;
|
|
56
|
-
context.font =
|
|
60
|
+
context.font = `15px ${stack}`;
|
|
57
61
|
context.textAlign = "center";
|
|
58
62
|
context.fillText(entries.length ? "Add one more option to spin" : "Type options to spin", center, center);
|
|
59
63
|
status.textContent = entries.length ? "One more option to go." : "Add options, then spin.";
|
|
@@ -73,7 +77,7 @@ export function mount(slot) {
|
|
|
73
77
|
context.rotate(a0 + slice / 2);
|
|
74
78
|
context.textAlign = "right";
|
|
75
79
|
context.fillStyle = textOn(SEGMENTS[i % SEGMENTS.length]);
|
|
76
|
-
context.font =
|
|
80
|
+
context.font = `600 13px ${stack}`;
|
|
77
81
|
const label = entry.length > 13 ? `${entry.slice(0, 12)}…` : entry;
|
|
78
82
|
context.fillText(label, radius - 12, 4);
|
|
79
83
|
context.restore();
|
|
@@ -90,7 +94,7 @@ export function mount(slot) {
|
|
|
90
94
|
context.lineTo(center + 8, 2);
|
|
91
95
|
context.lineTo(center, 20);
|
|
92
96
|
context.closePath();
|
|
93
|
-
context.fillStyle =
|
|
97
|
+
context.fillStyle = jade;
|
|
94
98
|
context.fill();
|
|
95
99
|
if (status.dataset.winner !== "1") status.textContent = "Add options, then spin.";
|
|
96
100
|
}
|
|
@@ -123,7 +127,7 @@ export function mount(slot) {
|
|
|
123
127
|
status.dataset.winner = "1";
|
|
124
128
|
winner = entries[winnerIndex];
|
|
125
129
|
status.textContent = winner;
|
|
126
|
-
const chip = el("span", { class: "
|
|
130
|
+
const chip = el("span", { class: "rounded-full border border-orbit bg-deck-2 px-2.5 py-1 text-2xs font-semibold", role: "listitem" }, winner);
|
|
127
131
|
historyBox.prepend(chip);
|
|
128
132
|
while (historyBox.children.length > 12) historyBox.lastChild.remove();
|
|
129
133
|
}
|
|
@@ -150,8 +154,7 @@ export function mount(slot) {
|
|
|
150
154
|
stack(panel("Options, one per line", listField)),
|
|
151
155
|
stage(canvas, status, historyBox)
|
|
152
156
|
),
|
|
153
|
-
foot: actions(copyWinner, clearButton, spinButton)
|
|
154
|
-
});
|
|
157
|
+
foot: actions(copyWinner, clearButton, spinButton)});
|
|
155
158
|
slot.append(card.node);
|
|
156
159
|
draw();
|
|
157
160
|
|
|
@@ -159,8 +162,7 @@ export function mount(slot) {
|
|
|
159
162
|
unmount() {
|
|
160
163
|
cancelAnimationFrame(frame);
|
|
161
164
|
busy = false;
|
|
162
|
-
}
|
|
163
|
-
};
|
|
165
|
+
}};
|
|
164
166
|
}
|
|
165
167
|
|
|
166
168
|
export default { mount };
|
|
@@ -4,8 +4,7 @@ import { bench, stack, panel, textArea, notice, switchControl, copyButton, actio
|
|
|
4
4
|
const CLAUSES = [
|
|
5
5
|
"SELECT", "FROM", "WHERE", "GROUP BY", "ORDER BY", "HAVING", "LIMIT", "OFFSET", "VALUES", "SET",
|
|
6
6
|
"INSERT INTO", "UPDATE", "DELETE FROM", "INNER JOIN", "LEFT JOIN", "RIGHT JOIN", "FULL JOIN",
|
|
7
|
-
"CROSS JOIN", "JOIN", "UNION ALL", "UNION", "CREATE TABLE", "ALTER TABLE", "DROP TABLE", "WITH", "RETURNING"
|
|
8
|
-
];
|
|
7
|
+
"CROSS JOIN", "JOIN", "UNION ALL", "UNION", "CREATE TABLE", "ALTER TABLE", "DROP TABLE", "WITH", "RETURNING"];
|
|
9
8
|
const WORDS = [...CLAUSES, "AND", "OR", "ON", "AS", "IN", "IS", "NOT", "NULL", "LIKE", "BETWEEN", "DISTINCT", "ASC", "DESC", "CASE", "WHEN", "THEN", "ELSE", "END"];
|
|
10
9
|
|
|
11
10
|
const toPattern = (word) => word.replace(/ /g, "\\s+");
|
|
@@ -33,18 +32,16 @@ function format(sql, upper) {
|
|
|
33
32
|
out = out.replace(clauseRe, "\n$1");
|
|
34
33
|
out = out.replace(/\s*\b(AND|OR)\b/gi, "\n $1");
|
|
35
34
|
out = out.replace(/\(\s+/g, "(").replace(/\s+\)/g, ")");
|
|
36
|
-
out = out.replace(/\n{3
|
|
35
|
+
out = out.replace(/\n{3}/g, "\n\n").trim();
|
|
37
36
|
return restore(out, store);
|
|
38
37
|
}
|
|
39
38
|
|
|
40
39
|
export function mount(slot) {
|
|
41
40
|
const source = textArea({
|
|
42
41
|
rows: 12,
|
|
43
|
-
class: "mvb-in mvb-in--area mvb-in--mono",
|
|
44
42
|
"aria-label": "SQL",
|
|
45
|
-
value: "select id, name from users where active = true and age > 18 order by name"
|
|
46
|
-
});
|
|
47
|
-
const result = textArea({ rows: 12, readonly: true, class: "mvb-in mvb-in--area mvb-in--mono", "aria-label": "Formatted SQL" });
|
|
43
|
+
value: "select id, name from users where active = true and age > 18 order by name"});
|
|
44
|
+
const result = textArea({ rows: 12, readonly: true, "aria-label": "Formatted SQL" });
|
|
48
45
|
const error = notice("error");
|
|
49
46
|
let upper = true;
|
|
50
47
|
|
|
@@ -67,8 +64,7 @@ export function mount(slot) {
|
|
|
67
64
|
stack(panel("Options", casing), panel("SQL", source), error),
|
|
68
65
|
stack(panel("Formatted", result))
|
|
69
66
|
),
|
|
70
|
-
foot: actions(copyButton(() => result.value, "Copy query", "fill"))
|
|
71
|
-
});
|
|
67
|
+
foot: actions(copyButton(() => result.value, "Copy query", "fill"))});
|
|
72
68
|
slot.append(card.node);
|
|
73
69
|
run();
|
|
74
70
|
|
|
@@ -4,7 +4,7 @@ import { el, bench, stack, panel, textArea, switchControl, copyButton, actions,
|
|
|
4
4
|
const bytes = (text) => new TextEncoder().encode(text).length;
|
|
5
5
|
|
|
6
6
|
const tile = (key, value) =>
|
|
7
|
-
el("div", { class: "
|
|
7
|
+
el("div", { class: "grid gap-0.5 rounded-r1 border border-orbit bg-deck-2 px-3.5 py-3" }, el("span", { class: "wrap-anywhere text-xl font-bold tracking-[-0.02em] tabular-nums" }, value), el("span", { class: "text-2xs font-semibold text-ink-3" }, key));
|
|
8
8
|
|
|
9
9
|
function minify(sql, stripComments) {
|
|
10
10
|
let out = "";
|
|
@@ -59,12 +59,10 @@ function minify(sql, stripComments) {
|
|
|
59
59
|
export function mount(slot) {
|
|
60
60
|
const source = textArea({
|
|
61
61
|
rows: 12,
|
|
62
|
-
class: "mvb-in mvb-in--area mvb-in--mono",
|
|
63
62
|
"aria-label": "SQL",
|
|
64
|
-
value: "SELECT id,\n name\n FROM users\n WHERE active = true\n AND age > 18;"
|
|
65
|
-
});
|
|
66
|
-
const
|
|
67
|
-
const stats = el("div", { class: "mvb-stats" });
|
|
63
|
+
value: "SELECT id,\n name\n FROM users\n WHERE active = true\n AND age > 18;"});
|
|
64
|
+
const result = textArea({ rows: 12, readonly: true, "aria-label": "Minified SQL" });
|
|
65
|
+
const stats = el("div", { class: "grid gap-2.5 [grid-template-columns:repeat(auto-fit,minmax(120px,1fr))]" });
|
|
68
66
|
let stripComments = true;
|
|
69
67
|
|
|
70
68
|
const comments = switchControl("Remove comments", { checked: true });
|
|
@@ -91,8 +89,7 @@ export function mount(slot) {
|
|
|
91
89
|
stack(panel("Options", comments), panel("SQL", source)),
|
|
92
90
|
stack(panel("Minified", result), stats)
|
|
93
91
|
),
|
|
94
|
-
foot: actions(copyButton(() => result.value, "Copy query", "fill"))
|
|
95
|
-
});
|
|
92
|
+
foot: actions(copyButton(() => result.value, "Copy query", "fill"))});
|
|
96
93
|
slot.append(card.node);
|
|
97
94
|
run();
|
|
98
95
|
|
|
@@ -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 variance = tile("Variance");
|
|
25
25
|
const deviation = tile("Std deviation");
|
|
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))]" }, mean, variance, deviation, count);
|
|
28
28
|
const error = notice("error");
|
|
29
29
|
let kind = "population";
|
|
30
30
|
let text = "";
|
|
@@ -66,16 +66,14 @@ export function mount(slot) {
|
|
|
66
66
|
const chips = chipRow(
|
|
67
67
|
[
|
|
68
68
|
{ value: "population", label: "Population" },
|
|
69
|
-
{ value: "sample", label: "Sample" },
|
|
70
|
-
],
|
|
69
|
+
{ value: "sample", label: "Sample" }],
|
|
71
70
|
{
|
|
72
71
|
value: kind,
|
|
73
72
|
label: "Data set",
|
|
74
73
|
onChange: (value) => {
|
|
75
74
|
kind = value;
|
|
76
75
|
run();
|
|
77
|
-
}
|
|
78
|
-
}
|
|
76
|
+
}}
|
|
79
77
|
);
|
|
80
78
|
|
|
81
79
|
source.addEventListener("input", run);
|
|
@@ -85,8 +83,7 @@ export function mount(slot) {
|
|
|
85
83
|
stack(panel("Numbers", source), panel("Data set", chips), error),
|
|
86
84
|
stack(stats)
|
|
87
85
|
),
|
|
88
|
-
foot: actions(copyButton(() => text, "Copy summary", "fill"))
|
|
89
|
-
});
|
|
86
|
+
foot: actions(copyButton(() => text, "Copy summary", "fill"))});
|
|
90
87
|
slot.append(card.node);
|
|
91
88
|
run();
|
|
92
89
|
|
|
@@ -15,7 +15,7 @@ function format(ms) {
|
|
|
15
15
|
|
|
16
16
|
export function mount(slot) {
|
|
17
17
|
const display = output({ rows: 2 });
|
|
18
|
-
const listWrap = el("div", { class: "
|
|
18
|
+
const listWrap = el("div", { class: "max-h-80 overflow-auto" });
|
|
19
19
|
|
|
20
20
|
const startButton = button("Start", { icon: "play", kind: "fill" });
|
|
21
21
|
const lapButton = button("Lap", { icon: "plus" });
|
|
@@ -59,7 +59,7 @@ export function mount(slot) {
|
|
|
59
59
|
el("td", {}, format(total))
|
|
60
60
|
);
|
|
61
61
|
});
|
|
62
|
-
listWrap.replaceChildren(el("table", { class: "
|
|
62
|
+
listWrap.replaceChildren(el("table", { class: "w-full border-collapse font-mono text-sm [&_th]:wrap-anywhere [&_td]:wrap-anywhere [&_th]:border-b [&_td]:border-b [&_th]:border-orbit [&_td]:border-orbit [&_th]:px-2.5 [&_td]:px-2.5 [&_th]:py-2 [&_td]:py-2 [&_th]:text-start [&_td]:text-start [&_th]:font-sans [&_th]:text-2xs [&_th]:font-semibold [&_th]:text-ink-2 [&_tbody>tr:last-child>th]:border-b-0 [&_tbody>tr:last-child>td]:border-b-0" },
|
|
63
63
|
el("thead", {}, el("tr", {}, el("th", {}, "#"), el("th", {}, "Lap"), el("th", {}, "Total"))),
|
|
64
64
|
el("tbody", {}, ...rows)
|
|
65
65
|
));
|
|
@@ -115,8 +115,7 @@ export function mount(slot) {
|
|
|
115
115
|
stack(panel("Laps", listWrap)),
|
|
116
116
|
stack(display)
|
|
117
117
|
),
|
|
118
|
-
foot: actions(copyLaps, resetButton, lapButton, startButton)
|
|
119
|
-
});
|
|
118
|
+
foot: actions(copyLaps, resetButton, lapButton, startButton)});
|
|
120
119
|
slot.append(card.node);
|
|
121
120
|
paint();
|
|
122
121
|
renderLaps();
|
|
@@ -126,8 +125,7 @@ export function mount(slot) {
|
|
|
126
125
|
unmount() {
|
|
127
126
|
cancelAnimationFrame(frame);
|
|
128
127
|
running = false;
|
|
129
|
-
}
|
|
130
|
-
};
|
|
128
|
+
}};
|
|
131
129
|
}
|
|
132
130
|
|
|
133
131
|
export default { mount };
|
|
@@ -9,16 +9,16 @@ export function mount(slot) {
|
|
|
9
9
|
let list = Array.isArray(saved) ? saved : [];
|
|
10
10
|
|
|
11
11
|
const name = textInput({ placeholder: "Topic", "aria-label": "Topic" });
|
|
12
|
-
const date = el("input", { class: "
|
|
13
|
-
const listWrap = el("div", { class: "
|
|
14
|
-
const stats = el("div", { class: "
|
|
12
|
+
const date = el("input", { class: "min-w-0 rounded-r1 border border-orbit bg-deck text-ink transition-[border-color,box-shadow] duration-150 placeholder:text-ink-3 hover:border-orbit-hard focus:border-jade focus:outline-none focus:ring-3 focus:ring-jade-soft w-full min-h-[var(--ctl)] px-3 py-2.5 text-base", type: "date", "aria-label": "Session date" });
|
|
13
|
+
const listWrap = el("div", { class: "max-h-80 overflow-auto" });
|
|
14
|
+
const stats = el("div", { class: "grid gap-2.5 [grid-template-columns:repeat(auto-fit,minmax(120px,1fr))]" });
|
|
15
15
|
|
|
16
16
|
const addButton = button("Add", { icon: "plus", kind: "fill" });
|
|
17
17
|
|
|
18
18
|
const save = () => writePin(PIN, list);
|
|
19
|
-
const tile = (key, value) => el("div", { class: "
|
|
20
|
-
el("span", { class: "
|
|
21
|
-
el("span", { class: "
|
|
19
|
+
const tile = (key, value) => el("div", { class: "grid gap-0.5 rounded-r1 border border-orbit bg-deck-2 px-3.5 py-3" },
|
|
20
|
+
el("span", { class: "wrap-anywhere text-xl font-bold tracking-[-0.02em] tabular-nums" }, String(value)),
|
|
21
|
+
el("span", { class: "text-2xs font-semibold text-ink-3" }, key));
|
|
22
22
|
|
|
23
23
|
const sorted = () => list.slice().sort((a, b) => (a.date || "9999").localeCompare(b.date || "9999"));
|
|
24
24
|
|
|
@@ -50,7 +50,7 @@ export function mount(slot) {
|
|
|
50
50
|
if (!rows.length) {
|
|
51
51
|
listWrap.replaceChildren(blank("calendar-days", "No topics yet."));
|
|
52
52
|
} else {
|
|
53
|
-
listWrap.replaceChildren(el("table", { class: "
|
|
53
|
+
listWrap.replaceChildren(el("table", { class: "w-full border-collapse font-mono text-sm [&_th]:wrap-anywhere [&_td]:wrap-anywhere [&_th]:border-b [&_td]:border-b [&_th]:border-orbit [&_td]:border-orbit [&_th]:px-2.5 [&_td]:px-2.5 [&_th]:py-2 [&_td]:py-2 [&_th]:text-start [&_td]:text-start [&_th]:font-sans [&_th]:text-2xs [&_th]:font-semibold [&_th]:text-ink-2 [&_tbody>tr:last-child>th]:border-b-0 [&_tbody>tr:last-child>td]:border-b-0" },
|
|
54
54
|
el("thead", {}, el("tr", {}, el("th", {}, "Topic"), el("th", {}, "Date"), el("th", {}, ""))),
|
|
55
55
|
el("tbody", {}, ...rows.map(rowNode))));
|
|
56
56
|
}
|
|
@@ -77,8 +77,7 @@ export function mount(slot) {
|
|
|
77
77
|
stack(panel("Topic", name), panel("Session date", date)),
|
|
78
78
|
stack(stats, panel("Plan", listWrap))
|
|
79
79
|
),
|
|
80
|
-
foot: actions(copyButton(() => sorted().map((item) => `${item.date || "No date"} ${item.done ? "[x]" : "[ ]"} ${item.name}`).join("\n"), "Copy plan"), addButton)
|
|
81
|
-
});
|
|
80
|
+
foot: actions(copyButton(() => sorted().map((item) => `${item.date || "No date"} ${item.done ? "[x]" : "[ ]"} ${item.name}`).join("\n"), "Copy plan"), addButton)});
|
|
82
81
|
slot.append(card.node);
|
|
83
82
|
render();
|
|
84
83
|
|
|
@@ -5,8 +5,8 @@ const isTiming = (line) => line.includes("-->");
|
|
|
5
5
|
const isIndex = (line) => /^\d+$/.test(line.trim());
|
|
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
|
}
|
|
@@ -18,9 +18,9 @@ export function mount(slot) {
|
|
|
18
18
|
const linesTile = tile("Lines");
|
|
19
19
|
const longTile = tile("Long lines");
|
|
20
20
|
const longestTile = tile("Longest");
|
|
21
|
-
const stats = el("div", { class: "
|
|
21
|
+
const stats = el("div", { class: "grid gap-2.5 [grid-template-columns:repeat(auto-fit,minmax(120px,1fr))]" }, linesTile, longTile, longestTile);
|
|
22
22
|
|
|
23
|
-
const longWrap = el("div", { class: "
|
|
23
|
+
const longWrap = el("div", { class: "max-h-80 overflow-auto" });
|
|
24
24
|
const out = output({ rows: 10 });
|
|
25
25
|
const note = notice("note");
|
|
26
26
|
|
|
@@ -41,16 +41,16 @@ export function mount(slot) {
|
|
|
41
41
|
if (!long.length) {
|
|
42
42
|
longWrap.replaceChildren(blank("film", "No line runs past the limit."));
|
|
43
43
|
} else {
|
|
44
|
-
longWrap.replaceChildren(el("div", { class: "
|
|
45
|
-
...long.map((line) => el("div", { class: "
|
|
46
|
-
el("span", { class: "
|
|
47
|
-
el("span", { class: "
|
|
44
|
+
longWrap.replaceChildren(el("div", { class: "grid gap-px overflow-hidden rounded-r1 border border-orbit bg-orbit" },
|
|
45
|
+
...long.map((line) => el("div", { class: "grid gap-3 bg-deck px-3 py-[9px] [grid-template-columns:minmax(90px,34%)_minmax(0,1fr)]" },
|
|
46
|
+
el("span", { class: "wrap-anywhere text-2xs font-semibold text-ink-2" }, `${line.length}`),
|
|
47
|
+
el("span", { class: "wrap-anywhere font-mono text-2xs" }, line)))));
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
tidied = raw
|
|
51
51
|
.map((line) => line.replace(/\s+$/, ""))
|
|
52
52
|
.join("\n")
|
|
53
|
-
.replace(/\n{3
|
|
53
|
+
.replace(/\n{3}/g, "\n\n")
|
|
54
54
|
.replace(/^\n+|\n+$/g, "");
|
|
55
55
|
out.write(tidied);
|
|
56
56
|
}
|
|
@@ -63,8 +63,7 @@ export function mount(slot) {
|
|
|
63
63
|
stack(panel("Subtitles", source), panel("Maximum line length", max), note),
|
|
64
64
|
stack(stats, panel("Long lines", longWrap), panel("Tidied", out))
|
|
65
65
|
),
|
|
66
|
-
foot: actions(copyButton(() => tidied, "Copy text", "fill"))
|
|
67
|
-
});
|
|
66
|
+
foot: actions(copyButton(() => tidied, "Copy text", "fill"))});
|
|
68
67
|
slot.append(card.node);
|
|
69
68
|
run();
|
|
70
69
|
|