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

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
           sudo ./linux-install-1.10.1.763.sh
 
 
       - name: Compile CLJS
       - 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
       - name: Update APP Version
         run: |
         run: |
@@ -107,7 +107,7 @@ jobs:
           key: ${{ runner.os }}-node-modules
           key: ${{ runner.os }}-node-modules
 
 
       - name: Build/Release Electron App
       - name: Build/Release Electron App
-        run: yarn install --frozen-lockfile && yarn electron:make
+        run: yarn install  && yarn electron:make
         working-directory: ./static
         working-directory: ./static
 
 
       - name: Change Artifact Name For ZIP File
       - name: Change Artifact Name For ZIP File
@@ -154,7 +154,7 @@ jobs:
           key: ${{ runner.os }}-node-modules
           key: ${{ runner.os }}-node-modules
 
 
       - name: Build/Release Electron app
       - name: Build/Release Electron app
-        run: yarn install --frozen-lockfile && yarn electron:make
+        run: yarn install  && yarn electron:make
         working-directory: ./static
         working-directory: ./static
 
 
       - name: Change Artifact Name
       - name: Change Artifact Name
@@ -206,7 +206,7 @@ jobs:
           key: ${{ runner.os }}-node-modules
           key: ${{ runner.os }}-node-modules
 
 
       - name: Build/Release Electron App
       - name: Build/Release Electron App
-        run: yarn install --frozen-lockfile && yarn electron:make
+        run: yarn install  && yarn electron:make
         working-directory: ./static
         working-directory: ./static
 
 
       - name: Change DMG Name
       - name: Change DMG Name
