Allow explicit retry of failed native preview preparation

This commit is contained in:
karl committed 2026-10-13 14:27:58 +02:00
1 parent 370db7e791
commit f7f67afff9
4 files changed
+35 -3

No files matched your search

+15 -3
View File
@@ -264,9 +264,21 @@ const mediaLane = new LatestLane<PreviewRequest>(
(request) => previewRevision.accepts(request.revision), (request) => previewRevision.accepts(request.revision),
(request, error) => { (request, error) => {
if (!previewRevision.accepts(request.revision)) return; if (!previewRevision.accepts(request.revision)) return;
element("media-preview-status").hidden = false; const status = element("media-preview-status");
element("media-preview-status").textContent = status.hidden = false;
`Preview unavailable: ${String(error)}. Use Open file.`; status.textContent = `Preview unavailable: ${String(error)}. Use Open file.`;
const retry = document.createElement("button");
retry.type = "button";
retry.textContent = "Retry preview";
retry.addEventListener("click", () => {
const revision = previewRevision.retry(request.revision);
if (revision === undefined) return;
unloadMedia();
status.hidden = false;
status.textContent = "Preparing media preview…";
mediaLane.submit({ revision, result: request.result });
});
status.append(" ", retry);
}, },
); );
let displayedRequestId: number | null = null; let displayedRequestId: number | null = null;
+4
View File
@@ -98,6 +98,10 @@ export class PreviewRevision {
accepts(revision: number) { accepts(revision: number) {
return revision === this.revision && this.key !== ""; return revision === this.revision && this.key !== "";
} }
retry(revision: number): number | undefined {
if (!this.accepts(revision)) return undefined;
return ++this.revision;
}
clear() { clear() {
this.key = ""; this.key = "";
this.revision++; this.revision++;
+12
View File
@@ -11,6 +11,18 @@ import {
import { LatestLane } from "../src/search-state.ts"; import { LatestLane } from "../src/search-state.ts";
const settle = () => new Promise((resolve) => setImmediate(resolve)); const settle = () => new Promise((resolve) => setImmediate(resolve));
test("explicit preview retry invalidates old work and cannot retry a stale selection", () => {
const state = new PreviewRevision();
const first = state.select("first");
const retried = state.retry(first.revision);
assert.equal(typeof retried, "number");
assert.equal(state.accepts(first.revision), false);
assert.equal(state.accepts(retried), true);
assert.equal(state.retry(first.revision), undefined);
state.select("second");
assert.equal(state.retry(retried), undefined);
});
test("indexed content refresh invalidates a decoded preview at the same path", () => { test("indexed content refresh invalidates a decoded preview at the same path", () => {
const revision = new PreviewRevision(); const revision = new PreviewRevision();
const source = { kind: "timestamp_ms", number: 1200 }; const source = { kind: "timestamp_ms", number: 1200 };
+4
View File
@@ -19,6 +19,10 @@ Arbitrary paths, remote URLs, query strings and traversal are not preview grants
The generic Tauri asset protocol is disabled in the new configuration. The generic Tauri asset protocol is disabled in the new configuration.
Index-change notifications also advance the preview selection identity, so a Index-change notifications also advance the preview selection identity, so a
same-path image/recording reloads rather than retaining previously decoded media. same-path image/recording reloads rather than retaining previously decoded media.
Failed native preview preparation now offers an explicit **Retry preview**
button. It invalidates the old request revision and cannot restart a selection
that has since changed. There is no automatic retry loop. The added regression
passed with all 60 frontend tests, formatting and the production frontend build.
This does not hash every recording on every request. A same-size/mtime in-place This does not hash every recording on every request. A same-size/mtime in-place
write may remain undetected until a filesystem hint or byte-verifying rescan write may remain undetected until a filesystem hint or byte-verifying rescan