Просмотр исходного кода

Merge branch 'master' into feat/integration-plugins-core

charlie 4 лет назад
Родитель
Сommit
28159ce6a2

+ 5 - 5
.github/workflows/build-desktop-release.yml

@@ -51,7 +51,7 @@ jobs:
           sudo ./linux-install-1.10.1.763.sh
 
       - name: Compile CLJS
-        run: yarn install --frozen-lockfile && gulp build  && yarn cljs:release
+        run: yarn install  && gulp build  && yarn cljs:release
 
       - name: Update APP Version
         run: |
@@ -107,7 +107,7 @@ jobs:
           key: ${{ runner.os }}-node-modules
 
       - name: Build/Release Electron App
-        run: yarn install --frozen-lockfile && yarn electron:make
+        run: yarn install  && yarn electron:make
         working-directory: ./static
 
       - name: Change Artifact Name For ZIP File
@@ -154,7 +154,7 @@ jobs:
           key: ${{ runner.os }}-node-modules
 
       - name: Build/Release Electron app
-        run: yarn install --frozen-lockfile && yarn electron:make
+        run: yarn install  && yarn electron:make
         working-directory: ./static
 
       - name: Change Artifact Name
@@ -206,7 +206,7 @@ jobs:
           key: ${{ runner.os }}-node-modules
 
       - name: Build/Release Electron App
-        run: yarn install --frozen-lockfile && yarn electron:make
+        run: yarn install  && yarn electron:make
         working-directory: ./static
 
       - name: Change DMG Name
@@ -275,7 +275,7 @@ jobs:
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
         with:
           tag_name: ${{ github.event.inputs.tag-version }}
-          release_name: Desktop APP ${{ github.event.inputs.tag-version }} (Alpha Testing)
+          release_name: Desktop APP ${{ github.event.inputs.tag-version }} (Beta Testing)
           draft: ${{ github.event.inputs.is-draft }}
           prerelease: ${{ github.event.inputs.is-pre-release }}
 

+ 0 - 0
CODEBASE_OVERVIEW.MD → CODEBASE_OVERVIEW.md


+ 2 - 0
README.md

@@ -13,6 +13,8 @@ A local-first, non-linear, outliner notebook for organizing and sharing your per
 
 Use it to organize your todo list, to write your journals, or to record your unique life.
 
