Skip to content

About

Windows Recall-style screen memory for KDE Plasma 6 on Wayland: active-window screenshots, OCR, browser/Zotero/Anki context, fzf search

Resources

Stars

3 stars

Watchers

0 watching

Forks

Repository files navigation

linux_recall

Important

Why build this on Linux?

See a feature you need on another OS, like Windows Recall? On Linux you can build it yourself, today.
Think of one nobody has built yet, like going from a screenshot back to exactly what you were looking at? You can add that today too.
On other operating systems you can't, or at least not quickly.

A Windows Recall–style screen memory for KDE Plasma 6 on Wayland. Every minute (or on a hotkey) it screenshots the active window and records what you were looking at: the app, the window title, the browser URL, the paper and page open in Zotero, the Anki card under review, the Obsidian note, the file open in Neovim (in kitty or Neovide), the last shell command and its output, the Claude Code session, and the OCR'd text. Each capture is a small WebP plus a JSON file, searchable from the command line. You can jump back to the source with one key: the web page, the PDF page in Zotero, the Anki card, the Obsidian note, the file and line in Neovim, or the kitty window.

中文说明

Demo

linux-recall-demo.mp4

Features

  • Capture: active window only, downscaled to logical pixels (about 150 KB per shot); hotkey Meta+Alt+R or a systemd user service every 60 s
  • Deduplication: same app and a perceptually similar image (dHash) → the shot is dropped; every decision is logged with its similarity score
  • Time per app: linux-recall-usage sums how long each app was in front from the daemon's log, per day if you like, with browsers split by site and kitty by foreground program (--by detail)
  • Context per app:
    • browsers: URL of the active tab
    • Zotero: item, DOI, current page, and a zotero://open-pdf/...?page=N link
    • Anki: the card under review (or the Browse selection), with field text
    • Obsidian: vault, note, heading, tags, and the Markdown source lines visible on screen
    • InkyCap: notebox, note, and its title, date and tags (the note it last made active)
    • Okular: document path, title, author, and current page of the active tab
    • kitty: the focused tab and window, its shell and foreground processes; the last command and its output (shell integration); for Neovim the open file, cursor and visible text; for Claude Code the session id, title and last prompt
    • Neovide: the same Neovim record as in kitty
  • OCR: PaddleOCR cloud API (PP-OCRv6, Chinese + English), falling back to local RapidOCR on timeout or error
  • Search: jq recipes and lrf, an fzf picker with the matching line boxed in red on the screenshot (rendered inline in kitty), and ctrl-o to reopen the URL / PDF page (Zotero or Okular) / Anki card / Obsidian note / Neovim file / kitty window
  • Click to Do: Meta+Alt+C turns the active window into a page where the text in the screenshot is selectable word by word

Install

Requirements: KDE Plasma 6 (Wayland), Python ≥ 3.12, spectacle; uv when running from source. Optional: plasma-browser-integration plus its browser extension (URLs); Zotero 7+ with Settings → Advanced → Allow other applications on this computer to communicate with Zotero; Anki with AnkiConnect; Obsidian 1.12+ with its CLI enabled; PADDLEOCR_TOKEN for cloud OCR; jq, fzf, ImageMagick and kitty for search.

Arch Linux (AUR)

linux-recall-git is in the AUR. It builds from the latest commit and pulls in its dependencies from pacman (python-rapidocr also comes from the AUR), so use an AUR helper:

paru -S linux-recall-git                        # or yay -S, or makepkg -si in a clone of the AUR repo
linux-recall-install-hotkey                     # Meta+Alt+R: capture, Meta+Alt+C: Click to Do
systemctl --user enable --now linux-recall      # optional: capture every minute
source /usr/share/linux_recall/lr.zsh           # in ~/.zshrc, for lr-search / lr-highlight (lrf is in /usr/bin)

The search tools are optional dependencies: pacman -S --asdeps jq fzf imagemagick kitty plasma-browser-integration. Local OCR needs python-rapidocr; with python-openvino installed as well it runs on OpenVINO instead of ONNX Runtime. With the package, drop uv run from the commands below.

Debian / Ubuntu (.deb)

Each release has an amd64 .deb for distros with Plasma 6: Debian 13, Kubuntu 25.04+, KDE neon. Debian does not package onnxruntime or rapidocr, so the package bundles its own Python and dependencies under /usr/lib/linux-recall (about 500 MB installed, mostly OpenCV and ONNX Runtime).

