feat: add darktable web API interface with gallery, image detail, and settings screens

- Implemented main entry point for React application in `main.jsx`.
- Created `Gallery` component for displaying images with infinite scroll and pull-to-refresh functionality.
- Developed `ImageDetail` component for showing detailed information and actions for selected images.
- Added `Settings` component for configuring API connection settings.
- Integrated Vite for build and development with PWA support.
- Implemented caching strategies for API responses and image previews using Workbox.
This commit is contained in:
2026-06-10 23:38:06 -04:00
parent 0831ee943c
commit dca1d1c25b
23 changed files with 9411 additions and 23 deletions

View File

@@ -210,7 +210,15 @@ function M.get_preview(req)
end
end
return err(404, "no_preview", "no cached preview available for image " .. id)
-- No cached preview exists. Enqueue a full-pipeline thumbnail generation
-- so the bridge renders one; the client should retry after a few seconds.
enqueue_refresh(id)
return 202, json.encode({
status = "generating",
image_id = id,
retry_after = 4,
message = "thumbnail is being generated; retry in a few seconds",
}), "application/json; charset=utf-8", { ["Retry-After"] = "4" }
end
-- ── Tags ──────────────────────────────────────────────────────────────────────
@@ -410,9 +418,8 @@ end
-- ── Full-resolution export ────────────────────────────────────────────────────
-- POST /api/v1/images/:id/export
-- Enqueues a full-resolution JPEG export via the darktable bridge.
-- The bridge calls dt.new_format("jpeg"):write_image() which runs the complete
-- development pipeline. Returns 202 immediately; poll GET /full to check when ready.
-- Removes any stale export file, enqueues the bridge job, returns 202.
-- The client should poll GET /full until it receives a 200.
-- Requires scope: cache:refresh
function M.trigger_export(req)
local payload, s, b, ct = require_auth(req, "cache:refresh")
@@ -424,17 +431,17 @@ function M.trigger_export(req)
local img = db.get_image(id)
if not img then return err(404, "not_found", "image " .. id .. " not found") end
-- Remove stale export so the client can detect when the fresh one lands.
os.remove(export_path(id))
local queued, qerr = enqueue_export(id)
if not queued then
return err(500, "queue_error", qerr)
end
if not queued then return err(500, "queue_error", qerr) end
return 202, json.encode({
status = "accepted",
image_id = id,
filename = img.filename,
export = export_path(id),
message = "full-resolution export enqueued; poll GET /api/v1/images/" .. id .. "/full when ready",
message = "export enqueued; poll GET /api/v1/images/" .. id .. "/full",
}), "application/json; charset=utf-8"
end
@@ -455,9 +462,10 @@ function M.get_full(req)
local path = export_path(id)
local f = io.open(path, "rb")
if f then
local data = f:read("*a")
f:close()
return 200, data, "image/jpeg"
local data = f:read("*a"); f:close()
local stem = (img.filename or "export"):match("^(.+)%.[^.]+$") or (img.filename or "export")
local disposition = string.format('attachment; filename="%s.jpg"', stem)
return 200, data, "image/jpeg", { ["Content-Disposition"] = disposition }
end
-- Not yet exported — enqueue and tell the client to retry.