aboutsummaryrefslogtreecommitdiffstats
path: root/bookmarks/README.md
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-10-26 13:07:08 +0100
committerDanilo M. <danix@danix.xyz>2026-10-26 13:07:08 +0100
commite320c1e0e4452d9bf4903d64e0dd3e1b3ced95d2 (patch)
tree34847129057c55116bc056f1af61c892342582f3 /bookmarks/README.md
parentf161ba41344c6834f10eaa5064dc02bc9c1cfdf6 (diff)
downloadquickshell-e320c1e0e4452d9bf4903d64e0dd3e1b3ced95d2.tar.gz
quickshell-e320c1e0e4452d9bf4903d64e0dd3e1b3ced95d2.zip
bookmarks: Firefox bookmarks as a full-width overlay
A new standalone shell, shaped like hosts, on SUPER+b. bookmarks.py reads the bookmark tree and favicons of the profile a running Firefox holds (found through the pid in its lock symlink; the last-modified places.sqlite was tried first and picked the guest profile after a stray launch touched it). Firefox keeps both databases in exclusive lock mode, so they are copied with their -wal and the copy is read: an immutable=1 open of the original skipped the log and kept showing bookmarks deleted minutes before. The grid spans DP-1, a ListView of card rows so only visible rows exist, grouped by folder with each folder's own bookmarks before its subfolders. Six icon buttons (Ctrl+S cycles) sort by folder, newest, title, site, most visited or link state. The wheel scrolls three rows a notch through a MouseArea, since Flickable's own step crawled; only the selected card carries a Glow, through a Loader. Favicons are extracted by content hash; a page without one takes any icon Firefox holds for its host, which found 52 more of 504. bookmarks.py --links (Ctrl+R) checks every http(s) bookmark, HEAD then GET, 32 at a time, in about 22s, and caches the result. Dead means 404/410, an NXDOMAIN or a refused connection; 403, 429, timeouts, TLS and resolver failures are only "unsure", because a 32-thread burst produced EAI_AGAIN that a serial retry resolved. Bookmarks open with xdg-open: a plain `firefox <url>` does not reach an instance started with -P and opened the default profile beside it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'bookmarks/README.md')
-rw-r--r--bookmarks/README.md64
1 files changed, 64 insertions, 0 deletions
diff --git a/bookmarks/README.md b/bookmarks/README.md
new file mode 100644
index 0000000..61efb2a
--- /dev/null
+++ b/bookmarks/README.md
@@ -0,0 +1,64 @@
+# bookmarks
+
+Firefox's bookmarks as a grid of cards across DP-1, grouped by folder, over
+a dimmed screen. SUPER+b opens it, Escape or a click outside closes it.
+
+Each card shows the favicon, the title, the host and, after a link check, a
+badge: red for a link that is gone (404, 410, a host that no longer
+resolves, a refused connection), yellow for one that may only be refusing a
+script (403, 429, a timeout, a TLS error, a 5xx).
+
+Type to filter (every word must match title, address or folder), arrows
+and PageUp/PageDown to move. Enter opens the bookmark with `xdg-open`, so it
+lands in the running Firefox profile rather than a new one. Tab shows
+only the dead ones; Ctrl+R checks every link again.
+
+The icon buttons sort, and Ctrl+S cycles them: Firefox's folder order,
+newest first (headed by year), title (headed by letter), site, most visited,
+and link state (dead, unsure, fine, not checked). Ties keep folder order.
+Outside folder order each card names its folder.
+
+The wheel scrolls three rows of cards a notch, set in the `MouseArea` over
+the grid; Flickable's own step was a few pixels.
+
+`bookmarks.py` builds the list on every opening, so a bookmark added in
+Firefox shows the next time. It reads `places.sqlite` and `favicons.sqlite`
+of the profile a running Firefox holds, found by its `lock` symlink's pid,
+else the one with the largest `places.sqlite` (`BOOKMARKS_PROFILE` overrides
+both). Firefox holds them in exclusive lock mode, so they are copied with
+their `-wal` to a temporary directory and the copy is read: the log is where
+Firefox puts recent changes, a deletion included, until a checkpoint.
+Nothing is ever written to them: deleting a dead bookmark is done in Firefox.
+
+Folders keep Firefox's order, toolbar first, but a folder's own bookmarks
+come before its subfolders rather than interleaved with them. Tags are left
+out. Favicons are extracted to `~/.cache/keystone/bookmarks/icons/`, named by
+content hash; a bookmark with no icon of its own takes any icon Firefox holds
+for the same host, and one with none at all shows the host's initial.
+
+The link check (`bookmarks.py --links`) sends one request to every http(s)
+bookmark, 32 at a time, HEAD and then GET if HEAD fails, and writes
+`~/.cache/keystone/bookmarks/links.json`. It took 22s over 503 links. It runs
+only when asked, and a site down at that moment reads dead until the next
+check.
+
+## Running
+
+ qs -p ./bookmarks
+ qs -p ./bookmarks ipc call bookmarks toggle
+
+## Blur
+
+Optional, in the Hyprland config:
+
+ hl.layer_rule({
+ name = "blur-bookmarks",
+ match = { namespace = "^(keystone-bookmarks)$" },
+ blur = true,
+ xray = false,
+ ignore_alpha = 0.1,
+ })
+
+## Check
+
+ /usr/bin/python3 bookmarks/bookmarks.py --check