diff --git a/CHANGELOG.md b/CHANGELOG.md index 574c5d3..3f46b15 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,7 +19,7 @@ Versioning: [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - "Translate" inline button replaced with a primary FAB using the `translate` Material Symbols icon, stacked directly under the camera FAB at the bottom-right corner (closest to the thumb on phones). The camera FAB is now the *secondary* in the stack, just above. Both 56 × 56 px, same accent fill. The translate FAB gains a pulsing-ring `busy` animation during a running translation; `goBtn.textContent` swap is gone (was meaningless for an icon-only button). `body` got `~9 rem` of bottom padding so content scrolls clear of the stack. - FAB stack rides above the on-screen keyboard on mobile via `interactive-widget=resizes-content` in the viewport meta tag (handled by Chrome on Android). - Layout: From / To dropdowns now occupy 2 columns at the top (was 3 columns including Model). -- Model selector moved into a kebab (⋮) menu in the top-left corner. Uses a native `
` element so toggle/keyboard-accessibility works without extra JS. +- Model selector moved into a slide-out side drawer (hamburger menu in the top-left). Drawer opens from the left with a backdrop, closes via the X button, backdrop click, or `Esc`. Honours Android safe-area insets. - Default source language is now Dutch (was Auto-detect). Target stays English. - Crop modal: - No default crop box. User drags on the image to draw the region; falls back to full image if they confirm without drawing. diff --git a/static/index.html b/static/index.html index f283e4b..38c20e6 100644 --- a/static/index.html +++ b/static/index.html @@ -51,44 +51,94 @@ margin-bottom: 1rem; } - /* Kebab menu (top-left) housing the Model dropdown — keeps the main - controls focused on the From/To pair. */ - .kebab { + /* Hamburger menu (top-left) → slide-out side drawer that houses the + Model selector (and future per-app settings). */ + .drawer-toggle { position: fixed; top: calc(1rem + env(safe-area-inset-top, 0px)); left: calc(1rem + env(safe-area-inset-left, 0px)); - z-index: 50; - } - .kebab > summary { - list-style: none; width: 40px; height: 40px; + padding: 0; + margin: 0; border-radius: 50%; border: 1px solid var(--border); background: var(--panel); color: var(--fg); + z-index: 50; display: flex; align-items: center; justify-content: center; cursor: pointer; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.18); } - .kebab > summary::-webkit-details-marker { display: none; } - .kebab > summary::marker { display: none; } - .kebab > summary .material-symbols-outlined { font-size: 22px; } - .kebab[open] > summary { border-color: var(--accent); color: var(--accent); } - .kebab-panel { - position: absolute; - top: calc(100% + 0.5rem); - left: 0; - background: var(--bg); - border: 1px solid var(--border); - border-radius: 8px; - padding: 0.75rem; - min-width: 220px; - box-shadow: 0 4px 14px rgba(0, 0, 0, 0.3); + .drawer-toggle .material-symbols-outlined { font-size: 22px; } + + .drawer-backdrop { + position: fixed; + inset: 0; + background: rgba(0, 0, 0, 0.55); + opacity: 0; + pointer-events: none; + transition: opacity 0.2s; + z-index: 200; } - .kebab-panel label { + .drawer-backdrop.open { opacity: 1; pointer-events: auto; } + + .drawer { + position: fixed; + top: 0; + left: 0; + bottom: 0; + width: min(85vw, 320px); + background: var(--bg); + border-right: 1px solid var(--border); + padding: + max(1rem, env(safe-area-inset-top, 0px)) + 1.25rem + max(1rem, env(safe-area-inset-bottom, 0px)) + max(1.25rem, env(safe-area-inset-left, 0px)); + transform: translateX(-100%); + transition: transform 0.25s ease; + z-index: 201; + box-shadow: 4px 0 14px rgba(0, 0, 0, 0.3); + display: flex; + flex-direction: column; + gap: 1rem; + overflow-y: auto; + } + .drawer.open { transform: translateX(0); } + .drawer-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 0.5rem; + } + .drawer-header h2 { + margin: 0; + font-size: 1rem; + font-weight: 500; + letter-spacing: 0.04em; + color: var(--muted); + text-transform: uppercase; + } + .drawer-close { + background: transparent; + border: none; + color: var(--muted); + width: 32px; + height: 32px; + padding: 0; + margin: 0; + border-radius: 4px; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + } + .drawer-close:hover { color: var(--fg); background: var(--panel); } + .drawer-close .material-symbols-outlined { font-size: 20px; } + .drawer label { display: block; font-size: 0.7rem; letter-spacing: 0.06em; @@ -432,13 +482,22 @@ -
- more_vert -
+ +
+
+

finrod

Translation via Ollama · loremaster of tongues
@@ -1189,6 +1248,28 @@ Vim.mapCommand("L", "action", "finrodFocusNext", {}, { context: "normal" }); } + // Side drawer (Settings) wiring — opens from the top-left hamburger. + const drawerEl = document.getElementById("drawer"); + const drawerBackdrop = document.getElementById("drawer-backdrop"); + const drawerToggle = document.getElementById("drawer-toggle"); + const drawerCloseBtn = document.getElementById("drawer-close"); + function openDrawer() { + drawerEl.classList.add("open"); + drawerBackdrop.classList.add("open"); + drawerEl.setAttribute("aria-hidden", "false"); + } + function closeDrawer() { + drawerEl.classList.remove("open"); + drawerBackdrop.classList.remove("open"); + drawerEl.setAttribute("aria-hidden", "true"); + } + drawerToggle.addEventListener("click", openDrawer); + drawerCloseBtn.addEventListener("click", closeDrawer); + drawerBackdrop.addEventListener("click", closeDrawer); + document.addEventListener("keydown", (e) => { + if (e.key === "Escape" && drawerEl.classList.contains("open")) closeDrawer(); + }); + fillLangs(); loadModels().then(() => refreshLoaded(true));