Forráskód Böngészése

Merge branch 'master' into zotero/poc

Weihua Lu 4 éve
szülő
commit
bb9565a6c0
47 módosított fájl, 726 hozzáadás és 420 törlés
  1. 34 0
      externs.js
  2. 2 2
      package.json
  3. 10 2
      resources/css/common.css
  4. 1 1
      resources/package.json
  5. 27 19
      src/main/frontend/components/block.cljs
  6. 64 45
      src/main/frontend/components/block.css
  7. 5 10
      src/main/frontend/components/content.cljs
  8. 21 8
      src/main/frontend/components/editor.cljs
  9. 62 5
      src/main/frontend/components/export.cljs
  10. 4 3
      src/main/frontend/components/header.cljs
  11. 46 16
      src/main/frontend/components/page.cljs
  12. 1 1
      src/main/frontend/components/repo.cljs
  13. 1 0
      src/main/frontend/components/search.cljs
  14. 13 3
      src/main/frontend/components/settings.cljs
  15. 26 31
      src/main/frontend/components/sidebar.cljs
  16. 1 1
      src/main/frontend/components/widgets.cljs
  17. 2 0
      src/main/frontend/config.cljs
  18. 2 1
      src/main/frontend/db.cljs
  19. 31 1
      src/main/frontend/db/model.cljs
  20. 2 0
      src/main/frontend/db_schema.cljs
  21. 1 0
      src/main/frontend/dicts.cljs
  22. 32 36
      src/main/frontend/extensions/graph.cljs
  23. 0 12
      src/main/frontend/extensions/graph.css
  24. 57 49
      src/main/frontend/extensions/graph/pixi.cljs
  25. 1 1
      src/main/frontend/extensions/slide.cljs
  26. 2 4
      src/main/frontend/format.cljs
  27. 49 41
      src/main/frontend/format/block.cljs
  28. 18 24
      src/main/frontend/format/mldoc.cljs
  29. 34 2
      src/main/frontend/handler/editor.cljs
  30. 39 14
      src/main/frontend/handler/export.cljs
  31. 4 16
      src/main/frontend/handler/page.cljs
  32. 1 1
      src/main/frontend/handler/plugin.cljs
  33. 11 9
      src/main/frontend/modules/outliner/file.cljs
  34. 38 29
      src/main/frontend/modules/shortcut/config.cljs
  35. 0 1
      src/main/frontend/modules/shortcut/core.cljs
  36. 11 0
      src/main/frontend/modules/shortcut/data_helper.cljs
  37. 2 0
      src/main/frontend/modules/shortcut/dict.cljs
  38. 3 1
      src/main/frontend/publishing.cljs
  39. 6 6
      src/main/frontend/publishing/html.cljs
  40. 13 2
      src/main/frontend/state.cljs
  41. 26 8
      src/main/frontend/text.cljs
  42. 1 2
      src/main/frontend/ui.cljs
  43. 1 1
      src/main/frontend/version.cljs
  44. 11 0
      src/main/logseq/api.cljs
  45. 1 1
      src/test/frontend/format/block_test.cljs
  46. 1 1
      templates/config.edn
  47. 8 10
      yarn.lock

+ 34 - 0
externs.js

@@ -75,6 +75,40 @@ dummy.render = function() {};
 dummy.get = function() {};
 dummy.addItem = function() {};
 dummy.removeItem = function() {};
