All notable changes to this project are documented here.
The format follows Keep a Changelog, and this project adheres to Semantic Versioning.
Nothing yet.
- JSON Formatter: a Tree view alongside the text output. It shows the same document the text does, with sorted keys and expanded embedded JSON, as a collapsible tree. Only the rows on screen are rendered, so large documents scroll smoothly. It opens a couple of levels deep, and has Expand all / Collapse all and full keyboard navigation (arrow keys, Home/End, Enter).
- JSON Formatter: copy the selected tree node's path as an RFC 6901 pointer
(
/items/0/id), a dot path (items[0].id) or a jq path (.items[0].id). - JSON Formatter: embedded JSON strings can be expanded one field at a time. A field nested inside another payload unlocks once its parent is expanded.
- JSON Formatter: auto-fixes for almost-JSON. It strips a JSONP wrapper, the
)]}'prefix, a byte-order mark and comments; converts single quotes; quotes bare keys; and removes trailing commas. Apply them in place, or review the change side by side in Diff first. - JSON Formatter: lossless mode. When a document has numbers JavaScript
cannot hold exactly, like
12345678901234567890, format and minify keep them digit for digit instead of rounding them. This includes numbers inside expanded embedded JSON. - Diff: Ignore whitespace, Ignore case and Trim toggles. They change what counts as a difference, never the text you pasted, so highlights still land on your original characters. When nothing else differs, the badge says what it ignored.
- Diff: copy the comparison as a unified patch that applies with
patch. It is always exact, so the Ignore toggles never change it.
- Diff: highlights inside a changed line now cover whole words and punctuation instead of stray characters, and the change count counts changed blocks of lines (what Alt+↓ steps through) rather than individual edits.
- JSON Formatter: with "Expand embedded JSON" on, a key named
__proto__was silently dropped from the output. It is now kept like any other key.
- HTML to Text: paste HTML and get readable plain text back. Paragraphs, lists
and tables keep their structure;
script,style,head,svg, hidden elements and icon ligatures (role="img") are dropped, and entities are decoded. Link URLs and class-based hidden detection (.hidden,.sr-only) are optional toggles.
- v0.2.3 was tagged but never released or deployed. The Release workflow pasted the changelog notes straight into a shell command, so the backticks in them were executed as command substitution and the step failed after the tag had been pushed, skipping Deploy. The notes are now passed through an environment variable. This release ships the 0.2.3 fixes as well.
- The security note in the ID generator rendered as overlapping fragments. It
was a paragraph with
display: flex, which makes every inline child — both text runs and the inline<code>— a separate flex item laid out in a row. - Dropdowns had cramped padding and the native arrow overlapped the border at our control height. The native arrow is now suppressed and replaced with a chevron drawn inside the padding box, with room reserved for it.
- The preset dropdown always displayed "Preset…" regardless of the settings in use. It now shows whichever preset the current options match, derived from those options rather than stored, so it survives a reload and falls back to "Custom" as soon as any field changes.
- The sidebar collapse control moved from the bottom of the tool list to the
top, where it is visible without scrolling, and the collapse now animates.
Both respect
prefers-reduced-motion.
- Releases tagged correctly but never deployed. The Release workflow only
started Deploy when no
RELEASE_TOKENwas configured, assuming a PAT-pushed tag would trigger it natively. With a PAT present that assumption took over silently: the tag was pushed, Release reported success, and Deploy had zero runs. Release now always starts Deploy explicitly.
- The Release workflow checked out the commit that triggered it rather than the
branch tip, so any re-run built on a stale base and its push was rejected as
a non-fast-forward. It now checks out
main, and each push is guarded so a partially-completed attempt resumes instead of blocking every retry. .envis now ignored. Nothing here needs one — the app has no backend and no runtime secrets — but an untracked.envis exactly what gets committed by accident on a public repository.
- ID Generator — prefixed identifiers in the Stripe style
(
sk_live_plus 24 random characters): set a prefix, separator, length, alphabet and batch size. Doubles as a password generator via a symbol alphabet and an option to drop confusable glyphs (0/O,1/l/I).- Live entropy readout with a qualitative strength verdict. Deliberately no "time to crack" figure: that depends entirely on assumed hardware and on whether the value is hashed, so quoting one would be false precision.
- Presets for Stripe secret and test keys, object ids, API tokens, hex session ids, passwords and human-readable codes.
- Values come from
crypto.getRandomValueswith rejection sampling, nevervalue % n, which biases toward the start of the alphabet. - Settings persist; generated values never do.
- Collapsible sidebar, kept as an icon rail with accessible names intact.
- Link to the GitHub repository in the header.
- Continuous integration on every pull request, including an entry-bundle size budget so a heavy dependency escaping a lazy chunk fails the build rather than reaching production.
- Release automation: merging to
maincreates a version tag, and the tag is what deploys. A merge that does not change the version inpackage.jsonreleases nothing, so merging and releasing are separate decisions.
- Focus rings on full-bleed text areas were drawn outside the element and clipped by the surrounding pane, so only the top and right edges were visible. They are now drawn inset.
- Unbiased randomness primitives moved to
src/lib/random.ts, now shared by the list shuffler and the ID generator.
First public release. Five tools, live at https://devtools.fadeltd.dev.
- Side-by-side and unified views, sharing one editor state so toggling keeps cursor and scroll position.
- Word-level highlighting inside changed lines, collapsed unchanged regions, and
exact
+n −m ~kstatistics computed from the diff rather than scraped from the rendered view. Alt+↑/Alt+↓to jump between changes; file picker per side.- Size guardrails: degrades to line-only highlighting over 2 MB and refuses input that cannot be displayed usefully, rather than hanging.
- Encode and decode with correct UTF-8 handling via
TextEncoder, using the nativeUint8Arraybase64 API where available and a chunked fallback elsewhere. - base64url alphabet, optional padding, and PEM/MIME line wrapping.
- Input sanitiser that strips
data:URI headers, percent-encoding, BOMs and line wrapping — and reports every change it made, so a failed decode is never ambiguous about whether the tool mangled the input. - Invalid characters are located by line and column, since the native API throws without a position.
- Sort (natural, alphabetical, codepoint, length), remove duplicates with four output modes, randomize, reverse.
- Correct handling of trailing newlines, CRLF and lone CR, and byte-order marks.
a\nb\nis two lines, not three. - Explicit blank-line policy rather than silently dropping them.
- Warns when lines differ only by Unicode normalisation form.
- Format, minify, sort keys, escape and unescape JSON strings.
- Reports every distinct problem — not just the first — each with line, column and a tab-aware caret. Trailing commas are reported at the comma rather than at the closing brace that failed to parse.
- Detects NDJSON and offers to wrap it as an array.
- Warns separately about numbers that lose precision and numbers whose value survives but whose printed form changes.
- Expands JSON that was serialised into a string, recursively, for the double-encoded payloads that structured logs produce. Expansion is a view over the source, so it is reversible exactly.
- Characters in all four meanings that differ in practice: grapheme clusters, code points, UTF-16 units and UTF-8 bytes.
- Letter, digit, punctuation, symbol and whitespace breakdown.
- Words, unique words, average and longest word, via
Intl.Segmenterrather than whitespace splitting. - Sentence counting that does not split on abbreviations or initials.
- Paragraphs, lines, longest line, reading and speaking time.
- Word and phrase frequency with density, and an opt-in stop-word filter.
- Command palette (
⌘K) that searches titles, aliases and competitor names, and prefetches a tool's code as you arrow through results. - Pin up to nine tools to
⌘1–⌘9;?shows a cheatsheet generated from the live shortcut registry. - Per-tool state persistence that restores your last input, splitting small state (localStorage) from large payloads (IndexedDB). Tools handling credentials never persist and show a visible "Not saved" badge.
- Responsive layout that stacks panes on mobile rather than shrinking them.
- Build-time prerendering per tool route plus a generated sitemap, so each tool has real HTML for cold loads and link previews.
- Content Security Policy with no
unsafe-eval, which also acts as a dependency filter. - No backend, no analytics, and no third-party requests. Fonts are self-hosted so the page contacts nothing but its own origin.