Skip to content

release: toolbelt v0.8.0, four new tools and guides - #272

Merged
anurag629 merged 50 commits into
productionfrom
develop
Oct 23, 2026
Merged

anurag629 merged 50 commits into
productionfrom
develop

Conversation

@anurag629

Copy link
Copy Markdown
Member

Four new tools, a guides section and a long list of fixes, almost all of them from this month's contributors.

New tools

Guides

JSON formatter

Base64 and URL encoder

JWT decoder

Invoice generator

Everywhere else

New tools and a new section, so this is a minor release (v0.8.0).

Come hang out with us on Discord: https://discord.gg/mpGdJyjaRQ

anurag629 and others added 30 commits October 17, 2026 08:38
…188)

* feat(color-converter): add color converter & contrast checker (#84)

* fix(color-converter): address maintainer review feedback for #84

- switch registry accentVar to --fuchsia and accentHex to #F26DF0
- replace --violet styling in ColorConverterClient with --fuchsia
- add peer-focus-visible styling to swatch labels on keyboard focus
- remove conflicting btn btn-ghost classes from FG/BG buttons to match info pill
- remove NAMED_COLORS, transparent support, and Named label
- simplify wcagRating to return only normalText and largeText without overloads
- add tests for 3-value space-separated rgb/hsl syntax
- fix contrastRatio translucent alpha compositing order over white and add regression test
…178)

HistoryPanel created its own useHistory(storageKey) copy of the same
localStorage state, and useLocalStorage does not sync separate instances.
The panel therefore never saw entries the tool had just pushed (they showed
up only after a reload), and the tool's stale copy wrote removed entries back
on its next push.

The tools now own the single instance and pass it to the panel as a prop
(HistoryApi), and the panel's own max default of 20 is gone, so the history
size stays defined once per tool (15, as both tools already passed).

Three files, +14/-6; lib/useLocalStorage.ts is unchanged.
* feat: add Unix timestamp converter

* fix: address timestamp converter review feedback

* fix: restore upstream files after review
…ith 3 updates (#198)

Bumps the dev-dependencies group with 3 updates in the / directory: [postcss](https://github.com/postcss/postcss), [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest) and [wrangler](https://github.com/cloudflare/workers-sdk/tree/HEAD/packages/wrangler).


Updates `postcss` from 8.5.29 to 8.5.30
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](postcss/postcss@8.5.29...8.5.30)

Updates `vitest` from 4.1.11 to 4.1.12
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md)
- [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.12/packages/vitest)

