Skip to content

feat: add pipeline links, ready-made tools, offline use, HEIC input and background removal - #38

Merged
devchaudhary24k merged 19 commits into
mainfrom
feat/version-2
Nov 9, 2026
Merged

devchaudhary24k merged 19 commits into
mainfrom
feat/version-2

Conversation

@devchaudhary24k

Copy link
Copy Markdown
Member

Version 2 of Hexlode. The pipeline becomes something people can share and find as a ready tool, the app works offline and opens files from the system, iPhone photos open, and backgrounds can be removed on the device.

Linear project: https://linear.app/pixelactstudio/project/hexlode-version-2-d5a785ddbca8 (DAMN-18 to DAMN-25). The plan is in implementation.md (phases 4 to 7), the scope in idea.md, and the decisions in ADRs 0013 to 0017.

What's in it

Pipeline links (DAMN-19, ADR 0013)

  • Copy link in the Studio and on saved tool pages. The link carries the pipeline file, deflated, after the # of /tools/shared, so no server stores it.
  • The shared page offers Save to my tools and Open in the Studio. A damaged link, one from a newer version, or a pipeline too large for a link (over 7,800 characters, such as one with a big logo) each gets a clear message.

Built-in tools (DAMN-20, ADR 0014)

  • Eight ready-made tools at /tools/<slug>, each with its own title, description, share card and sitemap entry, plus a stated reason in src/features/built-in-tools/tools.ts:
    • HEIC to JPG
    • WebP to JPG (transparency becomes white, not black)
    • JPG to PDF
    • Instagram without cropping (4:5 or square at 1080 px)
    • Product photos on a white background (cut out, trimmed, centred on a white square at about 85% fill, up to 2000 px)
    • Favicon generator
    • Watermark photos
    • Add logo to photos
  • A tool can show the settings of chosen nodes and say what must be set before it runs (a logo, for example).
  • Tools that follow platform rules link the guidance and the date it was checked.
  • Listed in the Tools menu, home page, footer and open page. Saved pipeline tool pages now ask not to be indexed.
  • Pad / Extend gains a margin setting.

Run count (DAMN-21)

  • Saved pipelines count their runs, and run_started carries pipelineRuns.
  • Run events and page views can name a built-in tool.

Offline and installable (DAMN-22, ADR 0015)

  • /sw.js is generated from the client build. Every script, style, font and codec is cached on the first visit; pages load network-first and fall back to a cached copy or the app shell.
  • An offline badge shows in the top bar.
  • ONNX Runtime's WebAssembly (about 41 MB) is cached the first time Remove background runs, not on install.

Open with Hexlode (DAMN-23)

  • File handlers for images and .hexlode files on desktop Chromium. Images open on /open, which lets the user pick a quick, built-in or saved tool.
  • An Android share target opens Strip metadata set to remove location.

HEIC input (DAMN-24, ADR 0016)

  • libheif and libde265 run in WebAssembly through libheif-js, decode only.
  • Photos turn upright exactly once, and EXIF and GPS are read and edited in place.
  • A HEIC image whose pixels changed becomes JPEG (PNG with transparency), with a warning.
  • The file list draws previews of HEIC, JPEG XL and QOI files.

Remove background (DAMN-25, ADR 0017)

  • A node and a quick tool, using BiRefNet_lite 512 (MIT) run by ONNX Runtime Web on WebGPU or WebAssembly.
  • One shared inference worker holds the model.
  • The model is served from Hexlode's own /models/ route (fetched once from a pinned Hugging Face revision and checked by SHA-256), downloaded only after the user agrees, and kept in Cache Storage.
  • Soft edges get their colour estimated, so no fringe shows on white.

Needs your attention

  • HEVC patents: shipping a software HEVC decoder carries patent-pool risk. ADR 0016 sets it out. Removing HEIC later means dropping the decoder and the heic format only.
  • Model hosting: the first model request needs outbound HTTPS to huggingface.co. Mount a volume at HEXLODE_MODEL_DIR so the 94 MB file survives deploys. DEPLOY.md covers both.
  • Speed without WebGPU: background removal on WebAssembly takes roughly 9 to 17 s per photo on one thread. WebGPU speed is estimated, not measured. A session needs about 2.5 GB, so low-memory phones may fail with an error.
  • Uncompressed runtime: Nitro serves ONNX Runtime's WebAssembly uncompressed. Compression at the proxy would cut it to about 7 MB.
  • Share target: any site can POST images to /share-target. They only appear in Strip metadata; nothing runs until the user does.
  • New dependencies: libheif-js and onnxruntime-web. They were added without re-resolving the TanStack packages pinned to latest.

Testing

  • pnpm validate passes: Biome, types, 61 test files with 2,109 tests (unit and browser, including the node pair matrix with HEIC and Remove background), and the build.
  • pnpm test:e2e (new, run after pnpm build) passes 8 of 8 against the production build: offline load and conversion, the Studio offline, a saved tool through the app shell, the share target and the launch queue.
  • pnpm test:model (new, opt-in, downloads the 94 MB model) passes 6 of 6 for background removal, with IoU between 0.877 and 0.997 against exact masks.
  • New pages were screenshot-checked at 2560×1400 and 390×844 in dark and light.

Share copies a link that carries the pipeline file, deflated, after the #,
and opens it as a pipeline tool page at /tools/shared with Save and Open in
the Studio. Saved pipelines count their runs, and the run event carries the
count. Run events and page views can name a built-in tool.
WebP to JPG, JPG to PDF, Instagram without cropping, Favicon generator,
Watermark photos and Add logo to photos run as pipeline tools at
/tools/<slug>, rendered on the server with their own titles and share
cards. A tool can show the settings of chosen nodes and say what must be
set before it runs. The Tools menu, home page and footer list them, and
saved pipeline tool pages ask not to be indexed.
HEIC and HEIF photos open wherever other images do: the Files node, every
quick tool, pipeline tool pages and the Studio, with live previews.