sudo apt install ./linux-recall_*_amd64.deb     # the file downloaded from the release
linux-recall-install-hotkey                     # Meta+Alt+R: capture, Meta+Alt+C: Click to Do
systemctl --user enable --now linux-recall      # optional: capture every minute
source /usr/share/linux_recall/lr.zsh           # in ~/.zshrc, for lr-search / lr-highlight (lrf is in /usr/bin)

jq, fzf, imagemagick, kitty and plasma-browser-integration are recommended and installed by default; lrf highlights matches with ImageMagick 7 (magick), which Ubuntu 24.04 and older lack. With the package, drop uv run from the commands below.

From source

git clone https://github.com/junyixu/linux_recall ~/WorkSpace/windows_recall_linux
cd ~/WorkSpace/windows_recall_linux
uv sync                              # or `uv venv --system-site-packages && uv sync` to use pacman's python-openvino
./scripts/install-hotkey.sh          # Meta+Alt+R: capture, Meta+Alt+C: Click to Do

# optional: capture every minute; uv.conf points the unit at .venv (edit it if you cloned elsewhere)
cp systemd/linux-recall.service ~/.config/systemd/user/
install -Dm644 systemd/uv.conf ~/.config/systemd/user/linux-recall.service.d/uv.conf
systemctl --user daemon-reload && systemctl --user enable --now linux-recall

Cloud OCR token

Important

Neither the packages nor the source install set PADDLEOCR_TOKEN; you have to add it yourself. Without it, OCR is local only and Click to Do (cloud only) does not work.

Get an access token from Baidu AI Studio's PaddleOCR page, then put it in ~/.config/environment.d/ so the hotkey and the service see it (a variable exported only in ~/.zshrc does not reach them):

mkdir -p ~/.config/environment.d
echo 'PADDLEOCR_TOKEN=...' >> ~/.config/environment.d/linux-recall.conf

Then log out and back in; the session reads environment.d only at login.

Why this is harder on KWin Wayland

On X11, any client can read the window list, the focused window and any window's pixels. Wayland deliberately removes all three, and KWin doesn't implement the generic protocols that wlroots compositors offer (wlr-screencopy, ext-image-copy-capture, ext-foreign-toplevel-list). That's why OpenRecall (mss → XGetImage) fails outright on a Wayland session, and screenpipe, which can grab whole monitors through the ScreenCast portal, still can't tell which window is focused on Plasma: its active-window lookup only knows Hyprland, Sway and X11.

What linux_recall uses instead:

Need X11 way What works on KWin Wayland (used here)
Focused window, its geometry, screens _NET_ACTIVE_WINDOW, xdotool A throw-away KWin script loaded over D-Bus
Screenshot XGetImage Spectacle in background mode (it's on KWin's screenshot whitelist)
Browser URL read the window / X properties Plasma Browser Integration's D-Bus tab list
Global hotkey XGrabKey kglobalaccel over D-Bus
Idle / locked XScreenSaver org.freedesktop.ScreenSaver.GetActive

How it works

flowchart LR
    T["Meta+Alt+R<br/>or daemon tick (60 s)"] --> K["KWin script over D-Bus<br/>active window + screens"]
    K --> S["spectacle -b -a -S<br/>full-resolution window shot"]
    S --> C{"which app?"}
    C -->|browser| B["Plasma Browser Integration<br/>tab URL"]
    C -->|Zotero| Z["Zotero Local API<br/>+ reader state → page"]
    C -->|Anki| A["AnkiConnect<br/>current card"]
    C -->|Obsidian| M["obsidian eval<br/>note + visible lines"]
    C -->|Okular| P["org.kde.okular D-Bus<br/>document + page"]
    C -->|kitty| N["kitten @ ls → shell → nvim<br/>current file + cursor"]
    B & Z & A & M & P & N & C --> D{"daemon only:<br/>same app and dHash ≥ 0.95?"}
    D -->|yes| X["drop"]
    D -->|no| W["save downscaled WebP + JSON"]
    W --> O["OCR the full-res original<br/>cloud → local fallback"]
    O --> J["rewrite JSON with text + boxes"]
Loading

Active window: a KWin script over D-Bus

Wayland has no API for "which window is focused", but KWin's scripting engine can see everything. kwin.py writes a short JavaScript file that reads workspace.activeWindow and workspace.screens, loads it with org.kde.KWin /Scripting loadScript, runs it, and the script calls back into our own D-Bus connection with callDBus(...) carrying a JSON payload. Then the script is unloaded. This is the same trick kdotool uses, but one round trip returns every field we need (caption, app id, pid, geometry, output, per-screen scale) in about 50 ms.

Screenshots: Spectacle, then downscale

KWin's org.kde.KWin.ScreenShot2 only accepts callers listed in a whitelisted .desktop file, so the tool runs spectacle --background --nonotify --new-instance -a -S:

  • -a -S captures only the active window, without its transparent drop shadow
  • --new-instance matters: Spectacle is single-instance over D-Bus, so without it a hotkey shot during a daemon shot gets forwarded to the first process and its --output is lost
  • --scaled only works together with full-desktop mode, so HiDPI window shots are downscaled in Python

On a 2× screen a window shot is 3840 px wide, but OCR runs on that full-resolution original, and only the copy that's kept is downscaled (÷ the output scale). OCR on the downscaled copy lost more than half the lines of small terminal text (105 → 42). OCR boxes are then mapped to the saved image's pixels.

Browser URL: Plasma Browser Integration

The browser extension exports all tabs through a KRunner interface (org.kde.plasma.browser_integration /TabsRunner). It doesn't say which tab is active, so the window caption (minus " — Mozilla Firefox") is matched exactly against tab titles. Two gotchas: TabsRunner caches the tab list until Teardown() (otherwise you get stale titles), and tabs with identical titles are recorded as "match": "ambiguous". Private windows are skipped.

Zotero: Local API + reader state

Zotero 7+ serves a read-only copy of its web API at http://localhost:23119/api/, but it has no endpoint for "the open reader tab". The reader tab's window caption is Title - Author - Year - Zotero, so the tool searches with qmode=titleCreatorYear and reads the current page from storage/<attachment>/.zotero-reader-state (pageIndex, 0-based), which Zotero also keeps for linked files. ctrl-o later opens zotero://open-pdf/library/items/<attachment>?page=N.

Anki: AnkiConnect

While reviewing, guiCurrentCard + cardsInfo give the card, note, deck and fields; in the Browse window, guiSelectedNotes gives the selection. Fields are stored as plain text, so card content is searchable. ctrl-o calls guiBrowse cid:<card> and guiSelectCard, then raises the Browse window with kdotool, because KWin's focus-stealing prevention blocks Anki from raising an already-open window itself.

Okular: its own D-Bus interface

Each Okular process owns org.kde.okular-<pid> (the pid KWin reports), and every document tab is an object at /okular, /okular2, … answering currentDocument, currentPage, pages and documentMetaData. Nothing marks the active tab, so, as with browser tabs, the one whose title or file name equals the window caption wins (match: "ambiguous" when several do). A handful of D-Bus calls take about 2 ms. ctrl-o runs okular --page N <file>.

Obsidian: the official CLI

Obsidian 1.12+ ships a CLI that talks to the running app (Settings → General → Command line interface). The window caption is Note - Vault - Obsidian 1.x, so the tool runs obsidian vault=<Vault> eval code=… in that vault's window: one call (about 0.3 s) returns the active file, view mode, cursor, heading, tags, and the Markdown source lines on screen (in reading view, the rendered sections inside the pane; in editing view, CodeMirror's posAtCoords at the top and bottom edges). ctrl-o opens obsidian://open?vault=…&file=….

kitty and Neovim: remote control + msgpack-RPC

With allow_remote_control yes and listen_on unix:/tmp/kitty in kitty.conf, kitty listens on /tmp/kitty-<pid> and exports that address to its children as KITTY_LISTEN_ON; the KWin window's pid is kitty's, so the address is read from a child's /proc/<pid>/environ. kitten @ ls gives the tree kitty → OS window → tab → window (the shell, e.g. zsh) → foreground_processes (what the shell is running). Neovim is split into a TUI process and an nvim --embed server child (in its own process group, so kitty doesn't list it); the server listens on $XDG_RUNTIME_DIR/nvim.<server pid>.0, and one nvim --server … --remote-expr call (about 10–30 ms) returns the current file, cursor, mode, cwd, listed buffers, and the exact text visible in every window of the current tabpage (lines w0..w$ with their line numbers; plugin UIs and secret-looking files such as .env or ~/.ssh/* keep only the path). lrf searches that text too and shows matches as file:line, so ctrl-o jumps to the matching line rather than wherever the cursor was. OCR still runs, for the statusline, other UI and the boxed preview. ctrl-o goes back to it: if that Neovim is still running, it :drops the file and sets the cursor over the same socket, then kitten @ focus-window brings up its kitty window (buffers, splits and jumplist intact); otherwise it opens nvim +<line> <file> in a new kitty tab. Neovide works the same way: its nvim --embed child is the server, ctrl-o raises the Neovide window with kdotool windowactivate <KWin internal id>, or starts neovide -- +<line> <file>. For other kitty captures, ctrl-o focuses the same kitty window, or its tab if the window was closed (ids are never reused within a kitty process, and a restarted kitty listens on a different /tmp/kitty-<pid>, so a stale id can't hit the wrong window).

With kitty's shell integration, kitten @ ls also reports each window's last_reported_cmdline, at_prompt and last_cmd_exit_status, and kitten @ get-text --extent last_cmd_output returns that command's output, or the output so far while it's still running: the exact terminal text, no OCR (the last 200 lines are kept; full-screen programs and commands like pass, gpg, printenv or cat .env keep only the command line). Claude Code writes ~/.claude/sessions/<pid>.json (session id, cwd, status) for each running CLI; the end of the transcript ~/.claude/projects/*/<session id>.jsonl has the session's ai-title and last-prompt. If the kitty window is gone, ctrl-o runs claude --resume <session id> in a new tab.

OCR: cloud first, local fallback

  • Cloud: the PaddleOCR API (PP-OCRv6, needs PADDLEOCR_TOKEN). It downscales detection input to 960 px and rejects anything above 4000 px, so a whole three-monitor desktop (6098 px wide) came back with 2 lines. Uploading just the window gives 100+ lines. A full queue (code 10010) is retried with backoff. Spectacle's WebP is uploaded as is: re-encoding it as PNG gave identical text at 4× the size, and JPEG (a second lossy pass) lost 5–60% of the characters.
  • Local: RapidOCR (PaddleOCR models), used when the cloud misses --ocr-timeout (default 60 s, covering the whole attempt) or errors. The JSON records which engine ran and why the fallback happened. It runs on OpenVINO when that is installed, otherwise ONNX Runtime: same models, identical text, but a 3840×2080 window on 4 threads costs 8.6 s of CPU instead of 22 s. OpenVINO's GPU plugin (Xe2 iGPU) was no faster, and its default f16 changed ~5% of the lines, so OCR stays on the CPU. It runs in a child process, since the runtime keeps ~400 MB after the first run.

On a CPU budget, cloud OCR is almost free (~0.1 s of CPU; the 5–60 s is upload and queueing) and the screenshot itself is ~0.6 s, mostly Spectacle. So the daemon never blocks on OCR and keeps local OCR out of the user's way:

  • Kept shots are saved with "ocr": null and their full-resolution original (~0.35 MB, about 3× the saved copy) goes to ~/.cache/linux_recall/pending/; a background thread OCRs the newest one (cloud only, --ocr-timeout defaults to 180 s there) and fills in the JSON. The queue survives restarts and keeps at most 1500 originals.
  • A cloud failure pauses cloud calls for 1 min, doubling up to 30 min; the next shot after the pause probes whether it's back. A missing or rejected token pauses 30 min and sends one notification.
  • On a metered connection (NetworkManager's Metered property; Android phone hotspots announce it) the cloud isn't used at all. --cloud-ocr unmetered is the default for both the daemon and the hotkey, whose shots then join the same queue; always ignores metering and never keeps OCR local. To mark a connection NetworkManager doesn't recognize: nmcli connection modify <name> connection.metered yes.
  • Without the cloud, local OCR only runs while the screen is locked and the laptop is plugged in (a merely failing cloud first gets an hour to come back). One child process OCRs shot after shot, so the runtime and models load once instead of costing ~0.8 s per shot; unlocking kills it within 0.5 s (that shot stays queued), and so does an empty queue. It uses 4 threads: per 3840×2080 window, OpenVINO on 2/4/8 threads took 4.0/3.3/2.5 s and 7.1/8.6/11.3 s of CPU, so a busy day's ~600 shots need about half an hour of a locked night.

The JSON is written twice, atomically: right after the screenshot ("ocr": null) and again after OCR, so a failed OCR never loses the capture.

Deduplication: dHash

similarity.py computes a 256-bit difference hash; similarity is 1 − Hamming distance / 256. Measured on this setup: identical 1.0, clock tick 0.988, one new line of text 0.977, a new paragraph 0.93, 300 px scroll 0.82, unrelated content 0.5–0.73. The daemon drops a shot when it's the same app as the last saved one and similarity ≥ --threshold (0.95); an app switch is always kept, and nothing is taken while the screen is locked or the active window is excluded (see Privacy). Each cycle logs one line and appends to ~/.cache/linux_recall/daemon.jsonl:

KEEP  kitty        similarity 0.543 (threshold 0.95)  changed  [kept 2, skipped 0]
SKIP  kitty        similarity 1.000 (threshold 0.95)  similar  [kept 2, skipped 1]
KEEP  firefox      similarity 0.612 (threshold 0.95)  app kitty -> firefox  [kept 3, skipped 1]

With active use it keeps close to one shot a minute, about 10 MB per active hour.

Because skipped cycles are logged too, linux-recall-usage can tell how long each app was in front: every cycle counts until the next one (capped at twice the interval, so suspend doesn't count), locked time is shown apart.

kitty                   6h05m   50.0%  ██████████████████████████████
firefox                 2h52m   23.6%  ██████████████▏
org.telegram.desktop    1h26m   11.8%  ███████
total                  12h10m
locked                  9h55m

Click to Do: selectable text over a screenshot

Meta+Alt+C screenshots the active window, OCRs it with per-word boxes, and opens an HTML page in Firefox: the screenshot with an invisible text layer on top, built the way pdf.js does it. Every character is an absolutely positioned <span>, stretched with scaleX to cover its box, and lines are ordered column by column, so a drag doesn't pull in the sidebar. The cloud's word boxes are often off by several pixels (7 px median, a whole word in the worst case), so clicktodo/textfit.py re-aligns every character to the actual ink in the screenshot, using dynamic programming over glyph gaps.

Usage

uv run linux-recall-capture                     # one capture (what the hotkey runs)
uv run linux-recall-capture --mode fullscreen   # whole desktop; OCR still only the active window
uv run linux-recall-capture --full-res          # keep HiDPI resolution
journalctl --user -u linux-recall -f -o cat     # watch the daemon's keep/skip decisions
uv run linux-recall-usage --today              # time per app, from the daemon's log (--by detail: site / kitty program)

Captures live in ~/.local/share/linux_recall/captures/YYYY-MM-DD/<id>.{webp,json}; logs and caches in ~/.cache/linux_recall/.

Search

source scripts/lr.zsh      # in ~/.zshrc

lrf lunar                  # fzf: app + time │ matching line, keyword in red │ site / paper / deck
                           #   preview: screenshot with the match boxed (kitty), enter: print the JSON path,
                           #   ctrl-f: print the file open in Neovim (app shows as kitty(neovim)),
                           #   ctrl-t: print the InkyCap note's path, ctrl-s: open image,
                           #   ctrl-o: reopen the URL / Zotero or Okular page / Anki card / Neovim file and line / kitty window
lr-search 截图保存          # plain TSV: time, app, URL or title, image path
lr-highlight <capture.json> lunar   # PNG with matching lines boxed

Or query the JSON directly:

cd ~/.local/share/linux_recall/captures
jq -r --arg q lunar '
  select([.ocr.text, .window.caption, .browser.url] | map(. // "") | join("\n")
         | ascii_downcase | contains($q | ascii_downcase))
  | [.captured_at[:19], .window.app_name, (.browser.url // .window.caption)] | @tsv' */*.json

More recipes (timelines, per-app filters, papers read, dedup statistics): README.zh-CN.md.

Capture format (abridged)

{
  "schema_version": 14,
  "captured_at": "2026-09-23T20:51:42.366+02:00",
  "trigger": "timer",                                   // or "hotkey"
  "screenshot": {"file": "….webp", "mode": "window", "width": 1969, "height": 1068,
                 "scale": 0.5128, "dhash": "…"},
  "window":  {"app_name": "Firefox", "caption": "…", "desktop_file": "firefox", "pid": 2770, "output": "DP-1"},
  "browser": {"url": "https://…", "match": "exact"},    // null unless a browser
  "zotero":  {"title": "…", "doi": "…", "page": 48,
              "open_link": "zotero://open-pdf/library/items/I2R4MPPK?page=48"},
  "anki":    {"mode": "review", "card_id": 1706122964826, "deck": "…", "fields": {"Front": "…"},
              "browse_query": "cid:1706122964826"},
  "kitty":   {"address": "unix:/tmp/kitty-7201", "tab": {"id": 35, "title": "…"}, "window": {"id": 37, "pid": 16134, "cmdline": ["/bin/zsh"], "cwd": "…"},
              "foreground_processes": [{"pid": 98870, "cmdline": ["nvim"], "cwd": "…"}],
              "nvim": {"pid": 98870, "server_pid": 98871, "file": "/…/notes.md", "line": 17, "col": 1,
                       "filetype": "markdown", "modified": false, "mode": "n", "cwd": "…", "buffers": ["…"],
                       "windows": [{"file": "/…/notes.md", "buftype": "", "current": true, "first": 3,
                                    "lines": ["line 3 of the file", "line 4", "…"]}]},
              "shell":  {"cmdline": "make test", "at_prompt": true, "exit_status": 1, "output_lines": 312,
                         "output": ["… the last 200 lines …"]},
              "claude": {"session_id": "c26e9654-…", "cwd": "…", "status": "busy", "title": "…", "last_prompt": "…",
                         "transcript": "~/.claude/projects/…/c26e9654-….jsonl"}},
  "neovide": {"nvim": {"file": "…", "line": 17, "windows": […]}},   // null unless Neovide
  "obsidian": {"vault": "Notes", "file": "diary/2026/09/2026-09-23.md", "mode": "preview", "heading": "…",
               "tags": ["#diary"], "first": 5, "lines": ["line 5 of the note", "…"],
               "open_link": "obsidian://open?vault=Notes&file=diary/2026/09/2026-09-23.md"},
  "inkycap": {"notebox": "notes", "file": "reading/attention.typ", "path": "/home/…/notes/reading/attention.typ",
              "title": "…", "date": "2026-09-24", "zid": 20260924152701, "tags": ["ml"], "aliases": []},
  "okular":  {"path": "/home/…/paper.pdf", "file": "paper.pdf", "title": "…", "author": "…", "page": 12, "pages": 134,
              "tabs": 1, "match": "exact"},
  "ocr":     {"engine": "paddleocr-cloud PP-OCRv6", "fallback_reason": null, "elapsed_s": 4.9,
              "text": "…", "lines": [{"text": "…", "score": 0.99, "box": [[x, y], …]}]}
}

Privacy

Everything is stored unencrypted on your disk, and cloud OCR uploads the window image to Baidu AI Studio. A small exclusion list (linux_recall/exclude.py) is checked right after the KWin query, before Spectacle runs, so nothing of those windows is ever written: password managers (KeePassXC, Bitwarden, 1Password, KWallet), auth prompts (polkit, ksshaskpass, pinentry), Spectacle's own region-select overlay (a frozen image of the whole desktop), and windows whose caption contains Private Browsing, (Incognito), (Private) or [InPrivate], and browser tabs on listed domains (SITES, e.g. a bank; the URL lookup moved ahead of the screenshot for this). Only the active window is checked, and a site is only caught when Plasma Browser Integration reports the URL, so review what you record. Unset PADDLEOCR_TOKEN in environment.d to keep OCR local.

Status

A personal tool, built and tested on Arch Linux + Plasma 6.7 with three mixed-DPI monitors. Next up: SQLite FTS5 index, event-driven triggers (window switch, ext-idle-notify-v1), expiring old images while keeping the JSON, and encryption. Making the JSON more compact is low priority: the data directory sits on btrfs with transparent compression, so the verbose text already takes little space on disk.

Similar projects

  • Windows Recall (Microsoft, Copilot+ PCs): the original idea; Windows only.
  • OpenRecall: open-source Recall clone in Python (AGPL-3.0). Captures with mss, which fails with XGetImage() failed on a Wayland session.
  • screenpipe: event-driven capture, accessibility tree plus OCR, SQLite full-text search. Now source-available rather than open source; on Plasma it records whole monitors through the ScreenCast portal, but its active-window lookup only supports Hyprland, Sway and X11, so it can't tell which native Wayland window you were in.
  • Windrecorder: records and OCRs the screen; Windows only.
  • ActivityWatch: tracks app and window usage time without screenshots; on KWin Wayland it needs awatcher.
  • NormCap: one-off "select a region, copy its text" OCR, which works on Wayland. It's the closest to Click to Do.

About

Windows Recall-style screen memory for KDE Plasma 6 on Wayland: active-window screenshots, OCR, browser/Zotero/Anki context, fzf search

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages