SCM — Screen Memories
Local-first: geen accounts, geen cloud en geen uploads. De inference draait volledig op je Mac.
Wat SCM onderscheidt
- Zoek zoals je denkt – Beschrijf een herinnering in gewone taal; een lokaal vision-model doet de rest.
- Video, tot op het moment – Scènes worden gesegmenteerd en voorzien van embeddings, zodat je direct bij het juiste shot uitkomt en niet alleen bij het videobestand.
- Tekst en dialoog ook – OCR voor zichtbare tekst en exacte zoekopdrachten naar gesproken regels via Whisper, elk als een eigen modus.
- Je eigen tabbladen en prompts – Sla elke zoekopdracht op als een tabblad; tabbladen voor screenshots en e-mail kunnen worden in- en uitgeschakeld.
- Zelfonderhoudende bibliotheek – Gemonitorde mappen importeren automatisch, content-hashes voorkomen duplicaten bij het hernoemen van bestanden, en modelwissels zorgen voor re-embedding op de achtergrond zonder het zoeken te blokkeren.
- Echt privé – Je media verlaat nooit de machine. Modellen worden één keer gedownload; daarna werkt alles offline.
Vijf manieren om te zoeken
| Modus | Wat wordt gevonden |
|---|---|
| Bestanden | Complete foto's/video's op basis van betekenis — vision ranking met boosts voor bestandsnaam en frases. |
| Scènes | Momenten binnen video's — zoek naar een specifiek shot en spring direct naar de tijdcode. |
| OCR | Tekst die zichtbaar is in afbeeldingen en frames, letterlijk gematcht (Tesseract; Engels + 35 taalopties). |
| Dialoog | Exacte gesproken woorden in video's (Whisper), met verschillende niveaus van exactheid. |
| LLM's (optioneel) | Lokale chat over de dialoog, OCR en bestandsnamen die je Mac al heeft geëxtraheerd — inclusief geciteerde antwoorden. |
Vereisten
- macOS (gepakket met electron-builder; menu-bar/tray functies zijn uitsluitend voor macOS).
- Bun — het project gebruikt Bun als package manager en runner.
- Node modules geïnstalleerd:
bun install. - Modellen: Bij het eerste gebruik van een model worden de weights gedownload (~435MB voor het standaard CLIP-model); daarna werkt het volledig offline.
Downloaden
De eenvoudigste installatie is via Homebrew (Apple Silicon, macOS 12+). De cask van de tap verwijdert automatisch de macOS-quarantinelabel bij elke installatie en upgrade, zodat de app start zonder handmatige Gatekeeper-stappen:
brew tap allenv0/scm
brew trust allenv0/scm
brew install --cask allenv0/scm/scm
Upgrades behouden hetzelfde gedrag:
brew upgrade --cask allenv0/scm/scm
Voor wie de voorkeur geeft aan minimale rechten (trust alleen de cask in plaats van de hele tap):
brew tap allenv0/scm
brew trust --cask allenv0/scm/scm
brew install --cask scm
De tap bevindt zich op allenv0/homebrew-scm.
Ontwikkeling
bun run dev # bouwt de renderer bundle en start de Electron-app
bun start # start de Electron-app zonder opnieuw te bouwen
bun run build # bouwt enkel de renderer bundle in dist/
Het app-pakket bouwen (DMG / ZIP)
bun run dist # ondertekend als er een identity in de keychain staat
bun run dist:unsigned # slaat code-sign detectie over
Dit proces voert twee stappen sequentieel uit:
vite build— compileert de React renderer naardist/(verwerkt alle wijzigingen ondersrc/).electron-builder --mac— pakket de app. Het bundelt de nieuwedist/bundle samen metmain.js,preload.js,main-lib/enindexer/(de bestandenlijst is geconfigureerd onderbuild.filesinpackage.json), en produceert vervolgens de installers.
Output: De installers komen in dist-app/ (zie build.directories.output in package.json) — zoek naar SCM-0.2.4.dmg en SCM-0.2.4.zip.
Functies
Bestanden
Het typen start een directe pre-pass op bestandsnaam-trefwoorden, waarna het vision-model het overneemt. Resultaten worden gescoord via cosinus-gelijkenis tegen image embeddings, met gated phrase- en bestandsnaam-boosts, een 'honesty floor' die per model is gekalibreerd, en een diversiteitsfilter voor bijna-duplicaten. Elke tegel bevat een badge "waarom het matchte" (Visual match / Filename match / …) en een hover-tooltip met de score-uitsplitsing per component. CJK-zoekopdrachten zoeken als overlappende bigrams ("台北車站" matcht ook 台北 en 車站).
Scènes
Elk scènesegment in alle video's wordt gescoord, zodat een hit direct op het exacte shot landt. Tegels tonen de scène-poster met een tijdcode-badge; het openen van de video springt direct naar dat moment. Een noise gate geeft "geen scène-match" terug in plaats van het raster te vullen met onzin, en elke video draagt maximaal 3 scènes bij.
OCR
Matcht het fractie van de query-tokens die letterlijk zichtbaar zijn in de OCR-tekst van elke afbeelding. De bestandsnaam wordt genegeerd en er is geen vision-model bij betrokken, waardoor het werkt zelfs als de AI-engine nog opwarmt of offline is. Gematchte woorden worden in amber omkaderd op de tegels en in de lightbox.
Dialoog
Exacte letterlijke retrieval via Whisper-transcripties — geen embeddings, geen drempelwaarden, werkt ook als de AI-engine uitstaat. Resultaten komen in drie niveaus:
- Exacte regel (aaneengesloten frase in één uiting).
- Exacte woorden (alle woorden in één uiting of een venster van ≤8s).
- Gesproken woorden (alle woorden in dezelfde video).
Gematchte woorden worden gemarkeerd in een tekstfragment; het openen van een resultaat zoekt direct naar de regel.
LLM's (Ask)
Optioneel — er wordt niets gedownload of uitgevoerd totdat dit is ingeschakeld in Settings → LLMs Chat. Een llama.cpp sidecar verbonden met loopback beantwoordt vragen op basis van bewijs dat de app al heeft geëxtraheerd (dialoogregels, OCR-tekst en bestandsnaam-trefwoorden), met genummerde citaten waarop je kunt klikken. Antwoorden worden token-voor-token gestreamd met een live tok/s readout.
Prefixen zoals /screenshots, /videos en /email vernauwen het corpus. De knop 'Stop' behoudt het gedeeltelijke antwoord; bij ontbrekend bewijs wordt het proces afgebroken voordat het model überhaupt start.
| Chat-model | Grootte | Notities |
|---|---|---|
| Qwen3 1.7B (standaard) | ~1.1GB | Snelle dagelijkse chat; past op 8GB Macs |
| Llama 3.2 3B | ~2GB | Sterkere lange antwoorden; vereist meer headroom |
Tabbladen & bibliotheekweergaven
Ingebouwde browse-tabbladen: All, Videos, plus Screenshots en Email (de laatste twee zijn togglebaar in Settings → Smart Tabs). Het selecteren van Videos activeert automatisch de Scènes-modus.
Elke zoekopdracht kan als tabblad worden opgeslagen: de 'pin pill' onder de zoekbalk slaat de huidige prompt op met de bijbehorende modus (Files/Scenes/OCR/Dialogue) — maximaal 20 tabbladen, hernoembaar en exact herstelbaar.
Er zijn vijf semantische weergaven achter herdefinieerbare sneltoetsen (standaard ⌘1–⌘5), plus ⌘I voor import / AI Insights, en ⌘, voor Instellingen. Zoekopdrachten blijven in het geselecteerde tabblad: kies eerst de scope (bijv. Screenshots), en zoek dan. In de LLM-chat is dit expliciet via /screenshots, /videos of /email.
Email-tabblad
Toont foto's waarvan de zichtbare OCR-tekst een e-mailadres bevat. Dit is een overlappende weergave (een foto behoudt ook zijn eigen categorie). De detectie is OCR-tolerant: het voegt adressen samen die door Tesseract zijn gesplitst over word-boxes, en handelt ruis aan zoals komma's in plaats van punten ("gmail,com"), gesplitste TLD's ("gmail. com"), bracket-obfuscatie ("allen [at] gmail [dot] com") en gedicteerde adressen ("allen at gmail dot com"). Tegels tonen een contactstrip; uitvouwen maakt kopiëren of mailen mogelijk.
Screenshots-tabblad
De classificatie van screenshots is ongevoelig voor hernoemen. Vier signalen worden gebruikt in volgorde van prioriteit:
- Handmatige override (rechtermuisknop op tegel).
- Bestandsnaam-vocabulaire (30+ gelokaliseerde OS-screenshotnamen in 20+ talen).
- PNG/JPEG metadata probe (leest "screenshot" uit PNG text chunks / EXIF UserComment).
- Source-folder hint.
Alles wat niet matcht, komt terecht in Projects.
Visuele modellen
Vier schakelbare modellen via ONNX Runtime; het actieve model wordt per bibliotheek gekozen:
| Model | Rol | Snelheid (CPU) | Download |
|---|---|---|---|
| CLIP ViT-L/14@336 (standaard) | Beste real-world video scène-search | ~480–570ms/img | ~435MB |
| SigLIP-2-B/16 | Snelste bulk-import | ~50–100ms/img | ~412MB |
| SigLIP-2-L/16@256 | Hoog detail (1024-dim) — kleine objecten, borden, on-screen tekst | ~200ms/img | ~850MB |
| SigLIP-B/16@384 | Maximum detail | ~480ms/img | ~214MB |
Het wisselen van model zorgt voor een re-embedding van de hele bibliotheek. De overstap is direct; de rest wordt op de achtergrond ingevuld. Tot die tijd valt de zoekfunctie terug op trefwoorden in de bestandsnaam. Per-model text-mean centering voorkomt bias in text embeddings, zodat similarity scores eerlijk blijven over verschillende modellen.
Video zoek-pijplijn
ffmpeg scant elke video op shot-grenzen en bouwt een segmentplan, gesampled volgens de dichtheid die je kiest in Settings → Video Search. Elk preset toont de gemeten tijd en schijfkosten:
| Preset | Seconden per punt | Segmentbudget |
|---|---|---|
| Eco | 60 | 4–32 |
| Balanced (standaard) | 30 | 8–128 |
| Detailed | 15 | 12–256 |
| Ultra | 5 | 16–1024 |
| Ultra Pro | 2.5 | 24–2048 (bevestiging vereist) |
Elk segment embedt het midpoint-frame en bewaart een poster. Shot-plannen worden per bestand gecached (pad + grootte + mtime + config fingerprint), zodat re-imports de detectie overslaan.
Dialoog-transcriptie: Gebruikt Whisper tiny.en (~150MB, standaard) of base.en (~300MB). Het wisselen van model zorgt voor een nieuwe transcriptie van alle video's. Complete video's embedden drie frames (20/50/80%) gemiddeld; GIF's embedden een gemiddelde van de middelste frames.
OCR
Tesseract draait in een eigen worker, gescheiden van het vision-model. Engels staat altijd aan; 35 extra talen zijn schakelbaar in Settings → Photo Search (standaard: Vereenvoudigd + Traditioneel Chinees, Japans, Koreaans). Elk talenpakket wordt één keer gedownload (~2.4–5MB; ~17MB voor de standaardset) en werkt daarna offline. Word-boxes worden opgeslagen met de tekst, zodat matches ter plekke gemarkeerd kunnen worden; CJK-tekst wordt zonder spaties samengevoegd en e-mailfragmenten die over word-boxes verspreid zijn, worden gereconstrueerd.
Import & bibliotheekbeheer
Import kan via ⌘I, drag-and-drop, of gemonitorde mappen. Het importeren van een map start de monitoring (live fs.watch plus een re-sync bij elke start). Probleembestanden worden tot 3 keer opnieuw geprobeerd, daarna worden ze overgeslagen bij syncs totdat ze wijzigen.
Renoem-bestendige deduplicatie: Elk bestand wordt content-hashed (SHA-256) vóór kopieeracties, en onjuiste extensies worden genormaliseerd via MIME sniffing.
Genummerde embedding-versies (Settings → Library): Point-in-time snapshots van de gehele doorzoekbare staat (index, embeddings van elk model, scènes en transcriptie-sidecars), met een herstelfunctie (automatische back-up eerst) en een 'Fresh Start' danger zone. Maximum: 10 versies.
Alle gegevens staan onder ~/Library/Application Support/scm (overschrijfbaar via MEMORIESDATADIR): de index JSON, Float32 embedding bins per model, scène- en transcriptie-sidecars, thumbnails en posters.
Privacy door ontwerp
De renderer is een gesandboxte app:// bundle — met contextIsolation, OS sandbox, en een CSP gepind op 'self' (+ Google Fonts CDN voor display-type, met monospace fallback wanneer offline).
Alleen main-process workers downloaden bestanden, één keer per item: vision weights (Hugging Face), OCR-talenpakketten (Tesseract CDN), Whisper weights, en — indien geactiveerd — de llama.cpp sidecar en GGUF chat-modellen (GitHub + Hugging Face), sha256-geverifieerd bij download.
Media wordt gekopieerd naar de door de app beheerde bibliotheek en vanaf schijf gestreamd. Geen telemetrie, geen accounts, geen uploads.
Instellingen & Details
Een macOS-stijl instellingenvenster met tien panelen (Library, Appearance, Grid, Smart Tabs, Photo Search, Video Search, LLMs Chat, Global Shortcut, Keyboard, Menu Bar).
Extra's:
- Thema's: light/dark/system.
- CRT-schermeffect voor de lightbox.
- 6 alternatieve app-iconen.
- Menu-bar-only modus.
- Opneembaar global shortcut.
- First-run onboarding tour.
- Background-work trays (scènes / transcripties / OCR) met een globale pauzeknop.
- Statusbalk met versie en aantal geïndexeerde video's.
Opmerkingen
- Eerste build is traag:
electron-builderdownloadt de Electron-binary enffmpegbij de eerste run; volgende builds zijn veel sneller. - Code signing: Zonder een Apple Developer identity in de keychain worden DMG's ongetekend gebouwd. De app draait lokaal nog steeds, maar macOS kan bij de eerste start een rechtermuisklik → Open vereisen.
- Rebuilds: Omdat
main.js,preload.js,main-lib/enindexer/vanuit broncode worden gepakket (niet gecached), produceert eenbun run distna bewerkingen direct een nieuw pakket.
Testen
bun run test:all # volledige verificatie batterij (unit + smoke suites)
bun run smoke:indexer # headless smoke test van de CLIP indexer
bun test test/ # unit tests (pure modules, geen Electron nodig)
bun run lint # eslint
bun run format:check # prettier
Unit tests dekken de pure cores: ranking, dialogue exact-match, CJK tokens, de Whisper model ladder, MIME sniffing, transcript fusion, etc.
E2E smoke tests draaien in de Electron-app via ELECTRONSMOKE* omgevingsvariabelen — meer dan een dozijn scenario's van boot/protocol checks tot search matrices, modelmigratie en Ask-modus (drivers in scripts/e2e/, aangeroepen in main.js; bijv. bun run smoke:ask, smoke:deep, smoke:grid).
Benchmarks: bun run bench:inference, bench:enrich, en bench:detect schrijven JSON-rapporten naar MDs/bench-*.
Projectstructuur
main.js: Electron main process: bibliotheek, IPC, indexer worker pool,app://protocol.preload.js:contextBridge— steltwindow.memoriesbeschikbaar voor de renderer.main-lib/: Main-process modules gesplitst uitmain.js(instellingen, library store, rank search, Ask retrieval, LLM sidecar config, embedding versies, …).indexer/: vision/OCR/ASR workers (utility processes) + video utils + model registry.src/: React renderer (grid, zoekmodi, lightbox, tabbladen, instellingen, onboarding).scripts/: bench scripts, E2E drivers, de smoke batterij (smoke-all.sh).test/: unit + integratietests.MDs/: design docs, bench rapporten, plannen.dist/: vite build output (renderer bundle).dist-app/: electron-builder output (DMG / ZIP).
Groetjes,