mevebu 1.9.0 → 1.11.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
@@ -2,7 +2,7 @@
2
2
 
3
3
  # mevebu
4
4
 
5
- **188 free browser tools, one page each. No sign up. No uploads. Everything runs in your tab.**
5
+ **198 free browser tools, one page each. No sign up. No uploads. Everything runs in your tab.**
6
6
 
7
7
  A private toolbox for the small jobs that do not deserve a whole app: format JSON, decode a JWT, make a password, build a cron line, pick a color, scan a QR code, and a lot more.
8
8
 
@@ -10,7 +10,7 @@ A private toolbox for the small jobs that do not deserve a whole app: format JSO
10
10
  [![npm downloads](https://img.shields.io/npm/dm/mevebu?color=2ea44f)](https://www.npmjs.com/package/mevebu)
11
11
  [![License: MIT](https://img.shields.io/github/license/mevebu/mevebu?color=blue)](./LICENSE)
12
12
  [![GitHub stars](https://img.shields.io/github/stars/mevebu/mevebu?style=flat&color=yellow)](https://github.com/mevebu/mevebu/stargazers)
13
- ![Tools](https://img.shields.io/badge/tools-188-brightgreen)
13
+ ![Tools](https://img.shields.io/badge/tools-198-brightgreen)
14
14
  ![Privacy](https://img.shields.io/badge/privacy-100%25%20in--browser-blue)
15
15
 
16
16
  [Quick start](#quick-start) · [What's inside](#whats-inside) · [Privacy](#privacy-by-design) · [Host it yourself](#host-it-yourself) · [Development](#development) · [FAQ](#faq)
@@ -23,7 +23,7 @@ A private toolbox for the small jobs that do not deserve a whole app: format JSO
23
23
 
24
24
  ## What is mevebu?
25
25
 
26
- mevebu is a collection of **188 small online tools** that all run **inside your browser**. Each tool lives on its own page, does one job, and does it fast. Most of them update as you type, so there is no submit button and no waiting.
26
+ mevebu is a collection of **198 small online tools** that all run **inside your browser**. Each tool lives on its own page, does one job, and does it fast. Most of them update as you type, so there is no submit button and no waiting.
27
27
 
28
28
  Think of it as a clean, private alternative to the usual "free online tools" websites. There are no accounts, no ads in your way, no uploads, and no tracking. Your text, passwords, tokens, and files stay on your device.
29
29
 
@@ -65,14 +65,14 @@ mevebu tries port `5555` first, then `5678`, then `5432`, and finally any free p
65
65
 
66
66
  ## What's inside
67
67
 
68
- mevebu has **188 tools in 20 categories**. Here is the full breakdown:
68
+ mevebu has **198 tools in 20 categories**. Here is the full breakdown:
69
69
 
70
70
  | Category | Tools |
71
71
  | ------------------ | ----: |
72
72
  | Random | 3 |
73
73
  | Passwords and keys | 10 |
74
74
  | Developer | 21 |
75
- | Convert | 5 |
75
+ | Convert | 15 |
76
76
  | Text and data | 14 |
77
77
  | Daily use | 8 |
78
78
  | Math | 17 |
@@ -89,7 +89,7 @@ mevebu has **188 tools in 20 categories**. Here is the full breakdown:
89
89
  | GitHub | 8 |
90
90
  | Units | 8 |
91
91
  | QR codes | 2 |
92
- | **Total** | **188** |
92
+ | **Total** | **198** |
93
93
 
94
94
  The counts come straight from the tool registry, so this table and the app never drift apart.
95
95
 
@@ -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
 
@@ -179,7 +180,7 @@ That is all. The catalog, search, breadcrumbs, and category pages pick it up aut
179
180
  ## FAQ
180
181
 
181
182
  **What is mevebu?**
182
- mevebu is a free, open source collection of 188 browser based tools for developers, students, creators, and everyday use. Every tool runs locally in your browser.
183
+ mevebu is a free, open source collection of 198 browser based tools for developers, students, creators, and everyday use. Every tool runs locally in your browser.
183
184
 
184
185
  **Is mevebu really free?**
185
186
  Yes. It is MIT licensed, with no paid tier, no account, and no usage limits.
@@ -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")))