@@ -275,7 +275,7 @@ jobs:
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
         with:
         with:
           tag_name: ${{ github.event.inputs.tag-version }}
           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 }}
           draft: ${{ github.event.inputs.is-draft }}
           prerelease: ${{ github.event.inputs.is-pre-release }}
           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.
 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)
 ## [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!
 [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",
         "ignore": "^5.1.8",
         "is-svg": "4.2.2",
         "is-svg": "4.2.2",
         "jszip": "^3.5.0",
         "jszip": "^3.5.0",
-        "mldoc": "0.6.23",
+        "mldoc": "0.7.0",
         "path": "^0.12.7",
         "path": "^0.12.7",
         "posthog-js": "^1.10.2",
         "posthog-js": "^1.10.2",
         "react": "^17.0.2",
         "react": "^17.0.2",

+ 1 - 1
resources/package.json

@@ -1,6 +1,6 @@
 {
 {
   "name": "Logseq",
   "name": "Logseq",
-  "version": "0.1.2",
+  "version": "0.1.4",
   "main": "electron.js",
   "main": "electron.js",
   "author": "Logseq",
   "author": "Logseq",
   "description": "A privacy-first, open-source platform for knowledge management and collaboration.",
   "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!
                                                     (editor-handler/delete-asset-of-block!
                                                      {:block-id    block-id
                                                      {:block-id    block-id
                                                       :local?      local?
                                                       :local?      local?
+                                                      :delete-local? (first sub-selected)
                                                       :repo        (state/get-current-repo)
                                                       :repo        (state/get-current-repo)
                                                       :href        src
                                                       :href        src
                                                       :title       title
                                                       :title       title
@@ -388,7 +389,7 @@
                  (util/encode-str page)
                  (util/encode-str page)
                  (rfe/href :page {:name redirect-page-name}))
                  (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)]
           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
         (ui/tippy {:html        [:div.tippy-wrapper.overflow-y-auto.p-4
                                  {:style {:width          735
                                  {:style {:width          735
                                           :text-align     "left"
                                           :text-align     "left"
@@ -530,7 +531,7 @@
     (let [block (and (util/uuid-string? id)
     (let [block (and (util/uuid-string? id)
                      (db/pull-block (uuid id)))]
                      (db/pull-block (uuid id)))]
       (if block
       (if block
-        [:span.block-ref-wrap
+        [:div.block-ref-wrap.inline
          {:on-mouse-down
          {:on-mouse-down
           (fn [e]
           (fn [e]
             (util/stop e)
             (util/stop e)
@@ -552,7 +553,7 @@
                          :span.block-ref
                          :span.block-ref
                          (map-inline config label))
                          (map-inline config label))
                        title)]
                        title)]
-           (if-not (:preview? config)
+           (if (and (not (util/mobile?)) (not (:preview? config)))
              (ui/tippy {:html        [:div.tippy-wrapper.overflow-y-auto.p-4
              (ui/tippy {:html        [:div.tippy-wrapper.overflow-y-auto.p-4
                                       {:style {:width      735
                                       {:style {:width      735
                                                :text-align "left"
                                                :text-align "left"
@@ -1255,6 +1256,7 @@
                                 :as t}]
                                 :as t}]
   (let [config (assoc config :block t)
   (let [config (assoc config :block t)
         slide? (boolean (:slide? config))
         slide? (boolean (:slide? config))
+        block-ref? (:block-ref? config)
         html-export? (:html-export? config)
         html-export? (:html-export? config)
         checkbox (when (and (not pre-block?)
         checkbox (when (and (not pre-block?)
                             (not html-export?))
                             (not html-export?))
@@ -1276,7 +1278,8 @@
                                ;; FIXME: construct the proper level later
                                ;; FIXME: construct the proper level later
                                2))
                                2))
         elem (if heading-level
         elem (if heading-level
-               (keyword (str "h" heading-level))
+               (keyword (str "h" heading-level
+                             (when block-ref? ".inline")))
                :span.inline)]
                :span.inline)]
     (->elem
     (->elem
      elem
      elem
@@ -1470,24 +1473,30 @@
 (rum/defc block-content < rum/reactive
 (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?]
   [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)
   (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?)
         dragging? (rum/react *dragging?)
         content (if (string? content) (string/trim content) "")
         content (if (string? content) (string/trim content) "")
         mouse-down-key (if (util/ios?)
         mouse-down-key (if (util/ios?)
                          :on-click
                          :on-click
                          :on-mouse-down ; TODO: it seems that Safari doesn't work well with on-mouse-down
                          :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
     [:div.block-content.inline
      (cond-> {:id (str "block-content-" uuid)}
      (cond-> {:id (str "block-content-" uuid)}
        (not slide?)
        (not slide?)
-       (merge attrs))
+       (merge attrs)
+
+       block-ref?
+       (assoc :class "cursor-pointer"))
 
 
      [:span
      [:span
      ;; .flex.relative {:style {:width "100%"}}
      ;; .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 {
 .block-body ul, .block-body ol, .block-body dl {
     margin-bottom: 2em;
     margin-bottom: 2em;
+
+    > li {
+      margin: 0;
+    }
+}
+
+.block-body ol {
+  list-style-position: inside;
+  margin-left: 0;
+
+  > li {
+    &::marker, > p {
+      display: inline-block;
+    }
+  }
 }
 }
 
 
 .block-children {
 .block-children {

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

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

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

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

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

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

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

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

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

@@ -207,6 +207,21 @@
              (conn/get-conn repo-url) pred)
              (conn/get-conn repo-url) pred)
         db-utils/seq-flatten)))
         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
 (defn get-file-blocks
   [repo-url path]
   [repo-url path]
   (-> (d/q '[:find ?block
   (-> (d/q '[:find ?block
@@ -1136,9 +1151,10 @@
         [@(d/conn-from-datoms datoms db-schema/schema) assets]))))
         [@(d/conn-from-datoms datoms db-schema/schema) assets]))))
 
 
 (defn delete-blocks
 (defn delete-blocks
-  [repo-url files]
+  [repo-url files delete-page?]
   (when (seq files)
   (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))))
       (mapv (fn [eid] [:db.fn/retractEntity eid]) blocks))))
 
 
 (defn delete-files
 (defn delete-files

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

@@ -244,10 +244,12 @@
         :graph-view "View Graph"
         :graph-view "View Graph"
         :publishing "Publishing"
         :publishing "Publishing"
         :export "Export"
         :export "Export"
-        :export-json "Export as JSON"
         :export-markdown "Export as standard Markdown (no block properties)"
         :export-markdown "Export as standard Markdown (no block properties)"
+        :export-opml "Export as OPML"
         :export-public-pages "Export public pages"
         :export-public-pages "Export public pages"
+        :export-json "Export as JSON"
         :export-edn "Export as EDN"
         :export-edn "Export as EDN"
+        :export-datascript-edn "Export datascript EDN"
         :convert-markdown "Convert Markdown headings to unordered lists (# -> -)"
         :convert-markdown "Convert Markdown headings to unordered lists (# -> -)"
         :all-graphs "All graphs"
         :all-graphs "All graphs"
         :all-pages "All pages"
         :all-pages "All pages"
@@ -892,6 +894,7 @@
            :re-index "重新建立索引"
            :re-index "重新建立索引"
            :export-json "以 JSON 格式导出"
            :export-json "以 JSON 格式导出"
            :export-markdown "以 Markdown 格式导出"
            :export-markdown "以 Markdown 格式导出"
+           :export-opml "以 OPML 格式导出"
            :convert-markdown "转换 Markdown 格式(Unordered list 或 Heading)"
            :convert-markdown "转换 Markdown 格式(Unordered list 或 Heading)"
            :unlink "解除绑定"
            :unlink "解除绑定"
            :search (if config/publishing?
            :search (if config/publishing?
@@ -1137,6 +1140,7 @@
              :re-index "重新建立索引"
              :re-index "重新建立索引"
              :export-json "以 JSON 格式導出"
              :export-json "以 JSON 格式導出"
              :export-markdown "以 Markdown 格式導出"
              :export-markdown "以 Markdown 格式導出"
+             :export-opml "以 OPML 格式導出"
              :convert-markdown "轉換 Markdown 格式(Unordered list 或 Heading)"
              :convert-markdown "轉換 Markdown 格式(Unordered list 或 Heading)"
              :unlink "解除綁定"
              :unlink "解除綁定"
              :search (if config/publishing?
              :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)
         @editor-atom)
       (let [[config id attr code theme] (:rum/args state)
       (let [[config id attr code theme] (:rum/args state)
             original-mode (get attr :data-lang)
             original-mode (get attr :data-lang)
-            mode (or original-mode "javascript")
+            mode original-mode
             clojure? (contains? #{"clojure" "clj" "text/x-clojure" "cljs" "cljc"} mode)
             clojure? (contains? #{"clojure" "clj" "text/x-clojure" "cljs" "cljc"} mode)
             mode (if clojure? "clojure" (text->cm-mode mode))
             mode (if clojure? "clojure" (text->cm-mode mode))
             lisp? (or clojure?
             lisp? (or clojure?
@@ -146,7 +146,7 @@
         (when editor
         (when editor
           (let [element (.getWrapperElement editor)]
           (let [element (.getWrapperElement editor)]
             (.on editor "blur" (fn [_cm e]
             (.on editor "blur" (fn [_cm e]
-                                 (util/stop e)
+                                 (when e (util/stop e))
                                  (state/set-block-component-editing-mode! false)
                                  (state/set-block-component-editing-mode! false)
                                  (when-not @esc-pressed?
                                  (when-not @esc-pressed?
                                    (save-file-or-block-when-blur-or-esc! editor textarea config state))))
                                    (save-file-or-block-when-blur-or-esc! editor textarea config state))))
@@ -177,10 +177,11 @@
                  state)}
                  state)}
   [state config id attr code theme options]
   [state config id attr code theme options]
   [:div.extensions__code
   [: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
    [:textarea (merge {:id id
                       :default-value code} attr)]])
                       :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"
      "https://cdnjs.cloudflare.com/ajax/libs/asciidoctor.js/1.5.9/asciidoctor.min.js"
      ok-handler))
      ok-handler))
   (exportMarkdown [this content config references]
   (exportMarkdown [this content config references]
+    (throw "not support"))
+  (exportOPML [this content config title]
     (throw "not support")))
     (throw "not support")))

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

@@ -609,7 +609,7 @@
                             (map :db/id))
                             (map :db/id))
            {:block/keys [refs]} new-block
            {:block/keys [refs]} new-block
            ref-pages (filter :block/name refs)
            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 (merge
                   block
                   block
                   new-block
                   new-block

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

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

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

@@ -5,4 +5,5 @@
   (toHtml [this content config])
   (toHtml [this content config])
   (loaded? [this])
   (loaded? [this])
   (lazyLoad [this ok-handler])
   (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]
             [goog.object :as gobj]
             [lambdaisland.glogi :as log]
             [lambdaisland.glogi :as log]
             [promesa.core :as p]
             [promesa.core :as p]
-            [frontend.ui :as ui]))
+            [frontend.ui :as ui]
+            [frontend.error :as error]))
 
 
 (defn set-global-error-notification!
 (defn set-global-error-notification!
   []
   []
   (set! js/window.onerror
   (set! js/window.onerror
         (fn [message, source, lineno, colno, error]
         (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!
 (defn- watch-for-date!
   []
   []

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

@@ -1380,28 +1380,35 @@
                 url))))))))
                 url))))))))
 
 
 (defn delete-asset-of-block!
 (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)
   (let [block (db-model/query-block-by-uuid block-id)
         _ (or block (throw (str block-id " not exists")))
         _ (or block (throw (str block-id " not exists")))
         format (:block/format block)
         format (:block/format block)
         text (:block/content block)
         text (:block/content block)
         content (string/replace text full-text "")]
         content (string/replace text full-text "")]
     (save-block! repo block content)
     (save-block! repo block content)
-    (when local?
+    (when (and local? delete-local?)
       ;; FIXME: should be relative to current block page path
       ;; 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
 ;; assets/journals_2021_02_03_1612350230540_0.png
 (defn resolve-relative-path
 (defn resolve-relative-path
   [file-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)
     (util/get-relative-path current-file file-path)
     file-path))
     file-path))
 
 
@@ -1983,7 +1990,9 @@
                                                  exclude-properties))
                                                  exclude-properties))
                      :block/meta (dissoc (:block/meta block) :start-pos :end-pos)
                      :block/meta (dissoc (:block/meta block) :start-pos :end-pos)
                      :block/content new-content
                      :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
        (if file
          (assoc m :block/file (select-keys file [:db/id]))
          (assoc m :block/file (select-keys file [:db/id]))
          m)))))
          m)))))

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

@@ -1,21 +1,23 @@
 (ns frontend.handler.export
 (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.db :as db]
-            [frontend.format.protocol :as fp]
+            [frontend.extensions.zip :as zip]
             [frontend.format :as f]
             [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.handler.common :as common-handler]
-            [frontend.extensions.zip :as zip]
-            [frontend.modules.file.core :as outliner-file]
             [frontend.handler.file :as file-handler]
             [frontend.handler.file :as file-handler]
+            [frontend.modules.file.core :as outliner-file]
             [frontend.modules.outliner.tree :as outliner-tree]
             [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]))
             [promesa.core :as p]))
 
 
 
 
