mevebu 0.1.0 → 0.2.1
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/README.md +2 -14
- package/mevebu/app/mevebu.css +45 -0
- package/mevebu/app/mevebu.js +85 -0
- package/mevebu/index.html +1 -1
- package/mevebu/libs/css/tools.css +0 -20
- package/mevebu/libs/js/core.js +48 -0
- package/mevebu/libs/js/icons.js +2 -3
- package/mevebu/libs/js/registry.js +0 -18
- package/package.json +1 -1
- package/mevebu/libs/js/tools/dice-roller.js +0 -59
package/README.md
CHANGED
|
@@ -31,11 +31,11 @@ The tools open in your default browser. Press `Ctrl+C` in the terminal to stop.
|
|
|
31
31
|
|
|
32
32
|
## Tools
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
15 tools, each with its own page. Most update as you type, with no submit button.
|
|
35
35
|
|
|
36
36
|
| Category | Tools |
|
|
37
37
|
| :--- | :--- |
|
|
38
|
-
| Random | Spin the Wheel, Name Picker, Coin Flip
|
|
38
|
+
| Random | Spin the Wheel, Name Picker, Coin Flip |
|
|
39
39
|
| Passwords and keys | Password Generator, JWT Generator, Hash Generator |
|
|
40
40
|
| Developer | UUID Generator, Color Converter |
|
|
41
41
|
| Convert | Base64, URL Encoder, Case Converter |
|
|
@@ -47,18 +47,6 @@ The tools open in your default browser. Press `Ctrl+C` in the terminal to stop.
|
|
|
47
47
|
```sh
|
|
48
48
|
npm run dev # serve on http://localhost:5173
|
|
49
49
|
npm run icons # regenerate vendored lucide icons
|
|
50
|
-
npm run ship # bump version, commit, tag, push, and publish to npm
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
## Release
|
|
54
|
-
|
|
55
|
-
`npm run ship` walks through a release: it asks for an update message, lets you pick a patch,
|
|
56
|
-
minor, or major bump, then commits, tags, pushes, and publishes to npm.
|
|
57
|
-
|
|
58
|
-
```sh
|
|
59
|
-
npm run ship # interactive
|
|
60
|
-
npm run ship -- "fix qr size" minor
|
|
61
|
-
npm run ship -- "release" patch -y # non-interactive
|
|
62
50
|
```
|
|
63
51
|
|
|
64
52
|
## License
|
package/mevebu/app/mevebu.css
CHANGED
|
@@ -962,6 +962,28 @@ textarea.mvb-out { resize: vertical; }
|
|
|
962
962
|
.mvb-modal__body p { margin-top: 8px; color: var(--mvb-ink-2); font-size: 15.5px; text-wrap: pretty; }
|
|
963
963
|
.mvb-modal__actions { margin-top: 20px; display: grid; gap: 8px; }
|
|
964
964
|
|
|
965
|
+
/* version readout inside the update modal */
|
|
966
|
+
.mvb-vers { margin-top: 18px; border: 1px solid var(--mvb-orbit); border-radius: var(--mvb-r2); overflow: hidden; }
|
|
967
|
+
.mvb-vers__row { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 12px 16px; }
|
|
968
|
+
.mvb-vers__row + .mvb-vers__row { border-top: 1px solid var(--mvb-orbit); }
|
|
969
|
+
.mvb-vers__label { font-size: 14px; color: var(--mvb-ink-3); }
|
|
970
|
+
.mvb-vers__val { font-family: var(--mvb-mono); font-size: 15px; font-weight: 600; }
|
|
971
|
+
.mvb-vers__row--new .mvb-vers__val { color: var(--mvb-comet); }
|
|
972
|
+
.mvb-vers__hint { margin-top: 14px !important; font-size: 14px !important; }
|
|
973
|
+
|
|
974
|
+
.mvb-cmd {
|
|
975
|
+
margin-top: 10px;
|
|
976
|
+
display: flex;
|
|
977
|
+
align-items: center;
|
|
978
|
+
gap: 8px;
|
|
979
|
+
flex-wrap: wrap;
|
|
980
|
+
background: var(--mvb-deck-2);
|
|
981
|
+
border: 1px solid var(--mvb-orbit);
|
|
982
|
+
border-radius: var(--mvb-r2);
|
|
983
|
+
padding: 10px 10px 10px 16px;
|
|
984
|
+
}
|
|
985
|
+
.mvb-cmd__text { flex: 1 1 auto; min-width: 0; font-family: var(--mvb-mono); font-size: 14px; overflow-wrap: anywhere; }
|
|
986
|
+
|
|
965
987
|
/* ---------- command palette ---------- */
|
|
966
988
|
|
|
967
989
|
.mvb-veil--pal { padding: clamp(60px, 12vh, 140px) 20px 20px; }
|
|
@@ -1105,6 +1127,29 @@ textarea.mvb-out { resize: vertical; }
|
|
|
1105
1127
|
.mvb-foot__count { font-variant-numeric: tabular-nums; }
|
|
1106
1128
|
.mvb-foot__count:empty { display: none; }
|
|
1107
1129
|
|
|
1130
|
+
/* version chip in the footer; turns into an update button when npm has a newer one */
|
|
1131
|
+
.mvb-version {
|
|
1132
|
+
display: inline-flex;
|
|
1133
|
+
align-items: center;
|
|
1134
|
+
gap: 6px;
|
|
1135
|
+
padding: 3px 10px;
|
|
1136
|
+
min-height: 28px;
|
|
1137
|
+
font: inherit;
|
|
1138
|
+
font-size: 13px;
|
|
1139
|
+
font-variant-numeric: tabular-nums;
|
|
1140
|
+
color: var(--mvb-ink-3);
|
|
1141
|
+
background: transparent;
|
|
1142
|
+
border: 1px solid var(--mvb-orbit);
|
|
1143
|
+
border-radius: 999px;
|
|
1144
|
+
cursor: pointer;
|
|
1145
|
+
transition: color var(--mvb-t) var(--mvb-ease), border-color var(--mvb-t) var(--mvb-ease), background var(--mvb-t) var(--mvb-ease);
|
|
1146
|
+
}
|
|
1147
|
+
.mvb-version:hover { color: var(--mvb-ink); border-color: var(--mvb-orbit-hard); }
|
|
1148
|
+
.mvb-version:focus-visible { outline: 2px solid var(--mvb-comet); outline-offset: 2px; }
|
|
1149
|
+
.mvb-version--new { color: var(--mvb-comet); border-color: var(--mvb-comet); background: var(--mvb-nebula); font-weight: 600; }
|
|
1150
|
+
.mvb-version--new:hover { color: var(--mvb-on-comet); background: var(--mvb-comet); }
|
|
1151
|
+
.mvb-version .mvb-ico { width: 15px; height: 15px; }
|
|
1152
|
+
|
|
1108
1153
|
@media (max-width: 720px) {
|
|
1109
1154
|
.mvb-foot__top { grid-template-columns: 1fr; }
|
|
1110
1155
|
.mvb-foot__nav { grid-template-columns: repeat(2, 1fr); }
|
package/mevebu/app/mevebu.js
CHANGED
|
@@ -6,6 +6,7 @@ import { route, start, match, currentPath, go } from "../libs/js/router.js";
|
|
|
6
6
|
import {
|
|
7
7
|
$, $$, el, icon, getFavorites, isFavorite, toggleFavorite, setTheme,
|
|
8
8
|
modal, button, copyButton, openPalette, shuffle, toolcard,
|
|
9
|
+
VERSION, PACKAGE_NAME, checkForUpdate, updateCommand, compareVersions,
|
|
9
10
|
} from "../libs/js/core.js";
|
|
10
11
|
|
|
11
12
|
const DONATE_URL = "https://buymeacoffee.com/fastro";
|
|
@@ -137,6 +138,79 @@ function openDonate() {
|
|
|
137
138
|
});
|
|
138
139
|
}
|
|
139
140
|
|
|
141
|
+
// `latest` is the published version, or null when the check could not run.
|
|
142
|
+
function openUpdate(latest) {
|
|
143
|
+
const updatable = latest && compareVersions(latest, VERSION) > 0;
|
|
144
|
+
const command = updateCommand();
|
|
145
|
+
|
|
146
|
+
const commandBox = el(
|
|
147
|
+
"div",
|
|
148
|
+
{ class: "mvb-cmd" },
|
|
149
|
+
el("code", { class: "mvb-cmd__text" }, command),
|
|
150
|
+
copyButton(() => command, "Copy command", "fill")
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
modal({
|
|
154
|
+
label: "Update mevebu",
|
|
155
|
+
body: [
|
|
156
|
+
el("div", { class: "mvb-modal__art" }, el("img", { src: "./icon.png", alt: "", width: "72", height: "72" })),
|
|
157
|
+
el("h2", { class: "mvb-modal__title" }, updatable ? `mevebu ${latest} is out` : "mevebu is up to date"),
|
|
158
|
+
updatable
|
|
159
|
+
? el("p", {}, "A newer version is published. Update your global install, then run ", el("code", {}, PACKAGE_NAME), " again.")
|
|
160
|
+
: el("p", {}, "You are on the latest version. Nothing to do."),
|
|
161
|
+
el("div", { class: "mvb-vers" },
|
|
162
|
+
el("div", { class: "mvb-vers__row" },
|
|
163
|
+
el("span", { class: "mvb-vers__label" }, "Installed"),
|
|
164
|
+
el("span", { class: "mvb-vers__val" }, VERSION)),
|
|
165
|
+
el("div", { class: "mvb-vers__row" + (updatable ? " mvb-vers__row--new" : "") },
|
|
166
|
+
el("span", { class: "mvb-vers__label" }, "Latest"),
|
|
167
|
+
el("span", { class: "mvb-vers__val" }, latest ?? "—"))),
|
|
168
|
+
updatable ? el("p", { class: "mvb-vers__hint" }, "Copy the command below and run it in your terminal.") : null,
|
|
169
|
+
updatable ? commandBox : null,
|
|
170
|
+
],
|
|
171
|
+
actions: updatable
|
|
172
|
+
? [el("a", { class: "mvb-btn", href: `https://www.npmjs.com/package/${PACKAGE_NAME}/v/${latest}`, target: "_blank", rel: "noopener" }, "What changed")]
|
|
173
|
+
: [],
|
|
174
|
+
veilClass: "mvb-veil--help",
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// ---------- version ----------
|
|
179
|
+
|
|
180
|
+
// The footer chip shows the current version. When npm reports a newer one, the
|
|
181
|
+
// chip turns into an "update" button and the modal explains how to upgrade.
|
|
182
|
+
async function startVersionChip() {
|
|
183
|
+
const chip = $("#mvb-version");
|
|
184
|
+
if (!chip) return;
|
|
185
|
+
chip.textContent = `v${VERSION}`;
|
|
186
|
+
|
|
187
|
+
let latest = null;
|
|
188
|
+
try {
|
|
189
|
+
const result = await checkForUpdate();
|
|
190
|
+
latest = result?.latest ?? null;
|
|
191
|
+
} catch {
|
|
192
|
+
/* offline: keep the plain version label */
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const outdated = latest && compareVersions(latest, VERSION) > 0;
|
|
196
|
+
if (outdated) {
|
|
197
|
+
chip.classList.add("mvb-version--new");
|
|
198
|
+
chip.setAttribute("aria-label", `Update available: version ${latest}`);
|
|
199
|
+
chip.replaceChildren(icon("circle-arrow-up"), el("span", {}, `Update to v${latest}`));
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
chip.addEventListener("click", (event) => {
|
|
203
|
+
event.preventDefault();
|
|
204
|
+
openUpdate(latest);
|
|
205
|
+
});
|
|
206
|
+
chip.addEventListener("keydown", (event) => {
|
|
207
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
208
|
+
event.preventDefault();
|
|
209
|
+
openUpdate(latest);
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
|
|
140
214
|
function footer() {
|
|
141
215
|
const donate = button("Donate", {
|
|
142
216
|
icon: "coffee",
|
|
@@ -197,6 +271,16 @@ function footer() {
|
|
|
197
271
|
"Source: ",
|
|
198
272
|
el("a", { href: REPO_URL, target: "_blank", rel: "noopener" }, "github.com/mevebu/mevebu"),
|
|
199
273
|
el("span", { class: "mvb-foot__count", id: "mvb-starcount" })
|
|
274
|
+
),
|
|
275
|
+
el(
|
|
276
|
+
"p",
|
|
277
|
+
{ class: "mvb-foot__legal" },
|
|
278
|
+
el("button", {
|
|
279
|
+
class: "mvb-version",
|
|
280
|
+
id: "mvb-version",
|
|
281
|
+
type: "button",
|
|
282
|
+
title: "Check for updates",
|
|
283
|
+
}, "—")
|
|
200
284
|
)
|
|
201
285
|
)
|
|
202
286
|
)
|
|
@@ -727,4 +811,5 @@ function globalKeys() {
|
|
|
727
811
|
mountShell();
|
|
728
812
|
globalKeys();
|
|
729
813
|
startStarCount();
|
|
814
|
+
startVersionChip();
|
|
730
815
|
start(render);
|
package/mevebu/index.html
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<meta charset="utf-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
6
|
<title>mevebu, small tools that run in your browser</title>
|
|
7
|
-
<meta name="description" content="
|
|
7
|
+
<meta name="description" content="15 small tools that run in your browser. Passwords, QR codes, random draws, text and encoding. No accounts, no tracking." />
|
|
8
8
|
<link rel="icon" type="image/png" href="./icon.png" />
|
|
9
9
|
<link rel="stylesheet" href="./app/mevebu.css" />
|
|
10
10
|
<link rel="stylesheet" href="./libs/css/tools.css" />
|
|
@@ -43,26 +43,6 @@
|
|
|
43
43
|
.mvb-flip__result { font-size: 19px; font-weight: 700; letter-spacing: -0.01em; }
|
|
44
44
|
.mvb-flip__tally { font-size: 14px; color: var(--mvb-ink-3); font-variant-numeric: tabular-nums; }
|
|
45
45
|
|
|
46
|
-
/* ---------- dice ---------- */
|
|
47
|
-
|
|
48
|
-
.mvb-faces { display: flex; flex-wrap: wrap; gap: 10px; justify-content: center; }
|
|
49
|
-
|
|
50
|
-
.mvb-die {
|
|
51
|
-
min-width: 56px;
|
|
52
|
-
height: 56px;
|
|
53
|
-
display: inline-flex;
|
|
54
|
-
align-items: center;
|
|
55
|
-
justify-content: center;
|
|
56
|
-
padding: 0 10px;
|
|
57
|
-
background: var(--mvb-deck);
|
|
58
|
-
border: 1px solid var(--mvb-orbit-hard);
|
|
59
|
-
border-radius: var(--mvb-r1);
|
|
60
|
-
font-family: var(--mvb-mono);
|
|
61
|
-
font-size: 20px;
|
|
62
|
-
font-weight: 700;
|
|
63
|
-
font-variant-numeric: tabular-nums;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
46
|
/* ---------- meter ---------- */
|
|
67
47
|
|
|
68
48
|
.mvb-meter { height: 8px; border-radius: 999px; background: var(--mvb-deck-2); overflow: hidden; }
|
package/mevebu/libs/js/core.js
CHANGED
|
@@ -590,4 +590,52 @@ export function blank(iconName, text) {
|
|
|
590
590
|
);
|
|
591
591
|
}
|
|
592
592
|
|
|
593
|
+
// ---------- version ----------
|
|
594
|
+
|
|
595
|
+
// Kept in one place. The CLI reads the same value from package.json.
|
|
596
|
+
export const VERSION = "0.2.1";
|
|
597
|
+
export const PACKAGE_NAME = "mevebu";
|
|
598
|
+
export const REPO_SLUG = "mevebu/mevebu";
|
|
599
|
+
|
|
600
|
+
/** Compare two X.Y.Z strings. Returns 1, 0, or -1 for a > b, a == b, a < b. */
|
|
601
|
+
export function compareVersions(a, b) {
|
|
602
|
+
const left = String(a).split(".").map((part) => parseInt(part, 10) || 0);
|
|
603
|
+
const right = String(b).split(".").map((part) => parseInt(part, 10) || 0);
|
|
604
|
+
for (let i = 0; i < 3; i++) {
|
|
605
|
+
if ((left[i] ?? 0) > (right[i] ?? 0)) return 1;
|
|
606
|
+
if ((left[i] ?? 0) < (right[i] ?? 0)) return -1;
|
|
607
|
+
}
|
|
608
|
+
return 0;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
/**
|
|
612
|
+
* Asks the npm registry for the latest published version. Fails silently when
|
|
613
|
+
* offline, so the app never blocks on the network. Resolves to
|
|
614
|
+
* `{ latest, current, outdated }` or null when the check did not complete.
|
|
615
|
+
*/
|
|
616
|
+
export async function checkForUpdate({ timeout = 5000 } = {}) {
|
|
617
|
+
const controller = new AbortController();
|
|
618
|
+
const timer = setTimeout(() => controller.abort(), timeout);
|
|
619
|
+
try {
|
|
620
|
+
const response = await fetch(`https://registry.npmjs.org/${PACKAGE_NAME}/latest`, {
|
|
621
|
+
signal: controller.signal,
|
|
622
|
+
headers: { accept: "application/json" },
|
|
623
|
+
});
|
|
624
|
+
clearTimeout(timer);
|
|
625
|
+
if (!response.ok) return null;
|
|
626
|
+
const data = await response.json();
|
|
627
|
+
const latest = data?.version;
|
|
628
|
+
if (!latest) return null;
|
|
629
|
+
return { latest, current: VERSION, outdated: compareVersions(latest, VERSION) > 0 };
|
|
630
|
+
} catch {
|
|
631
|
+
clearTimeout(timer);
|
|
632
|
+
return null;
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
/** The command that updates an existing global install. */
|
|
637
|
+
export function updateCommand() {
|
|
638
|
+
return `npm install -g ${PACKAGE_NAME}`;
|
|
639
|
+
}
|
|
640
|
+
|
|
593
641
|
export { hasIcon };
|
package/mevebu/libs/js/icons.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// GENERATED FILE, DO NOT EDIT.
|
|
2
|
-
// Source: lucide v1.57.0 (ISC), icons/
|
|
2
|
+
// Source: lucide v1.57.0 (ISC), icons/54 names from scripts/vendor-icons.mjs
|
|
3
3
|
// Regenerate with: npm run icons
|
|
4
4
|
|
|
5
5
|
const D = {
|
|
@@ -15,6 +15,7 @@ const D = {
|
|
|
15
15
|
"corner-down-left": [ ["path", { d: "M20 4v7a4 4 0 0 1-4 4H4" }], ["path", { d: "m9 10-5 5 5 5" }] ],
|
|
16
16
|
"info": [ ["circle", { cx: "12", cy: "12", r: "10" }], ["path", { d: "M12 16v-4" }], ["path", { d: "M12 8h.01" }] ],
|
|
17
17
|
"circle-question-mark": [ ["circle", { cx: "12", cy: "12", r: "10" }], ["path", { d: "M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3" }], ["path", { d: "M12 17h.01" }] ],
|
|
18
|
+
"circle-arrow-up": [ ["circle", { cx: "12", cy: "12", r: "10" }], ["path", { d: "m16 12-4-4-4 4" }], ["path", { d: "M12 16V8" }] ],
|
|
18
19
|
"shuffle": [ ["path", { d: "m18 14 4 4-4 4" }], ["path", { d: "m18 2 4 4-4 4" }], ["path", { d: "M2 18h1.973a4 4 0 0 0 3.3-1.7l5.454-8.6a4 4 0 0 1 3.3-1.7H22" }], ["path", { d: "M2 6h1.972a4 4 0 0 1 3.6 2.2" }], ["path", { d: "M22 18h-6.041a4 4 0 0 1-3.3-1.8l-.359-.45" }] ],
|
|
19
20
|
"shield-check": [ [ "path", { d: "M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z" } ], ["path", { d: "m9 12 2 2 4-4" }] ],
|
|
20
21
|
"terminal": [ ["path", { d: "M12 19h8" }], ["path", { d: "m4 17 6-6-6-6" }] ],
|
|
@@ -24,7 +25,6 @@ const D = {
|
|
|
24
25
|
"circle-dashed": [ ["path", { d: "M10.1 2.182a10 10 0 0 1 3.8 0" }], ["path", { d: "M13.9 21.818a10 10 0 0 1-3.8 0" }], ["path", { d: "M17.609 3.721a10 10 0 0 1 2.69 2.7" }], ["path", { d: "M2.182 13.9a10 10 0 0 1 0-3.8" }], ["path", { d: "M20.279 17.609a10 10 0 0 1-2.7 2.69" }], ["path", { d: "M21.818 10.1a10 10 0 0 1 0 3.8" }], ["path", { d: "M3.721 6.391a10 10 0 0 1 2.7-2.69" }], ["path", { d: "M6.391 20.279a10 10 0 0 1-2.69-2.7" }] ],
|
|
25
26
|
"users": [ ["path", { d: "M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2" }], ["path", { d: "M16 3.128a4 4 0 0 1 0 7.744" }], ["path", { d: "M22 21v-2a4 4 0 0 0-3-3.87" }], ["circle", { cx: "9", cy: "7", r: "4" }] ],
|
|
26
27
|
"coins": [ ["path", { d: "M13.744 17.736a6 6 0 1 1-7.48-7.48" }], ["path", { d: "M15 6h1v4" }], ["path", { d: "m6.134 14.768.866-.5 2 3.464" }], ["circle", { cx: "16", cy: "8", r: "6" }] ],
|
|
27
|
-
"dices": [ ["rect", { width: "12", height: "12", x: "2", y: "10", rx: "2", ry: "2" }], ["path", { d: "m17.92 14 3.5-3.5a2.24 2.24 0 0 0 0-3l-5-4.92a2.24 2.24 0 0 0-3 0L10 6" }], ["path", { d: "M6 18h.01" }], ["path", { d: "M10 14h.01" }], ["path", { d: "M15 6h.01" }], ["path", { d: "M18 9h.01" }] ],
|
|
28
28
|
"key-round": [ [ "path", { d: "M2.586 17.414A2 2 0 0 0 2 18.828V21a1 1 0 0 0 1 1h3a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h1a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h.172a2 2 0 0 0 1.414-.586l.814-.814a6.5 6.5 0 1 0-4-4z" } ], ["circle", { cx: "16.5", cy: "7.5", r: ".5", fill: "currentColor" }] ],
|
|
29
29
|
"signature": [ [ "path", { d: "m21 17-2.156-1.868A.5.5 0 0 0 18 15.5v.5a1 1 0 0 1-1 1h-2a1 1 0 0 1-1-1c0-2.545-3.991-3.97-8.5-4a1 1 0 0 0 0 5c4.153 0 4.745-11.295 5.708-13.5a2.5 2.5 0 1 1 3.31 3.284" } ], ["path", { d: "M3 21h18" }] ],
|
|
30
30
|
"hash": [ ["line", { x1: "4", x2: "20", y1: "9", y2: "9" }], ["line", { x1: "4", x2: "20", y1: "15", y2: "15" }], ["line", { x1: "10", x2: "8", y1: "3", y2: "21" }], ["line", { x1: "16", x2: "14", y1: "3", y2: "21" }] ],
|
|
@@ -57,7 +57,6 @@ const D = {
|
|
|
57
57
|
"loader": [ ["path", { d: "M12 2v4" }], ["path", { d: "m16.2 7.8 2.9-2.9" }], ["path", { d: "M18 12h4" }], ["path", { d: "m16.2 16.2 2.9 2.9" }], ["path", { d: "M12 18v4" }], ["path", { d: "m4.9 19.1 2.9-2.9" }], ["path", { d: "M2 12h4" }], ["path", { d: "m4.9 4.9 2.9 2.9" }] ],
|
|
58
58
|
"play": [ [ "path", { d: "M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z" } ] ],
|
|
59
59
|
"lock": [ ["rect", { width: "18", height: "11", x: "3", y: "11", rx: "2", ry: "2" }], ["path", { d: "M7 11V7a5 5 0 0 1 10 0v4" }] ],
|
|
60
|
-
"dice-5": [ ["rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", ry: "2" }], ["path", { d: "M16 8h.01" }], ["path", { d: "M8 8h.01" }], ["path", { d: "M8 16h.01" }], ["path", { d: "M16 16h.01" }], ["path", { d: "M12 12h.01" }] ],
|
|
61
60
|
};
|
|
62
61
|
|
|
63
62
|
const SVG_NS = "http://www.w3.org/2000/svg";
|
|
@@ -70,24 +70,6 @@ export const TOOLS = [
|
|
|
70
70
|
keywords: "random toss decide heads tails",
|
|
71
71
|
load: () => import("./tools/coin-flip.js"),
|
|
72
72
|
},
|
|
73
|
-
{
|
|
74
|
-
id: "dice-roller",
|
|
75
|
-
category: "random",
|
|
76
|
-
name: "Dice Roller",
|
|
77
|
-
icon: "dices",
|
|
78
|
-
note: "Roll d4 to d20, one die or fifty.",
|
|
79
|
-
about: "Pick a die, set how many to roll, and see each result.",
|
|
80
|
-
tutorial: {
|
|
81
|
-
intro: "Roll polyhedral dice from d4 to d20, one or many at a time.",
|
|
82
|
-
steps: [
|
|
83
|
-
"Pick a die with the chips (d4 to d20).",
|
|
84
|
-
"Set how many dice to roll, between 1 and 50.",
|
|
85
|
-
"The dice roll as you change a setting. Press Roll for a fresh throw.",
|
|
86
|
-
],
|
|
87
|
-
},
|
|
88
|
-
keywords: "random dice d6 d20 roll tabletop rpg",
|
|
89
|
-
load: () => import("./tools/dice-roller.js"),
|
|
90
|
-
},
|
|
91
73
|
|
|
92
74
|
{
|
|
93
75
|
id: "password-generator",
|
package/package.json
CHANGED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
// Polyhedral dice from d4 to d20, one die or many, with each result shown.
|
|
2
|
-
import { el, randomInt, chipRow, numberInput, button, bench, stack, panel, output, actions, readPin, writePin, toolcard } from "../core.js";
|
|
3
|
-
|
|
4
|
-
const SIDES = [4, 6, 8, 10, 12, 20];
|
|
5
|
-
|
|
6
|
-
export function mount(slot) {
|
|
7
|
-
const PIN = "dice-roller";
|
|
8
|
-
const saved = readPin(PIN, { sides: 6, count: "3" });
|
|
9
|
-
let sides = SIDES.includes(Number(saved.sides)) ? Number(saved.sides) : 6;
|
|
10
|
-
|
|
11
|
-
const chips = chipRow(SIDES.map((value) => ({ value, label: `d${value}` })), {
|
|
12
|
-
value: sides,
|
|
13
|
-
label: "Die",
|
|
14
|
-
onChange: (value) => {
|
|
15
|
-
sides = value;
|
|
16
|
-
roll();
|
|
17
|
-
},
|
|
18
|
-
});
|
|
19
|
-
const countField = numberInput({ value: saved.count ?? "3", min: "1", max: "50", "aria-label": "How many dice" });
|
|
20
|
-
const faces = el("div", { class: "mvb-faces", "aria-live": "polite" });
|
|
21
|
-
const sum = output({ rows: 1 });
|
|
22
|
-
const rollButton = button("Roll", { icon: "dices", kind: "fill" });
|
|
23
|
-
|
|
24
|
-
function totalCount() {
|
|
25
|
-
return Math.max(1, Math.min(50, parseInt(countField.value || "1", 10) || 1));
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function roll() {
|
|
29
|
-
const total = totalCount();
|
|
30
|
-
const rolls = Array.from({ length: total }, () => 1 + randomInt(sides));
|
|
31
|
-
faces.replaceChildren(...rolls.map((value) => el("span", { class: "mvb-die" }, String(value))));
|
|
32
|
-
sum.write(String(rolls.reduce((a, b) => a + b, 0)));
|
|
33
|
-
writePin(PIN, { sides, count: String(total) });
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// A new die or a new count rolls right away. The button stays for a fresh throw.
|
|
37
|
-
rollButton.addEventListener("click", () => {
|
|
38
|
-
countField.value = String(totalCount());
|
|
39
|
-
roll();
|
|
40
|
-
});
|
|
41
|
-
countField.addEventListener("input", roll);
|
|
42
|
-
countField.addEventListener("change", () => {
|
|
43
|
-
countField.value = String(totalCount());
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
const card = toolcard({
|
|
47
|
-
body: bench(
|
|
48
|
-
stack(panel("Die", chips), panel("Dice to roll", countField)),
|
|
49
|
-
stack(panel("Results", faces), panel("Total", sum))
|
|
50
|
-
),
|
|
51
|
-
foot: actions(rollButton),
|
|
52
|
-
});
|
|
53
|
-
slot.append(card.node);
|
|
54
|
-
roll();
|
|
55
|
-
|
|
56
|
-
return {};
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export default { mount };
|