mevebu 1.9.0 → 1.10.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/README.md CHANGED
@@ -109,7 +109,7 @@ Browse everything from the home page or by category, or just press `Ctrl+K` and
109
109
 
110
110
  ## Privacy by design
111
111
 
112
- mevebu is built so that your data has nowhere to go. Every tool is a static HTML file, a tiny JavaScript module, and a Lucide icon. There is no server side code, no analytics, and no API key.
112
+ mevebu is built so that your data has nowhere to go. Every tool is a static HTML file and a small JavaScript module. Icons are Lucide, brand marks are Simple Icons, and the libraries that do real work (mathjs, pdf-lib, pdf.js, jsQR) are vendored into the folder rather than fetched from a CDN. There is no server side code, no analytics, and no API key.
113
113
 
114
114
  When you paste a password, a token, or a CSV into a tool:
115
115
 
@@ -148,6 +148,7 @@ cd mevebu
148
148
  npm install
149
149
  npm run css # build the stylesheet from tailwind.src.css
150
150
  npm run icons # regenerate the vendored Lucide icon set
151
+ npm run social # regenerate the vendored Simple Icons brand marks
151
152
  npm start # serve the static folder on http://localhost:5555
152
153
  ```
153
154
 
@@ -2,6 +2,7 @@
2
2
  // wiring, the home page, the tool index, and the bench a tool mounts into.
3
3
 
4
4
  import { CATEGORIES, TOOLS, getTool, getCategory, toolsInCategory, toolHref } from "../libs/js/registry.js";
5
+ import { socialIcon } from "../libs/js/social-icons.js";
5
6
  import { route, start, match, currentPath, go } from "../libs/js/router.js";
6
7
  import {
7
8
  $, $$, el, icon, getFavorites, isFavorite, toggleFavorite, setTheme,
@@ -11,6 +12,28 @@ import {
11
12
  } from "../libs/js/core.js";
12
13
 
13
14
  const REPO_URL = "https://github.com/mevebu/mevebu";
15
+ const WAITSEC_URL = "https://github.com/mevebu/waitsec";
16
+ const ORG_URL = "https://github.com/mevebu";
17
+ const CREATOR = {
18
+ name: "Ilyas Mukhlisin",
19
+ instagram: "https://www.instagram.com/ilyas.mukhlisin",
20
+ threads: "https://www.threads.com/@ilyas.mukhlisin",
21
+ };
22
+
23
+ /* One list drives the footer modal and the about page, so an account is added
24
+ in one place and both surfaces follow. */
25
+ const MEVEBU_SOCIALS = [
26
+ { name: "GitHub", handle: "@mevebu", href: ORG_URL, icon: "github" },
27
+ { name: "Instagram", handle: "@mevebu01", href: "https://www.instagram.com/mevebu01", icon: "instagram" },
28
+ { name: "Threads", handle: "@mevebu01", href: "https://www.threads.net/@mevebu01", icon: "threads" },
29
+ { name: "TikTok", handle: "@mevebu01", href: "https://www.tiktok.com/@mevebu01", icon: "tiktok" },
30
+ { name: "Facebook", handle: "mevebu", href: "https://www.facebook.com/mevebu", icon: "facebook" },
31
+ { name: "YouTube", handle: "@mevebu", href: "https://www.youtube.com/@mevebu", icon: "youtube" },
32
+ ];
33
+ const CREATOR_SOCIALS = [
34
+ { name: "Instagram", handle: "@ilyas.mukhlisin", href: CREATOR.instagram, icon: "instagram" },
35
+ { name: "Threads", handle: "@ilyas.mukhlisin", href: CREATOR.threads, icon: "threads" },
36
+ ];
14
37
 
15
38
  const NAV = [
16
39
  { path: "/", label: "Home" },
@@ -276,6 +299,46 @@ async function startVersionChip() {
276
299
  });
277
300
  }
278
301
 
302
+ function followRow(item) {
303
+ return el(
304
+ "a",
305
+ {
306
+ class:
307
+ "flex items-center gap-3 rounded-r1 border border-orbit bg-deck-2 p-2.5 text-ink no-underline transition-colors duration-150 hover:border-orbit-hard hover:bg-deck",
308
+ href: item.href,
309
+ target: "_blank",
310
+ rel: "noopener",
311
+ },
312
+ el("span", { class: "grid size-9 flex-none place-items-center rounded-lg bg-jade-soft text-jade" }, socialIcon(item.icon, "size-[18px]")),
313
+ el("span", { class: "grid min-w-0 gap-0.5" },
314
+ el("span", { class: "text-sm font-semibold" }, item.name),
315
+ el("span", { class: "truncate text-2xs text-ink-3" }, item.handle)),
316
+ icon("external-link", "ms-auto size-4 flex-none text-ink-3")
317
+ );
318
+ }
319
+
320
+ function followGroup(title, items) {
321
+ return el(
322
+ "section",
323
+ { class: "grid gap-2" },
324
+ el("h3", { class: "m-0 text-2xs font-bold tracking-[0.08em] text-ink-3 uppercase" }, title),
325
+ el("div", { class: "grid gap-1.5" }, ...items.map(followRow))
326
+ );
327
+ }
328
+
329
+ function openFollow() {
330
+ modal({
331
+ label: "Follow mevebu",
332
+ body: [
333
+ modalHead("users-round", "Follow mevebu"),
334
+ el("p", { class: "m-0 text-sm leading-[1.6] text-ink-2" },
335
+ "Release notes, new tools, and the small updates in between. Pick a network."),
336
+ followGroup("mevebu", MEVEBU_SOCIALS),
337
+ followGroup(`${CREATOR.name}, creator`, CREATOR_SOCIALS),
338
+ ],
339
+ });
340
+ }
341
+
279
342
  function footer() {
280
343
  // A finger needs 44px on a phone, so the whole list is a comfortable target
281
344
  // there. The desktop row stays as it was, at text height.
@@ -322,6 +385,17 @@ function footer() {
322
385
  "div",
323
386
  { class: "mt-[clamp(26px,4vw,44px)] flex flex-wrap items-center gap-x-5 gap-y-2 border-t border-orbit pt-5 text-2xs text-ink-3" },
324
387
  el("p", { class: "m-0 flex items-center gap-2" }, "MIT licensed."),
388
+ el(
389
+ "button",
390
+ {
391
+ class:
392
+ "inline-flex min-h-7 cursor-pointer items-center gap-1.5 rounded-full border border-orbit bg-transparent px-2.5 py-[3px] font-sans text-2xs font-semibold text-ink-2 transition-colors duration-150 hover:border-jade hover:bg-jade-soft hover:text-jade max-[45rem]:min-h-11",
393
+ type: "button",
394
+ onclick: () => openFollow(),
395
+ },
396
+ icon("users-round", "size-3.5"),
397
+ "Follow us"
398
+ ),
325
399
  el(
326
400
  "p",
327
401
  { class: "m-0 flex items-center gap-2" },
@@ -872,16 +946,69 @@ async function toolPage(tool, category) {
872
946
  }
873
947
 
874
948
  function aboutPage() {
949
+ const H1 = "m-0 text-[clamp(34px,6vw,52px)] font-bold tracking-[-0.03em]";
950
+ const SUB = "mt-3 mb-0 max-w-[64ch] text-pretty text-md leading-[1.6] text-ink-2";
875
951
  const block = (title, ...nodes) =>
876
952
  el("section", { class: ABOUT_BLOCK }, el("h2", { class: "text-lg font-bold tracking-[-0.01em]" }, title), ...nodes);
877
953
 
954
+ const stat = (value, label) =>
955
+ el("div", { class: "grid gap-0.5 rounded-r2 border border-orbit bg-deck-2 px-4 py-3" },
956
+ el("span", { class: "text-2xl font-bold tracking-[-0.02em] text-ink tabular-nums" }, value),
957
+ el("span", { class: "text-2xs font-semibold tracking-[0.06em] text-ink-3 uppercase" }, label));
958
+
959
+ const repoCard = (name, body, href) =>
960
+ el("a", {
961
+ class: "grid content-start gap-1.5 rounded-r2 border border-orbit bg-deck p-4 no-underline transition-colors duration-150 hover:border-orbit-hard hover:bg-deck-2",
962
+ href,
963
+ target: "_blank",
964
+ rel: "noopener",
965
+ },
966
+ el("span", { class: "flex items-center gap-2 text-md font-bold text-ink" },
967
+ icon("git-branch", "size-4 flex-none text-jade"), name),
968
+ el("span", { class: "text-sm leading-[1.55] text-ink-2" }, body),
969
+ el("span", { class: "mt-1 inline-flex items-center gap-1.5 font-mono text-2xs font-semibold text-jade" },
970
+ href.replace("https://", ""), icon("external-link", "size-3")));
971
+
972
+ const creatorLink = (item) =>
973
+ el("a", {
974
+ class: "inline-flex min-h-11 items-center gap-2 rounded-r1 border border-orbit bg-deck px-3 text-sm font-semibold text-ink no-underline transition-colors duration-150 hover:border-orbit-hard hover:bg-deck-2",
975
+ href: item.href,
976
+ target: "_blank",
977
+ rel: "noopener",
978
+ }, socialIcon(item.icon, "size-[17px] text-jade"), item.name);
979
+
878
980
  document.title = "About · mevebu";
879
981
  return el(
880
982
  "div",
881
983
  { class: PAGE_NARROW },
882
- el("h1", { class: LEAD }, "About mevebu"),
883
- el("p", { class: LEDE },
884
- `mevebu is ${TOOLS.length} small tools that run in your browser. Each one has its own page and does a single job.`),
984
+ el("div", { class: "flex items-start gap-4" },
985
+ el("img", { class: "mt-1 block size-14 flex-none", src: "./assets/logo.png", alt: "", width: "56", height: "56" }),
986
+ el("div", { class: "min-w-0" },
987
+ el("h1", { class: H1 }, "About mevebu"),
988
+ el("p", { class: SUB },
989
+ "mevebu is a GitHub organization that builds small tools for the browser. Every tool runs on your device, with no account and nothing to upload."))),
990
+
991
+ el("div", { class: "mt-7 grid grid-cols-2 gap-2.5 sm:grid-cols-4" },
992
+ stat(String(TOOLS.length), "Tools"),
993
+ stat(String(CATEGORIES.length), "Categories"),
994
+ stat("2", "Repositories"),
995
+ stat("MIT", "License")),
996
+
997
+ block("The organization",
998
+ el("p", { class: "m-0 leading-[1.65] text-ink-2" },
999
+ "Everything sits under ", el("a", { class: TEXT_LINK, href: ORG_URL, target: "_blank", rel: "noopener" }, "github.com/mevebu"),
1000
+ ". The organization holds two repositories: Mevebu, the toolbox you are using right now, and Waitsec, a set of guardrails for coding agents.")),
1001
+
1002
+ block("The repositories",
1003
+ el("div", { class: "grid gap-2.5 sm:grid-cols-2" },
1004
+ repoCard("Mevebu", `${TOOLS.length} browser tools in one static folder. No build step on your machine, no server behind it.`, REPO_URL),
1005
+ repoCard("Waitsec", "Guardrails for coding agents: stop first, think first, and write less code.", WAITSEC_URL))),
1006
+
1007
+ block("The creator",
1008
+ el("p", { class: "m-0 leading-[1.65] text-ink-2" },
1009
+ el("strong", { class: "font-semibold text-ink" }, CREATOR.name),
1010
+ " designs, builds and maintains both repositories. Follow the work on Instagram or Threads."),
1011
+ el("div", { class: "flex flex-wrap gap-2" }, ...CREATOR_SOCIALS.map(creatorLink))),
885
1012
 
886
1013
  block("Running it",
887
1014
  el("p", {}, "Install it once from npm. The command starts a local server and opens your browser at the address it prints."),
@@ -892,7 +1019,7 @@ function aboutPage() {
892
1019
  el("p", {}, "Add one object to ", el("code", { class: CODE }, "mevebu/libs/js/registry.js"), " and one file in ", el("code", { class: CODE }, "mevebu/libs/js/tools/"), ". The home page, the index, the categories, search and the route all come from that entry.")),
893
1020
 
894
1021
  block("Built with",
895
- el("p", {}, "Hand written HTML, CSS and JavaScript, with Tailwind for the stylesheet. Icons are Lucide and the QR decoder is jsQR."),
1022
+ el("p", {}, "Hand written HTML, CSS and JavaScript, with Tailwind for the stylesheet. The scientific calculator runs on mathjs, icons are Lucide, brand marks are Simple Icons, and the QR decoder is jsQR."),
896
1023
  el("p", {}, "Source and issues: ",
897
1024
  el("a", { class: TEXT_LINK, href: REPO_URL, target: "_blank", rel: "noopener" }, "github.com/mevebu/mevebu")),
898
1025
  el("p", {}, copyButton(() => location.href, "Copy this page link")))
@@ -13,6 +13,10 @@
13
13
  --text-lg--line-height: 1.35;
14
14
  --text-xl: 22px;
15
15
  --text-xl--line-height: 1.3;
16
+ --text-2xl: 1.5rem;
17
+ --text-2xl--line-height: calc(2 / 1.5);
18
+ --text-3xl: 1.875rem;
19
+ --text-3xl--line-height: calc(2.25 / 1.875);
16
20
  --font-weight-medium: 500;
17
21
  --font-weight-semibold: 600;
18
22
  --font-weight-bold: 700;
@@ -193,6 +197,9 @@
193
197
  .collapse {
194
198
  visibility: collapse;
195
199
  }
200
+ .invisible {
201
+ visibility: hidden;
202
+ }
196
203
  .visible {
197
204
  visibility: visible;
198
205
  }
@@ -244,6 +251,9 @@
244
251
  .z-90 {
245
252
  z-index: 90;
246
253
  }
254
+ .col-span-2 {
255
+ grid-column: span 2 / span 2;
256
+ }
247
257
  .container {
248
258
  width: 100%;
249
259
  @media (width >= 40rem) {
@@ -316,6 +326,9 @@
316
326
  .mt-5 {
317
327
  margin-top: calc(var(--spacing) * 5);
318
328
  }
329
+ .mt-7 {
330
+ margin-top: calc(var(--spacing) * 7);
331
+ }
319
332
  .mt-8 {
320
333
  margin-top: calc(var(--spacing) * 8);
321
334
  }
@@ -361,6 +374,9 @@
361
374
  .block {
362
375
  display: block;
363
376
  }
377
+ .contents {
378
+ display: contents;
379
+ }
364
380
  .flex {
365
381
  display: flex;
366
382
  }
@@ -379,13 +395,28 @@
379
395
  .table {
380
396
  display: table;
381
397
  }
398
+ .table-cell {
399
+ display: table-cell;
400
+ }
382
401
  .aspect-\[4\/3\] {
383
402
  aspect-ratio: 4/3;
384
403
  }
404
+ .size-3 {
405
+ width: calc(var(--spacing) * 3);
406
+ height: calc(var(--spacing) * 3);
407
+ }
408
+ .size-3\.5 {
409
+ width: calc(var(--spacing) * 3.5);
410
+ height: calc(var(--spacing) * 3.5);
411
+ }
385
412
  .size-4 {
386
413
  width: calc(var(--spacing) * 4);
387
414
  height: calc(var(--spacing) * 4);
388
415
  }
416
+ .size-5 {
417
+ width: calc(var(--spacing) * 5);
418
+ height: calc(var(--spacing) * 5);
419
+ }
389
420
  .size-6 {
390
421
  width: calc(var(--spacing) * 6);
391
422
  height: calc(var(--spacing) * 6);
@@ -402,6 +433,10 @@
402
433
  width: calc(var(--spacing) * 11);
403
434
  height: calc(var(--spacing) * 11);
404
435
  }
436
+ .size-14 {
437
+ width: calc(var(--spacing) * 14);
438
+ height: calc(var(--spacing) * 14);
439
+ }
405
440
  .size-16 {
406
441
  width: calc(var(--spacing) * 16);
407
442
  height: calc(var(--spacing) * 16);
@@ -414,6 +449,10 @@
414
449
  width: 15px;
415
450
  height: 15px;
416
451
  }
452
+ .size-\[17px\] {
453
+ width: 17px;
454
+ height: 17px;
455
+ }
417
456
  .size-\[18px\] {
418
457
  width: 18px;
419
458
  height: 18px;
@@ -480,6 +519,9 @@
480
519
  .h-full {
481
520
  height: 100%;
482
521
  }
522
+ .h-px {
523
+ height: 1px;
524
+ }
483
525
  .max-h-80 {
484
526
  max-height: calc(var(--spacing) * 80);
485
527
  }
@@ -498,6 +540,9 @@
498
540
  .min-h-7 {
499
541
  min-height: calc(var(--spacing) * 7);
500
542
  }
543
+ .min-h-8 {
544
+ min-height: calc(var(--spacing) * 8);
545
+ }
501
546
  .min-h-11 {
502
547
  min-height: calc(var(--spacing) * 11);
503
548
  }
@@ -507,9 +552,15 @@
507
552
  .min-h-40 {
508
553
  min-height: calc(var(--spacing) * 40);
509
554
  }
555
+ .min-h-\[1\.2em\] {
556
+ min-height: 1.2em;
557
+ }
510
558
  .min-h-\[22px\] {
511
559
  min-height: 22px;
512
560
  }
561
+ .min-h-\[56px\] {
562
+ min-height: 56px;
563
+ }
513
564
  .min-h-\[120px\] {
514
565
  min-height: 120px;
515
566
  }
@@ -579,6 +630,9 @@
579
630
  .max-w-\[62ch\] {
580
631
  max-width: 62ch;
581
632
  }
633
+ .max-w-\[64ch\] {
634
+ max-width: 64ch;
635
+ }
582
636
  .max-w-\[340px\] {
583
637
  max-width: 340px;
584
638
  }
@@ -645,6 +699,9 @@
645
699
  .\[grid-template-columns\:88px_minmax\(0\,1fr\)_62px\] {
646
700
  grid-template-columns: 88px minmax(0,1fr) 62px;
647
701
  }
702
+ .\[grid-template-columns\:minmax\(0\,1fr\)_auto\] {
703
+ grid-template-columns: minmax(0,1fr) auto;
704
+ }
648
705
  .\[grid-template-columns\:minmax\(90px\,34\%\)_minmax\(0\,1fr\)\] {
649
706
  grid-template-columns: minmax(90px,34%) minmax(0,1fr);
650
707
  }
@@ -666,6 +723,9 @@
666
723
  .grid-cols-2 {
667
724
  grid-template-columns: repeat(2, minmax(0, 1fr));
668
725
  }
726
+ .grid-cols-4 {
727
+ grid-template-columns: repeat(4, minmax(0, 1fr));
728
+ }
669
729
  .grid-cols-\[minmax\(0\,1fr\)_auto\] {
670
730
  grid-template-columns: minmax(0,1fr) auto;
671
731
  }
@@ -964,9 +1024,15 @@
964
1024
  .p-2 {
965
1025
  padding: calc(var(--spacing) * 2);
966
1026
  }
1027
+ .p-2\.5 {
1028
+ padding: calc(var(--spacing) * 2.5);
1029
+ }
967
1030
  .p-3\.5 {
968
1031
  padding: calc(var(--spacing) * 3.5);
969
1032
  }
1033
+ .p-4 {
1034
+ padding: calc(var(--spacing) * 4);
1035
+ }
970
1036
  .p-5 {
971
1037
  padding: calc(var(--spacing) * 5);
972
1038
  }
@@ -988,6 +1054,12 @@
988
1054
  .p-\[var\(--card-pad\)\] {
989
1055
  padding: var(--card-pad);
990
1056
  }
1057
+ .px-0 {
1058
+ padding-inline: 0px;
1059
+ }
1060
+ .px-1 {
1061
+ padding-inline: var(--spacing);
1062
+ }
991
1063
  .px-2 {
992
1064
  padding-inline: calc(var(--spacing) * 2);
993
1065
  }
@@ -1156,10 +1228,18 @@
1156
1228
  .font-sans {
1157
1229
  font-family: var(--sans);
1158
1230
  }
1231
+ .text-2xl {
1232
+ font-size: var(--text-2xl);
1233
+ line-height: var(--tw-leading, var(--text-2xl--line-height));
1234
+ }
1159
1235
  .text-2xs {
1160
1236
  font-size: var(--text-2xs);
1161
1237
  line-height: var(--tw-leading, var(--text-2xs--line-height));
1162
1238
  }
1239
+ .text-3xl {
1240
+ font-size: var(--text-3xl);
1241
+ line-height: var(--tw-leading, var(--text-3xl--line-height));
1242
+ }
1163
1243
  .text-base {
1164
1244
  font-size: var(--text-base);
1165
1245
  line-height: var(--tw-leading, var(--text-base--line-height));
@@ -1199,14 +1279,29 @@
1199
1279
  .text-\[0\.92em\] {
1200
1280
  font-size: 0.92em;
1201
1281
  }
1282
+ .text-\[clamp\(34px\,6vw\,52px\)\] {
1283
+ font-size: clamp(34px, 6vw, 52px);
1284
+ }
1202
1285
  .leading-\[1\.5\] {
1203
1286
  --tw-leading: 1.5;
1204
1287
  line-height: 1.5;
1205
1288
  }
1289
+ .leading-\[1\.6\] {
1290
+ --tw-leading: 1.6;
1291
+ line-height: 1.6;
1292
+ }
1206
1293
  .leading-\[1\.45\] {
1207
1294
  --tw-leading: 1.45;
1208
1295
  line-height: 1.45;
1209
1296
  }
1297
+ .leading-\[1\.55\] {
1298
+ --tw-leading: 1.55;
1299
+ line-height: 1.55;
1300
+ }
1301
+ .leading-\[1\.65\] {
1302
+ --tw-leading: 1.65;
1303
+ line-height: 1.65;
1304
+ }
1210
1305
  .leading-normal {
1211
1306
  --tw-leading: var(--leading-normal);
1212
1307
  line-height: var(--leading-normal);
@@ -1231,6 +1326,10 @@
1231
1326
  --tw-tracking: -0.02em;
1232
1327
  letter-spacing: -0.02em;
1233
1328
  }
1329
+ .tracking-\[-0\.03em\] {
1330
+ --tw-tracking: -0.03em;
1331
+ letter-spacing: -0.03em;
1332
+ }
1234
1333
  .tracking-\[-0\.025em\] {
1235
1334
  --tw-tracking: -0.025em;
1236
1335
  letter-spacing: -0.025em;
@@ -1239,6 +1338,10 @@
1239
1338
  --tw-tracking: -0.035em;
1240
1339
  letter-spacing: -0.035em;
1241
1340
  }
1341
+ .tracking-\[0\.06em\] {
1342
+ --tw-tracking: 0.06em;
1343
+ letter-spacing: 0.06em;
1344
+ }
1242
1345
  .tracking-\[0\.08em\] {
1243
1346
  --tw-tracking: 0.08em;
1244
1347
  letter-spacing: 0.08em;
@@ -1297,13 +1400,25 @@
1297
1400
  .uppercase {
1298
1401
  text-transform: uppercase;
1299
1402
  }
1403
+ .italic {
1404
+ font-style: italic;
1405
+ }
1300
1406
  .tabular-nums {
1301
1407
  --tw-numeric-spacing: tabular-nums;
1302
1408
  font-variant-numeric: var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,);
1303
1409
  }
1410
+ .line-through {
1411
+ text-decoration-line: line-through;
1412
+ }
1304
1413
  .no-underline {
1305
1414
  text-decoration-line: none;
1306
1415
  }
1416
+ .overline {
1417
+ text-decoration-line: overline;
1418
+ }
1419
+ .underline {
1420
+ text-decoration-line: underline;
1421
+ }
1307
1422
  .underline-offset-\[3px\] {
1308
1423
  text-underline-offset: 3px;
1309
1424
  }
@@ -1360,6 +1475,10 @@
1360
1475
  --tw-blur: blur(8px);
1361
1476
  filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,);
1362
1477
  }
1478
+ .grayscale {
1479
+ --tw-grayscale: grayscale(100%);
1480
+ filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,);
1481
+ }
1363
1482
  .invert {
1364
1483
  --tw-invert: invert(100%);
1365
1484
  filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,);
@@ -1438,6 +1557,9 @@
1438
1557
  .\[image-rendering\:pixelated\] {
1439
1558
  image-rendering: pixelated;
1440
1559
  }
1560
+ .\[start\:end\] {
1561
+ start: end;
1562
+ }
1441
1563
  .group-data-\[on\]\:text-ink:is(:where(.group)[data-on] *) {
1442
1564
  color: var(--ink);
1443
1565
  }
@@ -1474,6 +1596,13 @@
1474
1596
  .focus-within\:border-jade:focus-within {
1475
1597
  border-color: var(--jade);
1476
1598
  }
1599
+ .focus-within\:ring-3:focus-within {
1600
+ --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);
1601
+ box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
1602
+ }
1603
+ .focus-within\:ring-jade-soft:focus-within {
1604
+ --tw-ring-color: var(--jade-soft);
1605
+ }
1477
1606
  @media (hover: hover) {
1478
1607
  .hover\:border-danger:hover {
1479
1608
  border-color: var(--danger);
@@ -1532,6 +1661,10 @@
1532
1661
  .focus\:border-jade:focus {
1533
1662
  border-color: var(--jade);
1534
1663
  }
1664
+ .focus\:ring-0:focus {
1665
+ --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);
1666
+ box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
1667
+ }
1535
1668
  .focus\:ring-3:focus {
1536
1669
  --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);
1537
1670
  box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
@@ -1703,6 +1836,9 @@
1703
1836
  .sm\:grid-cols-2 {
1704
1837
  grid-template-columns: repeat(2, minmax(0, 1fr));
1705
1838
  }
1839
+ .sm\:grid-cols-4 {
1840
+ grid-template-columns: repeat(4, minmax(0, 1fr));
1841
+ }
1706
1842
  }
1707
1843
  @media (width >= 45rem) {
1708
1844
  .min-\[45rem\]\:flex {
@@ -750,7 +750,7 @@ export function blank(iconName, text) {
750
750
  // ---------- version ----------
751
751
 
752
752
  // Kept in one place. The CLI reads the same value from package.json.
753
- export const VERSION = "1.9.0";
753
+ export const VERSION = "1.10.0";
754
754
  export const PACKAGE_NAME = "mevebu";
755
755
  export const REPO_SLUG = "mevebu/mevebu";
756
756
 
@@ -1,5 +1,5 @@
1
1
  // GENERATED FILE, DO NOT EDIT.
2
- // Source: lucide v1.59.0 (ISC), icons/174 names from scripts/vendor-icons.mjs
2
+ // Source: lucide v1.57.0 (ISC), icons/174 names from scripts/vendor-icons.mjs
3
3
  // Regenerate with: npm run icons
4
4
 
5
5
  const D = {
@@ -44,6 +44,7 @@ const D = {
44
44
  "rotate-cw": [ ["path", { d: "M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8" }], ["path", { d: "M21 3v5h-5" }] ],
45
45
  "download": [ ["path", { d: "M12 15V3" }], ["path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }], ["path", { d: "m7 10 5 5 5-5" }] ],
46
46
  "upload": [ ["path", { d: "M12 3v12" }], ["path", { d: "m17 8-5-5-5 5" }], ["path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }] ],
47
+ "delete": [ [ "path", { d: "M10 5a2 2 0 0 0-1.344.519l-6.328 5.74a1 1 0 0 0 0 1.481l6.328 5.741A2 2 0 0 0 10 19h10a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2z" } ], ["path", { d: "m12 9 6 6" }], ["path", { d: "m18 9-6 6" }] ],
47
48
  "arrow-left-right": [ ["path", { d: "M8 3 4 7l4 4" }], ["path", { d: "M4 7h16" }], ["path", { d: "m16 21 4-4-4-4" }], ["path", { d: "M20 17H4" }] ],
48
49
  "trash": [ ["path", { d: "M10 11v6" }], ["path", { d: "M14 11v6" }], ["path", { d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6" }], ["path", { d: "M3 6h18" }], ["path", { d: "M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" }] ],
49
50
  "plus": [ ["path", { d: "M5 12h14" }], ["path", { d: "M12 5v14" }] ],
@@ -166,7 +167,6 @@ const D = {
166
167
  "receipt": [ ["path", { d: "M12 17V7" }], ["path", { d: "M16 8h-6a2 2 0 0 0 0 4h4a2 2 0 0 1 0 4H8" }], [ "path", { d: "M4 3a1 1 0 0 1 1-1 1.3 1.3 0 0 1 .7.2l.933.6a1.3 1.3 0 0 0 1.4 0l.934-.6a1.3 1.3 0 0 1 1.4 0l.933.6a1.3 1.3 0 0 0 1.4 0l.933-.6a1.3 1.3 0 0 1 1.4 0l.934.6a1.3 1.3 0 0 0 1.4 0l.933-.6A1.3 1.3 0 0 1 19 2a1 1 0 0 1 1 1v18a1 1 0 0 1-1 1 1.3 1.3 0 0 1-.7-.2l-.933-.6a1.3 1.3 0 0 0-1.4 0l-.934.6a1.3 1.3 0 0 1-1.4 0l-.933-.6a1.3 1.3 0 0 0-1.4 0l-.933.6a1.3 1.3 0 0 1-1.4 0l-.934-.6a1.3 1.3 0 0 0-1.4 0l-.933.6a1.3 1.3 0 0 1-.7.2 1 1 0 0 1-1-1z" } ] ],
167
168
  "banknote": [ ["rect", { width: "20", height: "12", x: "2", y: "6", rx: "2" }], ["circle", { cx: "12", cy: "12", r: "2" }], ["path", { d: "M6 12h.01M18 12h.01" }] ],
168
169
  "package": [ [ "path", { d: "M11 21.73a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73z" } ], ["path", { d: "M12 22V12" }], ["polyline", { points: "3.29 7 12 12 20.71 7" }], ["path", { d: "m7.5 4.27 9 5.15" }] ],
169
- "film": [ ["rect", { width: "18", height: "18", x: "3", y: "3", rx: "2" }], ["path", { d: "M7 3v18" }], ["path", { d: "M3 7.5h4" }], ["path", { d: "M3 12h18" }], ["path", { d: "M3 16.5h4" }], ["path", { d: "M17 3v18" }], ["path", { d: "M17 7.5h4" }], ["path", { d: "M17 16.5h4" }] ],
170
170
  "video": [ ["path", { d: "m16 13 5.223 3.482a.5.5 0 0 0 .777-.416V7.87a.5.5 0 0 0-.752-.432L16 10.5" }], ["rect", { x: "2", y: "6", width: "14", height: "12", rx: "2" }] ],
171
171
  "git-pull-request": [ ["circle", { cx: "18", cy: "18", r: "3" }], ["circle", { cx: "6", cy: "6", r: "3" }], ["path", { d: "M13 6h3a2 2 0 0 1 2 2v7" }], ["line", { x1: "6", x2: "6", y1: "9", y2: "21" }] ],
172
172
  "weight": [ ["circle", { cx: "12", cy: "5", r: "3" }], [ "path", { d: "M6.5 8a2 2 0 0 0-1.905 1.46L2.1 18.5A2 2 0 0 0 4 21h16a2 2 0 0 0 1.925-2.54L19.4 9.5A2 2 0 0 0 17.48 8Z" } ] ],
@@ -1355,17 +1355,18 @@ export const TOOLS = [
1355
1355
  category: "math",
1356
1356
  name: "Scientific Calculator",
1357
1357
  icon: "calculator",
1358
- note: "Do scientific math with a keypad.",
1359
- about: "Trigonometry, logs, powers and roots, in degrees or radians.",
1358
+ note: "Do scientific math with a live keypad.",
1359
+ about: "Variables, fractions, constants and a full function set, in degrees or radians.",
1360
1360
  tutorial: {
1361
- intro: "Do scientific math with a keypad.",
1361
+ intro: "A scientific calculator that evaluates as you type.",
1362
1362
  steps: [
1363
- "Type an expression or use the keys.",
1364
- "Pick degrees or radians for the trig functions.",
1365
- "Press = to evaluate. The history keeps recent results.",
1363
+ "Type an expression or press the keys. The result updates while you type.",
1364
+ "Press = or Enter to commit it to the history.",
1365
+ "Keep values with a = 5, then use a in the next line. ans holds the last result.",
1366
+ "Switch Degrees or Radians, and Decimal or Fraction for the result.",
1366
1367
  ],
1367
1368
  },
1368
- keywords: "scientific calculator trigonometry log sin cos tan",
1369
+ keywords: "scientific calculator trigonometry log sin cos tan variables fractions ans desmos",
1369
1370
  load: () => import("./tools/scientific-calculator.js"),
1370
1371
  },
1371
1372
  {
@@ -3091,7 +3092,7 @@ export const TOOLS = [
3091
3092
  id: "script-planner",
3092
3093
  category: "content",
3093
3094
  name: "Script Planner",
3094
- icon: "film",
3095
+ icon: "video",
3095
3096
  note: "Lay out a video script.",
3096
3097
  about: "Hook, intro, body and outro, with word and time estimates.",
3097
3098
  tutorial: {
@@ -3127,7 +3128,7 @@ export const TOOLS = [
3127
3128
  id: "subtitle-formatter",
3128
3129
  category: "content",
3129
3130
  name: "Subtitle Formatter",
3130
- icon: "film",
3131
+ icon: "video",
3131
3132
  note: "Check subtitle line lengths.",
3132
3133
  about: "Flags lines that run too long and tidies blank lines.",
3133
3134
  tutorial: {
@@ -0,0 +1,32 @@
1
+ // GENERATED FILE, DO NOT EDIT.
2
+ // Source: simple-icons v16.35.0 (CC0 1.0), 6 brand marks from scripts/vendor-social.mjs
3
+ // Regenerate with: npm run social
4
+
5
+ const D = {
6
+ "github": { title: "GitHub", path: "M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12" },
7
+ "instagram": { title: "Instagram", path: "M7.0301.084c-1.2768.0602-2.1487.264-2.911.5634-.7888.3075-1.4575.72-2.1228 1.3877-.6652.6677-1.075 1.3368-1.3802 2.127-.2954.7638-.4956 1.6365-.552 2.914-.0564 1.2775-.0689 1.6882-.0626 4.947.0062 3.2586.0206 3.6671.0825 4.9473.061 1.2765.264 2.1482.5635 2.9107.308.7889.72 1.4573 1.388 2.1228.6679.6655 1.3365 1.0743 2.1285 1.38.7632.295 1.6361.4961 2.9134.552 1.2773.056 1.6884.069 4.9462.0627 3.2578-.0062 3.668-.0207 4.9478-.0814 1.28-.0607 2.147-.2652 2.9098-.5633.7889-.3086 1.4578-.72 2.1228-1.3881.665-.6682 1.0745-1.3378 1.3795-2.1284.2957-.7632.4966-1.636.552-2.9124.056-1.2809.0692-1.6898.063-4.948-.0063-3.2583-.021-3.6668-.0817-4.9465-.0607-1.2797-.264-2.1487-.5633-2.9117-.3084-.7889-.72-1.4568-1.3876-2.1228C21.2982 1.33 20.628.9208 19.8378.6165 19.074.321 18.2017.1197 16.9244.0645 15.6471.0093 15.236-.005 11.977.0014 8.718.0076 8.31.0215 7.0301.0839m.1402 21.6932c-1.17-.0509-1.8053-.2453-2.2287-.408-.5606-.216-.96-.4771-1.3819-.895-.422-.4178-.6811-.8186-.9-1.378-.1644-.4234-.3624-1.058-.4171-2.228-.0595-1.2645-.072-1.6442-.079-4.848-.007-3.2037.0053-3.583.0607-4.848.05-1.169.2456-1.805.408-2.2282.216-.5613.4762-.96.895-1.3816.4188-.4217.8184-.6814 1.3783-.9003.423-.1651 1.0575-.3614 2.227-.4171 1.2655-.06 1.6447-.072 4.848-.079 3.2033-.007 3.5835.005 4.8495.0608 1.169.0508 1.8053.2445 2.228.408.5608.216.96.4754 1.3816.895.4217.4194.6816.8176.9005 1.3787.1653.4217.3617 1.056.4169 2.2263.0602 1.2655.0739 1.645.0796 4.848.0058 3.203-.0055 3.5834-.061 4.848-.051 1.17-.245 1.8055-.408 2.2294-.216.5604-.4763.96-.8954 1.3814-.419.4215-.8181.6811-1.3783.9-.4224.1649-1.0577.3617-2.2262.4174-1.2656.0595-1.6448.072-4.8493.079-3.2045.007-3.5825-.006-4.848-.0608M16.953 5.5864A1.44 1.44 0 1 0 18.39 4.144a1.44 1.44 0 0 0-1.437 1.4424M5.8385 12.012c.0067 3.4032 2.7706 6.1557 6.173 6.1493 3.4026-.0065 6.157-2.7701 6.1506-6.1733-.0065-3.4032-2.771-6.1565-6.174-6.1498-3.403.0067-6.156 2.771-6.1496 6.1738M8 12.0077a4 4 0 1 1 4.008 3.9921A3.9996 3.9996 0 0 1 8 12.0077" },
8
+ "threads": { title: "Threads", path: "M18.263 11.097c-.03-3.486-1.92-5.586-5.111-5.586-2.13 0-3.922.963-4.863 2.499l2.062 1.438c.535-.843 1.272-1.543 2.628-1.543 1.528 0 2.318.85 2.544 2.431a15 15 0 0 0-2.236-.173c-4.125 0-6.068 1.867-6.068 4.336s1.943 3.99 4.804 3.99c3.139 0 5.013-2.115 5.781-4.735.798.361 1.348 1.204 1.348 2.47 0 3.387-3.907 5.232-7.22 5.232-4.885 0-8.077-3.207-8.077-8.424 0-6.392 4.223-10.487 9.9-10.487 3.808 0 5.69 1.671 6.97 3.914l2.108-1.475C21.44 2.078 18.331 0 13.663 0 6.227 0 1.168 5.277 1.168 12.934c0 7 4.953 11.066 10.856 11.066 4.878 0 9.809-2.846 9.809-7.716 0-2.545-1.46-4.231-3.569-5.187m-6.33 4.855c-1.077 0-2.026-.512-2.026-1.453 0-1.483 1.822-1.934 3.606-1.934.678 0 1.34.045 1.927.173-.422 1.927-1.671 3.215-3.508 3.214Z" },
9
+ "tiktok": { title: "TikTok", path: "M12.525.02c1.31-.02 2.61-.01 3.91-.02.08 1.53.63 3.09 1.75 4.17 1.12 1.11 2.7 1.62 4.24 1.79v4.03c-1.44-.05-2.89-.35-4.2-.97-.57-.26-1.1-.59-1.62-.93-.01 2.92.01 5.84-.02 8.75-.08 1.4-.54 2.79-1.35 3.94-1.31 1.92-3.58 3.17-5.91 3.21-1.43.08-2.86-.31-4.08-1.03-2.02-1.19-3.44-3.37-3.65-5.71-.02-.5-.03-1-.01-1.49.18-1.9 1.12-3.72 2.58-4.96 1.66-1.44 3.98-2.13 6.15-1.72.02 1.48-.04 2.96-.04 4.44-.99-.32-2.15-.23-3.02.37-.63.41-1.11 1.04-1.36 1.75-.21.51-.15 1.07-.14 1.61.24 1.64 1.82 3.02 3.5 2.87 1.12-.01 2.19-.66 2.77-1.61.19-.33.4-.67.41-1.06.1-1.79.06-3.57.07-5.36.01-4.03-.01-8.05.02-12.07z" },
10
+ "facebook": { title: "Facebook", path: "M9.101 23.691v-7.98H6.627v-3.667h2.474v-1.58c0-4.085 1.848-5.978 5.858-5.978.401 0 .955.042 1.468.103a8.68 8.68 0 0 1 1.141.195v3.325a8.623 8.623 0 0 0-.653-.036 26.805 26.805 0 0 0-.733-.009c-.707 0-1.259.096-1.675.309a1.686 1.686 0 0 0-.679.622c-.258.42-.374.995-.374 1.752v1.297h3.919l-.386 2.103-.287 1.564h-3.246v8.245C19.396 23.238 24 18.179 24 12.044c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.628 3.874 10.35 9.101 11.647Z" },
11
+ "youtube": { title: "YouTube", path: "M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z" },
12
+ };
13
+
14
+ const SVG_NS = "http://www.w3.org/2000/svg";
15
+
16
+ export const SOCIAL_NAMES = Object.keys(D);
17
+
18
+ /** Builds an inline <svg> for a brand name, or null when it is unknown. */
19
+ export function socialIcon(name, cls = "size-5") {
20
+ const icon = D[name];
21
+ if (!icon) return null;
22
+ const el = document.createElementNS(SVG_NS, "svg");
23
+ el.setAttribute("class", cls);
24
+ el.setAttribute("viewBox", "0 0 24 24");
25
+ el.setAttribute("fill", "currentColor");
26
+ el.setAttribute("role", "img");
27
+ el.setAttribute("aria-label", icon.title);
28
+ const path = document.createElementNS(SVG_NS, "path");
29
+ path.setAttribute("d", icon.path);
30
+ el.append(path);
31
+ return el;
32
+ }