diff --git a/.gitignore b/.gitignore index b74d22ca79..b79ff1f693 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,10 @@ node_modules/ /resources/static/js/main.js /resources/static/js/cljs-runtime /resources/static/js/manifest.edn +/resources/static/js/publishing/*.map +/resources/static/js/publishing/main.js +/resources/static/js/publishing/cljs-runtime +/resources/static/js/publishing/manifest.edn /resources/static/style.css tauri-release/ @@ -37,4 +41,4 @@ strings.csv resources/static/style.css .calva -web/.calva \ No newline at end of file +web/.calva diff --git a/web/package.json b/web/package.json index f3796d7297..be8bb44d6f 100644 --- a/web/package.json +++ b/web/package.json @@ -13,8 +13,8 @@ "tailwindcss": "^1.3.4" }, "scripts": { - "watch": "clojure -A:cljs watch app", - "release": "clojure -A:cljs release app", + "watch": "clojure -A:cljs watch app publishing", + "release": "clojure -A:cljs release app publishing", "debug": "clojure -A:cljs release app --debug", "report": "clojure -A:cljs run shadow.cljs.build-report app report.html", "clean": "/usr/bin/rm -rf target; /usr/bin/rm -rf ../resources/static/js/compiled; /usr/bin/rm -rf ../resources/static/js/cljs-runtime;" diff --git a/web/shadow-cljs.edn b/web/shadow-cljs.edn index e029db66c7..c7391e191f 100644 --- a/web/shadow-cljs.edn +++ b/web/shadow-cljs.edn @@ -21,6 +21,23 @@ ;; :public-dir "resources/static/css"})] :devtools ;; before live-reloading any code call this function + {:before-load frontend.core/stop + ;; after live-reloading finishes call this function + :after-load frontend.core/start + :preloads [devtools.preload]}} + :publishing + {:target :browser + :modules {:main {:init-fn frontend.publishing/init}} + + :output-dir "../resources/static/js/publishing" + :asset-path "/static/js" + + :compiler-options {:infer-externs :auto + :output-feature-set :es6 + :externs ["datascript/externs.js" + "externs.js"]} + :devtools + ;; before live-reloading any code call this function {:before-load frontend.core/stop ;; after live-reloading finishes call this function :after-load frontend.core/start diff --git a/web/src/main/frontend/components/sidebar.cljs b/web/src/main/frontend/components/sidebar.cljs index 7d172f5a96..d22f1c5702 100644 --- a/web/src/main/frontend/components/sidebar.cljs +++ b/web/src/main/frontend/components/sidebar.cljs @@ -22,6 +22,7 @@ [frontend.handler.editor :as editor-handler] [frontend.handler.repo :as repo-handler] [frontend.handler.route :as route-handler] + [frontend.handler.export :as export] [frontend.config :as config] [frontend.keyboards :as keyboards] [dommy.core :as d] @@ -93,6 +94,8 @@ token (state/sub :encrypt/token) ;; TODO: remove this daily-migrating? (state/sub [:daily/migrating?])] + (prn {:repo (state/get-current-repo) + :conns (db/get-conn)}) (rum/with-context [[t] i18n/*tongue-context*] [:div.max-w-7xl.mx-auto (cond @@ -331,7 +334,12 @@ [:a svg/user]])]) (let [logged? (:name me)] (->> - [(when current-repo + [(when (and logged? current-repo) + {:title (t :publishing) + :options {:on-click (fn [] + (export/export-repo-as-zip! current-repo))} + :icon nil}) + (when current-repo {:title (t :graph) :options {:href (rfe/href :graph)} :icon svg/graph-sm}) @@ -374,6 +382,8 @@ (remove nil?))) {}) + [:a#download-as-zip.hidden] + [:a.hover:text-gray-900.text-gray-500.ml-3.hidden.md:block {:on-click (fn [] (state/toggle-sidebar-open?!))} diff --git a/web/src/main/frontend/dicts.cljs b/web/src/main/frontend/dicts.cljs index 3ef0aee703..027ce663d4 100644 --- a/web/src/main/frontend/dicts.cljs +++ b/web/src/main/frontend/dicts.cljs @@ -292,6 +292,7 @@ title: How to take dummy notes? :search "Search or Create Page" :new-page "New page" :graph "Graph" + :publishing "publishing" :all-repos "All repos" :all-pages "All pages" :all-files "All files" @@ -531,6 +532,7 @@ title: How to take dummy notes? :search "搜索或者创建新页面" :new-page "新页面" :graph "图谱" + :publishing "发布/下载 HTML 文件" :all-repos "所有库" :all-pages "所有页面" :all-files "所有文件" diff --git a/web/src/main/frontend/handler/export.cljs b/web/src/main/frontend/handler/export.cljs index b933d464bc..5b7667d499 100644 --- a/web/src/main/frontend/handler/export.cljs +++ b/web/src/main/frontend/handler/export.cljs @@ -4,7 +4,8 @@ [frontend.util :as util] [cljs-bean.core :as bean] [clojure.string :as string] - [goog.dom :as gdom])) + [goog.dom :as gdom] + [frontend.publishing.html :as html])) (defn copy-block! [block-id] @@ -50,3 +51,14 @@ (.setAttribute anchor "href" url) (.setAttribute anchor "download" file-path) (.click anchor)))))) + +(defn export-repo-as-zip! + [repo] + (when-let [db (db/get-conn repo)] + (let [db-str (db/db->string db) + html-str (str "data:text/html;charset=UTF-8," + (js/encodeURIComponent (html/publishing-html db-str)))] + (when-let [anchor (gdom/getElement "download-as-zip")] + (.setAttribute anchor "href" html-str) + (.setAttribute anchor "download" (str (last (string/split repo #"/")) ".html")) + (.click anchor))))) diff --git a/web/src/main/frontend/handler/image.cljs b/web/src/main/frontend/handler/image.cljs index 9d3e33498f..f927f4b77d 100644 --- a/web/src/main/frontend/handler/image.cljs +++ b/web/src/main/frontend/handler/image.cljs @@ -11,38 +11,41 @@ (defn render-local-images! [] - (let [images (array-seq (gdom/getElementsByTagName "img")) - get-src (fn [image] (.getAttribute image "src")) - local-images (filter - (fn [image] - (let [src (get-src image)] - (and src - (not (or (util/starts-with? src "http://") - (util/starts-with? src "https://")))))) - images)] - (doseq [img local-images] - (gobj/set img - "onerror" - (fn [] - (gobj/set (gobj/get img "style") - "display" "none"))) - (let [path (get-src img) - path (string/replace-first path "file:" "") - path (if (= (first path) \.) - (subs path 1) - path)] - (util/p-handle - (fs/read-file-2 (util/get-repo-dir (state/get-current-repo)) - path) - (fn [blob] - (let [blob (js/Blob. (array blob) (clj->js {:type "image"})) - img-url (image/create-object-url blob)] - (gobj/set img "src" img-url) - (gobj/set (gobj/get img "style") - "display" "initial"))) - (fn [error] - (println "Can't read local image file: ") - (js/console.dir error))))))) + (try + (let [images (array-seq (gdom/getElementsByTagName "img")) + get-src (fn [image] (.getAttribute image "src")) + local-images (filter + (fn [image] + (let [src (get-src image)] + (and src + (not (or (util/starts-with? src "http://") + (util/starts-with? src "https://")))))) + images)] + (doseq [img local-images] + (gobj/set img + "onerror" + (fn [] + (gobj/set (gobj/get img "style") + "display" "none"))) + (let [path (get-src img) + path (string/replace-first path "file:" "") + path (if (= (first path) \.) + (subs path 1) + path)] + (util/p-handle + (fs/read-file-2 (util/get-repo-dir (state/get-current-repo)) + path) + (fn [blob] + (let [blob (js/Blob. (array blob) (clj->js {:type "image"})) + img-url (image/create-object-url blob)] + (gobj/set img "src" img-url) + (gobj/set (gobj/get img "style") + "display" "initial"))) + (fn [error] + (println "Can't read local image file: ") + (js/console.dir error)))))) + (catch js/Error e + nil))) (defn request-presigned-url [file filename mime-type uploading? url-handler on-processing] diff --git a/web/src/main/frontend/publishing.cljs b/web/src/main/frontend/publishing.cljs index c7e6346d54..f71353d300 100644 --- a/web/src/main/frontend/publishing.cljs +++ b/web/src/main/frontend/publishing.cljs @@ -15,13 +15,25 @@ ;; Both files and git libraries can be removed. ;; Maybe we can remove some handlers and components too. +;; There should be two publishing modes: +;; 1. Graph version, similar to logseq.com +;; 2. Traditional blog version, much faster to load +;; We might host the pages or blocks directly on logseq.com in the future. + +;; How to publish? +;; 1. When you click a publish button, it'll downloads a zip which includes the +;; html, css, javascript and other files (image, mp3, etc.), the serialized +;; data should include all the public pages and blocks. +;; 2. Built-in sync with Github Pages, you should specify a Github repo for publishing. + (defn restore-from-transit-str! [] (state/set-current-repo! "local") (when-let [data js/window.logseq_db] - (let [db-conn (d/create-conn db-schema/schema) - _ (swap! db/conns assoc "local" db-conn) - db (string->db logseq_db)] + (let [data (js/JSON.stringify data) + db-conn (d/create-conn db-schema/schema) + _ (swap! db/conns assoc "logseq-db/local" db-conn) + db (db/string->db data)] (reset! db-conn db)))) (defn set-router! @@ -42,7 +54,7 @@ ;; this is called in the index.html and must be exported ;; so it is available even in :advanced release builds - (db/restore-from-transit-str!) + (restore-from-transit-str!) (start)) (defn stop [] diff --git a/web/src/main/frontend/publishing/html.cljs b/web/src/main/frontend/publishing/html.cljs new file mode 100644 index 0000000000..b200f05f33 --- /dev/null +++ b/web/src/main/frontend/publishing/html.cljs @@ -0,0 +1,70 @@ +(ns frontend.publishing.html + (:require-macros [hiccups.core :as hiccups :refer [html]]) + (:require [frontend.config :as config])) + +(defn publishing-html + [transit-db] + (let [icon (config/asset-uri "/static/img/logo.png") + project "Logseq" + project-twitter "logseq" + title "A local-first knowledge base." + description "A local-first knowledge base which can be synced using Git."] + (html + [:head + [:meta {:charset "utf-8"}] + [:meta + {:content + "minimum-scale=1, initial-scale=1, width=device-width, shrink-to-fit=no", + :name "viewport"}] + [:link {:type "text/css", :href (config/asset-uri "/static/style.css"), :rel "stylesheet"}] + [:link + {:href icon + :type "image/png", + :rel "shortcut icon"}] + [:link + {:href icon + :sizes "192x192", + :rel "shortcut icon"}] + [:link + {:href icon + :rel "apple-touch-icon"}] + + [:meta {:name "apple-mobile-web-app-title" :content project}] + [:meta {:name "apple-mobile-web-app-capable" :content "yes"}] + [:meta {:name "apple-touch-fullscreen" :content "yes"}] + [:meta {:name "apple-mobile-web-app-status-bar-style" :content "black-translucent"}] + [:meta {:name "mobile-web-app-capable" :content "yes"}] + + [:meta {:content "summary", :name "twitter:card"}] + [:meta + {:content description + :name "twitter:description"}] + (if project-twitter + [:meta {:content (str "@" project-twitter), :name "twitter:site"}]) + [:meta {:content title, :name "twitter:title"}] + [:meta + {:content icon, + :name "twitter:image:src"}] + [:meta + {:content description, :name "twitter:image:alt"}] + [:meta {:content title, :property "og:title"}] + [:meta {:content "site", :property "og:type"}] + ;; FIXME: get current url + [:meta {:content "https://logseq.com", :property "og:url"}] + [:meta + {:content icon + :property "og:image"}] + [:meta + {:content description + :property "og:description"}] + [:title title] + [:meta {:content project, :property "og:site_name"}] + [:meta + {:description description}]] + [:body + [:div#root] + [:script (str "window.logseq_db=" transit-db)] + [:script {:src (config/asset-uri "/static/js/mldoc.min.js")}] + [:script {:src (config/asset-uri "/static/js/publishing.js")}] + ;; TODO: should make this configurable + [:script {:src (config/asset-uri "/static/js/highlight.min.js")}]]))) diff --git a/web/src/main/frontend/tools/html_export.cljs b/web/src/main/frontend/tools/html_export.cljs index 54c15d6511..0e58bbd651 100644 --- a/web/src/main/frontend/tools/html_export.cljs +++ b/web/src/main/frontend/tools/html_export.cljs @@ -31,8 +31,8 @@ (let [{:keys [slide] :as directives} (db/get-page-directives page-name) slide? slide blocks (if (:block/pre-block? (first blocks)) - (rest blocks) - blocks)] + (rest blocks) + blocks)] (if (seq blocks) (let [config {:html-export? true :slide? slide?} hiccup (if slide?