├── .gitignore ├── LICENCE ├── README.org ├── main.py ├── org-roam-server-light.el ├── public ├── bootstrap-toggle.min.css ├── bootstrap-toggle.min.js ├── bootstrap.min.css ├── bootstrap.min.js ├── favicon.ico ├── jquery-3.5.0.min.js ├── org.css ├── select2.min.css ├── select2.min.js └── vis-network.min.js ├── response ├── __init__.py ├── badRequestHandler.py ├── currentBufferHandler.py ├── defaultFiltersHandler.py ├── filePreviewHandler.py ├── networkVisHandler.py ├── requestHandler.py ├── roamBufferHandler.py ├── roamDataHandler.py ├── serverCSSHandler.py ├── staticHandler.py └── templateHandler.py ├── routes └── main.py ├── server.py ├── templates └── index.html └── variables.py /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .idea 3 | *.log 4 | tmp/ 5 | 6 | *.py[cod] 7 | *.egg 8 | build 9 | htmlcov 10 | .mypy_cache 11 | flycheck_* 12 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Göktuğ Karakaşlı 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.org: -------------------------------------------------------------------------------- 1 | #+TITLE: Readme 2 | 3 | * org-roam-server-light 4 | ** Org-roam v2 5 | For the =org-roam v2= use [[https://github.com/org-roam/org-roam-ui][org-roam-ui]] instead. 6 | =org-roam-server-light= only works with =org-roam v1= and will not be actively maintained. 7 | ---------------------------------------------------------------------------------------------------- 8 | 9 | This project is NOT part of official [[https://www.orgroam.com/][Org-roam]] project. 10 | This project started as personal experiment, proof-of-concept and doesn't aspire to become something more then that. 11 | It however uses "org-roam(-server)" name but does it out of good intentions: 12 | 1. to make this little experiment easier to find 13 | 2. to make the purpose of this project clear 14 | 15 | =org-roam-server-light= attempts to move [[https://github.com/org-roam/org-roam-server][org-roam-server]] functionality from emacs into external python server process due to subjective poor elisp / emacs performance when computing graph data for larger amount of org-roam files and serving them to web browser. 16 | 17 | ** Credits 18 | Client side code, assets and some elisp copied as they are from [[https://github.com/org-roam/org-roam-server][org-roam-server]], 19 | python code copied and expanded from [[https://github.com/aklatzke/python-webserver-part-2][aklatzke/python-webserver-part-2]] 20 | 21 | ** Prerequisites 22 | File-preview functionality requires [[https://pandoc.org/][pandoc]] being available in your PATH 23 | 24 | ** Installation 25 | #+BEGIN_EXAMPLE 26 | git clone https://github.com/AloisJanicek/org-roam-server-light.git 27 | #+END_EXAMPLE 28 | 29 | Depending on your emacs use one of the following snippets to install and configure =org-roam-server-light=. 30 | 31 | ** Elisp code 32 | if you are using doom emacs 33 | 34 | #+BEGIN_SRC emacs-lisp 35 | ;; .doom.d/packages.el 36 | 37 | (package! org-roam-server-light 38 | :recipe (:host github :repo "AloisJanicek/org-roam-server-light" 39 | :files ("*"))) 40 | #+END_SRC 41 | 42 | #+BEGIN_SRC emacs-lisp 43 | ;; .doom.d/config.el 44 | 45 | (use-package! org-roam-server-light 46 | :after org-roam 47 | :commands org-roam-server-light-mode 48 | :config 49 | ;; OPTIONAL example settings, `org-roam-server-light' will work without them 50 | (setq 51 | ;; enable arrows 52 | org-roam-server-light-network-vis-options "{ \"edges\": { \"arrows\": { \"to\": { \"enabled\": true,\"scaleFactor\": 1.15 } } } }" 53 | 54 | ;; change background color of web application 55 | org-roam-server-light-style "body.darkmode { background-color: #121212!important; }" 56 | 57 | ;; set default set of excluded or included tags 58 | ;; customize only the value of id, in this case "test" and "journal" 59 | org-roam-server-light-default-include-filters "[{ \"id\": \"test\", \"parent\" : \"tags\" }]" 60 | org-roam-server-light-default-exclude-filters "[{ \"id\": \"journal\", \"parent\" : \"tags\" }]" 61 | ) 62 | ) 63 | #+END_SRC 64 | 65 | if you are using other emacs with use-package 66 | 67 | #+BEGIN_SRC emacs-lisp 68 | ;; .emacs.d/init.el 69 | 70 | (use-package org-roam-server-light 71 | ;; MANDATORY: path to your local copy of this repository 72 | :load-path "/home/john/where-i-cloned-this" 73 | :after org-roam 74 | :commands org-roam-server-light-mode 75 | :config 76 | ;; OPTIONAL example settings, `org-roam-server-light' will work without them 77 | (setq 78 | ;; enable arrows 79 | org-roam-server-light-network-vis-options "{ \"edges\": { \"arrows\": { \"to\": { \"enabled\": true,\"scaleFactor\": 1.15 } } } }" 80 | 81 | ;; change background color of web application 82 | org-roam-server-light-style "body.darkmode { background-color: #121212!important; }" 83 | 84 | ;; set default set of excluded or included tags 85 | ;; customize only the value of id, in this case "test" and "journal" 86 | org-roam-server-light-default-include-filters "[{ \"id\": \"test\", \"parent\" : \"tags\" }]" 87 | org-roam-server-light-default-exclude-filters "[{ \"id\": \"journal\", \"parent\" : \"tags\" }]" 88 | ) 89 | ) 90 | #+END_SRC 91 | 92 | or in other emacs 93 | 94 | #+BEGIN_SRC emacs-lisp 95 | ;; .emacs.d/init.el 96 | 97 | (eval-after-load 'org-roam 98 | ;; OPTIONAL: customize some settings or leave them to their defaults 99 | ;; enable arrows 100 | (setq org-roam-server-light-network-vis-options "{ \"edges\": { \"arrows\": { \"to\": { \"enabled\": true,\"scaleFactor\": 1.5 } } } }" 101 | 102 | ;; OPTIONAL example: change background color of web application 103 | org-roam-server-light-style "body.darkmode { background-color: #121212!important; }" 104 | 105 | ;; OPTIONAL example: set default set of excluded or included tags 106 | ;; customize only the value of id, in this case "test" and "journal" 107 | org-roam-server-light-default-include-filters "[{ \"id\": \"test\", \"parent\" : \"tags\" }]" 108 | org-roam-server-light-default-exclude-filters "[{ \"id\": \"journal\", \"parent\" : \"tags\" }]" 109 | ) 110 | 111 | ;; finally load the main `org-roam-server-light' elisp file 112 | (load (expand-file-name "org-roam-server-light.el" "/home/john/where-i-cloned-this")) 113 | ) 114 | #+END_SRC 115 | 116 | ** Usage 117 | Running =org-roam-server-light-mode= will start org-roam-server-light server on http://localhost:8080 118 | 119 | ** TODO Roadmap and functionality overview [8/19] 120 | - [X] start/stop python web-server when enable/disable major mode in emacs 121 | - [X] build and serve JSON data based on =org-roam.db= for vis.Network 122 | - [X] org-roam-buffer sidepane (basic) 123 | - [ ] improve org-roam-buffer sidepane 124 | - add total count of backlinks 125 | - [X] keep track of current buffer 126 | - [X] file previews (basic) 127 | - [ ] improve file previews 128 | - add CREATED timestamp (?) 129 | - [ ] serve (certain) files linked from exported files 130 | - [ ] serve inline images 131 | - [ ] mark last captured file as current buffer 132 | - [ ] handle citation backlinks in org-roam-buffer sidepane 133 | - [X] serve custom JSON config for vis.Network 134 | =org-roam-server-light-network-vis-options= 135 | - [ ] ability to customize server url/port via elisp variable 136 | - [X] ability to customize CSS for web app via elisp variable 137 | - [ ] ability to customize CSS for exported files via elisp-variable 138 | - [X] filter items by org-roam tag in web app and ability to set default whitelist/blacklist 139 | =org-roam-server-light-default-include-filters= 140 | =org-roam-server-light-default-exclude-filters= 141 | - [ ] review path handling in python code 142 | - [ ] review exported files encoding issues on Windows (cp-1252 vs utf-8 weirdness) 143 | - [ ] review mechanism of sharing data between emacs and python web-server 144 | currently emacs writes data as text to plain-text files for python web-server to read it 145 | 146 | ** Licence 147 | org-roam-server-light is licensed under the MIT License. 148 | 149 | Licence of python part of the code is unclear because there is no licence specified in [[https://github.com/aklatzke/python-webserver-part-2][aklatzke/python-webserver-part-2]], which is where the python code originates. 150 | 151 | For Javascript and CSS libraries please refer to; 152 | 153 | - https://github.com/jquery/jquery/blob/master/LICENSE.txt 154 | - https://github.com/visjs/vis-network 155 | - https://github.com/twbs/bootstrap/blob/master/LICENSE 156 | - https://github.com/gongzhitaao/orgcss 157 | - https://github.com/select2/select2 158 | - https://github.com/minhur/bootstrap-toggle/blob/master/LICENSE 159 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import time 3 | import sys 4 | from http.server import HTTPServer 5 | from server import Server 6 | 7 | HOST_NAME = "localhost" 8 | PORT_NUMBER = 8080 9 | 10 | 11 | class QuietHandler(Server): 12 | def log_message(self, format, *args): 13 | pass 14 | 15 | 16 | if __name__ == "__main__": 17 | args = sys.argv[1:] 18 | if args and args[0] == '-d': 19 | httpd = HTTPServer((HOST_NAME, PORT_NUMBER), Server) 20 | else: 21 | httpd = HTTPServer((HOST_NAME, PORT_NUMBER), QuietHandler) 22 | print( 23 | "If you want to see full request log, pass '-d' debug switch to this program.") 24 | print(time.asctime(), "Server Starts - %s:%s" % (HOST_NAME, PORT_NUMBER)) 25 | try: 26 | httpd.serve_forever() 27 | except KeyboardInterrupt: 28 | pass 29 | httpd.server_close() 30 | print(time.asctime(), "Server Stops - %s:%s" % (HOST_NAME, PORT_NUMBER)) 31 | -------------------------------------------------------------------------------- /org-roam-server-light.el: -------------------------------------------------------------------------------- 1 | ;;; org-roam-server-light.el --- External Org Roam server -*- lexical-binding: t; -*- 2 | 3 | ;; Author: Göktuğ Karakaşlı 4 | ;; Alois Janíček 5 | ;; URL: https://github.com/AloisJanicek/org-roam-server-light 6 | ;; Version: 0.1 7 | ;; Package-Requires: ((org-roam "1.2.1") (org "9.3") (emacs "26.1") (dash "2.17.0") (f "0.20.0")) 8 | 9 | ;; MIT License 10 | 11 | ;; Permission is hereby granted, free of charge, to any person obtaining a copy 12 | ;; of this software and associated documentation files (the "Software"), to deal 13 | ;; in the Software without restriction, including without limitation the rights 14 | ;; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | ;; copies of the Software, and to permit persons to whom the Software is 16 | ;; furnished to do so, subject to the following conditions: 17 | 18 | ;; The above copyright notice and this permission notice shall be included in all 19 | ;; copies or substantial portions of the Software. 20 | 21 | ;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | ;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | ;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | ;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | ;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | ;; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | ;; SOFTWARE. 28 | 29 | ;;; Commentary: 30 | ;; A web application and web server to visualize the org-roam database. 31 | ;; Use M-x org-roam-server-light-mode RET to enable the global mode. 32 | ;; It will start a web server on http://127.0.0.1:8080 33 | ;; 34 | ;; This package differs from https://github.com/org-roam/org-roam-server by implementing 35 | ;; entire web server functionality in external python process as python seems to perform better 36 | ;; when building and serving JSON data for large and complex networks 37 | ;; like this: https://i.imgur.com/1D1VVoj.png 38 | 39 | 40 | (require 'f) 41 | (require 'org-roam) 42 | 43 | ;;; Code: 44 | (defgroup org-roam-server-light nil 45 | "org-roam-server-light customizable variables." 46 | :group 'org-roam) 47 | 48 | (defcustom org-roam-server-light-dir nil 49 | "Directory contenting org-roam-server-light repository. 50 | 51 | When left set to nil, directory will be guessed based on 52 | the file where `org-roam-server-light' is defined. 53 | " 54 | :group 'org-roam-server-light 55 | :type 'string) 56 | 57 | (defcustom org-roam-server-light-network-vis-options "" 58 | "Options to be passed directly to vis.Network, in JSON format. 59 | e.g. (json-encode (list (cons 'physics (list (cons 'enabled json-false))))) 60 | or { \"physics\": { \"enabled\": false } }" 61 | :group 'org-roam-server-light 62 | :type 'string) 63 | 64 | (defcustom org-roam-server-light-default-include-filters "null" 65 | "Options to set default include filters, in JSON format. 66 | e.g. (json-encode (list (list (cons 'id \"test\") (cons 'parent \"tags\")))) 67 | or [{ \"id\": \"test\", \"parent\" : \"tags\" }]" 68 | :group 'org-roam-server-light 69 | :type 'string) 70 | 71 | (defcustom org-roam-server-light-default-exclude-filters "null" 72 | "Options to set default exclude filters, in JSON format. 73 | e.g. (json-encode (list (list (cons 'id \"test\") (cons 'parent \"tags\")))) 74 | or [{ \"id\": \"test\", \"parent\" : \"tags\" }]" 75 | :group 'org-roam-server-light 76 | :type 'string) 77 | 78 | (defcustom org-roam-server-light-style "" 79 | "The CSS that can be used to customize the application." 80 | :group 'org-roam-server-light 81 | :type 'string) 82 | 83 | (defcustom org-roam-server-light-tmp-dir 84 | (let ((dir-name "org-roam-server-light/")) 85 | (if (or IS-WINDOWS IS-MAC) 86 | (concat (replace-regexp-in-string "\\\\" "/" 87 | (or (getenv "TMPDIR") 88 | (getenv "TMP"))) 89 | "/" dir-name) 90 | (concat "/tmp/" dir-name))) 91 | "Directory contenting org-roam-server-light repository." 92 | :group 'org-roam-server-light 93 | :type 'string) 94 | 95 | (defcustom org-roam-server-light-debug nil 96 | "Increase verbosity in output log buffer. 97 | 98 | When non-nil, allow python server to be more verbose 99 | in *org-roam-server-light-output-buffer* buffer." 100 | :group 'org-roam-server-light 101 | :type 'boolean) 102 | 103 | (defvar org-roam-server-light-last-roam-buffer "" 104 | "Variable storing name of the last org-roam buffer.") 105 | 106 | (defun org-roam-server-light-update-last-buffer () 107 | "Update `org-roam-server-light-last-roam-buffer'." 108 | (let ((buf (or (buffer-base-buffer (current-buffer)) (current-buffer)))) 109 | (when (org-roam--org-roam-file-p 110 | (buffer-file-name buf)) 111 | (setq org-roam-server-light-last-roam-buffer 112 | (car (last (split-string (org-roam--path-to-slug (buffer-name buf)) "/")))) 113 | (f-write-text 114 | org-roam-server-light-last-roam-buffer 115 | 'utf-8 116 | (concat org-roam-server-light-tmp-dir "org-roam-server-light-last-roam-buffer"))))) 117 | 118 | (defun org-roam-server-light-find-file-hook-function () 119 | "If the current visited file is an `org-roam` file, update the current buffer." 120 | (when (org-roam--org-roam-file-p) 121 | (add-hook 'post-command-hook #'org-roam-server-light-update-last-buffer nil t) 122 | (org-roam-server-light-update-last-buffer))) 123 | 124 | ;;;###autoload 125 | (define-minor-mode org-roam-server-light-mode 126 | "Start the http server and serve org-roam files." 127 | :lighter "" 128 | :global t 129 | :init-value nil 130 | (let* ((title "org-roam-server-light")) 131 | (if (not (ignore-errors org-roam-server-light-mode)) 132 | (progn 133 | (when (get-process title) 134 | (delete-process title)) 135 | (remove-hook 'find-file-hook #'org-roam-server-light-find-file-hook-function nil) 136 | (dolist (buf (org-roam--get-roam-buffers)) 137 | (with-current-buffer buf 138 | (remove-hook 'post-command-hook #'org-roam-server-light-update-last-buffer t)))) 139 | (progn 140 | (add-hook 'find-file-hook #'org-roam-server-light-find-file-hook-function nil nil) 141 | (unless (file-exists-p org-roam-server-light-tmp-dir) 142 | (make-directory org-roam-server-light-tmp-dir)) 143 | (f-write-text org-roam-server-light-network-vis-options 144 | 'utf-8 145 | (expand-file-name "org-roam-server-light-network-vis-options" org-roam-server-light-tmp-dir)) 146 | (f-write-text org-roam-server-light-default-include-filters 147 | 'utf-8 148 | (expand-file-name "org-roam-server-light-default-include-filters" org-roam-server-light-tmp-dir)) 149 | (f-write-text org-roam-server-light-default-exclude-filters 150 | 'utf-8 151 | (expand-file-name "org-roam-server-light-default-exclude-filters" org-roam-server-light-tmp-dir)) 152 | (f-write-text org-roam-server-light-style 153 | 'utf-8 154 | (expand-file-name "org-roam-server-light-style" org-roam-server-light-tmp-dir)) 155 | (f-write-text org-roam-db-location 156 | 'utf-8 157 | (expand-file-name "org-roam-db-location" org-roam-server-light-tmp-dir)) 158 | (f-write-text org-roam-directory 159 | 'utf-8 160 | (expand-file-name "org-roam-directory" org-roam-server-light-tmp-dir)) 161 | 162 | (let ((default-directory (or org-roam-server-light-dir 163 | (file-name-directory (symbol-file #'org-roam-server-light-mode)))) 164 | (exec (concat "python main.py" (when org-roam-server-light-debug " -d")))) 165 | (if (and (file-writable-p default-directory) 166 | (file-readable-p (expand-file-name "main.py" default-directory))) 167 | (start-process-shell-command "org-roam-server-light" "*org-roam-server-light-output-buffer*" exec) 168 | (user-error "Looks like there isn't \"main.py\" to start server in the %s directory" default-directory))))))) 169 | 170 | (provide 'org-roam-server-light) 171 | ;;; org-roam-server-light.el ends here 172 | -------------------------------------------------------------------------------- /public/bootstrap-toggle.min.css: -------------------------------------------------------------------------------- 1 | /*! ======================================================================== 2 | * Bootstrap Toggle: bootstrap-toggle.css v2.2.0 3 | * http://www.bootstraptoggle.com 4 | * ======================================================================== 5 | * Copyright 2014 Min Hur, The New York Times Company 6 | * Licensed under MIT 7 | * ======================================================================== */ 8 | .checkbox label .toggle,.checkbox-inline .toggle{margin-left:-20px;margin-right:5px} 9 | .toggle{position:relative;overflow:hidden} 10 | .toggle input[type=checkbox]{display:none} 11 | .toggle-group{position:absolute;width:200%;top:0;bottom:0;left:0;transition:left .35s;-webkit-transition:left .35s;-moz-user-select:none;-webkit-user-select:none} 12 | .toggle.off .toggle-group{left:-100%} 13 | .toggle-on{position:absolute;top:0;bottom:0;left:0;right:50%;margin:0;border:0;border-radius:0} 14 | .toggle-off{position:absolute;top:0;bottom:0;left:50%;right:0;margin:0;border:0;border-radius:0} 15 | .toggle-handle{position:relative;margin:0 auto;padding-top:0;padding-bottom:0;height:100%;width:0;border-width:0 1px} 16 | .toggle.btn{min-width:59px;min-height:34px} 17 | .toggle-on.btn{padding-right:24px} 18 | .toggle-off.btn{padding-left:24px} 19 | .toggle.btn-lg{min-width:79px;min-height:45px} 20 | .toggle-on.btn-lg{padding-right:31px} 21 | .toggle-off.btn-lg{padding-left:31px} 22 | .toggle-handle.btn-lg{width:40px} 23 | .toggle.btn-sm{min-width:50px;min-height:30px} 24 | .toggle-on.btn-sm{padding-right:20px} 25 | .toggle-off.btn-sm{padding-left:20px} 26 | .toggle.btn-xs{min-width:35px;min-height:22px} 27 | .toggle-on.btn-xs{padding-right:12px} 28 | .toggle-off.btn-xs{padding-left:12px} -------------------------------------------------------------------------------- /public/bootstrap-toggle.min.js: -------------------------------------------------------------------------------- 1 | /*! ======================================================================== 2 | * Bootstrap Toggle: bootstrap-toggle.js v2.2.0 3 | * http://www.bootstraptoggle.com 4 | * ======================================================================== 5 | * Copyright 2014 Min Hur, The New York Times Company 6 | * Licensed under MIT 7 | * ======================================================================== */ 8 | +function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.toggle"),f="object"==typeof b&&b;e||d.data("bs.toggle",e=new c(this,f)),"string"==typeof b&&e[b]&&e[b]()})}var c=function(b,c){this.$element=a(b),this.options=a.extend({},this.defaults(),c),this.render()};c.VERSION="2.2.0",c.DEFAULTS={on:"On",off:"Off",onstyle:"primary",offstyle:"default",size:"normal",style:"",width:null,height:null},c.prototype.defaults=function(){return{on:this.$element.attr("data-on")||c.DEFAULTS.on,off:this.$element.attr("data-off")||c.DEFAULTS.off,onstyle:this.$element.attr("data-onstyle")||c.DEFAULTS.onstyle,offstyle:this.$element.attr("data-offstyle")||c.DEFAULTS.offstyle,size:this.$element.attr("data-size")||c.DEFAULTS.size,style:this.$element.attr("data-style")||c.DEFAULTS.style,width:this.$element.attr("data-width")||c.DEFAULTS.width,height:this.$element.attr("data-height")||c.DEFAULTS.height}},c.prototype.render=function(){this._onstyle="btn-"+this.options.onstyle,this._offstyle="btn-"+this.options.offstyle;var b="large"===this.options.size?"btn-lg":"small"===this.options.size?"btn-sm":"mini"===this.options.size?"btn-xs":"",c=a('