{"id":50902986,"url":"https://github.com/stacksjs/image-optimization","last_synced_at":"2026-06-16T04:31:33.282Z","repository":{"id":354575909,"uuid":"1224159079","full_name":"stacksjs/image-optimization","owner":"stacksjs","description":null,"archived":false,"fork":false,"pushed_at":"2026-04-29T05:24:15.000Z","size":49,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-29T07:10:01.551Z","etag":null,"topics":["image","image-processing","imageoptim","optimization"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/stacksjs.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-29T02:38:20.000Z","updated_at":"2026-04-29T05:24:19.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/stacksjs/image-optimization","commit_stats":null,"previous_names":["stacksjs/image-optimization"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/stacksjs/image-optimization","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stacksjs%2Fimage-optimization","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stacksjs%2Fimage-optimization/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stacksjs%2Fimage-optimization/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stacksjs%2Fimage-optimization/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/stacksjs","download_url":"https://codeload.github.com/stacksjs/image-optimization/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stacksjs%2Fimage-optimization/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34391702,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-16T02:00:06.860Z","response_time":126,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["image","image-processing","imageoptim","optimization"],"created_at":"2026-06-16T04:31:32.367Z","updated_at":"2026-06-16T04:31:33.269Z","avatar_url":"https://github.com/stacksjs.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\u003cimg src=\".github/art/cover.jpg\" alt=\"image-optimization — native-style image optimization, built on stx + Bun\"\u003e\u003c/p\u003e\n\n[![GitHub Actions][github-actions-src]][github-actions-href]\n[![License][license-src]][license-href]\n[![Made with stx][stx-badge-src]][stx-badge-href]\n\n# image-optimization\n\nA native-style image-optimization app modelled on [ImageOptim](https://imageoptim.com), built with [stx](https://github.com/stacksjs/stx) + [Bun](https://bun.sh) and our pure-TypeScript image libraries.\n\n**No runtime dependencies beyond Bun and the `ts-*` libs** — every codec is in-process TypeScript:\n\n| Format | Library |\n|--------|---------|\n| PNG    | [`@stacksjs/ts-png`](https://github.com/stacksjs/ts-png) |\n| JPEG   | [`ts-jpeg`](https://github.com/stacksjs/ts-jpeg) |\n| WebP   | [`@stacksjs/ts-webp`](https://github.com/stacksjs/ts-webp) |\n| BMP    | [`@stacksjs/ts-bmp`](https://github.com/stacksjs/ts-bmp) |\n| GIF    | [`ts-gif`](https://github.com/stacksjs/ts-gif) |\n| AVIF   | [`@stacksjs/ts-avif`](https://github.com/stacksjs/ts-avif) |\n| SVG    | text minify (CDATA-aware) |\n\n```\n┌──────────────────────────────────────────────────────────┐\n│ ●●●            ImageOptimization                         │\n├──────────────────────────────────────────────────────────┤\n│  cover.jpg     84.2 KB   21.0 KB   −75.0%  ⬇  ✕         │\n│  hero.png       1.2 MB    872 KB   −28.4%  ⬇  ✕         │\n│  banner.gif      62 KB    21 KB    −64.9%  ⬇  ✕         │\n│                                                ┌────┐    │\n│                                                │ ↓  │    │\n│                                                └────┘    │\n├──────────────────────────────────────────────────────────┤\n│  +  3 files · saved 1.4 MB (66.2%)            ⋯  ↻ Again │\n└──────────────────────────────────────────────────────────┘\n```\n\n## Install\n\nDownload the latest `.dmg` from [Releases](https://github.com/stacksjs/image-optimization/releases/latest), drag the `.app` into `/Applications`, done.\n\n## Run from source\n\n```bash\nbun run setup        # provision system + JS deps (pantry install + bun install)\nbun run dev          # browser\nbun run dev:native   # native Craft window (macOS)\n```\n\nBun is the only system requirement — declared in `deps.yaml` and provisioned by [pantry](https://github.com/home-lang/pantry) when you run `pantry install` (or `bun run setup`). Everything else is a regular `package.json` dependency.\n\nThe `--native` flag relies on:\n\n- `@stacksjs/desktop` being built in the local stx repo\n- the `craft` binary on your `$PATH` (or at `~/.bun/bin/craft` / `CRAFT_BINARY_PATH=…`)\n\nIf `--native` can't find a craft binary, it transparently falls back to opening the system browser at the dev URL.\n\n## Distribution\n\n### `.dmg` builds\n\nPushing a `v*` tag triggers `.github/workflows/release.yml` which:\n\n1. Spins up a **macOS** runner\n2. Uses **pantry** to provision `bun` + `craft-native.org` (the native shell binary) — declared in [`deps.yaml`](./deps.yaml)\n3. Builds the stx site with `bun run build`\n4. Calls `packageApp()` from [`@craft-native/craft`](https://github.com/stacksjs/craft) to produce a signed-or-unsigned `.app` and a `.dmg` (via `hdiutil`)\n5. Uploads the `.dmg` to the GitHub Release via [`stacksjs/action-releaser`](https://github.com/stacksjs/action-releaser)\n\nThe packaging logic lives in [`scripts/package.ts`](./scripts/package.ts). To package locally:\n\n```bash\nbun run build:dmg\n# → dist/ImageOptimization-\u003cversion\u003e.dmg\n```\n\nOptional code-signing / notarization: provide `APPLE_SIGN_IDENTITY`, `APPLE_ID`, and `APPLE_APP_PASSWORD` as repo secrets and the workflow forwards them to `packageApp`.\n\n### Pantry registry\n\n```bash\nbun run release:patch   # 0.0.1 → 0.0.2 → tag → workflow runs → DMG on Releases\nbun run release:minor   # 0.0.x → 0.1.0 → tag → workflow runs → DMG on Releases\n```\n\nContinuous-release tarballs are also published to the pantry registry at `registry.pantry.dev` on every push to `main` (see [`ci.yml`](./.github/workflows/ci.yml)). Auth: `PANTRY_REGISTRY_TOKEN` repo secret.\n\n## Architecture\n\n```\n┌──────────────────────────────────────┐\n│  Craft native window (WKWebView)     │  ← UI\n│   (or system browser w/o --native)   │\n└────────────────┬─────────────────────┘\n                 │ fetch /optimize\n                 ▼\n┌──────────────────────────────────────┐\n│  stx dev server (Bun.serve)          │  ← server\n│   ├ /         → src/pages/index.stx  │\n│   └ /optimize → apiRoutes (config)   │\n└────────────────┬─────────────────────┘\n                 │ dispatch by magic bytes\n                 ▼\n┌──────────────────────────────────────────────────────────┐\n│  ts-png · ts-jpeg · ts-webp · ts-bmp · ts-gif · ts-avif  │  ← image libs\n│   (pure TS, zero runtime deps)                            │\n└──────────────────────────────────────────────────────────┘\n```\n\n## Format coverage\n\n| Format | Strategy |\n|--------|----------|\n| PNG    | decode → re-deflate at level 9 across 4 strategies, keep smallest |\n| JPEG   | decode → re-encode at q=85 (note: drops EXIF / orientation) |\n| WebP   | decode → re-encode lossless |\n| BMP    | decode → re-encode (drops alpha when fully opaque) |\n| SVG    | text minify, CDATA-aware |\n| GIF    | palette tightening per-frame + global-palette consolidation (lossless) |\n| AVIF   | ISOBMFF re-mux: strip Exif/XMP/`mime` items, thumbnails, redundant brands |\n| TIFF · ICO · HEIC | detected and reported, no optimizer yet (passthrough w/ note) |\n\nOutput is only kept when smaller than the input — already-optimized files come back unchanged with `passthrough: true` in `x-stx-result`.\n\n## UX\n\n- **Drag and drop** any number of images onto the window\n- **Click `+`** to pick files via the system dialog\n- **Per-row download** to save the optimized bytes\n- **Per-row remove** to drop a file from the list\n- **Toolbar summary** shows total bytes saved across all rows in real time\n- **`Again`** re-runs the optimizer on every file (useful after a settings change)\n- After the first drop, the **drop zone shrinks into the corner** but stays interactive\n- **Errors surface in the toolbar in red** so failures aren't silent\n\n## Layout\n\n```\nimage-optimization/\n├── src/\n│   ├── pages/\n│   │   └── index.stx       — the ImageOptim UI (drop zone, list, toolbar)\n│   ├── components/\n│   │   ├── DropZone.stx    — full-window drop target\n│   │   ├── Toolbar.stx     — bottom action bar\n│   │   └── FileRow.stx     — per-file row template\n│   └── optimize.ts         — magic-byte format detection → per-format pipeline\n├── scripts/\n│   └── package.ts          — builds .app + .dmg via @craft-native/craft\n├── .config/\n│   ├── stx.ts              — registers /optimize via apiRoutes\n│   └── crosswind.ts        — content paths for utility-class generation\n├── deps.yaml               — pantry-provisioned system deps (bun, craft)\n└── package.json\n```\n\n## Notes on dependencies\n\nThe image libs are imported by relative path from `~/Code/Libraries/ts-*`, and stx itself is invoked via the local repo at `~/Code/Tools/stx/stx`. This mirrors how the example in the stx monorepo is wired up — the libs remain editable in place rather than being copied into `node_modules`.\n\n## License\n\nMIT — see [LICENSE](./LICENSE.md)\n\n\u003c!-- Badges --\u003e\n\n[github-actions-src]: https://img.shields.io/github/actions/workflow/status/stacksjs/image-optimization/ci.yml?branch=main\u0026style=flat\u0026colorA=18181B\u0026colorB=4F46E5\n[github-actions-href]: https://github.com/stacksjs/image-optimization/actions/workflows/ci.yml\n[license-src]: https://img.shields.io/github/license/stacksjs/image-optimization.svg?style=flat\u0026colorA=18181B\u0026colorB=4F46E5\n[license-href]: ./LICENSE.md\n[stx-badge-src]: https://img.shields.io/badge/built%20with-stx-4F46E5?style=flat\u0026colorA=18181B\n[stx-badge-href]: https://github.com/stacksjs/stx\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstacksjs%2Fimage-optimization","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstacksjs%2Fimage-optimization","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstacksjs%2Fimage-optimization/lists"}