Updates `wrangler` from 4.149.0 to 4.151.0
- [Release notes](https://github.com/cloudflare/workers-sdk/releases)
- [Commits](https://github.com/cloudflare/workers-sdk/commits/wrangler@4.151.0/packages/wrangler)

---
updated-dependencies:
- dependency-name: postcss
  dependency-version: 8.5.30
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-dependencies
- dependency-name: vitest
  dependency-version: 4.1.12
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-dependencies
- dependency-name: wrangler
  dependency-version: 4.151.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: dev-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…locations (#181)

* Fix: Update parseSyntaxError to use lossless-json for accurate error locations

* Refactored: review feedback on fallback logic and test cases

* Apply batched suggestions from code review

Co-authored-by: Anurag Verma <78868769+anurag629@users.noreply.github.com>

* Apply batched suggestions from code review

Co-authored-by: Anurag Verma <78868769+anurag629@users.noreply.github.com>

* style: apply formatting fix for else try catch block

---------

Co-authored-by: Anurag Verma <78868769+anurag629@users.noreply.github.com>
* feat: add Text Case Converter tool

Closes #81

* fix: address review feedback on case converter a11y

- drop textarea aria-label (visible label already associated via htmlFor)
- drop invalid aria-label from output pre elements
- stop uppercasing output labels so casing stays readable

---------

Co-authored-by: harshit-exe-dev <harshit-exe-dev@users.noreply.github.com>
…ds (#207)

* fix: give the JWT decoder switch real tab semantics and name its fields

The Decode/Encode switch looked like tabs but was two plain buttons, so screen
reader users had no tab semantics and no arrow-key navigation. Follow the
Base64 tool: a tablist with aria-label, role=tab on each button with
aria-selected, aria-controls and a roving tabIndex, Arrow/Home/End handling
with wraparound, and role=tabpanel with aria-labelledby on each panel.

Also name the fields that had none: the token textarea, the Endpoint input
(now tied to its label with htmlFor and id), the payload editor, the
secret/key inputs and the JWKS URL. The decoding error block becomes
role=alert, and the verification toggle gets aria-expanded and aria-controls.

No className changes, so both themes look identical.

* fix: point the verification toggle at the element it expands

The toggle got aria-controls="jwt-verification-explained" but nothing carried that\nid, so the control referenced nothing. Give the explanation block the id it\nis pointed at.
Signed-off-by: Anurag Yadav <anuragyadav2787@gmail.com>
* preserve array structure in XML conversion

Signed-off-by: Anurag Yadav <anuragyadav2787@gmail.com>

* Added test

Signed-off-by: Anurag Yadav <anuragyadav2787@gmail.com>

---------

Signed-off-by: Anurag Yadav <anuragyadav2787@gmail.com>
* fix: quote YAML 1.1 ambiguous strings

* fix: handle YAML 1.1 numeric underscores
The invoice generator showed a small "Loading..." card until the working
invoice was read from localStorage after mount. When the editor and
preview then appeared, the FAQ and CTA sections below were pushed down by
over a thousand pixels, giving a Cumulative Layout Shift of 0.57 on
desktop and 0.47 on mobile.

Render the real toolbar, editor and preview from the first paint, using a
fixed placeholder invoice (no random ids or dates, so server and client
HTML match), and swap in the seeded invoice after mount. While the
placeholder is showing the sections are inert and marked aria-busy so
nothing can be typed into it.

Lighthouse (production build, median of runs): desktop CLS 0.567 -> 0.003,
mobile CLS 0.465 -> 0.000.

Closes #159

Co-authored-by: Saif Ullah jafri <saifullahjafri@Saifs-MacBook-Air.local>
* fix: keep JSON Find focus and highlight full matches

* fix: preserve JSON editor selection while Find is open
Co-authored-by: lb1192176991-lab <lb1192176991-lab@users.noreply.github.com>
* ci: add Lighthouse CI performance budgets (#48)

* ci: address maintainer review feedback for #48

- update lighthouse script to always run summary after lhci assert fails

- tighten CLS error threshold to 0.25 in lighthouserc.json

- add .lighthouseci/ to .gitignore
…230)

nextInvoiceNumber is meant to keep the prefix and zero padding of the
highest saved invoice, but an empty prefix was replaced with "INV" and
every number was padded to at least four digits, so 1001 became INV1002
and INV-07 became INV-0008.

Keep the prefix exactly as saved, even when it is empty, and pad to the
width of the highest number's own digits, so 1001 -> 1002, INV-07 ->
INV-08, A1 -> A2 and 2026-001 -> 2026-002. A number that outgrows its
width just gets longer (INV-99 -> INV-100). INV plus four digits is used
only when no invoice has a number yet, so the list still starts at
INV0001.

Closes #220

Co-authored-by: Saif Ullah jafri <saifullahjafri@Saifs-MacBook-Air.local>
Pasting text like password123, hello-world, 2024-01-15 or user_id_123
into the Base64 Text tab in Encode mode switched to Decode and showed
"Decoded successfully but result is not valid UTF-8 text". detectBase64
says yes to anything of eight or more base64-alphabet characters with a
digit, +, /, =, _ or -, and atob decodes all four, but not to text.

Add detectBase64Text, which also requires the decoded bytes to be valid
UTF-8, and use it in the paste handler. Real base64 of text such as
SGVsbG8= and aGVsbG8gd29ybGQ still switches. With the gzip option on,
gzipped base64 still switches too, as it did before. detectBase64 and
its tests are unchanged.

Closes #219

Co-authored-by: Saif Ullah jafri <saifullahjafri@Saifs-MacBook-Air.local>
prakashNalla and others added 20 commits October 21, 2026 15:39
…the other identifier styles (#229)

* fix(case-converter): drop punctuation from camelCase, snake_case and the other identifier styles

* fix(case-converter): keep combining marks in the identifier styles
Signed-off-by: Anurag Yadav <anuragyadav2787@gmail.com>
…240)

Look up standard claims and algorithms with Object.hasOwn so names such
as __proto__, constructor and toString no longer match what every object
inherits from Object.prototype. A token with a __proto__ claim or alg
crashed the whole tool.

Closes #232

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
…ead (#241)

The JSON to YAML conversion wrote each number exactly as it appeared in
the JSON. For an exponent that is wrong for YAML 1.1 parsers: PyYAML only
reads a float that has a dot and a signed exponent, so 1e3, 1.5e3, 1E3,
2e+5, -1e3 and 1.0e3 loaded as strings, and a Python script reading the
converted file got text where the JSON had a number.

When a number has an exponent, add ".0" if the part before the "e" has no
dot, and "+" if the exponent has no sign: 1e3 -> 1.0e+3, 1e-7 -> 1.0e-7,
1.5e3 -> 1.5e+3, 1E3 -> 1.0E+3, -1e3 -> -1.0e+3. The text of the number
is edited as is, so long values keep every digit, and numbers without an
exponent are unchanged. Checked with PyYAML 6.0, js-yaml and yaml.

Closes #237

Co-authored-by: Saif Ullah jafri <saifullahjafri@Saifs-MacBook-Air.local>
…unding it (#245)

toFixed(2) showed 4.4993 as 4.50 next to a failed 4.5:1 badge. formatContrastRatio in lib/color-utils.ts truncates to two decimals and has unit tests. Closes #235.
Converts integers between binary, octal, decimal, hex and any base from
2 to 36, and shows the two's complement for 8, 16, 32 and 64 bits.

- lib/number-base.ts: parseInteger, formatInteger, toTwosComplement and
  fromTwosComplement, all BigInt, so values above 2^53 stay exact. The
  parser reads the sign before a 0b/0o/0x prefix, ignores _ and spaces,
  and says which character is wrong and where.
- /number-base-converter: one input with a base select, Copy buttons on
  the base 2, 8, 10 and 16 outputs, a custom output base, and a bit-width
  select for the two's complement.
- Registry entry at the end of TOOLS, and the tool in the bug report
  dropdown.

Closes #128
…256)

V8 Date.parse rolls days 29-31 into the next month for short months.
When the input starts with YYYY-MM-DD, compare the typed day to that
month's length and return the existing validation error.

Closes #250
Six switches marked the selected button only with data-active, so a
screen reader heard two plain buttons. Following the URL encoder's mode
switch from #242, each wrapper is now a role="group" with an aria-label,
and each button has type="button" and aria-pressed. data-active stays
for the styling, so nothing changes visually.

- Base64 Text tab: direction and Standard/URL-safe
- Base64 File tab: line length
- JWT verify panel: Secret/JWKS
- JSON formatter: indent and Code/Tree

Adds component tests for the Base64, JSON formatter and JWT switches.

Closes #254
#249) (#260)

The plain-text heuristic counted printable ASCII over the first 200 bytes,
so any accented or non-Latin sentence whose multibyte UTF-8 encodings sit
above 0x7F was labeled "Unknown / binary" with no preview and a decoded.bin
download. Text detection now decodes the whole payload with the existing
strict decodeUtf8Bytes and accepts it when it carries no control characters
other than tab, line feed and carriage return. NUL stays a refusal (it is
valid UTF-8), so the broken-ftyp test still gives null, and empty input
still gives null as before.

Co-authored-by: Rahul Ranjan <11793455+rahul05ranjan@users.noreply.github.com>
… theme (#262)

The selected button's white overlay, rgba(255, 255, 255, 0.07), doesn't
show on the light background, so only a faint outline was left. A light
theme rule now gives it rgba(10, 11, 15, 0.08) instead, following the
other [data-theme="light"] overrides. The dark theme is unchanged.

Closes #259
The Base64 to File tab stripped a data:...;base64, prefix, but only when
the media type was followed directly by ";base64,". A data URI can have
";name=value" parameters in between (RFC 2397), so
data:text/plain;charset=utf-8;base64,... and
data:application/json;charset=UTF-8;base64,... failed with "Invalid base64
string: illegal character ":" at position 4". Exported SVG and JSON files
often carry ";charset=utf-8".

Move the prefix parsing into parseBase64DataUri in lib/base64-utils.ts and
allow parameters between the media type and ";base64,". Only the media type
is used for the MIME fallback, and the notice now shows the whole prefix
that was stripped. The scheme and "base64" match in any letter case, an
empty media type (data:;base64,...) falls back to text/plain, and a data
URI without ";base64" (data:text/plain,hello) is still not treated as
base64.

Closes #248

Co-authored-by: Saif Ullah jafri <saifullahjafri@Saifs-MacBook-Air.local>
The JSONPath panel parsed the input with JSON.parse, so large integers and
high-precision decimals were rounded in the matches and in Copy matches.
The expression still runs on the JSON.parse value, so filters behave as
before, but each match is now read from a lossless-json parse of the same
input at the match's JSON Pointer, and the panel prints the matches with
lossless-json's stringify. A duplicate key is reported as an Invalid JSON
error instead of crashing the tool.

Closes #238


Claude-Session: https://claude.ai/code/session_01STJRQtEaEPSLfiWGMn8Syr

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* feat(guides): add typed guides registry and routing (#35)

* fix(guides): correct JSON formatter guide details
@anurag629 anurag629 added the release:minor Release: bump minor version label Oct 23, 2026
@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
toolbelt-dev f1ab01b Oct 23 2026, 02:06 AM

@anurag629
anurag629 merged commit cdfe7c8 into production Oct 23, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release:minor Release: bump minor version

Projects

None yet

Development

Successfully merging this pull request may close these issues.