linkhut-dmenu.scm (4155B)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
#!/bin/guile \
-l ./linkhut.scm -e main -s
!#
;;; linkhut interaction via dmenu
;; Copyright (C) 2026 lou woell <lou@repetitions.de>
;;
;; This library is free software; you can redistribute it and/or modify it under
;; the terms of the GNU Lesser General Public License as published by the Free
;; Software Foundation; either version 3 of the License, or (at your option) any
;; later version.
;;
;; This library is distributed in the hope that it will be useful, but WITHOUT
;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
;; FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
;; details.
;;
;; You should have received a copy of the GNU Lesser General Public License
;; along with this library; if not, write to the Free Software Foundation, Inc.,
;; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
;;; Dependencies:
;; - guile 3.0
;; - linkhut account
;; - some dmenu equivalent
;;
;; Executing actions other than opening the link from dmenu depends on bemenu
;; style alternative actions communicated via exit code.
;;
;; Expects config file at $XDG_CONFIG_HOME/linkhut-dmenu/config
;;
;; The config file should contain an alist with entries for HOST and TOKEN.
;; e.g.:
;; `((token . "Bearer <TOKEN>")
;; (host . "api.ln.ht"))'
;;; Features:
;; - show dmenu of posts marked 'unread'
;; - open link in browser via `xdg-open'
;; - mark post as read
;;; Code:
(use-modules
(linkhut)
(rnrs io ports)
;; let-values
(srfi srfi-11)
;; readline
(ice-9 rdelim))
(define prompt "Bookmark: ")
(define (read-config)
(let ((config-path
(string-join
(list (or (getenv "XDG_CONFIG_HOME")
".")
"linkhut-dmenu"
"config") "/")))
(call-with-input-file config-path read)))
;; (post ...) -> ((format-string . post) ...)
(define (show posts)
(map (lambda (post)
(cons (format #f "~A, ~A"
(get-post-value 'title post)
(get-post-value 'tags post))
post))
posts))
;; Spawn a menu let the user select a post.
;; Returns two values:
;; 1. Menu exit code.
;; 2. The selected post.
(define (menu posts)
(let* ((entries (show posts))
(in-pipe (pipe))
(out-pipe (pipe))
(pid (spawn "dmenu" `("dmenu"
"-p" ,prompt
"-P" ">")
#:input (car in-pipe)
#:output (cdr out-pipe))))
(close-port (cdr out-pipe))
(map (lambda (entry) (write-line (car entry) (cdr in-pipe)))
entries)
(close-port (cdr in-pipe))
(let* ((return-code (/ (cdr (waitpid pid)) 256))
(result (read-line (car out-pipe)))
(selected-post (assoc result entries))
(selection (if selected-post
(cdr selected-post)
(format #f "No post selected.~%Result: ~A." result))))
(close-port (car out-pipe))
(values return-code selection))))
(define* (mark-read post #:optional (read #t) #:key (shared #t))
(post-add (get-post-value 'link post)
(get-post-value 'title post)
#:extended (get-post-value 'note post)
#:tags (filter (lambda (x) (not (string= x "unread")))
(get-post-value 'tags post))
#:shared shared
#:replace #t
#:toread read))
(define (open-link post)
(waitpid (spawn "xdg-open" `("xdg-open" ,(get-post-value 'link post)))))
(define (main args)
(let ((config (read-config)))
(parameterize ((token (assoc-ref config 'token))
(host (assoc-ref config 'host)))
(let-values
(((code post) (menu (post-get #:tag "unread"))))
(case code
((01) (format #t "menu aborted.~%~A" post))
((00) (open-link post)) ;; RET.
((10) (mark-read post)) ;; bemenu: selecting with M-1.
((11) (mark-read post #:shared #f)) ;; bemenu: selecting with M-2.
;; ...
(else (display post)))
(newline)))))
|