+dummy.resetNodeStyle = function() {};
+dummy.forEachNeighbor = function() {};
+dummy.graph = function() {};
+dummy.forEachEdge = function() {};
+dummy.resetEdgeStyle = function() {};
+dummy.getNodeAttributes = function() {};
+dummy.setNodeAttribute = function() {};
+dummy.resetView = function() {};
+dummy.destroy = function() {};
+dummy.size = function() {};
+dummy.id = function() {};
+dummy.color = function() {};
+dummy.TEXT = function() {};
+dummy.TextType = function() {};
+dummy.attr = function() {};
+dummy.force = function() {};
+dummy.distance = function() {};
+dummy.links = function() {};
+dummy.distanceMax = function() {};
+dummy.theta = function() {};
+dummy.strength = function() {};
+dummy.radius = function() {};
+dummy.tick = function() {};
+dummy.stop = function() {};
+dummy.addNode = function() {};
+dummy.addEdge = function() {};
+dummy.source = function() {};
+dummy.target = function() {};
+dummy.PixiGraph = function() {};
+dummy.createGraphWithGraph = function() {};
+dummy.resetView = function() {};
+dummy.dropNode = function() {};
+dummy.dropEdge = function() {};
+dummy.unhoverNode = function() {};
 
 /**
  * @typedef {{

+ 2 - 2
package.json

@@ -81,9 +81,9 @@
         "ignore": "^5.1.8",
         "is-svg": "4.2.2",
         "jszip": "^3.5.0",
-        "mldoc": "0.8.4",
+        "mldoc": "0.8.8",
         "path": "^0.12.7",
-        "pixi-graph-fork": "^0.0.9",
+        "pixi-graph-fork": "^0.1.3",
         "posthog-js": "^1.10.2",
         "react": "^17.0.2",
         "react-dom": "^17.0.2",

+ 10 - 2
resources/css/common.css

@@ -86,8 +86,8 @@ html[data-theme='dark'] {
 
 .white-theme,
 html[data-theme='light'] {
-  --ls-primary-background-color: #f6f6f6;
-  --ls-secondary-background-color: #f7f6f4;
+  --ls-primary-background-color: #ffffff;
+  --ls-secondary-background-color: #f7f7f7;
   --ls-tertiary-background-color: #f1eee8;
   --ls-quaternary-background-color: #e8e5de;
   --ls-table-tr-even-background-color: #f4f5f7;
@@ -728,6 +728,10 @@ li p:last-child,
   -webkit-animation: spin 2s infinite linear;
 }
 
+.loader-reverse {
+    -webkit-animation: spin 2s infinite linear reverse;
+}
+
 .canceled,
 .done {
   text-decoration: line-through;
@@ -1139,3 +1143,7 @@ html[data-theme='dark'] .keyboard-shortcut > code {
 .ui__modal-panel {
   border-radius: 8px;
 }
+
+.overflow-y-scroll {
+    overflow-y: scroll;
+}

+ 1 - 1
resources/package.json

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

+ 27 - 19
src/main/frontend/components/block.cljs

@@ -120,7 +120,7 @@
     (let [{:keys [link protocol]} m]
       (if (= protocol "file")
         link
-        (str protocol ":" link)))))
+        (str protocol "://" link)))))
 
 (defn- get-file-absolute-path
   [config path]
@@ -335,19 +335,22 @@
     :href href
     :on-click (fn [e]
                 (util/stop e)
-                (if (gobj/get e "shiftKey")
-                  (do
-                    (js/setTimeout #(editor-handler/insert-first-page-block-if-not-exists! redirect-page-name) 310)
-                    (when-let [page-entity (db/entity [:block/name redirect-page-name])]
-                      (state/sidebar-add-block!
-                       (state/get-current-repo)
-                       (:db/id page-entity)
-                       :page
-                       {:page page-entity})))
-                  (do
-                    (editor-handler/insert-first-page-block-if-not-exists! redirect-page-name)
-                    (route-handler/redirect! {:to :page
-                                              :path-params {:name redirect-page-name}})))
+                (let [create-first-block! (fn []
+                                            (when-not (editor-handler/add-default-title-property-if-needed! redirect-page-name)
+                                              (editor-handler/insert-first-page-block-if-not-exists! redirect-page-name)))]
+                  (if (gobj/get e "shiftKey")
+                   (do
+                     (js/setTimeout create-first-block! 310)
+                     (when-let [page-entity (db/entity [:block/name redirect-page-name])]
+                       (state/sidebar-add-block!
+                        (state/get-current-repo)
+                        (:db/id page-entity)
+                        :page
+                        {:page page-entity})))
+                   (do
+                     (create-first-block!)
+                     (route-handler/redirect! {:to :page
+                                               :path-params {:name redirect-page-name}}))))
                 (when (and contents-page?
                            (state/get-left-sidebar-open?))
                   (ui-handler/close-left-sidebar!)))}
@@ -385,11 +388,10 @@
     deval))
 
 (rum/defc page-preview-trigger
-  [{:keys [children sidebar? tippy-position tippy-distance fixed-position? open?] :as config} page-name]
+  [{:keys [children sidebar? tippy-position tippy-distance fixed-position? open? manual?] :as config} page-name]
   (let [redirect-page-name (model/get-redirect-page-name page-name (:block/alias? config))
         page-original-name (model/get-page-original-name redirect-page-name)
         debounced-open? (use-delayed-open open? page-name)
-        manual? (not (nil? open?))
         html-template (fn []
                         [:div.tippy-wrapper.overflow-y-auto.p-4
                          {:style {:width          600
@@ -413,7 +415,7 @@
                            (editor-handler/insert-first-page-block-if-not-exists! redirect-page-name)
                            (when-let [f (state/get-page-blocks-cp)]
                              (f (state/get-current-repo) page {:sidebar? sidebar? :preview? true})))])]
-    (if (and manual? open?)
+    (if (or (not manual?) open?)
       (ui/tippy {:html            html-template
                  :interactive     true
                  :open?           debounced-open?
@@ -1217,7 +1219,9 @@
                     (not (:pre-block? block))
                     (or (and (coll? children) (seq children))
                         (seq body)))
-        control-show? (util/react *control-show?)
+        control-show? (and
+                       (seq (:block/title block))
+                       (util/react *control-show?))
         ref-collapsed? (util/react *ref-collapsed?)
         dark? (= "dark" (state/sub :ui/theme))
         ref? (:ref? config)
@@ -1464,7 +1468,9 @@
 (rum/defc properties-cp
   [config block]
   (let [properties (walk/keywordize-keys (:block/properties block))
+        properties-order (:block/properties-order block)
         properties (apply dissoc properties property/built-in-properties)
+        properties-order (remove property/built-in-properties properties-order)
         pre-block? (:block/pre-block? block)
         properties (if pre-block?
                      (let [repo (state/get-current-repo)
@@ -1478,7 +1484,9 @@
                            (assoc properties :alias aliases))
                          properties))
                      properties)
-        properties (sort properties)]
+        properties (if (seq properties-order)
+                     (map (fn [k] [k (get properties k)]) properties-order)
+                     properties)]
     (cond
       (seq properties)
       [:div.block-properties

+ 64 - 45
src/main/frontend/components/block.css

@@ -1,6 +1,3 @@
-.blocks-container {
-}
-
 .block-content-wrapper {
   width: 100%;
 }
@@ -34,7 +31,7 @@
   .asset-container {
     display: inline-block;
     position: relative;
-    margin-top: .5rem;
+    margin-top: 0.5rem;
 
     .ctl {
       position: absolute;
@@ -47,7 +44,7 @@
       > a {
         padding: 3px;
         border-radius: 4px;
-        opacity: .4;
+        opacity: 0.4;
         user-select: none;
         background: var(--ls-primary-background-color);
 
@@ -55,7 +52,7 @@
           svg {
             color: var(--ls-primary-text-color);
 
-            opacity: .5;
+            opacity: 0.5;
             font-weight: normal;
           }
         }
@@ -105,12 +102,14 @@
   }
 }
 
-.block-body ul, .block-body ol, .block-body dl {
-    margin-bottom: 2em;
+.block-body ul,
+.block-body ol,
+.block-body dl {
+  margin-bottom: 2em;
 
-    > li {
-      margin: 0;
-    }
+  > li {
+    margin: 0;
+  }
 }
 
 .block-body ol {
@@ -118,7 +117,8 @@
   margin-left: 0;
 
   > li {
-    &::marker, > p {
+    &::marker,
+    > p {
       display: initial;
     }
   }
@@ -159,7 +159,7 @@
   cursor: alias;
 
   &:hover {
-    color: var(--ls-link-text-hover-color)
+    color: var(--ls-link-text-hover-color);
   }
 }
 
@@ -190,7 +190,7 @@
 }
 
 .page-properties {
-    background-color: var(--ls-page-properties-background-color);
+  background-color: var(--ls-page-properties-background-color);
 }
 
 .block-marker {
@@ -232,61 +232,80 @@
   margin-left: auto;
 }
 
-.ls-block h1, .editor-inner .h1 {
-    font-size: 2em;
+.ls-block h1,
+.editor-inner .h1 {
+  font-size: 2em;
+  min-height: 1.5em;
 }
 
-.ls-block h2, .editor-inner .h2 {
-    font-size: 1.5em;
+.ls-block h2,
+.editor-inner .h2 {
+  font-size: 1.5em;
+  min-height: 1.5em;
 }
 
-.ls-block h3, .editor-inner .h3 {
-    font-size: 1.17em;
+.ls-block h3,
+.editor-inner .h3 {
+  font-size: 1.17em;
+  min-height: 1.17em;
 }
 
-.ls-block h4, .editor-inner .h4 {
-    font-size: 1.12em;
+.ls-block h4,
+.editor-inner .h4 {
+  font-size: 1.12em;
+  min-height: 1.12em;
 }
 
-.ls-block h5, .editor-inner .h5 {
+.ls-block h5,
+.editor-inner .h5 {
   font-size: 0.83em;
+  min-height: 0.83em;
 }
 
-.ls-block h6, .editor-inner .h6 {
+.ls-block h6,
+.editor-inner .h6 {
   font-size: 0.75em;
+  min-height: 0.75em;
 }
 
-.ls-block :is(h1,h2,h3,h4,h5,h6), .editor-inner .h1, .editor-inner .h2, .editor-inner .h3,
-.editor-inner .h4, .editor-inner .h5, .editor-inner .h6 {
-  font-weight: 600
+.ls-block :is(h1, h2, h3, h4, h5, h6),
+.editor-inner :is(.h1, .h2, .h3, .h4, .h5, .h6) {
+  font-weight: 600;
 }
 
-.ls-block :is(h1, h2), .editor-inner .h1, .editor-inner .h2 {
-    border-bottom: 1px solid var(--ls-quaternary-background-color);
-    margin: 0.4em 0 0;
+.ls-block :is(h1, h2),
+.editor-inner :is(.h1, .h2) {
+  border-bottom: 1px solid var(--ls-quaternary-background-color);
+  margin: 0.4em 0 0;
 }
 
-.document-mode .ls-block h1, .document-mode .editor-inner .h1 {
-    margin: 0.67em 0;
+.document-mode .ls-block h1,
+.document-mode .editor-inner .h1 {
+  margin: 0.67em 0;
 }
-.document-mode .ls-block h2, .document-mode .editor-inner .h2 {
-    margin: 0.75em 0;
+.document-mode .ls-block h2,
+.document-mode .editor-inner .h2 {
+  margin: 0.75em 0;
 }
-.document-mode .ls-block h3, .document-mode .editor-inner .h3 {
-    margin: 0.83em 0;
+.document-mode .ls-block h3,
+.document-mode .editor-inner .h3 {
+  margin: 0.83em 0;
 }
-.document-mode .ls-block h4, .document-mode .editor-inner .h4 {
-    margin: 1.12em 0;
+.document-mode .ls-block h4,
+.document-mode .editor-inner .h4 {
+  margin: 1.12em 0;
 }
-.document-mode .ls-block h5, .document-mode .editor-inner .h5 {
-    margin: 1.5em 0;
+.document-mode .ls-block h5,
+.document-mode .editor-inner .h5 {
+  margin: 1.5em 0;
 }
-.document-mode .ls-block h6, .document-mode .editor-inner .h6 {
-    margin: 1.67em 0;
+.document-mode .ls-block h6,
+.document-mode .editor-inner .h6 {
+  margin: 1.67em 0;
 }
 
 .document-mode .ls-block {
-    margin-bottom: 2rem;
+    margin-bottom: 1rem;
 }
 
 .color-level {
@@ -350,7 +369,7 @@
     width: 6px;
     height: 6px;
     background-color: var(--ls-block-bullet-color, #394b59);
-    transition: transform .2s;
+    transition: transform 0.2s;
   }
 
   &.bullet-closed {
@@ -406,5 +425,5 @@ a:hover > .bullet-container .bullet {
 }
 
 a.filter svg {
-    transform: scale(0.9);
+  transform: scale(0.9);
 }

+ 5 - 10
src/main/frontend/components/content.cljs

@@ -19,6 +19,7 @@
             [cljs.pprint :as pprint]
             [frontend.handler.notification :as notification]
             [frontend.components.editor :as editor]
+            [frontend.components.export :as export]
             [frontend.context.i18n :as i18n]
             [frontend.text :as text]
             [frontend.handler.page :as page-handler]))
@@ -172,17 +173,11 @@
 
           (block-template block-id)
 
-          ;; (ui/menu-link
-          ;;  {:key "Make template"
-          ;;   :on-click (fn [_e]
-          ;;               (editor-handler/copy-block-ref! block-id))}
-          ;;  "Make template")
-
           (ui/menu-link
-           {:key "Copy as text"
-            :on-click (fn [_e]
-                        (export-handler/copy-block! block-id))}
-           "Copy as TEXT")
+           {:key "Export"
+            :on-click (fn [_]
+                        (state/set-modal! #(export/export-blocks block-id)))}
+           "Export")
 
           (ui/menu-link
            {:key "Copy as JSON"

+ 21 - 8
src/main/frontend/components/editor.cljs

@@ -106,6 +106,7 @@
                              (block/page-preview-trigger
                                {:children        [:div (search/highlight-exact-query page-name q)]
                                 :open?           chosen?
+                                :manual?         true
                                 :fixed-position? true
                                 :tippy-distance  24
                                 :tippy-position  (if sidebar? "left" "right")}
@@ -382,12 +383,21 @@
     (set-up-key-down! repo state format)
     (set-up-key-up! state input input-id search-timeout)))
 
-(defn- get-editor-heading-class
-  [content heading-level]
-  (if (string/includes? content "\n")
-    nil
-    (if heading-level (str "h" heading-level))))
-
+(def starts-with? clojure.string/starts-with?)
+
+(defn get-editor-heading-class [content]
+  (cond
+    (string/includes? content "\n") "multiline-block"
+    (starts-with? content "# ") "h1"
+    (starts-with? content "## ") "h2"
+    (starts-with? content "### ") "h3"
+    (starts-with? content "#### ") "h4"
+    (starts-with? content "##### ") "h5"
+    (starts-with? content "###### ") "h6"
+    (starts-with? content "TODO ") "todo-block"
+    (starts-with? content "DOING ") "doing-block"
+    (starts-with? content "DONE ") "done-block"
+    :else "normal-block"))
 
 (rum/defc mock-textarea
   < rum/reactive
@@ -431,7 +441,10 @@
           :as   option} id config]
   (let [content (state/get-edit-content)
         heading-level (get state ::heading-level)]
-    [:div.editor-inner {:class (if block "block-editor" "non-block-editor")}
+    [:div.editor-inner {:class (str
+                                (if block "block-editor" "non-block-editor")
+                                " "
+                                (get-editor-heading-class content))}
      (when config/mobile? (mobile-bar state id))
      (ui/ls-textarea
       {:id                id
@@ -442,7 +455,7 @@
        :on-change         (editor-handler/editor-on-change! block id search-timeout)
        :on-paste          (editor-handler/editor-on-paste! id)
        :auto-focus        false
-       :class             (get-editor-heading-class content heading-level)})
+       :class             (get-editor-heading-class content)})
 
      (mock-textarea)
 

+ 62 - 5
src/main/frontend/components/export.cljs

@@ -14,9 +14,6 @@
        [:h1.title "Export"]
 
        [:ul.mr-1
-        [:li.mb-4
-         [:a.font-medium {:on-click #(export/convert-repo-markdown-v2! current-repo)}
-          (t :convert-markdown)]]
         (when (util/electron?)
           [:li.mb-4
            [:a.font-medium {:on-click #(export/export-repo-as-html! current-repo)}
@@ -36,8 +33,9 @@
         [:li.mb-4
          [:a.font-medium {:on-click #(export/export-repo-as-roam-json! current-repo)}
           (t :export-roam-json)]]
-        ]
-       [:a#download-as-edn.hidden]
+        [:li.mb-4
+         [:a.font-medium {:on-click #(export/convert-repo-markdown-v2! current-repo)}
+          (t :convert-markdown)]]]
        [:a#download-as-edn-v2.hidden]
        [:a#download-as-json-v2.hidden]
        [:a#download-as-roam-json.hidden]
@@ -65,3 +63,62 @@
          [:a#export-page-as-markdown.hidden]
          [:a#export-page-as-opml.hidden]
          [:a#convert-markdown-to-unordered-list-or-heading.hidden]]))))
+
+(def *export-block-type (atom :text))
+
+(def text-indent-style-options [{:label "dashes"
+                                 :selected false}
+                                {:label "spaces"
+                                 :selected false}
+                                {:label "no-indent"
+                                 :selected false}])
+
+(def *export-block-text-indent-style (atom "dashes"))
+
+(rum/defcs export-blocks
+  < rum/reactive
+  (rum/local false ::copied?)
+  [state root-block-id]
+  (let [current-repo (state/get-current-repo)
+        type (rum/react *export-block-type)
+        text-indent-style (rum/react *export-block-text-indent-style)
+        copied? (::copied? state)
+        content
+        (case type
+          :text (export/export-blocks-as-markdown current-repo root-block-id text-indent-style)
+          :opml (export/export-blocks-as-opml current-repo root-block-id)
+          (export/export-blocks-as-markdown current-repo root-block-id text-indent-style))]
+    [:div.export.w-96.resize
+     [:div
+      {:class "mb-2"}
+      (ui/button "Text"
+                 :class "mr-2 w-20"
+                 :on-click #(reset! *export-block-type :text))
+      (ui/button "OPML"
+                 :class "w-20"
+                 :on-click #(reset! *export-block-type :opml))]
+     [:textarea.overflow-y-auto.h-96 {:value content}]
+     (let [options (->> text-indent-style-options
+                        (mapv (fn [opt]
+                                (if (= text-indent-style (:label opt))
+                                  (assoc opt :selected true)
+                                  opt))))]
+       [:div.flex.items-center
+        [:label.mr-8 "Indentation style:"]
+        [:select.block.my-2.text-lg.rounded.border
+         {:style     {:padding "0 0 0 12px"
+                      :visibility (if (= :text type) "visible" "hidden")}
+          :on-change (fn [e]
+                       (let [value (util/evalue e)]
+                         (#(reset! *export-block-text-indent-style %) value)))}
+         (for [{:keys [label value selected]} options]
+           [:option (cond->
+                        {:key   label
+                         :value (or value label)}
+                      selected
+                      (assoc :selected selected))
+            label])]])
+     (ui/button (if @copied? "Copied to clipboard!" "Copy to clipboard")
+                :on-click (fn []
+                            (util/copy-to-clipboard! content)
+                            (reset! copied? true)))]))

+ 4 - 3
src/main/frontend/components/header.cljs

@@ -110,9 +110,10 @@
           :options {:href (rfe/href :all-journals)}
           :icon svg/calendar-sm})
 
-       {:title (t :settings)
-        :options {:on-click state/open-settings!}
-        :icon svg/settings-sm}
+       (when-not (state/publishing-enable-editing?)
+         {:title (t :settings)
+          :options {:on-click state/open-settings!}
+          :icon svg/settings-sm})
 
        (when developer-mode?
          {:title (t :plugins)

+ 46 - 16
src/main/frontend/components/page.cljs

@@ -327,7 +327,8 @@
                                                              (:db/id page)
                                                              :page-presentation
                                                              {:page page}))}}
-                                     (when-not contents?
+                                     (when (and (not contents?)
+                                                (not journal?))
                                        {:title   (t :page/rename)
                                         :options {:on-click #(state/set-modal! (rename-page-dialog title page-name))}})
 
@@ -455,12 +456,21 @@
 (defonce *n-hops (atom nil))
 (defonce *focus-nodes (atom []))
 (defonce *graph-reset? (atom false))
+(defonce *journal? (atom nil))
+(defonce *orphan-pages? (atom nil))
+(defonce *builtin-pages? (atom nil))
 
 (rum/defc graph-filters < rum/reactive
   [graph settings n-hops]
   (let [{:keys [layout journal? orphan-pages? builtin-pages?]
          :or {layout "gForce"
               orphan-pages? true}} settings
+        journal?' (rum/react *journal?)
+        orphan-pages?' (rum/react *orphan-pages?)
+        builtin-pages?' (rum/react *builtin-pages?)
+        journal? (if (nil? journal?') journal? journal?')
+        orphan-pages? (if (nil? orphan-pages?') orphan-pages? orphan-pages?')
+        builtin-pages? (if (nil? builtin-pages?') builtin-pages? builtin-pages?')
         set-setting! (fn [key value]
                        (let [new-settings (assoc settings key value)]
                          (config-handler/set-config! :graph/settings new-settings)))
@@ -505,19 +515,28 @@
                 ;; FIXME: why it's not aligned well?
                 [:div.mt-1
                  (ui/toggle journal?
-                            #(set-setting! :journal? (not journal?))
+                            (fn []
+                              (let [value (not journal?)]
+                                (reset! *journal? value)
+                                (set-setting! :journal? value)))
                             true)]]
                [:div.flex.items-center.justify-between.mb-2
                 [:span "Orphan pages"]
                 [:div.mt-1
                  (ui/toggle orphan-pages?
-                            #(set-setting! :orphan-pages? (not orphan-pages?))
+                            (fn []
+                              (let [value (not orphan-pages?)]
+                                (reset! *orphan-pages? value)
+                                (set-setting! :orphan-pages? value)))
                             true)]]
                [:div.flex.items-center.justify-between.mb-2
                 [:span "Built-in pages"]
                 [:div.mt-1
                  (ui/toggle builtin-pages?
-                            #(set-setting! :builtin-pages? (not builtin-pages?))
+                            (fn []
+                              (let [value (not builtin-pages?)]
+                                (reset! *builtin-pages? value)
+                                (set-setting! :builtin-pages? value)))
                             true)]]
                (when (seq focus-nodes)
                  [:div.flex.flex-col.mb-2
@@ -555,11 +574,18 @@
                   "Click to search"])]))
            {:search-filters search-graph-filters})]]]])))
 
+(defonce last-node-position (atom nil))
 (defn- graph-register-handlers
-  [graph focus-nodes n-hops]
+  [graph focus-nodes n-hops dark?]
   (.on graph "nodeClick"
        (fn [event node]
-         (graph/on-click-handler graph node event focus-nodes n-hops))))
+         (let [x (.-x event)
+               y (.-y event)
+               drag? (not= [node x y] @last-node-position)]
+           (graph/on-click-handler graph node event focus-nodes n-hops drag? dark?))))
+  (.on graph "nodeMousedown"
+       (fn [event node]
+         (reset! last-node-position [node (.-x event) (.-y event)]))))
 
 (rum/defc global-graph-inner < rum/reactive
   [graph settings theme]
@@ -588,7 +614,7 @@
                         :dark? dark?
                         :register-handlers-fn
                         (fn [graph]
-                          (graph-register-handlers graph *focus-nodes *n-hops))
+                          (graph-register-handlers graph *focus-nodes *n-hops dark?))
                         :reset? reset?})
        (graph-filters graph settings n-hops)])))
 
@@ -622,6 +648,18 @@
         reset? (rum/react *graph-reset?)]
     (global-graph-inner graph settings theme)))
 
+(rum/defc page-graph-inner < rum/static
+  [graph dark?]
+  [:div.sidebar-item.flex-col
+   (graph/graph-2d {:nodes (:nodes graph)
+                    :links (:links graph)
+                    :width 600
+                    :height 600
+                    :dark? dark?
+                    :register-handlers-fn
+                    (fn [graph]
+                      (graph-register-handlers graph (atom nil) (atom nil) dark?))})])
+
 (rum/defc page-graph < db-mixins/query rum/reactive
   []
   (let [page (or
@@ -634,15 +672,7 @@
                 (graph-handler/build-block-graph (uuid page) theme)
                 (graph-handler/build-page-graph page theme))]
     (when (seq (:nodes graph))
-      [:div.sidebar-item.flex-col
-       (graph/graph-2d {:nodes (:nodes graph)
-                        :links (:links graph)
-                        :width 600
-                        :height 600
-                        :dark? dark?
-                        :register-handlers-fn
-                        (fn [graph]
-                          (graph-register-handlers graph (atom nil) (atom nil)))})])))
+      (page-graph-inner graph dark?))))
 
 (rum/defc all-pages < rum/reactive
   ;; {:did-mount (fn [state]

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

@@ -99,7 +99,7 @@
       (when-not (= repo config/local-repo)
         (if (and nfs-repo? (nfs-handler/supported?))
           (let [syncing? (state/sub :graph/syncing?)]
-            [:div.opacity-60.refresh.hover:opacity-100 {:class (if syncing? "loader" "initial")}
+            [:div.opacity-60.refresh.hover:opacity-100 {:class (if syncing? "loader-reverse" "initial")}
              [:a.block.p-2
               {:on-click #(nfs-handler/refresh! repo refresh-cb)
                :title (str "Import files from the local directory: " (config/get-local-dir repo) ".\nVersion: "

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

@@ -164,6 +164,7 @@
                  :left 32
                  :height 400
                  :width 700
+                 :max-width "100%"
                  :overflow "auto"})
         :class (if all? "search-all" "absolute")}
        (ui/auto-complete

+ 13 - 3
src/main/frontend/components/settings.cljs

@@ -13,6 +13,7 @@
             [frontend.handler.ui :as ui-handler]
             [frontend.handler.user :as user-handler]
             [frontend.modules.instrumentation.core :as instrument]
+            [frontend.modules.shortcut.data-helper :as shortcut-helper]
             [frontend.state :as state]
             [frontend.ui :as ui]
             [frontend.util :refer [classnames] :as util]
@@ -170,8 +171,7 @@
                 config-handler/toggle-ui-show-brackets!
                 true)]]
    [:div {:style {:text-align "right"}}
-    ;; TODO: Fetch this shortcut from config.cljs so there's one source of truth
-    (ui/keyboard-shortcut [:meta :c :meta :b])]])
+    (ui/keyboard-shortcut (shortcut-helper/gen-shortcut-seq :ui/toggle-brackets))]])
 
 (defn language-row [t preferred-language]
   [:div.it.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-start
@@ -208,7 +208,7 @@
             :class    (classnames [{:active system-theme?}])} [:i.mode-system] [:strong "system"]]]]]
 
    [:div {:style {:text-align "right"}}
-    (ui/keyboard-shortcut [:t :t])]])
+    (ui/keyboard-shortcut (shortcut-helper/gen-shortcut-seq :ui/toggle-theme))]])
 
 (defn file-format-row [t preferred-format]
   [:div.it.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-start
@@ -334,6 +334,14 @@
                           (when (= "Enter" (util/ekey e))
                             (update-home-page e)))}]]]])])
 
+(defn enable-all-pages-public-row [t enable-all-pages-public?]
+  (toggle "all pages public"
+          (t :settings-page/enable-all-pages-public)
+          enable-all-pages-public?
+          (fn []
+            (let [value (not enable-all-pages-public?)]
+              (config-handler/set-config! :publishing/all-pages-public? value)))))
+
 (defn encryption-row [t enable-encryption?]
   (toggle "enable_encryption"
           (t :settings-page/enable-encryption)
@@ -417,6 +425,7 @@
         current-repo (state/get-current-repo)
         enable-journals? (state/enable-journals? current-repo)
         enable-encryption? (state/enable-encryption? current-repo)
+        enable-all-pages-public? (state/all-pages-public?)
         instrument-disabled? (state/sub :instrument/disabled?)
         logical-outdenting? (state/logical-outdenting?)
         enable-tooltip? (state/enable-tooltip?)
@@ -453,6 +462,7 @@
         (tooltip-row t enable-tooltip?)
         (timetracking-row t enable-timetracking?)
         (journal-row t enable-journals?)
+        (enable-all-pages-public-row t enable-all-pages-public?)
         (encryption-row t enable-encryption?)
         (keyboard-shortcuts-row t)
         (auto-push-row t current-repo enable-git-auto-push?)]

+ 26 - 31
src/main/frontend/components/sidebar.cljs

@@ -1,38 +1,28 @@
 (ns frontend.components.sidebar
-  (:require [rum.core :as rum]
-            [frontend.ui :as ui]
-            [frontend.components.theme :as theme]
-            [frontend.mixins :as mixins]
-            [frontend.db-mixins :as db-mixins]
-            [frontend.db :as db]
-            [frontend.components.widgets :as widgets]
+  (:require [cljs-drag-n-drop.core :as dnd]
+            [clojure.string :as string]
+            [frontend.components.header :as header]
             [frontend.components.journal :as journal]
-            [frontend.components.page :as page]
-            [frontend.components.settings :as settings]
-            [frontend.components.svg :as svg]
             [frontend.components.repo :as repo]
-            [frontend.components.commit :as commit]
-            [frontend.components.header :as header]
             [frontend.components.right-sidebar :as right-sidebar]
-            [frontend.storage :as storage]
-            [frontend.util :as util]
-            [frontend.state :as state]
-            [frontend.handler.ui :as ui-handler]
-            [frontend.handler.user :as user-handler]
+            [frontend.components.settings :as settings]
+            [frontend.components.theme :as theme]
+            [frontend.components.widgets :as widgets]
+            [frontend.config :as config]
+            [frontend.context.i18n :as i18n]
+            [frontend.db :as db]
+            [frontend.db-mixins :as db-mixins]
             [frontend.handler.editor :as editor-handler]
-            [frontend.handler.route :as route-handler]
-            [frontend.handler.export :as export]
             [frontend.handler.repo :as repo-handler]
+            [frontend.handler.route :as route-handler]
             [frontend.handler.web.nfs :as nfs-handler]
-            [frontend.config :as config]
-            [dommy.core :as d]
-            [clojure.string :as string]
-            [goog.object :as gobj]
-            [frontend.context.i18n :as i18n]
-            [reitit.frontend.easy :as rfe]
+            [frontend.mixins :as mixins]
+            [frontend.modules.shortcut.data-helper :as shortcut-dh]
+            [frontend.state :as state]
+            [frontend.ui :as ui]
+            [frontend.util :as util]
             [goog.dom :as gdom]
-            [frontend.handler.web.nfs :as nfs-handler]
-            [cljs-drag-n-drop.core :as dnd]))
+            [rum.core :as rum]))
 
 (defn nav-item
   [title href svg-d active? close-modal-fn]
@@ -268,8 +258,13 @@
     (ui/tippy {:html [:div.p-2
                       [:p.mb-2 [:b "Document mode"]]
                       [:ul
-                       [:li "Shift + Enter to create new block"]
-                       [:li "Click `D` or type `t d` to toggle document mode"]]]}
+                       [:li
+                        [:div.inline-block.mr-1 (ui/keyboard-shortcut (shortcut-dh/gen-shortcut-seq :editor/new-line))]
+                        [:p.inline-block  "to create new block"]]
+                       [:li
+                        [:p.inline-block.mr-1 "Click `D` or type"]
+                        [:div.inline-block.mr-1 (ui/keyboard-shortcut (shortcut-dh/gen-shortcut-seq :ui/toggle-document-mode))]
+                        [:p.inline-block "to toggle document mode"]]]]}
               [:a.block.px-1.text-sm.font-medium.bg-base-2.rounded-md.mx-2
                {:on-click state/toggle-document-mode!}
                "D"])))
@@ -345,8 +340,8 @@
           :close-fn    close-fn
           :route-match route-match})
         [:div.#app-container.h-screen.flex
-         {:class (if (state/sub :ui/sidebar-open?) "w-full" "overflow-hidden")}
-         [:div.flex-1.h-full.w-full.flex.flex-col#left-container.relative
+         [:div.flex-1.h-full.flex.flex-col#left-container.relative
+          {:class (if (state/sub :ui/sidebar-open?) "overflow-hidden" "w-full")}
           [:div.scrollbar-spacing#main-container
            (header/header {:open-fn        open-fn
                            :white?         white?

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

@@ -104,7 +104,7 @@
                             [:a {:href "https://web.dev/file-system-access/"
                                  :target "_blank"}
                              "new native filesystem API"]
-                            [:span ", please use any Chromium 86+ based browser like Chrome, Vivaldi, Edge, etc."]]))]]])]))
+                            [:span ", please use any Chromium 86+ based browser like Chrome, Vivaldi, Edge, etc. Notice that the API doesn't support mobile browsers at the moment."]]))]]])]))
 
 (rum/defcs add-graph <
   [state & {:keys [graph-types]

+ 2 - 0
src/main/frontend/config.cljs

@@ -12,6 +12,8 @@
 (goog-define PUBLISHING false)
 (defonce publishing? PUBLISHING)
 
+(reset! state/publishing? publishing?)
+
 (def test? false)
 
 ;; :TODO: How to do this?

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

@@ -63,7 +63,8 @@
 
  [frontend.db.query-react
   react-query custom-query-result-transform]
- )
+
+ [frontend.db.default built-in-pages-names built-in-pages])
 
 ;; persisting DBs between page reloads
 (defn persist! [repo]

+ 31 - 1
src/main/frontend/db/model.cljs

@@ -1119,6 +1119,31 @@
        db)
       (db-utils/seq-flatten)))
 
+(defn get-public-false-pages
+  [db]
+  (-> (d/q
+        '[:find ?p
+          :where
+          [?p :block/name]
+          [?p :block/properties ?properties]
+          [(get ?properties :public) ?pub]
+          [(= false ?pub)]]
+        db)
+      (db-utils/seq-flatten)))
+
+(defn get-public-false-block-ids
+  [db]
+  (-> (d/q
+        '[:find ?b
+          :where
+          [?p :block/name]
+          [?p :block/properties ?properties]
+          [(get ?properties :public) ?pub]
+          [(= false ?pub)]
+          [?b :block/page ?p]]
+        db)
+      (db-utils/seq-flatten)))
+
 (defn get-all-templates
   []
   (let [pred (fn [db properties]
@@ -1181,10 +1206,15 @@
 (defn clean-export!
   [db]
   (let [remove? #(contains? #{"me" "recent" "file"} %)
+        non-public-pages (get-public-false-pages db)
+        non-public-datoms (get-public-false-block-ids db)
+        non-public-datom-ids (set (concat non-public-pages non-public-datoms))
         filtered-db (d/filter db
                               (fn [db datom]
                                 (let [ns (namespace (:a datom))]
-                                  (not (remove? ns)))))
+                                  (and (not (remove? ns))
+                                       (not (contains? #{:block/file} (:a datom)))
+                                       (not (contains? non-public-datom-ids (:e datom)))))))
         datoms (d/datoms filtered-db :eavt)
         assets (get-assets datoms)]
     [@(d/conn-from-datoms datoms db-schema/schema) assets]))

+ 2 - 0
src/main/frontend/db_schema.cljs

@@ -68,6 +68,8 @@
 
    ;; block key value properties
    :block/properties {}
+   ;; vector
+   :block/properties-order {}
 
    ;; parsed ast
    :block/body {}

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

@@ -213,6 +213,7 @@
         :settings-page/enable-timetracking "Enable timetracking"
         :settings-page/enable-tooltip "Enable tooltips"
         :settings-page/enable-journals "Enable journals"
+        :settings-page/enable-all-pages-public "Enable all pages public when publishing"
         :settings-page/enable-encryption "Enable encryption feature"
         :settings-page/customize-shortcuts "Keyboard shortcuts"
         :settings-page/shortcut-settings "Customize shortcuts"

+ 32 - 36
src/main/frontend/extensions/graph.cljs

@@ -16,8 +16,6 @@
             [frontend.util :as util :refer [profile]]
             [cljs-bean.core :as bean]))
 
-(defonce clicked-page-timestamps (atom nil))
-
 (defn- highlight-node!
   [^js graph node]
   (.resetNodeStyle graph node
@@ -26,49 +24,47 @@
                                         :color "#6366F1"}})))
 
 (defn- highlight-neighbours!
-  [^js graph node]
+  [^js graph node focus-nodes dark?]
   (.forEachNeighbor
    (.-graph graph) node
    (fn [node attributes]
-     (let [attributes (bean/->clj attributes)
-           attributes (assoc attributes
-                             :color "#6366F1"
-                             :border {:width 2
-                                      :color "#6366F1"})]
-       (.resetNodeStyle graph node (bean/->js attributes))))))
+     (when-not (contains? focus-nodes node)
+       (let [attributes (bean/->clj attributes)
+             attributes (assoc attributes
+                               :color "#6366F1"
+                               :border {:width 2
+                                        :color "#6366F1"})]
+         (.resetNodeStyle graph node (bean/->js attributes)))))))
 
 (defn- highlight-edges!
-  [^js graph node]
+  [^js graph node dark?]
   (.forEachEdge
    (.-graph graph) node
    (fn [edge attributes]
      (.resetEdgeStyle graph edge (bean/->js {:width 1
-                                             :color "#A5B4FC"})))))
-
-(defn on-click-handler [graph node event *focus-nodes *n-hops]
-  (let [page-name (string/lower-case node)]
-    (when-not @*n-hops
-      ;; Don't trigger re-render
-      (swap! *focus-nodes
-            (fn [v]
-              (vec (distinct (conj v node))))))
-    ;; highlight current node
-    (let [node-attributes (-> (.getNodeAttributes (.-graph graph) node)
-                              (bean/->clj))]
-      (.setNodeAttribute (.-graph graph) node "parent" "ls-selected-nodes"))
-    (highlight-neighbours! graph node)
-    (highlight-edges! graph node)
+                                             :color (if dark? "#999" "#A5B4FC")})))))
 
-    ;; shift+click to select the page
-    (when-not (gobj/get event "shiftKey")
-      ;; double click to go to the page
-      (let [last-time (get @clicked-page-timestamps page-name)
-            new-time (util/time-ms)]
-        (swap! clicked-page-timestamps assoc page-name new-time)
-        (when (and last-time
-                   (< (- new-time last-time) 300))
-          (route-handler/redirect! {:to :page
-                                    :path-params {:name page-name}}))))))
+(defn on-click-handler [graph node event *focus-nodes *n-hops drag? dark?]
+  ;; shift+click to select the page
+  (if (or (gobj/get event "shiftKey") drag?)
+    (let [page-name (string/lower-case node)]
+      (when-not @*n-hops
+        ;; Don't trigger re-render
+        (swap! *focus-nodes
+               (fn [v]
+                 (vec (distinct (conj v node))))))
+      ;; highlight current node
+      (let [node-attributes (-> (.getNodeAttributes (.-graph graph) node)
+                                (bean/->clj))]
+        (.setNodeAttribute (.-graph graph) node "parent" "ls-selected-nodes"))
+      (highlight-neighbours! graph node (set @*focus-nodes) dark?)
+      (highlight-edges! graph node dark?))
+    (when-not drag?
+      (let [page-name (string/lower-case node)]
+        (.unhoverNode ^js graph node)
+        (route-handler/redirect! {:to :page
+                                  :path-params {:name page-name}})
+        ))))
 
 (defn reset-graph!
   [^js graph]
@@ -80,7 +76,7 @@
    :will-unmount (fn [state]
                    (when-let [graph (:graph state)]
                      (.destroy graph))
-                   (reset! clicked-page-timestamps nil)
+                   (reset! pixi/*graph-instance nil)
                    state)}
   [state opts]
   [:div.graph {:style {:height "100vh"}

+ 0 - 12
src/main/frontend/extensions/graph.css

@@ -7,15 +7,3 @@
     position: relative;
     z-index: 4;
 }
-
-#graphin-container {
-    height: 100%;
-    width: 100%;
-    position: relative;
-}
-.graphin-core {
-    height: 100%;
-    width: 100%;
-    min-height: 500px;
-    background: #fff;
-}

+ 57 - 49
src/main/frontend/extensions/graph/pixi.cljs

@@ -14,10 +14,11 @@
             [clojure.set :as set]
             [cljs-bean.core :as bean]
             ["pixi-graph-fork" :as Pixi-Graph]
+            ["@pixi/utils" :as pixi-utils]
             ["graphology" :as graphology]
             ["d3-force" :refer [forceSimulation forceManyBody forceCenter forceLink forceCollide forceRadial forceX forceY SimulationLinkDatum SimulationNodeDatum] :as force]))
 
-(def graph (gobj/get graphology "Graph"))
+(def Graph (gobj/get graphology "Graph"))
 
 (defonce colors
   ["#1f77b4"
@@ -37,9 +38,12 @@
                   (or (.-size node) 8))
           :border {:width 0}
           :color (fn [node]
-                   (if (gobj/get node "parent")
-                     (let [v (js/Math.abs (hash (.-id node)))]
-                       (nth colors (mod v (count colors))))
+                   (if-let [parent (gobj/get node "parent")]
+                     (when-let [parent (if (= parent "ls-selected-nodes")
+                                         parent
+                                         (.-id node))]
+                       (let [v (js/Math.abs (hash parent))]
+                        (nth colors (mod v (count colors)))))
                      (.-color node)))
           :label {:content (fn [node] (.-id node))
                   :type (.-TEXT (.-TextType Pixi-Graph))
@@ -58,18 +62,6 @@
           :label {:backgroundColor "rgba(238, 238, 238, 1)"}}
    :edge {:color "#A5B4FC"}})
 
-;; TODO: animation
-;; (defn ticked [^js link ^js node]
-;;   (-> link
-;;       (.attr "x1" (fn [d] (.. d -source -x)))
-;;       (.attr "y1" (fn [d] (.. d -source -y)))
-;;       (.attr "x2" (fn [d] (.. d -target -x)))
-;;       (.attr "y2" (fn [d] (.. d -target -y))))
-
-;;   (-> node
-;;       (.attr "cx" (fn [d] (.-x d)))
-;;       (.attr "cy" (fn [d] (.-y d)))))
-
 (defn layout!
   [nodes links]
   (let [simulation (forceSimulation nodes)]
@@ -87,40 +79,56 @@
         (.force "x" (-> (forceX 0) (.strength 0.02)))
         (.force "y" (-> (forceX 0) (.strength 0.02)))
         (.force "center" (forceCenter))
-        (.tick 30)
+        (.tick 3)
         (.stop))))
 
-(defn render!
-  [state]
-  (when-let [graph (:graph state)]
-    (.destroy graph))
-  (let [{:keys [nodes links style hover-style height register-handlers-fn dark?]} (first (:rum/args state))
-        style (or style (default-style dark?))
-        hover-style (or hover-style (default-hover-style dark?))
-        graph (graph.)
-        nodes-set (set (map :id nodes))
-        links (->> (filter (fn [link]
-                             (and (nodes-set (:source link)) (nodes-set (:target link)))) links)
-                   (distinct))
-        nodes-js (bean/->js nodes)
-        links-js (bean/->js links)]
-    (layout! nodes-js links-js)
-    (doseq [node nodes-js]
-      (.addNode graph (.-id node) node))
-    (doseq [link links-js]
-      (.addEdge graph (.-id (.-source link)) (.-id (.-target link)) link))
+(defonce *graph-instance (atom nil))
 
-    (if-let [container-ref (:ref state)]
-      (let [graph (new (.-PixiGraph Pixi-Graph)
-                       (bean/->js
-                        {:container @container-ref
-                         :graph graph
-                         :style style
-                         :hoverStyle hover-style
-                         :height height}))]
-        (when register-handlers-fn
-          (register-handlers-fn graph))
+(defn- clear-nodes!
+  [graph]
+  (.forEachNode graph (fn [node]
+                        (.dropNode graph node))))
 
-        ;; (.addEventListener container-ref)
-        (assoc state :graph graph))
-      state)))
+(defn render!
+  [state]
+  (try
+     (let [old-instance @*graph-instance
+           {:keys [graph pixi]} old-instance]
+       (when (and graph pixi)
+         (clear-nodes! graph))
+       (let [{:keys [nodes links style hover-style height register-handlers-fn dark?]} (first (:rum/args state))
+             style (or style (default-style dark?))
+             hover-style (or hover-style (default-hover-style dark?))
+             graph (or graph (Graph.))
+             nodes-set (set (map :id nodes))
+             links (->> (filter (fn [link]
+                                  (and (nodes-set (:source link)) (nodes-set (:target link)))) links)
+                        (distinct))
+             nodes (remove nil? nodes)
+             links (remove (fn [{:keys [source target]}] (or (nil? source) (nil? target))) links)
+             nodes-js (bean/->js nodes)
+             links-js (bean/->js links)]
+         (layout! nodes-js links-js)
+         (doseq [node nodes-js]
+           (.addNode graph (.-id node) node))
+         (doseq [link links-js]
+           (let [source (.-id (.-source link))
+                 target (.-id (.-target link))]
+             (.addEdge graph source target link)))
+         (if-let [{:keys [pixi]} @*graph-instance]
+           (.resetView pixi)
+           (when-let [container-ref (:ref state)]
+             (let [pixi-graph (new (.-PixiGraph Pixi-Graph)
+                                   (bean/->js
+                                    {:container @container-ref
+                                     :graph graph
+                                     :style style
+                                     :hoverStyle hover-style
+                                     :height height}))]
+               (reset! *graph-instance {:graph graph
+                                        :pixi pixi-graph})
+               (when register-handlers-fn
+                 (register-handlers-fn pixi-graph)))))))
+     (catch js/Error e
+       (js/console.error e)))
+  state)

+ 1 - 1
src/main/frontend/extensions/slide.cljs

@@ -39,7 +39,7 @@
   [:div
    [:p.text-sm
     [:span.opacity-70 "Tip: press "]
-    [:code "F"]
+    [:code "f"]
     [:span.opacity-70 " to go fullscreen"]]
    [:div.reveal {:style style}
     (when loading?

+ 2 - 4
src/main/frontend/format.cljs

@@ -35,10 +35,8 @@
 (defn get-default-config
   ([format]
    (mldoc/default-config format))
-  ([format heading-to-list?]
-   (mldoc/default-config format heading-to-list?))
-  ([format heading-to-list? exporting-keep-properties?]
-   (mldoc/default-config format heading-to-list? exporting-keep-properties?)))
+  ([format options]
+   (mldoc/default-config format options)))
 
 (defn to-html
   ([content format]

+ 49 - 41
src/main/frontend/format/block.cljs

@@ -193,37 +193,40 @@
 ;; TODO: we should move this to mldoc
 (defn extract-properties
   [properties]
-  (let [properties (into {} properties)
-        page-refs (->>
-                   (map (fn [v]
-                          (when (string? v)
-                            (let [result (text/split-page-refs-without-brackets v {:un-brackets? false})]
-                              (if (coll? result)
-                                (map text/page-ref-un-brackets! result)
-                                []))))
-                     (vals properties))
-                   (apply concat)
-                   (remove string/blank?))
-        properties (->> properties
-                        (medley/map-kv (fn [k v]
-                                         (let [k (-> (string/lower-case (name k))
-                                                     (string/replace " " "-")
-                                                     (string/replace "_" "-"))
-                                               k (if (contains? #{"custom_id" "custom-id"} k)
-                                                   "id"
-                                                   k)
-                                               v (if (coll? v)
-                                                   v
-                                                   (property/parse-property k v))
-                                               k (keyword k)
-                                               v (if (and
-                                                      (string? v)
-                                                      (contains? #{:alias :aliases :tags} k))
-                                                   (set [v])
-                                                   v)]
-                                           [k v]))))]
-    {:properties properties
-     :page-refs page-refs}))
+  (when (seq properties)
+    (let [properties (seq properties)
+          properties-order (keys properties)
+          page-refs (->>
+                     (map (fn [v]
+                            (when (string? v)
+                              (let [result (text/split-page-refs-without-brackets v {:un-brackets? false})]
+                                (if (coll? result)
+                                  (map text/page-ref-un-brackets! result)
+                                  []))))
+                       (map last properties))
+                     (apply concat)
+                     (remove string/blank?))
+          properties (->> properties
+                          (map (fn [[k v]]
+                                 (let [k (-> (string/lower-case (name k))
+                                             (string/replace " " "-")
+                                             (string/replace "_" "-"))
+                                       k (if (contains? #{"custom_id" "custom-id"} k)
+                                           "id"
+                                           k)
+                                       v (if (coll? v)
+                                           v
+                                           (property/parse-property k v))
+                                       k (keyword k)
+                                       v (if (and
+                                              (string? v)
+                                              (contains? #{:alias :aliases :tags} k))
+                                           (set [v])
+                                           v)]
+                                   [k v]))))]
+      {:properties (into {} properties)
+       :properties-order (map first properties)
+       :page-refs page-refs})))
 
 (defn- paragraph-timestamp-block?
   [block]
@@ -525,16 +528,21 @@
                           (>= level last-level)
                           [(conj children [id level])
                            #{}])
-
-                        block (-> (assoc block
-                                         :uuid id
-                                         :body (vec
-                                                (->> (reverse block-body)
-                                                     (map #(remove-indentations format (:level block) %))))
-                                         :properties (:properties properties)
-                                         :refs ref-pages-in-properties
-                                         :children (or current-block-children [])
-                                         :format format)
+                        block (cond->
+                                (assoc block
+                                      :uuid id
+                                      :body (vec
+                                             (->> (reverse block-body)
+                                                  (map #(remove-indentations format (:level block) %))))
+                                      :refs ref-pages-in-properties
+                                      :children (or current-block-children [])
+                                      :format format)
+                                (seq (:properties properties))
+                                (assoc :properties (:properties properties))
+
+                                (seq (:properties-order properties))
+                                (assoc :properties-order (:properties-order properties)))
+                        block (-> block
                                   (assoc-in [:meta :start-pos] start_pos)
                                   (assoc-in [:meta :end-pos] last-pos)
                                   ((fn [block]

+ 18 - 24
src/main/frontend/format/mldoc.cljs

@@ -9,7 +9,8 @@
             [goog.object :as gobj]
             [lambdaisland.glogi :as log]
             [medley.core :as medley]
-            ["mldoc" :as mldoc :refer [Mldoc]]))
+            ["mldoc" :as mldoc :refer [Mldoc]]
+            [linked.core :as linked]))
 
 (defonce parseJson (gobj/get Mldoc "parseJson"))
 (defonce parseInlineJson (gobj/get Mldoc "parseInlineJson"))
@@ -22,26 +23,20 @@
 
 (defn default-config
   ([format]
-   (default-config format false))
-  ([format export-heading-to-list?]
+   (default-config format {:export-heading-to-list? false}))
+  ([format {:keys [export-heading-to-list? export-keep-properties? export-md-indent-style]}]
    (let [format (string/capitalize (name (or format :markdown)))]
-     (js/JSON.stringify
-      (bean/->js
-       {:toc false
-        :heading_number false
-        :keep_line_break true
-        :format format
-        :heading_to_list export-heading-to-list?}))))
-  ([format export-heading-to-list? exporting-keep-properties?]
-   (let [format (string/capitalize (name (or format :markdown)))]
-     (js/JSON.stringify
-      (bean/->js
-       {:toc false
-        :heading_number false
-        :keep_line_break true
-        :format format
-        :heading_to_list export-heading-to-list?
-        :exporting_keep_properties exporting-keep-properties?})))))
+     (->> {:toc false
+           :heading_number false
+           :keep_line_break true
+           :format format
+           :heading_to_list (or export-heading-to-list? false)
+           :exporting_keep_properties export-keep-properties?
+           :export_md_indent_style export-md-indent-style}
+          (filter #(not(nil? (second %))))
+          (into {})
+          (bean/->js)
+          (js/JSON.stringify)))))
 
 (def default-references
   (js/JSON.stringify
@@ -145,9 +140,8 @@
                                    v (if (contains? #{:title :description :filters :roam_tags} k)
                                        v
                                        (text/split-page-refs-without-brackets v))]
-                               [k v])))
-                          (reverse)
-                          (into {}))
+                               [k v]))))
+          properties (into (linked/map) properties)
           macro-properties (filter (fn [x] (= :macro (first x))) properties)
           macros (if (seq macro-properties)
                    (->>
@@ -161,7 +155,7 @@
                     (into {}))
                    {})
           properties (->> (remove (fn [x] (= :macro (first x))) properties)
-                          (into {}))
+                          (into (linked/map)))
           properties (if (seq properties)
                        (cond-> properties
                          (:roam_key properties)

+ 34 - 2
src/main/frontend/handler/editor.cljs

@@ -683,6 +683,38 @@
       (when (db/page-empty? (state/get-current-repo) (:db/id page))
         (api-insert-new-block! "" {:page page-name})))))
 
+(defn default-properties-block
+  [title format page]
+  (let [properties (common-handler/get-page-default-properties title)
+        content (property/build-properties-str format properties)]
+    {:block/pre-block? true
+     :block/uuid (db/new-block-id)
+     :block/properties properties
+     :block/left page
+     :block/format format
+     :block/content content
+     :block/parent page
+     :block/unordered true
+     :block/page page}))
+
+(defn add-default-title-property-if-needed!
+  [page-name]
+  (when (string? page-name)
+    (when-let [page (db/entity [:block/name (string/lower-case page-name)])]
+      (when (db/page-empty? (state/get-current-repo) (:db/id page))
+        (let [title (or (:block/original-name page)
+                        (:block/name page))
+              format (get page :block/format :markdown)
+              create-title-property? (util/create-title-property? title)]
+          (when create-title-property?
+            (let [default-properties (default-properties-block title format (:db/id page))
+                  new-empty-block (-> (dissoc default-properties :block/pre-block? :block/uuid :block/left :block/properties)
+                                      (assoc :block/uuid (db/new-block-id)
+                                             :block/content ""
+                                             :block/left [:block/uuid (:block/uuid default-properties)]))]
+              (db/transact! [default-properties new-empty-block])
+              true)))))))
+
 (defn update-timestamps-content!
   [{:block/keys [repeated? marker format] :as block} content]
   (if repeated?
@@ -2924,7 +2956,7 @@
     :else
     ;; expand one level
     (let [blocks-with-level (all-blocks-with-level {})
-          max-level (apply max (map :block/level blocks-with-level))]
+          max-level (or (apply max (map :block/level blocks-with-level)) 99)]
       (loop [level 1]
         (if (> level max-level)
           nil
@@ -2958,7 +2990,7 @@
     ;; collapse by one level from outside
     (let [blocks-with-level
           (all-blocks-with-level {:collapse? true})
-          max-level (apply max (map :block/level blocks-with-level))]
+          max-level (or (apply max (map :block/level blocks-with-level)) 99)]
       (loop [level max-level]
         (if (zero? level)
           nil

+ 39 - 14
src/main/frontend/handler/export.cljs

@@ -34,7 +34,6 @@
    (outliner-tree/blocks->vec-tree
     (db/get-page-blocks-no-cache page) page) {:init-level 1
                                               :heading-to-list? true}))
-
 (defn- get-file-content
   [file-path]
   (if-let [page-name
@@ -68,12 +67,6 @@
    (outliner-tree/blocks->vec-tree (str (:block/uuid block)))
    (outliner-file/tree->file-content {:init-level 1})))
 
-(defn copy-block!
-  [block-id]
-  (when-let [block (db/pull [:block/uuid block-id])]
-    (let [content (:block/content block)]
-      (common-handler/copy-to-clipboard-without-id-property! (:block/format block) content))))
-
 (defn copy-block-as-json!
   [block-id]
   (when-let [repo (state/get-current-repo)]
@@ -196,7 +189,10 @@
                 (if v v
                     (let [[ref-blocks ref-pages]
                           (->> (if is-block?
-                                 [page-or-block]
+                                 (if (or (seq? page-or-block)
+                                         (vector? page-or-block))
+                                   (vec page-or-block)
+                                   [page-or-block])
                                  (db/get-page-blocks-no-cache
                                   repo page-or-block {:pull-keys '[:block/refs]}))
                                (filterv :block/refs)
@@ -242,9 +238,9 @@
       f)))
 
 (defn- get-page&block-refs-by-query
-  [repo page get-page&block-refs-by-query-aux]
+  [repo page-or-block get-page&block-refs-by-query-aux {:keys [is-block?] :or {is-block? false}}]
   (let [[block-ids page-ids]
-        (get-page&block-refs-by-query-aux repo page false #{} #{})
+        (get-page&block-refs-by-query-aux repo page-or-block is-block? #{} #{})
         blocks
         (db/pull-many repo '[*] block-ids)
         pages-name-and-content
@@ -420,14 +416,14 @@
   [repo files heading-to-list?]
   (let [get-page&block-refs-by-query-aux (get-embed-and-refs-blocks-pages-aux)
         f (if (< (count files) 30)      ;query db for per page if (< (count files) 30), or pre-compute whole graph's page&block-refs
-            #(get-page&block-refs-by-query repo % get-page&block-refs-by-query-aux)
+            #(get-page&block-refs-by-query repo % get-page&block-refs-by-query-aux {})
             (let [page&block-refs (page&block-refs repo)]
               #(get-page&block-refs repo % page&block-refs)))]
     (->> files
          (mapv (fn [{:keys [path content names format]}]
                  (when (first names)
                    [path (fp/exportMarkdown f/mldoc-record content
-                                            (f/get-default-config format heading-to-list?)
+                                            (f/get-default-config format {:export-heading-to-list? heading-to-list?})
                                             (js/JSON.stringify
                                              (clj->js (f (first names)))))])))
          (remove nil?))))
@@ -436,7 +432,7 @@
   [repo files]
   (let [get-page&block-refs-by-query-aux (get-embed-and-refs-blocks-pages-aux)
         f (if (< (count files) 30)      ;query db for per page if (< (count files) 30), or pre-compute whole graph's page&block-refs
-            #(get-page&block-refs-by-query repo % get-page&block-refs-by-query-aux)
+            #(get-page&block-refs-by-query repo % get-page&block-refs-by-query-aux {})
             (let [page&block-refs (page&block-refs repo)]
               #(get-page&block-refs repo % page&block-refs)))]
     (->> files
@@ -452,13 +448,42 @@
                                            (clj->js (f (first names)))))]))))
          (remove nil?))))
 
+(defn export-blocks-as-opml
+  [repo root-block-uuid]
+  (let [get-page&block-refs-by-query-aux (get-embed-and-refs-blocks-pages-aux)
+        f #(get-page&block-refs-by-query repo % get-page&block-refs-by-query-aux {:is-block? true})
+        root-block (db/entity [:block/uuid root-block-uuid])
+        blocks (db/get-block-and-children repo root-block-uuid)
+        refs (f blocks)
+        content (get-blocks-contents repo root-block-uuid)
+        format (or (:block/format root-block) (state/get-preferred-format))]
+    (fp/exportOPML f/mldoc-record content
+                   (f/get-default-config format)
+                   "untitled"
+                   (js/JSON.stringify (clj->js refs)))))
+
+(defn export-blocks-as-markdown
+  [repo root-block-uuid indent-style]
+  (let [get-page&block-refs-by-query-aux (get-embed-and-refs-blocks-pages-aux)
+        f #(get-page&block-refs-by-query repo % get-page&block-refs-by-query-aux {:is-block? true})
+        root-block (db/entity [:block/uuid root-block-uuid])
+        blocks (db/get-block-and-children repo root-block-uuid)
+        refs (f blocks)
+        content (get-blocks-contents repo root-block-uuid)
+        format (or (:block/format root-block) (state/get-preferred-format))]
+    (fp/exportMarkdown f/mldoc-record content
+                       (f/get-default-config format {:export-md-indent-style indent-style})
+                       (js/JSON.stringify (clj->js refs)))))
+
+
+
 (defn- convert-md-files-unordered-list-or-heading
   [repo files heading-to-list?]
   (->> files
        (mapv (fn [{:keys [path content names format]}]
                (when (first names)
                  [path (fp/exportMarkdown f/mldoc-record content
-                                          (f/get-default-config format heading-to-list? true)
+                                          (f/get-default-config format {:export-heading-to-list? heading-to-list? :export-keep-properties? true})
                                           nil)])))
        (remove nil?)))
 

+ 4 - 16
src/main/frontend/handler/page.cljs

@@ -50,20 +50,6 @@
   ([page-name] (when-let [page (db/entity [:block/name page-name])]
                  (:file/path (:block/file page)))))
 
-(defn default-properties-block
-  [title format page]
-  (let [properties (common-handler/get-page-default-properties title)
-        content (property/build-properties-str format properties)]
-    {:block/pre-block? true
-     :block/uuid (db/new-block-id)
-     :block/properties properties
-     :block/left page
-     :block/format format
-     :block/content content
-     :block/parent page
-     :block/unordered true
-     :block/page page}))
-
 (defn create!
   ([title]
    (create! title {}))
@@ -85,7 +71,7 @@
                    (let [page-entity [:block/uuid (:block/uuid page)]
                          create-title-property? (util/create-title-property? (:block/name page))]
                      (if create-title-property?
-                       (let [default-properties (default-properties-block (:block/original-name page) format page-entity)]
+                       (let [default-properties (editor-handler/default-properties-block (:block/original-name page) format page-entity)]
                          [page default-properties])
                        [page]))))
                pages)
@@ -468,7 +454,9 @@
   [repo]
   (->> (db/get-modified-pages repo)
        (remove util/file-page?)
-       (remove util/uuid-string?)))
+       (remove util/uuid-string?)
+       (remove (fn [p]
+                 (db/built-in-pages-names (string/upper-case p))))))
 
 (defn get-filters
   [page-name]

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

@@ -91,7 +91,7 @@
                            (string/replace matched link (util/node-path.join url link))
                            matched)))
                       content)]
-        (format/to-html content :markdown (mldoc/default-config :markdown false))))
+        (format/to-html content :markdown (mldoc/default-config :markdown))))
     (catch js/Error e
       (log/error :parse-user-md-exception e)
       content)))

+ 11 - 9
src/main/frontend/modules/outliner/file.cljs

@@ -9,7 +9,8 @@
             [frontend.handler.notification :as notification]
             [goog.object :as gobj]
             [frontend.state :as state]
-            [clojure.string :as string]))
+            [clojure.string :as string]
+            [frontend.config :as config]))
 
 (def write-chan (async/chan))
 
@@ -28,14 +29,15 @@
 
 (defn write-files!
   [page-db-ids]
-  (doseq [page-db-id (set page-db-ids)]
-    (try (do-write-file! page-db-id)
-         (catch js/Error e
-           (notification/show!
-            "Write file failed, please copy the changes to other editors in case of losing data."
-            [:div "Error: " (str (gobj/get e "stack"))]
-            :error)
-           (log/error :file/write-file-error {:error e})))))
+  (when-not config/publishing?
+    (doseq [page-db-id (set page-db-ids)]
+      (try (do-write-file! page-db-id)
+           (catch js/Error e
+             (notification/show!
+              "Write file failed, please copy the changes to other editors in case of losing data."
+              [:div "Error: " (str (gobj/get e "stack"))]
+              :error)
+             (log/error :file/write-file-error {:error e}))))))
 
 (defn sync-to-file
   [{page-db-id :db/id}]

+ 38 - 29
src/main/frontend/modules/shortcut/config.cljs

@@ -15,41 +15,41 @@
 (def default-config
   {:shortcut.handler/date-picker
    {:date-picker/complete
-    {:desc    "Date picker choose selected day"
+    {:desc    "Date picker: Choose selected day"
      :binding "enter"
      :fn      ui-handler/shortcut-complete}
     :date-picker/prev-day
-    {:desc    "Date picker select previous day"
+    {:desc    "Date picker: Select previous day"
      :binding "left"
      :fn      ui-handler/shortcut-prev-day}
     :date-picker/next-day
-    {:desc    "Date picker select next day"
+    {:desc    "Date picker: Select next day"
      :binding "right"
      :fn      ui-handler/shortcut-next-day}
     :date-picker/prev-week
-    {:desc    "Date picker select prev week"
+    {:desc    "Date picker: Select previous week"
      :binding "up"
      :fn      ui-handler/shortcut-prev-week}
     :date-picker/next-week
-    {:desc    "Date picker select next week"
+    {:desc    "Date picker: Select next week"
      :binding "down"
      :fn      ui-handler/shortcut-next-week}}
 
    :shortcut.handler/auto-complete
    {:auto-complete/prev
-    {:desc    "Auto-complete previous selected item"
+    {:desc    "Auto-complete: Choose selected item"
+     :binding "enter"
+     :fn      ui-handler/auto-complete-complete}
+    :auto-complete/shift-complete
+    {:desc    "Auto-complete: Select previous item"
      :binding "up"
      :fn      ui-handler/auto-complete-prev}
     :auto-complete/next
-    {:desc    "Auto-complete next selected item"
+    {:desc    "Auto-complete: Select next item"
      :binding "down"
      :fn      ui-handler/auto-complete-next}
     :auto-complete/complete
-    {:desc    "Auto-complete choose selected item"
-     :binding "enter"
-     :fn      ui-handler/auto-complete-complete}
-    :auto-complete/shift-complete
-    {:desc    "Auto-complete open selected item in sidebar"
+    {:desc    "Auto-complete: Open selected item in sidebar"
      :binding "shift+enter"
      :fn      ui-handler/auto-complete-shift-complete}}
 
@@ -72,7 +72,7 @@
      :binding "enter"
      :fn      editor-handler/keydown-new-block-handler}
     :editor/new-line
-    {:desc    "Newline in block"
+    {:desc    "New line in current block"
      :binding "shift+enter"
      :fn      editor-handler/keydown-new-line-handler}
     :editor/cycle-todo
@@ -104,7 +104,7 @@
      :binding "mod+shift+s"
      :fn      editor-handler/strike-through-format!}
     :editor/insert-link
-    {:desc    "Html Link"
+    {:desc    "HTML Link"
      :binding "mod+k"
      :fn      editor-handler/html-link-format!}
     :editor/move-block-up
@@ -116,39 +116,39 @@
      :binding (if mac? "mod+shift+down" "alt+shift+down")
      :fn      (editor-handler/move-up-down false)}
     :editor/clear-block
-    {:desc    "Clear entire block content"
+    {:desc    "Delete entire block content"
      :binding (if mac? "ctrl+l" "alt+l")
      :fn      editor-handler/clear-block-content!}
     :editor/kill-line-before
-    {:desc    "Kill line before cursor position"
+    {:desc    "Delete line before cursor position"
      :binding (if mac? "ctrl+u" "alt+u")
      :fn      editor-handler/kill-line-before!}
     :editor/kill-line-after
-    {:desc    "Kill line after cursor position"
+    {:desc    "Delete line after cursor position"
      :binding (if mac? false "alt+k")
      :fn      editor-handler/kill-line-after!}
     :editor/beginning-of-block
-    {:desc    "Move cursor to the beginning of block"
+    {:desc    "Move cursor to the beginning of a block"
      :binding (if mac? false "alt+a")
      :fn      editor-handler/beginning-of-block}
     :editor/end-of-block
-    {:desc    "Move cursor to the end of block"
+    {:desc    "Move cursor to the end of a block"
      :binding (if mac? false "alt+e")
      :fn      editor-handler/end-of-block}
     :editor/forward-word
-    {:desc    "Move cursor forward by word"
+    {:desc    "Move cursor forward a word"
      :binding (if mac? "ctrl+shift+f" "alt+f")
      :fn      editor-handler/cursor-forward-word}
     :editor/backward-word
-    {:desc    "Move cursor backward by word"
+    {:desc    "Move cursor backward a word"
      :binding (if mac? "ctrl+shift+b" "alt+b")
      :fn      editor-handler/cursor-backward-word}
     :editor/forward-kill-word
-    {:desc    "Kill a word forwards"
+    {:desc    "Delete a word forwards"
      :binding (if mac? "ctrl+w" "alt+d")
      :fn      editor-handler/forward-kill-word}
     :editor/backward-kill-word
-    {:desc    "Kill a word backwards"
+    {:desc    "Delete a word backwards"
      :binding (if mac? false "alt+w")
      :fn      editor-handler/backward-kill-word}
     :editor/replace-block-reference-at-point
@@ -241,11 +241,11 @@
      :binding "mod+shift+a"
      :fn      editor-handler/select-all-blocks!}
     :editor/zoom-in
-    {:desc    "Zoom in when editing / Forward"
+    {:desc    "Zoom in editing block / Forwards otherwise"
      :binding (if mac? "mod+." "alt+right")
      :fn      editor-handler/zoom-in!}
     :editor/zoom-out
-    {:desc    "Zoom out when editing / Back"
+    {:desc    "Zoom out editing block / Backwards otherwise"
      :binding (if mac? "mod+," "alt+left")
      :fn      editor-handler/zoom-out!}
     :ui/toggle-brackets
@@ -264,6 +264,14 @@
     {:desc    "Jump to journals"
      :binding (if mac? "mod+j" "alt+j")
      :fn      route-handler/go-to-journals!}
+    :go/backward
+    {:desc    "Backwards"
+     :binding "mod+open-square-bracket"
+     :fn      (fn [_] (js/window.history.back))}
+    :go/forward
+    {:desc    "Forwards"
+     :binding "mod+close-square-bracket"
+     :fn      (fn [_] (js/window.history.forward))}
     :search/re-index
     {:desc    "Rebuild search index"
      :binding "mod+c mod+s"
@@ -302,7 +310,7 @@
      :binding "t t"
      :fn      state/toggle-theme!}
     :ui/toggle-contents
-    {:desc    "Toggle Contents in sidebar"
+    {:desc    "Toggle Favorites in sidebar"
      :binding "t c"
      :fn      ui-handler/toggle-contents!}
     :ui/toggle-wide-mode
@@ -310,7 +318,7 @@
      :binding "t w"
      :fn      ui-handler/toggle-wide-mode!}
     :editor/toggle-open-blocks
-    {:desc    "Toggle open blocks, either collapse or expand all blocks"
+    {:desc    "Toggle open blocks (collapse or expand all blocks)"
      :binding "t o"
      :fn      editor-handler/toggle-open!}
     ;; :ui/toggle-between-page-and-file route-handler/toggle-between-page-and-file!
@@ -353,7 +361,9 @@
     :editor/zoom-out
     :editor/collapse-block-children
     :editor/expand-block-children
-    :editor/toggle-open-blocks]
+    :editor/toggle-open-blocks
+    :go/backward
+    :go/forward]
 
    :shortcut.category/block-editing
    ^{:doc "Block editing general"}
@@ -398,7 +408,6 @@
    :shortcut.category/toggle
    ^{:doc "Toggle"}
    [:ui/toggle-help
-    :ui/toggle-new-block
     :editor/toggle-open-blocks
     :ui/toggle-wide-mode
     :ui/toggle-document-mode

+ 0 - 1
src/main/frontend/modules/shortcut/core.cljs

@@ -47,7 +47,6 @@
             (log/debug :shortcut/register-shortcut {:id id :binding k})
             (.registerShortcut handler (util/keyname id) k)
             (catch js/Object e
-              (def e e)
               (log/error :shortcut/register-shortcut {:id id
                                                       :binding k
                                                       :error e})

+ 11 - 0
src/main/frontend/modules/shortcut/data_helper.cljs

@@ -92,8 +92,19 @@
       (str/replace "mod" (if util/mac? "cmd" "ctrl"))
       (str/replace "alt" (if util/mac? "opt" "alt"))
       (str/replace "shift+/" "?")
+      (str/replace "open-square-bracket" "[")
+      (str/replace "close-square-bracket" "]")
       (str/lower-case)))
 
+;; if multiple bindings, gen seq for first binding only for now
+(defn gen-shortcut-seq [id]
+  (let [bindings (shortcut-binding id)]
+    (if (false? bindings)
+      []
+      (-> bindings
+          first
+          (str/split  #" |\+")))))
+
 (defn binding-for-display [k binding]
   (let [tmp (cond
               (false? binding)

+ 2 - 0
src/main/frontend/modules/shortcut/dict.cljs

@@ -26,6 +26,8 @@
      :shortcut.ui/toggle-help                 "显示/关闭帮助"
      :shortcut.git/commit                     "提交消息"
      :shortcut.go/search                      "全文搜索"
+     :shortcut.go/backward                    "回退"
+     :shortcut.go/forward                     "前进"
      :shortcut.go/search-in-page              "在当前页面搜索"
      :shortcut.ui/toggle-document-mode        "切换文档模式"
      :shortcut.ui/toggle-contents             "打开/关闭目录"

+ 3 - 1
src/main/frontend/publishing.cljs

@@ -12,7 +12,8 @@
             [reitit.frontend.easy :as rfe]
             [cljs.reader :as reader]
             [frontend.components.page :as component-page]
-            [frontend.components.editor :as component-editor]))
+            [frontend.components.editor :as component-editor]
+            [frontend.modules.shortcut.core :as shortcut]))
 
 ;; The publishing site should be as thin as possible.
 ;; Both files and git libraries can be removed.
@@ -70,6 +71,7 @@
   (register-components-fns!)
   (restore-from-transit-str!)
   (restore-state!)
+  (shortcut/refresh!)
   (start))
 
 (defn stop []

+ 6 - 6
src/main/frontend/publishing/html.cljs

@@ -7,7 +7,7 @@
 (defn publishing-html
   [transit-db app-state]
   (let [{:keys [icon name alias title description url]} (:project (state/get-config))
-        icon (or icon "/static/img/logo.png")
+        icon (or icon "static/img/logo.png")
         project (or alias name)]
     (str "<!DOCTYPE html>\n"
          (hiccups.core/html
@@ -17,8 +17,8 @@
             {:content
              "minimum-scale=1, initial-scale=1, width=device-width, shrink-to-fit=no",
              :name "viewport"}]
-           [:link {:type "text/css", :href "/static/css/style.css", :rel "stylesheet"}]
-           [:link {:type "text/css", :href "/static/css/custom.css", :rel "stylesheet"}]
+           [:link {:type "text/css", :href "static/css/style.css", :rel "stylesheet"}]
+           [:link {:type "text/css", :href "static/css/custom.css", :rel "stylesheet"}]
            [:link
             {:href icon
              :type "image/png",
@@ -83,6 +83,6 @@
         }
       }(window.location))"]
             ;; TODO: should make this configurable
-           [:script {:src "/static/js/highlight.min.js"}]
-           [:script {:src "/static/js/interact.min.js"}]
-           [:script {:src "/static/js/main.js"}]]))))
+           [:script {:src "static/js/highlight.min.js"}]
+           [:script {:src "static/js/interact.min.js"}]
+           [:script {:src "static/js/main.js"}]]))))

+ 13 - 2
src/main/frontend/state.cljs

@@ -217,7 +217,9 @@
 
 (defn all-pages-public?
   []
-  (true? (:all-pages-public? (get-config))))
+  (let [value (:publishing/all-pages-public? (get-config))
+        value (if (some? value) value (:all-pages-public? (get-config)))]
+    (true? value)))
 
 (defn enable-grammarly?
   []
@@ -738,11 +740,20 @@
        :container (gobj/get container "id")
        :pos (cursor/pos (gdom/getElement edit-input-id))})))
 
+(defonce publishing? (atom nil))
+
+(defn publishing-enable-editing?
+  []
+  (and @publishing? (:publishing/enable-editing? (get-config))))
+
 (defn set-editing!
   ([edit-input-id content block cursor-range]
    (set-editing! edit-input-id content block cursor-range true))
   ([edit-input-id content block cursor-range move-cursor?]
-   (when (and edit-input-id block)
+   (when (and edit-input-id block
+              (or
+               (publishing-enable-editing?)
+               (not @publishing?)))
      (let [block-element (gdom/getElement (string/replace edit-input-id "edit-block" "ls-block"))
            container (util/get-block-container block-element)
            block (if container

+ 26 - 8
src/main/frontend/text.cljs

@@ -82,18 +82,32 @@
                :else
                [(conj acc s) not-matched-s])) [[] nil] coll)))
 
+(defn- sep-by-quotes
+  [s]
+  (string/split s #"(\"[^\"]*\")"))
+
+(defn- surrounded-by-quotes
+  [s]
+  (and (string? s)
+       (= (first s) (last s) \")))
+
 (defn split-page-refs-without-brackets
   ([s]
    (split-page-refs-without-brackets s {}))
   ([s {:keys [un-brackets?]
        :or {un-brackets? true}}]
-   (cond
-     (and (string? s)
+   (if (and (string? s)
             ;; Either a page ref, a tag or a comma separated collection
             (or (util/safe-re-find page-ref-re s)
-                (util/safe-re-find #"[\,|,|#]+" s)))
-     (let [result (->> (string/split s page-ref-re-2)
-                       (mapcat (fn [s] (if (string/includes? (string/trimr s) "]],")
+                (util/safe-re-find #"[\,|,|#|\"]+" s)))
+     (let [result (->> (sep-by-quotes s)
+                       (mapcat
+                        (fn [s]
+                          (if (surrounded-by-quotes s)
+                            [s]
+                            (string/split s page-ref-re-2))))
+                       (mapcat (fn [s] (if (and (string/includes? (string/trimr s) "]],")
+                                               (not (surrounded-by-quotes s)))
                                         (let [idx (string/index-of s "]],")]
                                           [(subs s 0 idx)
                                            "]]"
@@ -107,8 +121,14 @@
                        concat-nested-pages
                        (remove string/blank?)
                        (mapcat (fn [s]
-                                 (if (page-ref? s)
+                                 (cond
+                                   (surrounded-by-quotes s)
+                                   [(subs s 1 (dec (count s)))]
+
+                                   (page-ref? s)
                                    [(if un-brackets? (page-ref-un-brackets! s) s)]
+
+                                   :else
                                    (sep-by-comma s))))
                        (distinct))]
        (if (or (coll? result)
@@ -118,8 +138,6 @@
                result (map (fn [s] (string/replace s #"^#+" "")) result)]
            (set result))
          (first result)))
-
-     :else
      s)))
 
 (defn extract-level-spaces

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

@@ -416,8 +416,7 @@
            (if (or (= :meta key) (= "meta" key))
              (util/meta-key-name)
              (name key))])
-        sequence)]
-  )
+        sequence)])
 
 (defonce modal-show? (atom false))
 (rum/defc modal-overlay

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

@@ -1,3 +1,3 @@
 (ns frontend.version)
 
-(defonce version "0.2.2-2")
+(defonce version "0.2.3-1")

+ 11 - 0
src/main/logseq/api.cljs

@@ -376,6 +376,17 @@
 
 (def ^:export custom_query db/custom-query)
 
+(defn ^:export download_graph_db
+  []
+  (when-let [repo (state/get-current-repo)]
+    (when-let [db (db/get-conn repo)]
+      (let [db-str (if db (db/db->string db) "")
+            data-str (str "data:text/edn;charset=utf-8," (js/encodeURIComponent db-str))]
+        (when-let [anchor (gdom/getElement "download")]
+          (.setAttribute anchor "href" data-str)
+          (.setAttribute anchor "download" (str (string/replace repo "/" " ") ".transit"))
+          (.click anchor))))))
+
 ;; helpers
 (defn ^:export show_msg
   ([content] (show_msg content :success))

+ 1 - 1
src/test/frontend/format/block_test.cljs

@@ -25,7 +25,7 @@
 
   (are [x y] (= (vec (:page-refs (block/extract-properties x))) y)
     [["year" "1000"]] []
-    [["year" "\"1000\""]] []
+    [["year" "\"1000\""]] ["1000"]
     [["foo" "[[bar]] test"]] ["bar" "test"]
     [["foo" "[[bar]] test [[baz]]"]] ["bar" "test" "baz"]
     [["foo" "[[bar]] test [[baz]] [[nested [[baz]]]]"]] ["bar" "test" "baz" "nested [[baz]]"]

+ 1 - 1
templates/config.edn

@@ -40,7 +40,7 @@
  ;; When :all-pages-public? true, export repo would export all pages within that repo.
  ;; Regardless of whether you've set any page to public or not.
  ;; Example:
- ;; :all-pages-public? true
+ ;; :publishing/all-pages-public? true
 
  ;; Specify default home page and sidebar status for Logseq
  ;; If not specified, Logseq default opens journals page on startup

+ 8 - 10
yarn.lock

@@ -6049,10 +6049,10 @@ mkdirp@^0.5.0, mkdirp@^0.5.4, mkdirp@~0.5.1:
   dependencies:
     minimist "^1.2.5"
 
[email protected].4:
-  version "0.8.4"
-  resolved "https://registry.yarnpkg.com/mldoc/-/mldoc-0.8.4.tgz#264e95abfdbd66f36b8f55555df2d1b9caba9415"
-  integrity sha512-fNJLQPiOAmaz5t6VM6d8AvpwBs39sj21tIGLx782UBejnKLq1o5pGA3htI2nmY4ifvgwJDygxIf2mSkGZE44sg==
[email protected].8:
+  version "0.8.8"
+  resolved "https://registry.yarnpkg.com/mldoc/-/mldoc-0.8.8.tgz#4076f00712108caba3fbf4e5df4e2945b407d2c7"
+  integrity sha512-bulOsjdZS6jFVsgK2lwqZMNd5+liHlwFb2xwrovMdxnTFQ5F150k+RZF32RRZx6z62Y43RJUb+2vLGpXJK0qZQ==
   dependencies:
     yargs "^12.0.2"
 
@@ -6775,10 +6775,10 @@ pinkie@^2.0.0:
   resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz"
   integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA=
 
-pixi-graph-fork@^0.0.9:
-  version "0.0.9"
-  resolved "https://registry.yarnpkg.com/pixi-graph-fork/-/pixi-graph-fork-0.0.9.tgz#1eed19fb216da2ba618e7deaefef4194571a1e1b"
-  integrity sha512-N9j7BjCShk+3/beCT0ug70IVdzXQnXDvhGPAk3L1hFhcVnW/pyTpnXDGAIXpjFHZwU+Y/YrjMFFeRa1Hj3OcIA==
+pixi-graph-fork@^0.1.3:
+  version "0.1.3"
+  resolved "https://registry.yarnpkg.com/pixi-graph-fork/-/pixi-graph-fork-0.1.3.tgz#fc7beee363ba3ec71b35df30fce1d5b2ddf27732"
+  integrity sha512-NHmlG0FI2/xme/p9KXOPLbfrQ0UIcCq4GK36IsSYPGJznhUoJTlNbfT4ML7I3DL84vQx0TliGPAA+EmoVz6CDg==
   dependencies:
     "@pixi-essentials/cull" "^1.1.0"
     "@pixi/app" "^6.0.2"
@@ -7491,8 +7491,6 @@ react-icons@^2.2.7:
   version "2.2.7"
   resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-2.2.7.tgz#d7860826b258557510dac10680abea5ca23cf650"
   integrity sha512-0n4lcGqzJFcIQLoQytLdJCE0DKSA9dkwEZRYoGrIDJZFvIT6Hbajx5mv9geqhqFiNjUgtxg8kPyDfjlhymbGFg==
-  dependencies:
-    react-icon-base "2.1.0"
 
 react-is@^16.3.1, react-is@^16.8.1:
   version "16.13.1"