+<a href="https://www.producthunt.com/posts/logseq?utm_source=badge-review&utm_medium=badge&utm_souce=badge-logseq#discussion-body" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/review.svg?post_id=298158&theme=light" alt="Logseq - Your joyful, private digital garden | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
+
 ## [Download our free Desktop app](https://github.com/logseq/logseq/releases)
 [Sponsor our contributors on Open Collective](https://opencollective.com/logseq), Logseq will move to Stripe later!
 

+ 1 - 1
package.json

@@ -75,7 +75,7 @@
         "ignore": "^5.1.8",
         "is-svg": "4.2.2",
         "jszip": "^3.5.0",
-        "mldoc": "0.6.23",
+        "mldoc": "0.7.0",
         "path": "^0.12.7",
         "posthog-js": "^1.10.2",
         "react": "^17.0.2",

+ 1 - 1
resources/package.json

@@ -1,6 +1,6 @@
 {
   "name": "Logseq",
-  "version": "0.1.2",
+  "version": "0.1.4",
   "main": "electron.js",
   "author": "Logseq",
   "description": "A privacy-first, open-source platform for knowledge management and collaboration.",

+ 22 - 13
src/main/frontend/components/block.cljs

@@ -218,6 +218,7 @@
                                                     (editor-handler/delete-asset-of-block!
                                                      {:block-id    block-id
                                                       :local?      local?
+                                                      :delete-local? (first sub-selected)
                                                       :repo        (state/get-current-repo)
                                                       :href        src
                                                       :title       title
@@ -388,7 +389,7 @@
                  (util/encode-str page)
                  (rfe/href :page {:name redirect-page-name}))
           inner (page-inner config page-name href redirect-page-name page-entity contents-page? children html-export? label)]
-      (if-not preview?
+      (if (and (not (util/mobile?)) (not preview?))
         (ui/tippy {:html        [:div.tippy-wrapper.overflow-y-auto.p-4
                                  {:style {:width          735
                                           :text-align     "left"
@@ -530,7 +531,7 @@
     (let [block (and (util/uuid-string? id)
                      (db/pull-block (uuid id)))]
       (if block
-        [:span.block-ref-wrap
+        [:div.block-ref-wrap.inline
          {:on-mouse-down
           (fn [e]
             (util/stop e)
@@ -552,7 +553,7 @@
                          :span.block-ref
                          (map-inline config label))
                        title)]
-           (if-not (:preview? config)
+           (if (and (not (util/mobile?)) (not (:preview? config)))
              (ui/tippy {:html        [:div.tippy-wrapper.overflow-y-auto.p-4
                                       {:style {:width      735
                                                :text-align "left"
@@ -1255,6 +1256,7 @@
                                 :as t}]
   (let [config (assoc config :block t)
         slide? (boolean (:slide? config))
+        block-ref? (:block-ref? config)
         html-export? (:html-export? config)
         checkbox (when (and (not pre-block?)
                             (not html-export?))
@@ -1276,7 +1278,8 @@
                                ;; FIXME: construct the proper level later
                                2))
         elem (if heading-level
-               (keyword (str "h" heading-level))
+               (keyword (str "h" heading-level
+                             (when block-ref? ".inline")))
                :span.inline)]
     (->elem
      elem
@@ -1470,24 +1473,30 @@
 (rum/defc block-content < rum/reactive
   [config {:block/keys [uuid title body meta content marker page format repo children pre-block? properties idx container block-refs-count scheduled deadline repeated?] :as block} edit-input-id block-id slide?]
   (let [collapsed? (get properties :collapsed)
-        block-ref-with-title? (and (:block-ref? config) (seq title))
+        block-ref? (:block-ref? config)
+        block-ref-with-title? (and block-ref? (seq title))
         dragging? (rum/react *dragging?)
         content (if (string? content) (string/trim content) "")
         mouse-down-key (if (util/ios?)
                          :on-click
                          :on-mouse-down ; TODO: it seems that Safari doesn't work well with on-mouse-down
                          )
-        attrs {:blockid       (str uuid)
-               mouse-down-key (fn [e]
-                                (block-content-on-mouse-down e block block-id properties content format edit-input-id))
-               :on-drag-over  (fn [event] (block-content-on-drag-over event uuid))
-               :on-drag-leave (fn [_event] (block-content-on-drag-leave uuid))
-               :on-drop       (fn [event] (block-content-on-drop event block uuid))
-               :style {:width "100%"}}]
+        attrs (cond->
+                {:blockid       (str uuid)
+                 :on-drag-over  (fn [event] (block-content-on-drag-over event uuid))
+                 :on-drag-leave (fn [_event] (block-content-on-drag-leave uuid))
+                 :on-drop       (fn [event] (block-content-on-drop event block uuid))
+                 :style {:width "100%"}}
+                (not block-ref?)
+                (assoc mouse-down-key (fn [e]
+                                        (block-content-on-mouse-down e block block-id properties content format edit-input-id))))]
     [:div.block-content.inline
      (cond-> {:id (str "block-content-" uuid)}
        (not slide?)
-       (merge attrs))
+       (merge attrs)
+
+       block-ref?
+       (assoc :class "cursor-pointer"))
 
      [:span
      ;; .flex.relative {:style {:width "100%"}}

+ 15 - 0
src/main/frontend/components/block.css

@@ -108,6 +108,21 @@
 
 .block-body ul, .block-body ol, .block-body dl {
     margin-bottom: 2em;
+
+    > li {
+      margin: 0;
+    }
+}
+
+.block-body ol {
+  list-style-position: inside;
+  margin-left: 0;
+
+  > li {
+    &::marker, > p {
+      display: inline-block;
+    }
+  }
 }
 
 .block-children {

+ 1 - 1
src/main/frontend/components/editor.cljs

@@ -270,7 +270,7 @@
 
 (rum/defc absolute-modal < rum/static
   [cp set-default-width? {:keys [top left rect]}]
-  (let [max-height 500
+  (let [max-height 300
         max-width 300
         offset-top 24
         vw-height js/window.innerHeight

+ 18 - 1
src/main/frontend/components/export.cljs

@@ -24,13 +24,26 @@
         [:li.mb-4
          [:a.font-medium {:on-click #(export/export-repo-as-markdown! current-repo)}
           (t :export-markdown)]]
+        [:li.mb-4
+         [:a.font-medium {:on-click #(export/export-repo-as-opml! current-repo)}
+          (t :export-opml)]]
         [:li.mb-4
          [:a.font-medium {:on-click #(export/export-repo-as-edn! current-repo)}
-          (t :export-edn)]]]
+          (t :export-datascript-edn)]]
+        [:li.mb-4
+         [:a.font-medium {:on-click #(export/export-repo-as-edn-v2! current-repo)}
+          (t :export-edn)]]
+        [:li.mb-4
+         [:a.font-medium {:on-click #(export/export-repo-as-json-v2! current-repo)}
+          (t :export-json)]]
+        ]
        [:a#download-as-edn.hidden]
+       [:a#download-as-edn-v2.hidden]
+       [:a#download-as-json-v2.hidden]
        [:a#download-as-html.hidden]
        [:a#download-as-zip.hidden]
        [:a#export-as-markdown.hidden]
+       [:a#export-as-opml.hidden]
        [:a#convert-markdown-to-unordered-list-or-heading.hidden]])))
 
 
@@ -45,8 +58,12 @@
           [:li.mb-4
            [:a.font-medium {:on-click #(export/export-page-as-markdown! page)}
             (t :export-markdown)]]
+          [:li.mb-4
+           [:a.font-medium {:on-click #(export/export-page-as-opml! page)}
+            (t :export-opml)]]
           [:li.mb-4
            [:a.font-medium {:on-click #(export/convert-page-markdown-unordered-list-or-heading! page)}
             (t :convert-markdown)]]]
          [:a#export-page-as-markdown.hidden]
+         [:a#export-page-as-opml.hidden]
          [:a#convert-markdown-to-unordered-list-or-heading.hidden]]))))

+ 3 - 1
src/main/frontend/components/search.cljs

@@ -159,7 +159,9 @@
        {:style (merge
                 {:top 48
                  :left 32
-                 :width 700})
+                 :height 400
+                 :width 700
+                 :overflow "hidden"})
         :class (if all? "search-all" "absolute")}
        (ui/auto-complete
         result

+ 1 - 1
src/main/frontend/config.cljs

@@ -335,7 +335,7 @@
   [repo-url path]
   (if (and (util/electron?) (local-db? repo-url))
     path
-    (str (get-repo-dir repo-url) "/" path)))
+    (util/node-path.join (get-repo-dir repo-url) path)))
 
 (defn get-file-path
   [repo-url relative-path]

+ 18 - 2
src/main/frontend/db/model.cljs

@@ -207,6 +207,21 @@
              (conn/get-conn repo-url) pred)
         db-utils/seq-flatten)))
 
+(defn get-files-non-page-blocks
+  [repo-url paths]
+  (let [paths (set paths)
+        pred (fn [_db e]
+               (contains? paths e))]
+    (-> (d/q '[:find ?block
+               :in $ ?pred
+               :where
+               [?file :file/path ?path]
+               [(?pred $ ?path)]
+               [?block :block/file ?file]
+               [(missing? $ ?block :block/name)]]
+          (conn/get-conn repo-url) pred)
+        db-utils/seq-flatten)))
+
 (defn get-file-blocks
   [repo-url path]
   (-> (d/q '[:find ?block
@@ -1136,9 +1151,10 @@
         [@(d/conn-from-datoms datoms db-schema/schema) assets]))))
 
 (defn delete-blocks
-  [repo-url files]
+  [repo-url files delete-page?]
   (when (seq files)
-    (let [blocks (get-files-blocks repo-url files)]
+    (let [f (if delete-page? get-files-blocks get-files-non-page-blocks)
+          blocks (f repo-url files)]
       (mapv (fn [eid] [:db.fn/retractEntity eid]) blocks))))
 
 (defn delete-files

+ 5 - 1
src/main/frontend/dicts.cljs

@@ -244,10 +244,12 @@
         :graph-view "View Graph"
         :publishing "Publishing"
         :export "Export"
-        :export-json "Export as JSON"
         :export-markdown "Export as standard Markdown (no block properties)"
+        :export-opml "Export as OPML"
         :export-public-pages "Export public pages"
+        :export-json "Export as JSON"
         :export-edn "Export as EDN"
+        :export-datascript-edn "Export datascript EDN"
         :convert-markdown "Convert Markdown headings to unordered lists (# -> -)"
         :all-graphs "All graphs"
         :all-pages "All pages"
@@ -892,6 +894,7 @@
            :re-index "重新建立索引"
            :export-json "以 JSON 格式导出"
            :export-markdown "以 Markdown 格式导出"
+           :export-opml "以 OPML 格式导出"
            :convert-markdown "转换 Markdown 格式(Unordered list 或 Heading)"
            :unlink "解除绑定"
            :search (if config/publishing?
@@ -1137,6 +1140,7 @@
              :re-index "重新建立索引"
              :export-json "以 JSON 格式導出"
              :export-markdown "以 Markdown 格式導出"
+             :export-opml "以 OPML 格式導出"
              :convert-markdown "轉換 Markdown 格式(Unordered list 或 Heading)"
              :unlink "解除綁定"
              :search (if config/publishing?

+ 14 - 0
src/main/frontend/error.cljs

@@ -0,0 +1,14 @@
+(ns frontend.error
+  (:require [clojure.string :as string]))
+
+(defonce ignored
+  #{"ResizeObserver loop limit exceeded"})
+
+(defn ignored?
+  [message]
+  (let [message (str message)]
+    (boolean
+     (some
+      ;; TODO: some cases might need regex check
+      #(= (string/lower-case message) (string/lower-case %))
+      ignored))))

+ 8 - 7
src/main/frontend/extensions/code.cljs

@@ -117,7 +117,7 @@
         @editor-atom)
       (let [[config id attr code theme] (:rum/args state)
             original-mode (get attr :data-lang)
-            mode (or original-mode "javascript")
+            mode original-mode
             clojure? (contains? #{"clojure" "clj" "text/x-clojure" "cljs" "cljc"} mode)
             mode (if clojure? "clojure" (text->cm-mode mode))
             lisp? (or clojure?
@@ -146,7 +146,7 @@
         (when editor
           (let [element (.getWrapperElement editor)]
             (.on editor "blur" (fn [_cm e]
-                                 (util/stop e)
+                                 (when e (util/stop e))
                                  (state/set-block-component-editing-mode! false)
                                  (when-not @esc-pressed?
                                    (save-file-or-block-when-blur-or-esc! editor textarea config state))))
@@ -177,10 +177,11 @@
                  state)}
   [state config id attr code theme options]
   [:div.extensions__code
-   [:div.extensions__code-lang
-    (let [mode (string/lower-case (get attr :data-lang "javascript"))]
-      (if (= mode "text/x-clojure")
-        "clojure"
-        mode))]
+   (when-let [mode (:data-lang attr)]
+     [:div.extensions__code-lang
+      (let [mode (string/lower-case mode)]
+        (if (= mode "text/x-clojure")
+          "clojure"
+          mode))])
    [:textarea (merge {:id id
                       :default-value code} attr)]])

+ 2 - 0
src/main/frontend/format/adoc.cljs

@@ -28,4 +28,6 @@
      "https://cdnjs.cloudflare.com/ajax/libs/asciidoctor.js/1.5.9/asciidoctor.min.js"
      ok-handler))
   (exportMarkdown [this content config references]
+    (throw "not support"))
+  (exportOPML [this content config title]
     (throw "not support")))

+ 1 - 1
src/main/frontend/format/block.cljs

@@ -609,7 +609,7 @@
                             (map :db/id))
            {:block/keys [refs]} new-block
            ref-pages (filter :block/name refs)
-           path-ref-pages (concat ref-pages parent-refs)
+           path-ref-pages (concat ref-pages parent-refs [(:db/id page)])
            block (merge
                   block
                   new-block

+ 12 - 5
src/main/frontend/format/mldoc.cljs

@@ -16,6 +16,7 @@
 (defonce parseHtml (gobj/get Mldoc "parseHtml"))
 (defonce anchorLink (gobj/get Mldoc "anchorLink"))
 (defonce parseAndExportMarkdown (gobj/get Mldoc "parseAndExportMarkdown"))
+(defonce parseAndExportOPML (gobj/get Mldoc "parseAndExportOPML"))
 (defonce astExportMarkdown (gobj/get Mldoc "astExportMarkdown"))
 
 (defn default-config
@@ -48,22 +49,26 @@
 
 (defn parse-json
   [content config]
-  (parseJson content (or config default-config)))
+  (parseJson content config))
 
 (defn inline-parse-json
   [text config]
-  (parseInlineJson text (or config default-config)))
+  (parseInlineJson text config))
 
 (defn parse-export-markdown
   [content config references]
   (parseAndExportMarkdown content
-                          (or config default-config)
+                          config
                           (or references default-references)))
 
+(defn parse-export-opml
+  [content config title]
+  (parseAndExportOPML content config title))
+
 (defn ast-export-markdown
   [ast config references]
   (astExportMarkdown ast
-                     (or config default-config)
+                     config
                      (or references default-references)))
 
 ;; Org-roam
@@ -238,7 +243,9 @@
   (lazyLoad [this ok-handler]
     true)
   (exportMarkdown [this content config references]
-    (parse-export-markdown content config references)))
+    (parse-export-markdown content config references))
+  (exportOPML [this content config title]
+    (parse-export-opml content config title)))
 
 (defn plain->text
   [plains]

+ 2 - 1
src/main/frontend/format/protocol.cljs

@@ -5,4 +5,5 @@
   (toHtml [this content config])
   (loaded? [this])
   (lazyLoad [this ok-handler])
-  (exportMarkdown [this content config references]))
+  (exportMarkdown [this content config references])
+  (exportOPML [this content config title]))

+ 8 - 6
src/main/frontend/handler.cljs

@@ -26,17 +26,19 @@
             [goog.object :as gobj]
             [lambdaisland.glogi :as log]
             [promesa.core :as p]
-            [frontend.ui :as ui]))
+            [frontend.ui :as ui]
+            [frontend.error :as error]))
 
 (defn set-global-error-notification!
   []
   (set! js/window.onerror
         (fn [message, source, lineno, colno, error]
-          (notification/show!
-           (str "message=" message "\nsource=" source "\nlineno=" lineno "\ncolno=" colno "\nerror=" error)
-           :error
-           ;; Don't auto-hide
-           false))))
+          (when-not (error/ignored? message)
+            (notification/show!
+             (str "message=" message "\nsource=" source "\nlineno=" lineno "\ncolno=" colno "\nerror=" error)
+             :error
+             ;; Don't auto-hide
+             false)))))
 
 (defn- watch-for-date!
   []

+ 21 - 12
src/main/frontend/handler/editor.cljs

@@ -1380,28 +1380,35 @@
                 url))))))))
 
 (defn delete-asset-of-block!
-  [{:keys [repo href title full-text block-id local?] :as opts}]
+  [{:keys [repo href title full-text block-id local? delete-local?] :as opts}]
   (let [block (db-model/query-block-by-uuid block-id)
         _ (or block (throw (str block-id " not exists")))
         format (:block/format block)
         text (:block/content block)
         content (string/replace text full-text "")]
     (save-block! repo block content)
-    (when local?
+    (when (and local? delete-local?)
       ;; FIXME: should be relative to current block page path
-      (fs/unlink! (config/get-repo-path
-                   repo (-> href
-                            (string/replace #"^../" "/")
-                            (string/replace #"^assets://" ""))) nil))))
+      (when-let [href (if (util/electron?) href (second (re-find #"\((.+)\)$" full-text)))]
+        (fs/unlink! (config/get-repo-path
+                      repo (-> href
+                               (string/replace #"^../" "/")
+                               (string/replace #"^assets://" ""))) nil)))))
 
 ;; assets/journals_2021_02_03_1612350230540_0.png
 (defn resolve-relative-path
   [file-path]
-  (if-let [current-file (some-> (state/get-edit-block)
-                                :block/file
-                                :db/id
-                                (db/entity)
-                                :file/path)]
+  (if-let [current-file (or (some-> (state/get-edit-block)
+                                    :block/file
+                                    :db/id
+                                    (db/entity)
+                                    :file/path)
+
+                            ;; fix dummy file path of page
+                            (and (util/electron?)
+                                 (util/node-path.join
+                                  (config/get-repo-dir (state/get-current-repo))
+                                  (config/get-pages-directory) "_.md")))]
     (util/get-relative-path current-file file-path)
     file-path))
 
@@ -1983,7 +1990,9 @@
                                                  exclude-properties))
                      :block/meta (dissoc (:block/meta block) :start-pos :end-pos)
                      :block/content new-content
-                     :block/title new-title})]
+                     :block/title new-title
+                     :block/path-refs (->> (cons (:db/id page) (:block/path-refs block))
+                                           (remove nil?))})]
        (if file
          (assoc m :block/file (select-keys file [:db/id]))
          m)))))

+ 170 - 21
src/main/frontend/handler/export.cljs

@@ -1,21 +1,23 @@
 (ns frontend.handler.export
-  (:require [frontend.state :as state]
+  (:require [cljs-bean.core :as bean]
+            [cljs.pprint :as pprint]
+            [clojure.set :as s]
+            [clojure.string :as string]
+            [clojure.walk :as walk]
+            [datascript.core :as d]
+            [frontend.config :as config]
             [frontend.db :as db]
-            [frontend.format.protocol :as fp]
+            [frontend.extensions.zip :as zip]
             [frontend.format :as f]
-            [frontend.config :as config]
-            [datascript.core :as d]
-            [frontend.util :as util]
-            [cljs-bean.core :as bean]
-            [clojure.string :as string]
-            [goog.dom :as gdom]
-            [frontend.publishing.html :as html]
-            [frontend.text :as text]
+            [frontend.format.protocol :as fp]
             [frontend.handler.common :as common-handler]
-            [frontend.extensions.zip :as zip]
-            [frontend.modules.file.core :as outliner-file]
             [frontend.handler.file :as file-handler]
+            [frontend.modules.file.core :as outliner-file]
             [frontend.modules.outliner.tree :as outliner-tree]
+            [frontend.publishing.html :as html]
+            [frontend.state :as state]
+            [frontend.util :as util]
+            [goog.dom :as gdom]
             [promesa.core :as p]))
 
 
@@ -34,13 +36,22 @@
 
 (defn- get-file-content
   [file-path]
-  (let [page-name
-        (ffirst (d/q '[:find ?pn
-                       :where
-                       [?e :file/path file-path]
-                       [?p :block/file ?e]
-                       [?p :block/name ?pn]] (db/get-conn)))]
-    (get-page-content page-name)))
+  (if-let [page-name
+           (ffirst (d/q '[:find ?pn
+                          :in $ ?path
+                          :where
+                          [?p :block/file ?f]
+                          [?p :block/name ?pn]
+                          [?f :file/path ?path]]
+                        (db/get-conn) file-path))]
+    (get-page-content page-name)
+    (ffirst
+     (d/q '[:find ?content
+            :in $ ?path
+            :where
+            [?f :file/path ?path]
+            [?f :file/content ?content]]
+          (db/get-conn) file-path))))
 
 (defn- get-blocks-contents
   [repo root-block-uuid]
@@ -176,8 +187,7 @@
   (let [conn (db/get-conn repo)]
     (filter (fn [[path _]]
               (let [path (string/lower-case path)]
-                (or (string/ends-with? path ".md")
-                    (string/ends-with? path ".markdown"))))
+                (re-find #"\.(?:md|markdown)$" path)))
             (get-file-contents repo {:init-level 1
                                      :heading-to-list? true}))))
 
@@ -424,6 +434,19 @@
                                              (clj->js (f (first names)))))])))
          (remove nil?))))
 
+(defn- export-files-as-opml
+  [repo files]
+  (->> files
+       (mapv (fn [{:keys [path content names format]}]
+               (when (first names)
+                 (let [path
+                       (string/replace
+                        (string/lower-case path) #"(.+)\.(md|markdown|org)" "$1.opml")]
+                   [path (fp/exportOPML f/mldoc-record content
+                                        (f/get-default-config format)
+                                        (first names))]))))
+       (remove nil?)))
+
 (defn- convert-md-files-unordered-list-or-heading
   [repo files heading-to-list?]
   (->> files
@@ -482,6 +505,38 @@
                   (.setAttribute anchor "download" path)
                   (.click anchor))))))))))
 
+(defn export-repo-as-opml!
+  [repo]
+  (when-let [repo (state/get-current-repo)]
+    (when-let [files (get-file-contents-with-suffix repo)]
+      (let [files (export-files-as-opml repo files)
+            zip-file-name (str repo "_opml_" (quot (util/time-ms) 1000))]
+        (p/let [zipfile (zip/make-zip zip-file-name files repo)]
+          (when-let [anchor (gdom/getElement "export-as-opml")]
+            (.setAttribute anchor "href" (js/window.URL.createObjectURL zipfile))
+            (.setAttribute anchor "download" (.-name zipfile))
+            (.click anchor)))))))
+
+(defn export-page-as-opml!
+  [page-name]
+  (when-let [repo (state/get-current-repo)]
+    (when-let [file (db/get-page-file page-name)]
+      (when-let [path (:file/path file)]
+        (when-let [content (get-page-content page-name)]
+          (let [names [page-name]
+                format (f/get-format path)
+                files [{:path path :content content :names names :format format}]]
+            (let [files (export-files-as-opml repo files)]
+              (let [data (js/Blob. [(second (first files))]
+                                   (clj->js {:type "text/plain;charset=utf-8,"}))]
+                (let [anchor (gdom/getElement "export-page-as-opml")
+                      url (js/window.URL.createObjectURL data)
+                      opml-path (string/replace (string/lower-case path) #"(.+)\.(md|org|markdown)$" "$1.opml")]
+                  (.setAttribute anchor "href" url)
+                  (.setAttribute anchor "download" opml-path)
+                  (.click anchor))))))))))
+
+
 (defn convert-page-markdown-unordered-list-or-heading!
   [page-name]
   (when-let [repo (state/get-current-repo)]
@@ -514,3 +569,97 @@
             (.setAttribute anchor "href" (js/window.URL.createObjectURL zipfile))
             (.setAttribute anchor "download" (.-name zipfile))
             (.click anchor)))))))
+
+(defn- dissoc-properties [m ks]
+  (if (:block/properties m)
+    (update m :block/properties
+            (fn [v]
+              (apply dissoc v ks)))
+    m))
+
+(defn- nested-select-keys
+  [keyseq vec-tree]
+  (walk/postwalk
+   (fn [x]
+     (cond
+       (and (map? x) (contains? x :block/uuid))
+       (-> x
+           (s/rename-keys {:block/uuid :block/id
+                           :block/original-name :block/page-name})
+           (dissoc-properties [:id])
+           (select-keys keyseq))
+
+       :else
+       x))
+   vec-tree))
+
+(defn- blocks [conn]
+  {:version 1
+   :blocks
+   (->> (d/q '[:find (pull ?b [*])
+               :in $
+               :where
+               [?b :block/file]
+               [?b :block/original-name]
+               [?b :block/name]] conn)
+
+        (map (fn [[{:block/keys [name] :as page}]]
+               (assoc page
+                      :block/children
+                      (outliner-tree/blocks->vec-tree
+                       (db/get-page-blocks-no-cache
+                        (state/get-current-repo)
+                        name
+                        {:transform? false}) name))))
+
+        (nested-select-keys
+         [:block/id
+          :block/page-name
+          :block/properties
+          :block/format
+          :block/children
+          :block/title
+          :block/body
+          :block/content]))})
+
+(defn- file-name [repo extension]
+  (-> (string/replace repo config/local-db-prefix "")
+      (string/replace #"^/+" "")
+      (str "_" (quot (util/time-ms) 1000))
+      (str "." (string/lower-case (name extension)))))
+
+(defn export-repo-as-edn-v2!
+  [repo]
+  (when-let [conn (db/get-conn repo)]
+    (let [edn-str (with-out-str
+                    (pprint/pprint
+                     (blocks conn)))
+          data-str (str "data:text/edn;charset=utf-8," (js/encodeURIComponent edn-str))]
+      (when-let [anchor (gdom/getElement "download-as-edn-v2")]
+        (.setAttribute anchor "href" data-str)
+        (.setAttribute anchor "download" (file-name repo :edn))
+        (.click anchor)))))
+
+(defn- nested-update-id
+  [vec-tree]
+  (walk/postwalk
+   (fn [x]
+     (if (and (map? x) (contains? x :block/id))
+       (update x :block/id str)
+       x))
+   vec-tree))
+
+(defn export-repo-as-json-v2!
+  [repo]
+  (when-let [conn (db/get-conn repo)]
+    (let [json-str
+          (-> (blocks conn)
+              nested-update-id
+              clj->js
+              js/JSON.stringify)
+          data-str (str "data:text/json;charset=utf-8,"
+                        (js/encodeURIComponent json-str))]
+      (when-let [anchor (gdom/getElement "download-as-json-v2")]
+        (.setAttribute anchor "href" data-str)
+        (.setAttribute anchor "download" (file-name repo :json))
+        (.click anchor)))))

+ 2 - 1
src/main/frontend/handler/extract.cljs

@@ -63,7 +63,8 @@
           ref-tags (atom #{})
           blocks (map (fn [block]
                         (let [block-ref-pages (seq (:block/refs block))
-                              block-path-ref-pages (seq (:block/path-refs block))]
+                              page-lookup-ref [:block/name (string/lower-case page)]
+                              block-path-ref-pages (cons page-lookup-ref (seq (:block/path-refs block)))]
                           (when block-ref-pages
                             (swap! ref-pages set/union (set block-ref-pages)))
                           (-> block

+ 3 - 4
src/main/frontend/handler/migrate.cljs

@@ -20,10 +20,9 @@
                      (file/alter-file repo path content {:add-history? false
                                                          :reset? false})))
             (p/then (fn []
-                      (config-handler/set-config! :markdown/version 2)
-
-                      (p/let [_ (repo/push repo {:commit-message "Converted to new Markdown syntax!"})]
-                        (repo/re-index! nfs-handler/rebuild-index!)
+                      (p/let [_ (repo/push repo {:commit-message "Converted to new Markdown syntax!"})
+                              _ (repo/re-index! nfs-handler/rebuild-index!)]
+                        (js/setTimeout #(config-handler/set-config! :markdown/version 2) 2000)
 
                         (notification/show!
                          [:div

+ 1 - 1
src/main/frontend/handler/page.cljs

@@ -340,7 +340,7 @@
         page-name (string/lower-case page)]
     (if (and edit-block-file-path
              (state/org-mode-file-link? (state/get-current-repo)))
-      (if-let [ref-file-path (:file/path (:file/file (db/entity [:file/name page-name])))]
+      (if-let [ref-file-path (:file/path (db/get-page-file page-name))]
         (util/format "[[file:%s][%s]]"
                      (util/get-relative-path edit-block-file-path ref-file-path)
                      page)

+ 6 - 1
src/main/frontend/handler/repo.cljs

@@ -275,7 +275,12 @@
               add-files (filter-diffs "add")
               delete-files (if (seq remove-files)
                              (db/delete-files remove-files))
-              delete-blocks (db/delete-blocks repo-url (concat remove-files modify-files))
+              delete-blocks (db/delete-blocks repo-url remove-files true)
+              delete-blocks (->>
+                             (concat
+                              delete-blocks
+                              (db/delete-blocks repo-url modify-files false))
+                             (remove nil?))
               delete-pages (if (seq remove-files)
                              (db/delete-pages-by-files remove-files)
                              [])

+ 8 - 7
src/main/frontend/handler/ui.cljs

@@ -125,10 +125,10 @@
       (reset! current-idx (dec (count matched)))
       :else nil)
     (when-let [element (gdom/getElement (str "ac-" @current-idx))]
-      (let [ac-inner (gdom/getElement "ui__ac-inner")
-            element-top (gobj/get element "offsetTop")
-            scroll-top (- (gobj/get element "offsetTop") 360)]
-        (set! (.-scrollTop ac-inner) scroll-top)))))
+      (let [modal (gobj/get (gdom/getElement "ui__ac") "parentElement")
+            height (or (gobj/get modal "offsetHeight") 300)
+            scroll-top (- (gobj/get element "offsetTop") (/ height 2))]
+        (set! (.-scrollTop modal) scroll-top)))))
 
 (defn auto-complete-next
   [state e]
@@ -140,9 +140,10 @@
         (reset! current-idx 0)
         (swap! current-idx inc)))
     (when-let [element (gdom/getElement (str "ac-" @current-idx))]
-      (let [ac-inner (gdom/getElement "ui__ac-inner")
-            scroll-top (- (gobj/get element "offsetTop") 360)]
-        (set! (.-scrollTop ac-inner) scroll-top)))))
+      (let [modal (gobj/get (gdom/getElement "ui__ac") "parentElement")
+            height (or (gobj/get modal "offsetHeight") 300)
+            scroll-top (- (gobj/get element "offsetTop") (/ height 2))]
+        (set! (.-scrollTop modal) scroll-top)))))
 
 (defn auto-complete-complete
   [state e]

+ 2 - 1
src/main/frontend/ui.cljs

@@ -582,5 +582,6 @@
   (Tippy (merge {:arrow true
                  :sticky true
                  :theme (:ui/theme @state/state)
-                 :disabled (not (state/enable-tooltip?))} opts)
+                 :disabled (not (state/enable-tooltip?))
+                 :unmountHTMLWhenHide true} opts)
          child))

+ 0 - 1
src/main/frontend/ui.css

@@ -1,6 +1,5 @@
 #ui__ac {
   &-inner {
-    max-height: 400px;
     overflow-x: hidden;
     overflow-y: auto;
     position: relative;

+ 1 - 1
src/main/frontend/util.cljc

@@ -357,7 +357,7 @@
 
 #?(:cljs
    (defn stop [e]
-     (doto e (.preventDefault) (.stopPropagation))))
+     (when e (doto e (.preventDefault) (.stopPropagation)))))
 
 #?(:cljs
    (defn get-fragment

+ 1 - 1
src/main/frontend/version.cljs

@@ -1,3 +1,3 @@
 (ns frontend.version)
 
-(defonce version "0.1.2-2")
+(defonce version "0.1.4")

+ 4 - 4
yarn.lock

@@ -3917,10 +3917,10 @@ mkdirp@^0.5.4, mkdirp@~0.5.1:
   dependencies:
     minimist "^1.2.5"
 
-mldoc@0.6.23:
-  version "0.6.23"
-  resolved "https://registry.yarnpkg.com/mldoc/-/mldoc-0.6.23.tgz#4e6de4a31921eeeddb87ef1d3164eb5e27c051ec"
-  integrity sha512-UuE3NGuUthMak8tMqno8EHEy9WkQt1uVmKIuNFdM8vfkLgy8unV/IHZiLPJwvayaiSRfx+sXt16w3LDmZL+WxQ==
+mldoc@0.7.0:
+  version "0.7.0"
+  resolved "https://registry.yarnpkg.com/mldoc/-/mldoc-0.7.0.tgz#47bf1d324e098a1bc0b489df27fad54f16d23aea"
+  integrity sha512-0V5Dt0VWSwOVXB93oISr8IY2BqVTPHzOkUYVW5WLNxH4v6YEiVan9aiJ4EPr5Nlzl/0+UoLOeHQhuePXf43bCA==
   dependencies:
     yargs "^12.0.2"