The walk already turned its back on the folders a camera and an editor leave
behind: a name beginning with `.` or `@` is skipped, which is `.thumbnails`,
`.git`, `.Trash`, and the `@eaDir` a Synology writes beside every frame.
An external volume drags along a second set of names, and none of them begins
with a dot, so every one of them was walked. `$RECYCLE.BIN` and `RECYCLER` are
where Windows parks what the reader deleted — frames among them, at full size,
and every one of them decoded into a thumbnail on the way in. `System Volume
Information` is Windows' own bookkeeping. `#recycle` is the Synology share, and
`lost+found` is the directory a Linux volume keeps for repairs. A frame that
comes back out of a bin is a frame the reader threw away, and the pictures in a
share's recycle folder are pictures someone else deleted; neither belongs in
the catalogue, and both cost the reading the same seconds a real frame does.
The folder also stood in the column as a row of its own.
The rule is now one `SYSTEM_DIR` expression over both sets, matched
case-insensitively — the same volume spells the bin `$RECYCLE.BIN` on one drive
and `$Recycle.Bin` on the next — and the walk asks it before it looks at what
the entry is, so a refused folder is neither read nor named.
Verified:
roll-walk-check.mjs — all passed, with two new assertions: the stand-in roll
now carries `$RECYCLE.BIN`, `$Recycle.Bin`, `RECYCLER`, `System Volume
Information`, `#recycle` and `lost+found`, and every one of them holds a
file named like a frame, so nothing about the files can be what keeps them
out. No frame is read from them, and none of them reaches the column.
library-check.mjs — 43 steps, all passed. scan-nav-check.mjs — all passed.
frontend tsc --noEmit clean.
ponytail: the list is the names the volumes being read actually use, not a
guess at every spelling there is — a box that files its junk under something
else gets a line in that expression, which is the whole of the change. A folder
of the reader's own that happens to be called `#recycle` is skipped too; that
name is worth the trade.
Co-authored-by: PenguinHarness <noreply@penguin.local>
RecipesCam
RecipesCam is a camera and photo-editing app built around recipes — reusable
looks that carry a film simulation plus a full set of adjustments. You shoot or
open a photo, dial in a look, and keep it as a recipe you can apply again, share
as a .recipe file, or save to your account.
It ships twice from one repository: a React Native (Expo) app for iOS and
Android, and a self-contained web build under docker/ that runs the same
render pipeline in the browser.
What it does
- Shoot with a recipe applied. Live viewfinder, GPS-tagged captures, and the recipe baked into the saved file.
- Film simulations. Built-in looks — PROVIPES, VELVIPES, CLASSIC CHRIPES, CLASSIC NEGIPES, ASTIPES, ETERNIPES, ACRIPES, B&W HIGH CONTRAST and the LC STREETLIFE pair — each with its own grain and tone curve.
- The full adjustment set. Exposure, contrast, highlights and shadows, saturation, colour temperature, clarity, grain, and an HSL mixer with a colour picker that samples straight off the photo.
- Geometry. Crop to a fixed ratio or free-form, quarter turns, and a straighten ruler, plus printed frames (classic border, retro instant, wall frame portrait/landscape).
- Finishing. Watermark and GPS stamp, EXIF carried through the export, JPEG written with a proper 300 DPI JFIF header.
- On-device upscaling. A Real-ESRGAN pass runs locally when an export asks for more pixels than the source has — no server sees the photo.
- Recipes. Save, favourite, export and import
.recipefiles; the web build keeps them in your account, the phone build also keeps them on device.
The two builds
| Build | Where | Stack |
|---|---|---|
| iOS / Android | repo root | Expo + React Native, @shopify/react-native-skia for the render pipeline, NativeWind for styling |
| Web | docker/ |
Vite + React, CanvasKit (canvaskit-wasm) for the same pipeline, Fastify + SQLite API for accounts and recipes |
The render engine is shared by design: the web build compiles the app's own
src/utils/* and type definitions unchanged, with @shopify/react-native-skia
aliased to a CanvasKit shim (docker/frontend/src/engine/skiaShim.ts). A look
looks the same on both because it is the same code.
Running the web build
cd docker
cp .env.example .env
docker compose up -d --build
# → http://localhost:8090
Photos never leave the browser: grading, framing, watermarking and JPEG
encoding all run in the visitor's tab; the API only stores accounts and recipe
JSON. See docker/README.md for the layout and the proxy setup.
Running the app
npm install
npx expo start # Expo Go / dev client
npx expo run:android # or run:ios for a native build
Repository layout
App.tsx, src/ the Expo app: screens, tool rail, viewfinder, shaders
docker/ the web build (frontend + API + compose file)
frontend/shared/ vendored copies of the app's types and utils
frontend/src/engine/ CanvasKit shim, export engine, super-resolution
docs/ privacy policy
THIRD_PARTY_NOTICES.md licences of the bundled fonts, models and libraries
Licence
See LICENSE and THIRD_PARTY_NOTICES.md.