- libheif and libde265, compiled to WebAssembly by libheif-js, decode the
  primary image in the workers on first use. The loader and .wasm are
  separate files served by Hexlode and credited on the codec licences page.
- libheif applies irot, imir and clap, and the header reader reports the
  size after them. Reading HEIC metadata resets the EXIF orientation tag,
  so photos turn upright exactly once.
- EXIF, XMP and the colour profile are read with one HEIF module shared
  with AVIF, and Strip metadata and Set copyright edit them in place.
- Hexlode never writes HEIC. A HEIC image that must keep its own format
  becomes JPEG, or PNG when it has transparency, with a run warning.
- Fixtures laid out like phone photos (grid tiles, irot with an EXIF
  orientation, GPS, a Display P3 profile, alpha) join the node pair matrix
  and the quick tool and template exit gates.

Records the decoder choice, licence and patent risk in ADR 0016.
- Add a service worker at /sw.js, registered in production builds only. A
  Vite plugin lists every client build and public file once the bundle is
  complete and bundles the worker with that list and a content-hashed
  version, so every script, style, font and codec downloads on the first
  visit, codecs not used yet included
- Load pages from the network first and keep a copy of each page that loads
  only this build's files; offline, open that copy or the app shell
- Add /app-shell, a route without server rendering, so the server renders
  only the frame and the router renders pages never cached, such as
  pipeline tools
- Take over at once and delete earlier builds' caches, never another
  feature's; leave /api/, /models/, server functions and other origins to
  the network
- Show an Offline badge in the top bar while there is no connection
- Explain the offline copy on the privacy page, and record the decision in
  ADR 0015
- Add pnpm test:e2e, which drives the production build offline with
  Playwright, and run it in CI after the build
On a busy machine a page can take longer than expect.poll's one second to
render, so the checks gave up early. Wait with Playwright instead, with a
minute per page, run and download.
- Add file handlers to the web manifest for the image formats Hexlode reads
  and for .hexlode files (registered as application/vnd.hexlode.pipeline+json,
  so the app never claims all JSON), opening /open in a new window
- Add /open: it reads window.launchQueue, opens a .hexlode file in the Studio
  through the draft the Studio's own import writes, and asks which quick tool
  or saved pipeline tool should take the images
- Let quick tool and pipeline tool pages start with files handed to them in
  the tab's memory
- Add an Android share target for photos. The service worker keeps shared
  photos in Cache Storage until Strip metadata takes them, deletes them
  then, and opens the page set to remove location only. Without the worker
  the server redirects to Strip metadata without reading the photos
- Add the files_opened event and the open page to page_viewed
- Describe offline use and installing in idea.md, and list shared photos on
  the privacy page
Offline use after the first visit, an installable app, and opening images
and .hexlode files from the system. Pipeline tool pages take files handed
over from the open page, which also offers the built-in tools, and the
service worker downloads the built-in tool pages on install.
The list of added files draws previews of HEIC, JPEG XL and QOI files in a
small worker that decodes, turns upright and scales them, since browsers
other than Safari cannot show those formats themselves.
HEIC and HEIF photos open wherever other images do, decoded with libheif
in WebAssembly, and the file list draws previews of formats the browser
cannot show. The installed app also opens .heic and .heif files.
Turns iPhone HEIC photos into upright JPEG files that keep their date,
camera and location, a whole batch at once on the device.
Add a Remove background node and a quick tool at /remove-background. The node
runs BiRefNet_lite (MIT, a 512 x 512 ONNX export pinned by revision and SHA-256)
through ONNX Runtime Web, on WebGPU or else on WebAssembly, and cuts the subject
out onto transparency. JPEG becomes PNG; the quick tool saves PNG or WebP.

Hexlode's server fetches the pinned model once, checks it and serves it from
/models/ with long caching. Pages ask before the one-time download, say its
size, check the hash again and keep the model in Cache Storage. One inference
worker per page holds the model, and engine and preview workers reach it
through their own MessagePort, sending only the 512 x 512 image. Soft edges get
their colour estimated, so no fringe of the old background shows on white.

The estimate runs model work one item at a time and counts the download and
the model's start. Analytics record downloads and runs (backend, timings, error
codes). The privacy page, licences, share card, home page and Studio cover the
new node and tool. Tests use a 305-byte stand-in model; pnpm test:model runs
the real one on licensed photos with reference masks.
ADR 0017 explains the model choice, with the measurements behind it, how the
model is delivered and how inference fits the engine. Phase 7, the product
description, the glossary and the deployment guide describe the final design,
including HEXLODE_MODEL_DIR and the opt-in model quality suite.
Remove background cuts subjects out on the device with BiRefNet_lite run
by ONNX Runtime Web, as a node and a quick tool, with the model delivered
from Hexlode's own server. The offline worker keeps ONNX Runtime's
WebAssembly on first use instead of on install, so the first visit stays
small.
Cuts each product out, trims it, centres it on a pure white square with
a margin so it fills about 85% of the frame, and saves a JPEG of up to
2000 pixels, following Amazon's product image requirements.
CodeQL flagged the model server for creating files in the shared
temporary folder, and the worker message handlers for not checking where
messages come from.

The model server now makes its folder for its own user only, refuses a
folder that is a link, belongs to another user or others can write to,
creates the download exclusively with owner-only access, and serves a
file already in the folder only after its SHA-256 matches. Every worker
ignores messages that did not come from the page that started it.
@devchaudhary24k
devchaudhary24k merged commit 8d41805 into main Nov 9, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants