Commit Graph

684 Commits

Author SHA1 Message Date
3dtours e543f21764 chore(docker): stamp the image bundle with a3171dd 2026-10-13 14:58:22 +07:00
3dtours a3171dd40e fix(library): one row per frame of a drive root, however a build spelled its id 2026-10-13 14:57:49 +07:00
3dtours c506cdf645 chore(docker): stamp the image bundle with 9e2e6a6 2026-10-13 13:17:29 +07:00
3dtours bb997adb90 chore(docker): stamp the image bundle with 9e2e6a6 2026-10-13 12:55:17 +07:00
3dtours 9e2e6a6727 feat(library): close the mobile preview on the frame the reader swiped to, selected in the thumb grid 2026-10-13 12:54:45 +07:00
3dtours 9a40449f05 chore(docker): stamp the image bundle with 04461af 2026-10-13 12:33:00 +07:00
3dtours 04461afbdf fix(library): fold the picked drive root to the root it is, so a folder row draws its own frames and the subfolder toggle holds 2026-10-13 12:32:29 +07:00
3dtours a733236c56 chore(docker): stamp the image bundle with 6d3ee72 2026-10-13 11:50:31 +07:00
3dtours 6d3ee728b4 feat(library): mint a server frame's tile once and keep it, always show the whole branch under the open node, and draw the year tick as a grey rule 2026-10-13 11:50:01 +07:00
3dtours 150a55f7a3 chore(docker): stamp the image bundle with 1ca8ac6 2026-10-13 11:11:26 +07:00
3dtours 1ca8ac6031 feat(library): lay the ALL THUMBS wall out in justified rows per day, and stop clipping the frames at the pane's edges 2026-10-13 11:10:57 +07:00
3dtours 326bcb0c5e chore(docker): stamp the images at 822833c 2026-10-13 10:38:28 +07:00
3dtours 822833c1ef feat(library): cut the wall by month and day, and stand a ruler timeline on its right edge 2026-10-13 10:37:57 +07:00
3dtours f8cac67506 chore(docker): stamp the images at ddbde7d 2026-10-13 09:55:27 +07:00
3dtours ddbde7d426 feat(library): read the wall's time by the year and the month, and mint only the frames near the eye 2026-10-13 09:54:47 +07:00
3dtours 5d9b4c3058 chore(docker): stamp frontend image 62f244f 2026-10-13 09:38:38 +07:00
3dtours 62f244f3c2 fix(library): draw the wall whole and read its time down the right edge 2026-10-13 09:38:05 +07:00
3dtours d786b84eb1 chore(docker): stamp the images at fcded40 2026-10-13 09:16:33 +07:00
3dtours fcded40c65 fix(library): measure the wall's shelf in two steps so the scroll reaches its end 2026-10-13 09:15:59 +07:00
3dtours 7040d7c2e6 chore(docker): weigh the bundle that cuts the wall into days 2026-10-13 08:32:18 +07:00
3dtours ada0c3d93e feat(library): the all-thumbs wall is cut into the days its frames were shot 2026-10-13 08:31:50 +07:00
3dtours 40362e9379 chore(docker): weigh the bundle that files an album-less upload into the app's own album 2026-10-13 07:49:50 +07:00
3dtours a539f422c5 feat(immich): an upload with no album ticked lands in the app's own RecipesCam album, made on first use 2026-10-13 07:49:08 +07:00
3dtours 0e4e5218cf chore(docker): weigh the NAS bundle that carries the angle with the spots 2026-10-13 07:09:01 +07:00
3dtours b37ec97352 fix(studio): a dust spot is a place on the photo, so the angle carries it too 2026-10-13 07:08:28 +07:00
3dtours 66f044822e chore(docker): weigh the NAS bundle that carries the spots a quarter turn now carries 2026-10-13 06:40:22 +07:00
3dtours 1d7510366a fix(studio): a dust spot is a place on the photo, so a quarter turn carries it with the frame 2026-10-13 06:38:43 +07:00
3dtours 05a527557e chore(docker): weigh the NAS bundle that carries the refusal Immich itself gives 2026-10-12 22:39:02 +07:00
3dtours 897335b701 fix(studio): the refusal Immich gives is the one the upload dialog says 2026-10-12 22:38:30 +07:00
3dtours 25cd8d1d45 chore(docker): weigh the NAS bundle that carries the repair another look cannot take 2026-10-12 22:27:27 +07:00
3dtours bf5eeac14f fix(studio): the repair belongs to the frame, so a look loaded over it leaves it standing 2026-10-12 22:26:51 +07:00
3dtours fcd0957148 chore(docker): weigh the NAS bundle that carries the repair as it was 2026-10-12 22:06:14 +07:00
3dtours 550e688b3c revert(studio): the repair is drawn once again, and the model's own answer is the only one the stage shows
The two rounds were meant to answer the stroke sooner, but a preview cannot be
half a repair: the blurred fill is what a machine without the runtime gets, and
with it on the stage a spot of dirt came back smeared rather than gone — worst of
all on a run of strokes, where each new one cancels the model's round that was
still on its way and leaves the blur standing longest. The repair that works is
the one that waits for the model, and the wait is worth it.

The worker stays available but is switched off with the rest: this reverts the
runtime's proxy along with the second round, so the stage is exactly what it was
before, lag and all.

Reverts 5489114.
2026-10-12 22:05:45 +07:00
3dtours a4b9c41247 chore(docker): weigh the NAS bundle that carries the shelf walked from the picture 2026-10-12 21:56:27 +07:00
3dtours fa52ffc424 feat(library): the single view walks the shelf from the picture itself, by arrow and by finger
SINGLE is where a roll is read one frame at a time, and the step from one frame
to the next was a trip to the strip below the picture and back. The stage now
carries the pair itself, at the edges the eye already reads as "there is more
this way", dimmed at the ends of the roll rather than taken away so the pair does
not jump about. The press stops at the arrow: the zoom, the pan and the open
under it are the picture's.

The phone's full-screen view has no room for a pair, so the same step is the
reader's own finger. A drag that travelled sideways is a step the way the list is
read; one that wandered up and down, and a tap in place, are left alone.

Both walk the list on screen — filters and sort included — so what is drawn after
the step is what stood beside it.

Verified against a local preview with a seeded shelf: the arrows appear, the left
one is dimmed on the newest frame and the right one on the oldest, and a left
swipe on the phone moves to the next frame and back.
2026-10-12 21:55:58 +07:00
3dtours b244a79954 chore(docker): weigh the NAS bundle that carries the repair off the page's thread 2026-10-12 21:43:41 +07:00
3dtours 54891148a9 fix(studio): the repair runs off the page's own thread, and a stroke is answered twice so the hand is never held
The graph is seconds of solid arithmetic however large the crop is, and it ran
where the pointer lives: the brush lagged behind the hand for exactly as long as
the model took. The runtime's wasm provider now works in a worker of its own, so
the page keeps drawing while the repair is thought through.

The wait itself stays — it is the model's, not the page's — so a preview paints
the blurred fill first and lets the model's version of the same mask replace it.
Filing and export still wait for the model: the faster answer is for the eye
waiting on the brush and for nothing else.

Measured on the same photo and stroke, worst main-thread gap 3703 ms before and
487 ms after; previews land at 1.0 s and 5.3 s, and the model's render is the one
left on the stage.
2026-10-12 21:43:00 +07:00
3dtours 5535cb8210 chore(docker): weigh the NAS bundle that carries the brush that invents
25MB more than the last one — the MI-GAN weights, which are the whole of it.
2026-10-12 21:08:03 +07:00
3dtours 15fb4422dc feat(studio): HEAL invents what was under the brush with MI-GAN, and keeps the blurred fill for the machines that cannot run it
The dust brush filled its hole with a blur of the hole's own rim — the light
beside it, carried in. Right for a speck on a wall, and visibly thin over
anything with structure: a stroke across a shirt came back as the average of a
shirt. The fill is now a learned one.

engine/inpaint.ts runs MI-GAN (andraniksargsyan/migan, the same
`migan_pipeline_v2.onnx` inpaint-web ships) through onnxruntime-web on the
visitor's own machine: 27MB of weights in public/models, WebGPU where the graph
fits the provider, the processor otherwise, one warm-up pass so the first repair
is not the one that pays for the kernels. It is handed the masked crop and
nothing else — the model's mask polarity is 255 KEEP / 0 HOLE, the opposite of
the obvious guess, and the other way round repaints the frame and leaves the
hole (measured).

Step 6e of the renderer cuts the crop at the resolution the model will actually
run at (512 on its longest side — the graph is fully convolutional and would
take a whole frame, and ten seconds over one), builds the mask from the brush's
own circles, and draws the answer into the fill surface at its own place in the
box. The composite, the feathered mask and the untouched frame around it are
unchanged, so nothing outside the mask moves — and when the model cannot be had
the blurred fill is still built first and still stands. The preview gets this
for free: renderPhoto was already async and the preview was already debounced.

Node probe: the model + polarity + read/write path run; heal-check.mjs keeps
measuring the fallback and now asserts the wiring and the polarity. Browser:
engine/inpaint.ts in a real Chromium returns a patch (hole moves, kept pixels
within 0.02 of a level), and the built app renders a 42-spot stroke in 4.7s on
single-threaded wasm with no fallback warning — 1.15s and the warning when the
model is taken away.
2026-10-12 21:07:09 +07:00
3dtours 3917034643 chore(docker): weigh the NAS bundle that carries the mark a repair gives up 2026-10-12 20:40:20 +07:00
3dtours 6d437591a0 fix(studio): a repair wears its band only while the pointer is on it, so the photo is not left under a wash of the tool's own 2026-10-12 20:39:54 +07:00
3dtours cb198ed9fe chore(docker): weigh the NAS bundle that carries the heal's own corner 2026-10-12 20:24:09 +07:00
3dtours 2d9d7ef644 fix(studio): the heal's fill is drawn at the surface's own corner, not at the frame's, so a repair no longer wears the colour of the frame's far side 2026-10-12 20:23:32 +07:00
3dtours cf6b7f5b9f chore(docker): weigh the NAS bundle that carries the wall's own step 2026-10-12 19:57:50 +07:00
3dtours 73d1c22c30 fix(library): the wall takes a step off a single row, and a year of four frames stops stranding the shelf 2026-10-12 19:57:18 +07:00
3dtours c88d43a158 chore(docker): weigh the NAS bundle that carries the cloud reading's last word 2026-10-12 18:43:39 +07:00
3dtours dd9310b344 fix(library): a reading of the cloud says when it has finished, and a window is measured off the shelf that is up
The background walk behind a cloud node read its pages after the answer the
screen had already been given, and no one told the screen when the last page
landed: the note went on saying a reading was running for the rest of the
visit. The walk now gives its own last word, with the count it read.

The strip and the wall windows were measured off the node the effect was set
up on. The stage is drawn while a node is open, so a visit that loses its
nodes and gets them back draws a new strip and a new wall, and a window
measured off the old node — a box of nothing, a scroll always nought — never
moved again for the rest of the visit. Both now read the element that is up.
2026-10-12 18:43:13 +07:00
3dtours 9cbaed42b1 chore(docker): weigh the NAS bundle that carries the cascade heal brush
Same two images, rebuilt on the cascade heal and the Immich re-read; the checksum is the only thing the repo keeps.
2026-10-12 18:13:34 +07:00
3dtours 00063a7f7c fix(studio): the heal fill is a cascade of blurs, the brush is put down with its tab, and Immich is re-read on the way back
HEAL painted a grey blob over the photo it was meant to repair, and the
cause was one draw Skia gets wrong on this build: a snapshot used as a
SCALED source comes back with the alpha multiplied into its rgb a second
time, so a partly transparent rim — which is what the mask's feather is —
was read as a dark wash. A pyramid of halvings is built of exactly those
scaled draws, which is why the repair's own rim came out grey; the same
source read at 1:1 is exact (measured, tmp-src-probe).

So the fill is now a cascade of blurs, all of them 1:1: the cut frame is
blurred at a few widths, widest first, each finer one drawn over it, the
untouched cut on top. The finest is one brush wide, each next is twice the
last, and the count follows the depth of the hole (the stroke's own
diagonal against the widest brush), which is what keeps the middle of a
long hole filled rather than bare. Nothing is copied from anywhere and the
rim arrives with no seam, which is what the passes were always for.

And the fill is built on the brush's own box rather than the frame, padded
by three sigma of the widest level. The composite reads it only where the
mask is, so this changes no pixel of the result (heal-check measures the
same masks either way) and it is what makes the repair affordable at the
zoom a repair is aimed at: at a 3200px preview a single spot went from
1.7s of frozen main thread to 1.2s, and the heal's own share of that from
0.8s to 0.3s. What is left is the pipeline itself, which is a separate
matter.

The FIX brush outlived its tab. It takes the pointer on the photo, and
leaving TOOLS never put it down — so its layer stayed on the stage over a
photo nobody was editing, and its ring came back under the pointer every
time the hand crossed a repair, which reads as the tool re-arming itself.
It now goes down with the tab, with another chip or another strip, and on
a press outside its own two chips, the same way the mask tool does.

And the Immich button was missing for a visitor who had just connected a
server: the studio and the library are two screens of one page, both
mounted, so coming back is a route change and not a mount — the studio's
own effect, which reads the server list once a session, never ran again.
It is read again on a popstate that lands on /app.

Checks: node scripts/heal-check.mjs (the cascade and the box, to the
letter), npx tsc --noEmit, and the studio driven in a browser: the button
appears on the way back from the library, the brush goes down with TOOLS
and re-arms with HEAL, and a stroke over a photo lays one band with no grey
in it.
2026-10-12 18:12:45 +07:00
3dtours b4bb600cca chore(docker): weigh the NAS bundle that carries the one-mask heal brush 2026-10-12 16:16:01 +07:00