Commit Graph

370 Commits

Author SHA1 Message Date
3dtours d2549cfde1 fix(library): keep the shell the deploy replaced out of a failed navigation, and say when another tab holds the library open 2026-10-08 09:23:23 +07:00
3dtours eac206890a chore(docker): rebuild the NAS image bundle on f828fef 2026-10-08 08:43:01 +07:00
3dtours f828fefe13 fix(library): mark the roll's own row while it is read, and count rows from the catalogue up 2026-10-08 08:05:18 +07:00
3dtours c508727a80 chore(docker): rebuild the NAS image bundle on 26dab9a 2026-10-07 23:10:55 +07:00
3dtours 26dab9a92e fix(library): mark only the folder a reading stands in, and keep a one-folder reading out of the roll's count
The column's ring was drawn from the open node's roll: a row of another roll
had no path under it, so the empty prefix matched every row of the roll being
read and the whole column lit up. The roll's own row was marked too, at every
moment of a reading of it, and a reading kept to one folder counted its frames
onto the roll's row as well — a roll counted down to one branch of itself.
A queued request was drawn from the reading's roll instead of its own, so a
request on a second roll showed nothing at all.
2026-10-07 23:10:21 +07:00
3dtours 95d2b0cbaa fix(library): fix scanning spinner icon on active folder nodes and live counts update 2026-10-07 21:53:22 +07:00
3dtours d26e40a9ad chore(docker): rebuild the NAS image bundle on e56a791 2026-10-07 21:43:00 +07:00
3dtours e56a791b56 fix(library): unconditionally prevent browser context menu on folder tree nodes and fix absolute path matching 2026-10-07 21:40:14 +07:00
3dtours b95f303ffb chore(docker): rebuild the NAS image bundle on f43c3a8 2026-10-07 21:31:13 +07:00
3dtours f43c3a8014 fix(library): fix folder context menu lookup and ready check 2026-10-07 21:29:19 +07:00
3dtours b879116f84 chore(docker): rebuild the NAS image bundle on 06a5454 2026-10-07 21:19:13 +07:00
3dtours 06a545493f fix(library): fix photo count live update and optimize scan speed 2026-10-07 21:16:10 +07:00
3dtours e3ec0cf5ee chore(docker): rebuild the NAS image bundle on 66df58c 2026-10-07 20:15:12 +07:00
3dtours 66df58c7e4 fix(library): fix open location folder tree scroll and folder update path matching 2026-10-07 20:12:12 +07:00
3dtours dee70f7f1e chore(docker): rebuild the NAS image bundle on ced1a1c 2026-10-07 19:56:57 +07:00
3dtours ced1a1c4e1 fix(library): resolve root folder rename, open in explorer, open at location centering, UPDATE rescan bug, and optimize scan RAM memory usage 2026-10-07 19:16:03 +07:00
3dtours c3189c220d chore(docker): rebuild the NAS image bundle on ab7c9f0 2026-10-07 18:53:39 +07:00
3dtours ab7c9f0248 fix(library): support thumbnail generation and display for HEIC and TIF files 2026-10-07 18:43:11 +07:00
3dtours d24c3b5a17 fix(library): contain zoom in stage viewport and add drag/pan support for single view 2026-10-07 18:34:45 +07:00
3dtours ceba8b4e83 fix(ui): format LIGHT tab main chips to bold and accent, unbold TONE CURVE and DYNAMIC RANGE sub-chips 2026-10-07 18:22:07 +07:00
3dtours af97e53ab3 style(studio): place Color Chrome sub-buttons on 1 single row and format chip labels bold with theme accent colors 2026-10-07 17:59:50 +07:00
3dtours 4cdf828e9b fix(studio): ensure Color Chrome sub-chips are hidden when switching tabs 2026-10-07 17:41:00 +07:00
3dtours 18c09bf417 feat(studio): add right-click context menu (Solo mode, Collapse all, Expand all) for DevelopPanels, pin Color Chrome sub-chips, and fix library single view sorting & time range select 2026-10-07 17:35:20 +07:00
3dtours 2ab92a05fa feat(studio & library): sync Light & Color tab parameters (vibrance, highlight, shadow, white, black) to Gradient Mask & add multi-field sorting options 2026-10-07 12:36:55 +07:00
3dtours bd45a6b45a perf(library): persistent SPA shell, in-memory folder tree caching, and zero-flicker LRU thumbnail caching 2026-10-07 12:05:58 +07:00
3dtours 15dab6d65a feat(library & studio): add EXIF info bar, portrait frame panning, and multi-photo selection with Panorama/HDR context menu 2026-10-07 10:44:59 +07:00
3dtours 27e896661b build: update docker tarball and sha256 checksum for NAS release 2026-10-07 07:36:44 +07:00
3dtours d41118cebe chore: bump service worker cache version to recipescam-v11 2026-10-07 07:31:47 +07:00
3dtours 1d97f2a2f1 fix(raw): prioritize camera embedded preview for 100% exact color matching across SONY, OLYMPUS, FUJIFILM, NIKON, CANON, RICOH, LUMIX, LEICA, IPHONE, DNG 2026-10-07 07:29:42 +07:00
3dtours 38dcf6095c chore(docker): rebuild the NAS image bundle on 98db9ad 2026-10-06 22:57:50 +07:00
3dtours 98db9ad77d library: count a frame once when the read-back beats the handover
A reading this window holds hands each frame over the side as it stores it,
and the screen puts those rows into the strip on a half-second debounce. The
reading ends before that debounce fires, the screen reads the catalogue back
— and that read already carries the frames that were handed over, so the
rows still waiting are appended to a strip that has them: five files of a
plain JPEG folder read "10 photos", the album row read 10, and the strip drew
two tiles for every frame, one of them keyed the same as the other.

Measured on five JPEGs: header 10, album row 10, ten tiles of five ids. The
store held five rows and the reading reported 5/5 new frames the whole time,
so the count was the screen's alone. A second pass over the same folder read
five, because its read-back landed last.

The id is the frame, so the append now skips what the strip already has.
Same folder after: 5 photos, album row 5, five tiles of five ids, and no
duplicate-key warning from React. Six RAW bodies and the studio are untouched:
the fix is inside the catalogue screen's own row append.
2026-10-06 22:55:34 +07:00
3dtours 05c585a2e5 chore(docker): rebuild the NAS image bundle on 5ed5527 2026-10-06 22:13:17 +07:00
3dtours 5ed5527f32 raw: decode the preview already oriented and drop the second turn
Skia's Image.MakeImageFromEncoded applies the embedded JPEG's EXIF
orientation itself. A Panasonic RW2 preview stored 1920x1280 with
orientation 8 therefore decodes as 1280x1920 already — the frame the
camera meant.

previewGrid turned it a second time by its own per-orientation canvas
rotation, so a portrait frame's reference grid met the develop a quarter
turn out, previewMatch rejected the fit as folded and the RW2 opened with
no tone curve at all (~9-13 levels off across the shadows).

Drop the rotation and read the crop rectangle and aspect straight from
bmp.width()/bmp.height(), the display dimensions Skia hands back.

Panasonic RW2 now fits a real curve (dRGB 2.3,-0.0,0.4, was -9.1,-10.3,
-13.2). The five bodies that were already stable — ORF, NEF, DNG, both
RAF — measure exactly as before.

getJpegOrientation had no other caller and goes with it; raw-orf-check
drops its own byte-scanning preview finder, which crashed on DNG and RAF,
and reads the reference through the shipped extractEmbeddedJpeg.
2026-10-06 22:10:47 +07:00
3dtours 68442904c0 chore(docker): rebuild the NAS image bundle on 2af000c 2026-10-06 21:01:38 +07:00
3dtours 2af000c1cb library: open a phone's HEIC, and lock the shelves to accounts
A HEIC is the one camera file no engine on this platform will paint:
Chromium answers createImageBitmap with InvalidStateError and its
WebCodecs take no hvc1 still, and unlike a RAW there is no JPEG preview
inside to lift. It is decoded in software instead, by libheif built to
wasm, in a worker of its own — so a folder of them reads without the tab
stopping for a second, and what leaves the worker is the same JPEG
developRaw hands the studio. Nothing downstream knows the difference.

Into the studio it goes on the same terms as every other frame: dropped,
picked, or opened from the catalogue, no tier asked and the engine's own
colour untouched. The frame's date, ISO and position come off the HEIC
itself, which is passed along as the bytes the stamps read.

The catalogue's tile does not pay the decode: a phone writes a small
HEVC copy of its frame as an item of its own (thmb, 320x240 on an
iPhone), reachable only by walking the container by hand, and it decodes
in milliseconds where the frame takes a second — from the same 256KB the
catalogue already reads for a header.

