diff --git a/bookmark/static/app.js b/bookmark/static/app.js new file mode 100644 index 0000000..d13aca6 --- /dev/null +++ b/bookmark/static/app.js @@ -0,0 +1,42 @@ +// bookmark/bookmark/static/app.js +const statusEl = document.getElementById("status"); +const hintEl = document.getElementById("hint"); +const previewEl = document.getElementById("preview"); +const triggerButton = document.getElementById("trigger-button"); + +const STATE_COPY = { + waiting_front: { status: "Waiting for book...", hint: "Center the front cover in the guide box." }, + waiting_back: { status: "Front captured — flip it over", hint: "Center the back cover in the guide box." }, + book_complete: { status: "Book complete!", hint: "Tap Next Book to continue." }, +}; + +async function refreshStatus() { + const response = await fetch("/api/session-state"); + const body = await response.json(); + const copy = STATE_COPY[body.state] || { status: body.state, hint: "" }; + statusEl.textContent = copy.status; + hintEl.textContent = copy.hint; + triggerButton.textContent = body.state === "book_complete" ? "Next Book" : "Capture"; +} + +function refreshPreview() { + previewEl.src = `/api/preview.jpg?ts=${Date.now()}`; +} + +async function observe() { + await fetch("/api/observe", { method: "POST" }); + await refreshStatus(); +} + +triggerButton.addEventListener("click", async () => { + if (statusEl.textContent === "Book complete!") { + await fetch("/api/next-book", { method: "POST" }); + } else { + await fetch("/api/manual-trigger", { method: "POST" }); + } + await refreshStatus(); +}); + +setInterval(observe, 500); +setInterval(refreshPreview, 500); +refreshStatus(); diff --git a/bookmark/static/index.html b/bookmark/static/index.html index 9bb88d0..132f6e7 100644 --- a/bookmark/static/index.html +++ b/bookmark/static/index.html @@ -1,5 +1,22 @@ + -Books in Hand — Intake -

Books in Hand — Intake

+ + + + Books in Hand — Intake + + + + +
+
+ Camera preview +
+
Waiting for book...
+
Center the front cover in the guide box.
+ +
+ + diff --git a/bookmark/static/style.css b/bookmark/static/style.css new file mode 100644 index 0000000..1fd3f08 --- /dev/null +++ b/bookmark/static/style.css @@ -0,0 +1,48 @@ +/* bookmark/bookmark/static/style.css */ +.kiosk { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + min-height: 100vh; + padding: var(--space-lg); + gap: var(--space-md); + text-align: center; +} + +.kiosk__preview { + width: min(90vw, 640px); + aspect-ratio: 4 / 3; + background: var(--color-surface); + border-radius: var(--radius); + overflow: hidden; +} + +.kiosk__preview img { + width: 100%; + height: 100%; + object-fit: cover; +} + +.kiosk__status { + font-size: 1.5rem; + font-weight: 600; +} + +.kiosk__hint { + color: var(--color-muted); +} + +.kiosk__button { + padding: var(--space-md) var(--space-lg); + font-size: 1.25rem; + border: none; + border-radius: var(--radius); + background: var(--color-accent); + color: var(--color-bg); + cursor: pointer; +} + +.kiosk__button:active { + opacity: 0.8; +} diff --git a/bookmark/static/theme.css b/bookmark/static/theme.css new file mode 100644 index 0000000..38388a2 --- /dev/null +++ b/bookmark/static/theme.css @@ -0,0 +1,35 @@ +/* bookmark/bookmark/static/theme.css */ +:root { + --color-bg: #101418; + --color-surface: #1b2128; + --color-text: #f2f4f6; + --color-muted: #9aa5b1; + --color-accent: #4fb0ae; + --color-success: #5cb85c; + --color-warning: #e0a13a; + --space-sm: 0.5rem; + --space-md: 1rem; + --space-lg: 2rem; + --radius: 0.5rem; + --font-base: system-ui, -apple-system, "Segoe UI", sans-serif; +} + +@media (prefers-color-scheme: light) { + :root { + --color-bg: #f2f4f6; + --color-surface: #ffffff; + --color-text: #101418; + --color-muted: #5a6472; + } +} + +* { + box-sizing: border-box; +} + +body { + margin: 0; + background: var(--color-bg); + color: var(--color-text); + font-family: var(--font-base); +}