commit 073b25eb0b22b8306c6e711aff1483a20be2f8f8 from: Omar Polo date: Mon Nov 09 20:31:48 2020 UTC now it can even display pages! commit - 6b4d9ef20633b7e62988eb86b6e18da1e67519b2 commit + 073b25eb0b22b8306c6e711aff1483a20be2f8f8 blob - f109d967bacfc70956ec2c0f243e931c24547d17 blob + b6d8c80d80f6ba9748b686f4db959340645cbdad --- gemini.lisp +++ gemini.lisp @@ -79,7 +79,13 @@ response is fetched, then return the meta and the (dec (let ((resp (parse-response (read-until ssl-stream #\newline)))) (values resp (read-all-stream ssl-stream)))))) -(defun request (url) +(defgeneric request (url) + (:documentation "Perform a request for the URL")) + +(defmethod request ((url string)) + (request (quri:uri url))) + +(defmethod request ((url quri:uri)) (let* ((u (quri:uri url)) (port (or (quri:uri-port u) 1965)) (host (quri:uri-host u))) blob - a6728f74ef17bd880c198e6055a4b47da7729ec1 blob + e60cb1bfca592cf15d1b03512995e0e754a48ee2 --- gemtext.lisp +++ gemtext.lisp @@ -1,5 +1,7 @@ (in-package #:phos/gemtext) +(defparameter *relative-to* nil) + (defclass element () ((text :initarg :text))) @@ -26,8 +28,13 @@ :text text))) (defun make-link (url &optional text) - (make-instance 'link :url (quri:uri url) - :text text)) + (if *relative-to* + (let ((u (quri:copy-uri *relative-to*))) + (setf (quri:uri-path u) url) + (make-instance 'link :url u + :text text)) + (make-instance 'link :url (quri:uri url) + :text text))) (defun parse-link (s) "Parse a line into link." @@ -52,9 +59,14 @@ (defmacro markerp (line) `(uiop:string-prefix-p "```" ,line)) -(defun parse (in) - "Parse gemtext from the stream IN." +(defun parse (in &optional relative-to) + "Parse gemtext from the stream IN. + +RELATIVE-TO is the base URL of the page, it is used to transform url +to absolute urls, if null the transformation does not happen." (loop with doc = nil + with *relative-to* = (when relative-to + (quri:uri relative-to)) for line = (read-line in nil) unless line return (nreverse doc) @@ -68,14 +80,17 @@ when (markerp line) return (make-instance 'verbatim :alt label - :text (format nil "~{~A~%~^~}" content)) + :text (format nil "~{~A~%~^~}" + (nreverse content))) do (push line content)) (parse-line line)) doc))) -(defun parse-string (str) +(defun parse-string (str &optional relative-to) + "Parse the string STR as gemtext. See the documentation of `parse' +for more info." (with-input-from-string (s str) - (parse s))) + (parse s relative-to))) ;; (unparse ;; (with-open-file (stream #P"~/quicklisp/local-projects/phos/test.gmi") blob - 4dd6d7ccc81049f949f9085d7d6dfb50e2ff71d9 blob + d22930faa65e7e687ff98f6626097ede18476d8f --- package.lisp +++ package.lisp @@ -19,4 +19,4 @@ (defpackage #:phos/ui (:documentation "User Interface for phos") - (:use #:cl #:ltk)) + (:use #:cl #:nodgui)) blob - 0034e00ac26d68a3a2db8680e55c96e1fc5d99c4 blob + 4a5b1596b89438c292ec9ac0d0ab15ade98beb82 --- phos.asd +++ phos.asd @@ -6,8 +6,9 @@ :license "ISC" :version "0.0.1" :serial t - :depends-on ("quri" "cl-ppcre" "trivia" "ltk" "usocket" "cl+ssl" "cl-mime") + :depends-on ("quri" "cl-ppcre" "trivia" "nodgui" "usocket" "cl+ssl" "cl-mime") :components ((:file "package") (:file "phos") (:file "gemtext") - (:file "gemini"))) + (:file "gemini") + (:file "ui"))) blob - dbccf3c87a5ed3b58666dac25cd29027535476e4 blob + b584cd9f422621febd835f22f0a57b9e44e58fcf --- ui.lisp +++ ui.lisp @@ -1,19 +1,48 @@ (in-package #:phos/ui) +(defparameter *title-1-font* "serif 22" + "Font for the level 1 title.") + +(defparameter *title-2-font* "serif 19" + "Font for the level 2 title.") + +(defparameter *title-3-font* "serif 16" + "Font for the level 3 title.") + +(defparameter *verbatim-font* "monospace 12" + "Font for the verbatim element.") + +(defparameter *item-font* "serif 12" + "Font for the item.") + +(defparameter *link-font* "serif 12" + "Font for the links.") + +(defparameter *paragraph-font* "serif 12" + "Font for the normal text") + +(defparameter *window-content* nil) + +(defparameter *current-url* nil) + (defgeneric render (obj frame) - (:documentation "Render OBJ in the ltk FRAME")) + (:documentation "Render OBJ in the nodgui FRAME")) (defmethod render ((l list) f) (dolist (el l) (render el f))) (defmethod render ((title gemtext:title) f) - (with-slots ((text phos/gemtext:text) + (with-slots ((text phos/gemtext:text) (level phos/gemtext:level)) title (let ((w (make-instance 'label :master f - :text (format nil "#(level ~a) ~a" level text)))) + :font (case level + (1 *title-1-font*) + (2 *title-2-font*) + (3 *title-3-font*)) + :text (format nil "~v{~A~:*~} ~a" level '("#") text)))) (pack w :side :top)))) (defmethod render ((link gemtext:link) f) @@ -22,13 +51,18 @@ link (let ((w (make-instance 'button :master f - :text (format nil "~a" (or text url))))) + ;; :font *link-font* + :text (format nil "~a" (or text url)) + :command (lambda () + (format t "before rendering ~a~%" url) + (do-render url))))) (pack w :side :top)))) (defmethod render ((item gemtext:item) f) (with-slots ((text phos/gemtext:text)) item (let ((w (make-instance 'label :master f + :font *item-font* :text (format nil "* ~a" text)))) (pack w :side :top)))) @@ -36,6 +70,7 @@ (with-slots ((text phos/gemtext:text)) par (let ((w (make-instance 'label :master f + :font *paragraph-font* :text text))) (pack w :side :top)))) @@ -45,18 +80,39 @@ v (let ((w (make-instance 'label :master f - :text (format nil "```~a~%~a~&```" - (or alt "") - text)))) - (pack w :side :top)))) + :font *verbatim-font* + :text text))) + (pack w :side :top) + (when alt + (pack (make-instance 'label + :master f + :text alt) + :side :top))))) -(defun render-page (page) - (with-ltk () - (let ((frame (make-instance 'frame))) - (pack frame) - (render page frame)))) +(defun clear-window () + (pack-forget-all *window-content*)) -(defvar doc (with-open-file (stream #P"~/quicklisp/local-projects/phos/test.gmi") - (gemtext:parse stream))) +(defun do-render (url) + (let* ((uri (quri:uri url)) + (*current-url* uri) + (base (quri:copy-uri uri))) + (setf (quri:uri-path base) nil) + (clear-window) + (render (gemtext:parse-string "# loading...") + *window-content*) + (clear-window) + (multiple-value-bind (status body) (gemini:request url) + (declare (ignore status)) + (render (gemtext:parse-string body base) + *window-content*)))) -;; (render-page doc) +(defun main (url) + (with-nodgui (:title "phos") + (let ((sf (make-instance 'scrolled-frame))) + (setf *window-content* (interior sf)) + (pack sf :side :top :fill :both :expand t) + (do-render url)))) + +;; (nodgui.demo:demo) + +;; (main "gemini://gemini.omarpolo.com/")