web: a folder is chosen by picking one — the backup row names it and opens the picker, and restore always asks which backup

RESTORE was dead on a machine that had never written a backup: the button was
`disabled` while no folder was remembered, and nothing in the handler could ever
pick one, so the reader who had carried a copy over on a stick had no way to
point at it. The button now stands, and the folder it restores from is whatever
the reader picks in the dialog that opens — which is the whole of the choice
there is, because a backup is a folder of rows and tiles. The folder on the other
disk, the one made before the edit: the folder is the version.

BACK UP asked for a folder only the first time one was ever chosen. After that
it wrote into it in silence, and after a restart — when the browser takes the
permission back, since a permission outlives the tab only while the tab does —
it wrote nothing and said so, which is a backup that quietly stops happening.
It now picks whenever it cannot write, and the picker is the only thing that can
hand a remembered folder its permission back; a permission asked for with no
picker behind it is one the browser may refuse. The run that follows a scan is
untouched: it is guarded by the permission it would be asking for.

And the folder is a control, not a caption. The line that names it — with the
frames in it and when they were written — is what tells one backup from another,
and pressing it is how a folder is picked, a name given, a permission handed
back. It keeps the hint's own look through five lines of CSS, so the row still
reads as a caption and not as a third button.

What the line says is now read out of the folder rather than out of this
browser's memory of it. `pickBackupFolder` reads the catalogue file inside the
folder just picked and takes its count and its date as the row's own; a folder
with no catalogue of its own has none, and the row says so instead of showing
the numbers of the folder next door. That is also what the confirm names before
a restore — the frames and the time in the folder just picked, which is the
thing about to be restored. `restoreNow` already read that file; this reads its
header first.

Checked on the running bundle with the picker stubbed, since the dialog is one a
probe cannot press: with nothing remembered, RESTORE now opens the picker where
it did nothing at all, the row names the folder the picker returned, and a
folder without a catalogue of its own is refused with a line saying so rather
than restored as an empty catalogue. BACK UP opens it too, from the same fresh
state. The line computes to a button with no border, no background, the page's
own font and the hint's own 12px dim grey — a caption that happens to be
pressable. The wall, the strip, the grid, the deep link and the phone's recipes
all pass their checks.
This commit is contained in:
2026-10-01 17:12:29 +07:00
parent e2d468b77d
commit 39f80088c7
5 changed files with 111 additions and 26 deletions
+4 -3
View File
@@ -285,16 +285,17 @@ export const en: Dict = {
// profile takes the reading away from, and these two are what puts it back.
'lib.backup': 'BACK UP',
'lib.restore': 'RESTORE',
'lib.backupNone': 'No backup folder yet — this picks one and writes the catalogue into it.',
'lib.backupNone': 'No backup folder yet — press to pick one and write the catalogue into it.',
'lib.backupAt': 'Backup: {folder} · {n} frames · {time}',
'lib.backupBlocked': 'The browser has not let this page write to {folder}. Press BACK UP and allow it.',
'lib.backupPick': 'Pick the folder the catalogue is written into and read back from — the folder name is the backup name.',
'lib.backupRun': 'Backing up {done}/{total}…',
'lib.backupDone': 'Backed up {n} frames, {w} tiles written.',
'lib.backupFailed': 'The backup folder could not be written to.',
'lib.restoreConfirm': 'Restore the catalogue from {folder}? Frames the library already holds are left as they are.',
'lib.restoreConfirm': 'Restore {n} frames from {folder}, written {time}? Frames the library already holds are left as they are.',
'lib.restoreRun': 'Restoring {done}/{total}…',
'lib.restoreDone': 'Restored {n} frames. Add the folder again so every frame gets its file back.',
'lib.restoreNone': 'Pick a backup folder first — BACK UP does that and writes the catalogue in one go.',
'lib.restoreNothing': 'There is no catalogue of its own in {folder} — pick the folder a backup was written into.',
'lib.restoreFailed': 'The backup folder could not be read.',
'nav.admin': 'Admin',
+3 -2
View File
@@ -301,13 +301,14 @@ export const vi = {
'lib.backupNone': 'Chưa có thư mục sao lưu — bấm để chọn một thư mục và ghi danh mục vào đó.',
'lib.backupAt': 'Sao lưu: {folder} · {n} ảnh · {time}',
'lib.backupBlocked': 'Trình duyệt chưa cho trang ghi vào {folder}. Bấm SAO LƯU và cho phép.',
'lib.backupPick': 'Chọn thư mục mà danh mục được ghi vào và đọc lại từ đó — tên thư mục chính là tên bản sao lưu.',
'lib.backupRun': 'Đang sao lưu {done}/{total}…',
'lib.backupDone': 'Đã sao lưu {n} ảnh, ghi {w} thumbnail.',
'lib.backupFailed': 'Không ghi được vào thư mục sao lưu.',
'lib.restoreConfirm': 'Phục hồi danh mục từ {folder}? Những ảnh thư viện đã có sẽ được giữ nguyên.',
'lib.restoreConfirm': 'Phục hồi {n} ảnh từ {folder}, ghi lúc {time}? Những ảnh thư viện đã có sẽ được giữ nguyên.',
'lib.restoreRun': 'Đang phục hồi {done}/{total}…',
'lib.restoreDone': 'Đã phục hồi {n} ảnh. Thêm lại thư mục ảnh để mỗi ảnh lấy lại được tệp của nó.',
'lib.restoreNone': 'Chọn thư mục sao lưu trước — nút SAO LƯU làm việc đó và ghi danh mục luôn.',
'lib.restoreNothing': 'Thư mục {folder} không có danh mục sao lưu nào — hãy chọn đúng thư mục đã ghi sao lưu.',
'lib.restoreFailed': 'Không đọc được thư mục sao lưu.',
'adm.title': 'Quản trị dải phim',