@@ -34,13 +36,22 @@
 
 
 (defn- get-file-content
 (defn- get-file-content
   [file-path]
   [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
 (defn- get-blocks-contents
   [repo root-block-uuid]
   [repo root-block-uuid]
@@ -176,8 +187,7 @@
   (let [conn (db/get-conn repo)]
   (let [conn (db/get-conn repo)]
     (filter (fn [[path _]]
     (filter (fn [[path _]]
               (let [path (string/lower-case 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
             (get-file-contents repo {:init-level 1
                                      :heading-to-list? true}))))
                                      :heading-to-list? true}))))
 
 
@@ -424,6 +434,19 @@
                                              (clj->js (f (first names)))))])))
                                              (clj->js (f (first names)))))])))
          (remove nil?))))
          (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
 (defn- convert-md-files-unordered-list-or-heading
   [repo files heading-to-list?]
   [repo files heading-to-list?]
   (->> files
   (->> files
@@ -482,6 +505,38 @@
                   (.setAttribute anchor "download" path)
                   (.setAttribute anchor "download" path)
                   (.click anchor))))))))))
                   (.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!
 (defn convert-page-markdown-unordered-list-or-heading!
   [page-name]
   [page-name]
   (when-let [repo (state/get-current-repo)]
   (when-let [repo (state/get-current-repo)]
@@ -514,3 +569,97 @@
             (.setAttribute anchor "href" (js/window.URL.createObjectURL zipfile))
             (.setAttribute anchor "href" (js/window.URL.createObjectURL zipfile))
             (.setAttribute anchor "download" (.-name zipfile))
             (.setAttribute anchor "download" (.-name zipfile))
             (.click anchor)))))))
             (.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 #{})
           ref-tags (atom #{})
           blocks (map (fn [block]
           blocks (map (fn [block]
                         (let [block-ref-pages (seq (:block/refs 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
                           (when block-ref-pages
                             (swap! ref-pages set/union (set block-ref-pages)))
                             (swap! ref-pages set/union (set block-ref-pages)))
                           (-> block
                           (-> block

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

@@ -20,10 +20,9 @@
                      (file/alter-file repo path content {:add-history? false
                      (file/alter-file repo path content {:add-history? false
                                                          :reset? false})))
                                                          :reset? false})))
             (p/then (fn []
             (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!
                         (notification/show!
                          [:div
                          [:div

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

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

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

@@ -275,7 +275,12 @@
               add-files (filter-diffs "add")
               add-files (filter-diffs "add")
               delete-files (if (seq remove-files)
               delete-files (if (seq remove-files)
                              (db/delete-files 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)
               delete-pages (if (seq remove-files)
                              (db/delete-pages-by-files 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)))
       (reset! current-idx (dec (count matched)))
       :else nil)
       :else nil)
     (when-let [element (gdom/getElement (str "ac-" @current-idx))]
     (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
 (defn auto-complete-next
   [state e]
   [state e]
@@ -140,9 +140,10 @@
         (reset! current-idx 0)
         (reset! current-idx 0)
         (swap! current-idx inc)))
         (swap! current-idx inc)))
     (when-let [element (gdom/getElement (str "ac-" @current-idx))]
     (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
 (defn auto-complete-complete
   [state e]
   [state e]

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

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

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

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

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

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

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

@@ -1,3 +1,3 @@
 (ns frontend.version)
 (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:
   dependencies:
     minimist "^1.2.5"
     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:
   dependencies:
     yargs "^12.0.2"
     yargs "^12.0.2"