Two-pronged fix so the Translate + Scan FABs stay reachable when
the mobile soft keyboard is open:
- Add `interactive-widget=resizes-content` to the viewport meta —
Chrome on Android then shrinks the layout viewport when the
keyboard opens, so `position: fixed; bottom: ...` is naturally
above it. iOS Safari ignores this directive, hence:
- VisualViewport listener computes
inset = innerHeight - vv.height - vv.offsetTop
and writes it to a `--kb-inset` CSS custom property. The FAB
bottom calc uses `max(var(--kb-inset), safe-area-inset-bottom)`
so the keyboard inset wins when it's open and the home-indicator
inset wins when it isn't.
Net: tap the editor on iOS → keyboard slides up → Translate /
Scan FABs rise with it, no manual minimise required.
finrod
Tiny web translator backed by Ollama on radagast. Named for Finrod Felagund, Noldor loremaster of the tongues of Men.
Run
uv run uvicorn app:app --host 127.0.0.1 --port 8000 --reload
Then open http://127.0.0.1:8000.
Config
| Env var | Default | Notes |
|---|---|---|
OLLAMA_URL |
http://radagast.jpnadas.xyz:11434 |
Ollama base URL |
MODEL_FILTER |
gemma,aya |
Comma-separated substrings; model matches if it contains any. Empty = show all |
Models
For Pt/Nl/En translation, three worth A/B-ing:
ssh radagast 'ollama pull gemma2:9b' # already there — big-and-broad baseline
ssh radagast 'ollama pull gemma3:4b' # newer multilingual training, ~2-3× faster on Ivy Bridge
ssh radagast 'ollama pull aya-expanse:8b' # Cohere multilingual model, strong on Dutch
All three fit in radagast's 10 GB at Q4. Switching models in the dropdown forces Ollama to load the new weights — expect a 5-15 s pause on the first call after a switch while it reads from disk.
Build / push image
Built locally and pushed to Scaleway — no Gitea runners on the homelab yet, so there's no CI workflow.
One-time setup (host needs buildx + QEMU for cross-arch):
docker buildx create --name multi --use
docker run --privileged --rm tonistiigi/binfmt --install all
docker login rg.nl-ams.scw.cloud -u nologin -p <SCW_SECRET_KEY>
Then on every change:
./build.sh
That builds linux/amd64,linux/arm64 and pushes three tags:
:latest— moving pointer:<git-short-sha>— immutable, traceable:<pyproject-version>(e.g.:0.1.0) — semver
Deploy
Cluster manifests live in valinor/apps/finrod/ (bjw-s app-template, internal-only ingress at finrod.jpnadas.xyz). To roll out a new build, bump tag: in valinor/apps/finrod/values.yaml to the new semver on a branch → PR → merge → ArgoCD syncs.
Release workflow
This project uses Semantic Versioning and keeps a human-edited CHANGELOG.md.
To cut a release:
- On a branch, edit
CHANGELOG.md: move accumulated[Unreleased]notes into a new## [X.Y.Z] - YYYY-MM-DDsection. - Bump
versioninpyproject.tomltoX.Y.Z. - PR → merge to
main. - Tag the merge commit:
git checkout main && git pull git tag -a vX.Y.Z -m "vX.Y.Z" git push origin vX.Y.Z ./build.sh— the image now also gets:X.Y.Z(read frompyproject.toml).- In
valinor, bumpapps/finrod/values.yamltag:from the previous version toX.Y.Zon a branch → PR → ArgoCD picks it up.