The shelves themselves now want an account: a guest gets the note and
the way in, and the scan never starts.
2026-10-06 20:59:40 +07:00
3dtours c3386c1e0a assets: eight more mark faces from google/fonts
VT323, Alex Brush, Tilt Warp, Hahmlet, Pinyon Script, Allura, Dancing
Script and Amatic SC join the two the bundle already carried, each
subset to the same latin / latin-ext / vietnamese ranges and pinned to
its upright 400 instance where it is variable.
2026-10-06 20:59:34 +07:00
3dtours c39ad64899 chore(docker): rebuild the NAS image bundle on e5dda00 2026-10-06 18:23:24 +07:00
3dtours e5dda006e4 library: centre the quarter-turn pair on the stage bar
The bar under the picture is the grid it needs to be: equal free tracks either
side of the two buttons land them on the picture's own centre whatever OPEN and
the stars measure, and the pair keeps the ends it does not own. Under 700px the
stage is too narrow for one line, so the bar wraps as it did and the lines it
wraps to are centred.
2026-10-06 18:22:40 +07:00
3dtours 873d36d70f chore(docker): rebuild the NAS image bundle on e2e0297
The frontend image carries the library's quarter turn (catalogue `rot`, the
two chips under the preview, the wall and strip tiles, and the studio's
write-back), stamped e2e0297 at the foot of the rail.
2026-10-06 17:24:02 +07:00
3dtours e2e0297f31 library: stand a frame up in the catalogue, wall, strip and studio
The library stage carried no way to correct a frame shot on its side, and
nothing kept the correction. A quarter turn now rides on the catalogue row
(`rot`, beside `star`, carried over by a rescan), the two chips under the
preview step it, and the tile, the strip thumbnail and the stage all read it
through one bake so a frame is never stood on its side in one place and
upright in another. The studio opens a frame at that angle and a turn made
there is filed straight back onto the row, so either end of the turn is
what the frame comes up at next time.
2026-10-06 17:22:56 +07:00
3dtours 162024b9b3 chore(docker): rebuild the NAS image bundle on 31ca44e 2026-10-06 16:24:46 +07:00
3dtours 31ca44e839 fix(raw): stop the develop from handing the studio zero bytes
libraw-wasm moves the buffer it is given into its worker, so raw.open() detaches
the caller's array. developRaw read its embedded JPEG preview as a view of that
same array, so the open emptied the preview, Skia rejected the detached buffer
and the catch returned the emptied preview as the develop - every RAW opened from
the LIBRARY reached the studio as 0 bytes and the app said "Could not develop
this RAW file. Try again, or use its JPG."

Hand the worker a copy instead, in the develop and in the thumbnail path. The
EXIF stamps read off the caller's bytes afterwards are intact again.

The ORF check now hands the bytes over in the transfer list the way the package
does, so it fails on any develop that forgets this.
2026-10-06 16:16:32 +07:00
3dtours bd495540c4 fix(raw): fix infinite loop in JPEG binary scanner and ensure sub-100ms preview extraction 2026-10-06 15:35:30 +07:00
3dtours 82e72b7302 chore(pwa): bump service worker version to v10 & rebuild container 2026-10-06 15:05:33 +07:00
3dtours c64e2e42c0 fix(library): fix RAW develop fallback & optimize STUDIO to LIBRARY transition speed 2026-10-06 13:24:20 +07:00
3dtours f7c509eff1 fix(raw): fix RAW preview matching, EXIF orientation alignment & studio initial state 2026-10-06 12:23:38 +07:00
3dtours a7bffc9bf6 chore(docker): rebuild the NAS image bundle on 5bb5031
Both images are in it this time and both carry their tag, so a NAS loads and
runs the stack without a build. The single-image package from before the
bundle (recipescam-web-frontend-acbb2bb) is dropped.

VERSION=5bb5031 is stamped into the frontend, which is what the foot of the
studio's tab rail reads.
2026-10-06 10:46:08 +07:00
3dtours 5bb5031805 fix(raw): keep the blown highlights neutral after the tone curve
The blown gate draws a clipped pixel at its own maximum -- one value across
the three channels, so the frame's highlights carry no cast -- but the
per-channel tone curve runs after it and re-tints what the gate had just
made neutral. One cubic a channel, fitted on a grid that has no block left
to fit where the frame ran out (previewMatch drops the fully blown ones),
so at the plateau the three curves agree only at 1.0 and part company
either side of it.

On the ORF this was reported on, the moonlit sky came back 254,255,255 and
253,255,254 -- red under green across a quarter of the frame.

The gate is the develop's own statement that the pixel had no colour of its
own, so it is re-read on the value that leaves the shader. The blown pixels
now measure 254.95,254.94,254.97 (R/G 1.0000) against 253.44,254.94,254.51
(0.9941) before; their two most common triples, 254,255,255 at 149315 and
253,255,254 at 122127, collapse to 255,255,255 at 278099. The bright bands
land at -0.0,-0.0,-0.0 against the embedded preview where they were
-2.1,-0.0,-1.0, and midtones and the lower half are untouched.
2026-10-06 10:45:15 +07:00
3dtours 737ef39daa fix(raw): keep the ORF off olive by dropping the 3x3 preview match
The least-squares 3x3 that mapped the develop onto the camera's JPEG won
on luminance, whose variance is ~80x the chroma's, so it paid for its
match by crushing the R-G axis: 11.6 -> 4.6 standard deviations, against
8.3 in the camera's own preview and 12.3 in its JPEG. That is the olive
cast -- the sky and the rice both shifted yellow-green.

The colour chain was never at fault: camera_mul x rgb_cam already agrees
with LibRaw to the digit, and the app's fit is bit-identical to a numpy
reference of the same problem, which is how the 3x3 was cleared.

Fit only the per-channel tone curve now. R-G comes back to 8.6 (900px)
and 10.5 (full), and B/G lands on the preview's 0.841 to three places.
2026-10-06 08:29:07 +07:00
3dtours db3e556776 chore(docker): rebuild the NAS image bundle on b1ebb74 2026-10-06 07:29:50 +07:00