Sfoglia il codice sorgente

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

Charlie 4 anni fa
parent
commit
52b54a34af
44 ha cambiato i file con 704 aggiunte e 778 eliminazioni
  1. 1 1
      README.md
  2. 2 1
      deps.edn
  3. 1 1
      package.json
  4. 168 0
      resources/css/codemirror.solarized.css
  5. 1 1
      resources/package.json
  6. 21 25
      src/main/frontend/components/block.cljs
  7. 4 0
      src/main/frontend/components/block.css
  8. 1 2
      src/main/frontend/components/editor.cljs
  9. 1 1
      src/main/frontend/components/header.cljs
  10. 2 35
      src/main/frontend/components/journal.cljs
  11. 160 165
      src/main/frontend/components/page.cljs
  12. 1 1
      src/main/frontend/components/reference.cljs
  13. 1 7
      src/main/frontend/components/right_sidebar.cljs
  14. 0 5
      src/main/frontend/components/sidebar.css
  15. 2 2
      src/main/frontend/components/widgets.cljs
  16. 2 2
      src/main/frontend/db.cljs
  17. 2 1
      src/main/frontend/db/default.cljs
  18. 0 4
      src/main/frontend/db/migrate.cljs
  19. 13 41
      src/main/frontend/db/model.cljs
  20. 1 1
      src/main/frontend/db/outliner.cljs
  21. 1 0
      src/main/frontend/db/query_dsl.cljs
  22. 1 6
      src/main/frontend/db/utils.cljs
  23. 55 0
      src/main/frontend/extensions/calc.cljc
  24. 3 1
      src/main/frontend/extensions/code.cljs
  25. 6 286
      src/main/frontend/extensions/code.css
  26. 0 33
      src/main/frontend/handler/block.cljs
  27. 1 1
      src/main/frontend/handler/draw.cljs
  28. 71 92
      src/main/frontend/handler/editor.cljs
  29. 1 1
      src/main/frontend/handler/editor/keyboards.cljs
  30. 4 4
      src/main/frontend/handler/editor/lifecycle.cljs
  31. 2 2
      src/main/frontend/handler/extract.cljs
  32. 2 1
      src/main/frontend/handler/migrate.cljs
  33. 12 17
      src/main/frontend/handler/page.cljs
  34. 10 2
      src/main/frontend/modules/datascript_report/core.cljs
  35. 16 14
      src/main/frontend/modules/file/core.cljs
  36. 1 1
      src/main/frontend/modules/outliner/core.cljs
  37. 13 8
      src/main/frontend/modules/outliner/datascript.cljc
  38. 0 4
      src/main/frontend/routes.cljs
  39. 1 1
      src/main/frontend/version.cljs
  40. 24 0
      src/main/grammar/calc.bnf
  41. 4 4
      src/test/frontend/db/query_dsl_test.cljs
  42. 87 0
      src/test/frontend/extensions/calc_test.cljc
  43. 1 0
      tailwind.all.css
  44. 4 4
      yarn.lock

+ 1 - 1
README.md

@@ -67,7 +67,7 @@ Logseq is also made possible by the following projects:
 - Our blog: https://logseq.com/blog - Please be sure to visit our [About page](https://logseq.com/blog/about) for the latest updates of the app
 - Twitter: https://twitter.com/logseq
 - Discord: https://discord.gg/KpN4eHY - Where we answer questions, discuss workflows and share tips
-- 中文 Discord:https://discord.gg/yhNKeUdj
+- 中文 Discord:https://discord.gg/xYqcrXWymg
 - Github: https://github.com/logseq/logseq - everyone is encouraged to report issues!
 
 ---

+ 2 - 1
deps.edn

@@ -35,7 +35,8 @@
   expound/expound             {:mvn/version "0.8.6"}
   com.lambdaisland/glogi      {:mvn/version "1.0.116"}
   binaryage/devtools          {:mvn/version "1.0.2"}
-  camel-snake-kebab/camel-snake-kebab {:mvn/version "0.4.2"}}
+  camel-snake-kebab/camel-snake-kebab {:mvn/version "0.4.2"}
+  instaparse/instaparse       {:mvn/version "1.4.10"}}
 
  :aliases {:cljs {:extra-paths ["src/dev-cljs/" "src/test/" "src/electron/"]
                   :extra-deps  {org.clojure/clojurescript   {:mvn/version "1.10.844"}

+ 1 - 1
package.json

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

+ 168 - 0
resources/css/codemirror.solarized.css

@@ -0,0 +1,168 @@
+/*
+Solarized theme for code-mirror
+http://ethanschoonover.com/solarized
+*/
+
+/*
+Solarized color palette
+http://ethanschoonover.com/solarized/img/solarized-palette.png
+*/
+
+.solarized.base03 { color: #002b36; }
+.solarized.base02 { color: #073642; }
+.solarized.base01 { color: #586e75; }
+.solarized.base00 { color: #657b83; }
+.solarized.base0 { color: #839496; }
+.solarized.base1 { color: #93a1a1; }
+.solarized.base2 { color: #eee8d5; }
+.solarized.base3  { color: #fdf6e3; }
+.solarized.solar-yellow  { color: #b58900; }
+.solarized.solar-orange  { color: #cb4b16; }
+.solarized.solar-red { color: #dc322f; }
+.solarized.solar-magenta { color: #d33682; }
+.solarized.solar-violet  { color: #6c71c4; }
+.solarized.solar-blue { color: #268bd2; }
+.solarized.solar-cyan { color: #2aa198; }
+.solarized.solar-green { color: #859900; }
+
+/* Color scheme for code-mirror */
+
+.cm-s-solarized {
+  line-height: 1.45em;
+  color-profile: sRGB;
+  rendering-intent: auto;
+}
+.cm-s-solarized.cm-s-dark {
+  color: #839496;
+  background-color: #002b36;
+  text-shadow: #002b36 0 1px;
+}
+.cm-s-solarized.cm-s-light {
+  background-color: #fdf6e3;
+  color: #657b83;
+  text-shadow: #eee8d5 0 1px;
+}
+
+.cm-s-solarized .CodeMirror-widget {
+  text-shadow: none;
+}
+
+.cm-s-solarized .cm-header { color: #586e75; }
+.cm-s-solarized .cm-quote { color: #93a1a1; }
+
+.cm-s-solarized .cm-keyword { color: #cb4b16; }
+.cm-s-solarized .cm-atom { color: #d33682; }
+.cm-s-solarized .cm-number { color: #d33682; }
+.cm-s-solarized .cm-def { color: #2aa198; }
+
+.cm-s-solarized .cm-variable { color: #839496; }
+.cm-s-solarized .cm-variable-2 { color: #b58900; }
+.cm-s-solarized .cm-variable-3, .cm-s-solarized .cm-type { color: #6c71c4; }
+
+.cm-s-solarized .cm-property { color: #2aa198; }
+.cm-s-solarized .cm-operator { color: #6c71c4; }
+
+.cm-s-solarized .cm-comment { color: #586e75; font-style:italic; }
+
+.cm-s-solarized .cm-string { color: #859900; }
+.cm-s-solarized .cm-string-2 { color: #b58900; }
+
+.cm-s-solarized .cm-meta { color: #859900; }
+.cm-s-solarized .cm-qualifier { color: #b58900; }
+.cm-s-solarized .cm-builtin { color: #d33682; }
+.cm-s-solarized .cm-bracket { color: #cb4b16; }
+.cm-s-solarized .CodeMirror-matchingbracket { color: #859900; }
+.cm-s-solarized .CodeMirror-nonmatchingbracket { color: #dc322f; }
+.cm-s-solarized .cm-tag { color: #93a1a1; }
+.cm-s-solarized .cm-attribute { color: #2aa198; }
+.cm-s-solarized .cm-hr {
+  color: transparent;
+  border-top: 1px solid #586e75;
+  display: block;
+}
+.cm-s-solarized .cm-link { color: #93a1a1; cursor: pointer; }
+.cm-s-solarized .cm-special { color: #6c71c4; }
+.cm-s-solarized .cm-em {
+  color: #999;
+  text-decoration: underline;
+  text-decoration-style: dotted;
+}
+.cm-s-solarized .cm-error,
+.cm-s-solarized .cm-invalidchar {
+  color: #586e75;
+  border-bottom: 1px dotted #dc322f;
+}
+
+.cm-s-solarized.cm-s-dark div.CodeMirror-selected { background: #073642; }
+.cm-s-solarized.cm-s-dark.CodeMirror ::selection { background: rgba(7, 54, 66, 0.99); }
+.cm-s-solarized.cm-s-dark .CodeMirror-line::-moz-selection, .cm-s-dark .CodeMirror-line > span::-moz-selection, .cm-s-dark .CodeMirror-line > span > span::-moz-selection { background: rgba(7, 54, 66, 0.99); }
+
+.cm-s-solarized.cm-s-light div.CodeMirror-selected { background: #eee8d5; }
+.cm-s-solarized.cm-s-light .CodeMirror-line::selection, .cm-s-light .CodeMirror-line > span::selection, .cm-s-light .CodeMirror-line > span > span::selection { background: #eee8d5; }
+.cm-s-solarized.cm-s-light .CodeMirror-line::-moz-selection, .cm-s-light .CodeMirror-line > span::-moz-selection, .cm-s-light .CodeMirror-line > span > span::-moz-selection { background: #eee8d5; }
+
+/* Editor styling */
+
+
+
+/* Little shadow on the view-port of the buffer view */
+.cm-s-solarized.CodeMirror {
+  -moz-box-shadow: inset 7px 0 12px -6px #000;
+  -webkit-box-shadow: inset 7px 0 12px -6px #000;
+  box-shadow: inset 7px 0 12px -6px #000;
+}
+
+/* Remove gutter border */
+.cm-s-solarized .CodeMirror-gutters {
+  border-right: 0;
+}
+
+/* Gutter colors and line number styling based of color scheme (dark / light) */
+
+/* Dark */
+.cm-s-solarized.cm-s-dark .CodeMirror-gutters {
+  background-color: #073642;
+}
+
+.cm-s-solarized.cm-s-dark .CodeMirror-linenumber {
+  color: #586e75;
+  text-shadow: #021014 0 -1px;
+}
+
+/* Light */
+.cm-s-solarized.cm-s-light .CodeMirror-gutters {
+  background-color: #eee8d5;
+}
+
+.cm-s-solarized.cm-s-light .CodeMirror-linenumber {
+  color: #839496;
+}
+
+/* Common */
+.cm-s-solarized .CodeMirror-linenumber {
+  padding: 0 5px;
+}
+.cm-s-solarized .CodeMirror-guttermarker-subtle { color: #586e75; }
+.cm-s-solarized.cm-s-dark .CodeMirror-guttermarker { color: #ddd; }
+.cm-s-solarized.cm-s-light .CodeMirror-guttermarker { color: #cb4b16; }
+
+.cm-s-solarized .CodeMirror-gutter .CodeMirror-gutter-text {
+  color: #586e75;
+}
+
+/* Cursor */
+.cm-s-solarized .CodeMirror-cursor { border-left: 1px solid #819090; }
+
+/* Fat cursor */
+.cm-s-solarized.cm-s-light.cm-fat-cursor .CodeMirror-cursor { background: #77ee77; }
+.cm-s-solarized.cm-s-light .cm-animate-fat-cursor { background-color: #77ee77; }
+.cm-s-solarized.cm-s-dark.cm-fat-cursor .CodeMirror-cursor { background: #586e75; }
+.cm-s-solarized.cm-s-dark .cm-animate-fat-cursor { background-color: #586e75; }
+
+/* Active line */
+.cm-s-solarized.cm-s-dark .CodeMirror-activeline-background {
+  background: rgba(255, 255, 255, 0.06);
+}
+.cm-s-solarized.cm-s-light .CodeMirror-activeline-background {
+  background: rgba(0, 0, 0, 0.06);
+}

+ 1 - 1
resources/package.json

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

+ 21 - 25
src/main/frontend/components/block.cljs

@@ -376,7 +376,7 @@
                                (:block/alias? config)
                                page
 
-                               (db/page-empty? (state/get-current-repo) page)
+                               (db/page-empty? (state/get-current-repo) (:db/id page-entity))
                                (let [source-page (model/get-alias-source-page (state/get-current-repo)
                                                                               (string/lower-case page-name))]
                                  (or (when source-page (:block/name source-page))
@@ -1073,7 +1073,7 @@
                 (:block/uuid child)))))]))))
 
 (rum/defcs block-control < rum/reactive
-  [state config block uuid block-id body children dummy? collapsed? *ref-collapsed? *control-show?]
+  [state config block uuid block-id body children collapsed? *ref-collapsed? *control-show?]
   (let [has-child? (and
                     (not (:pre-block? block))
                     (or (and (coll? children) (seq children))
@@ -1108,9 +1108,8 @@
 
         :else
         [:span ""])]
-     [:a (if (not dummy?)
-           {:href (rfe/href :page {:name uuid})
-            :on-click (fn [e] (bullet-on-click e block config uuid))})
+     [:a {:href (rfe/href :page {:name uuid})
+          :on-click (fn [e] (bullet-on-click e block config uuid))}
       [:span.bullet-container.cursor
        {:id (str "dot-" uuid)
         :draggable true
@@ -1210,10 +1209,12 @@
 
 (rum/defc set-priority
   [block priority]
-  [:ul
-   (for [p (remove #(= priority %) ["A" "B" "C"])]
-     [:a.mr-2.text-base.tooltip-priority {:priority p
-                                          :on-click (fn [] (editor-handler/set-priority block p))}])])
+  [:div
+   (let [priorities (sort (remove #(= priority %) ["A" "B" "C"]))]
+     (for [p priorities]
+       [:a.mr-2.text-base.tooltip-priority {:key (str (random-uuid))
+                                            :priority p
+                                            :on-click (fn [] (editor-handler/set-priority block p))}]))])
 
 (rum/defc priority-text
   [priority]
@@ -1250,7 +1251,7 @@
 (declare block-content)
 
 (defn build-block-title
-  [{:keys [slide?] :as config} {:block/keys [uuid title tags marker priority anchor meta format content pre-block? dummy? block-refs-count page properties unordered level heading-level]
+  [{:keys [slide?] :as config} {:block/keys [uuid title tags marker priority anchor meta format content pre-block? block-refs-count page properties unordered level heading-level]
                                 :as t}]
   (let [config (assoc config :block t)
         slide? (boolean (:slide? config))
@@ -1457,8 +1458,7 @@
 (defn- block-content-on-drop
   [event block uuid]
   (util/stop event)
-  (when (and (not (dnd-same-block? uuid))
-             (not (:block/dummy? block)))
+  (when (not (dnd-same-block? uuid))
     (dnd/move-block @*dragging-block
                     block
                     false
@@ -1468,7 +1468,7 @@
   (editor-handler/unhighlight-blocks!))
 
 (rum/defc block-content < rum/reactive
-  [config {:block/keys [uuid title body meta content marker dummy? 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)
         block-ref-with-title? (and (:block-ref? config) (seq title))
         dragging? (rum/react *dragging?)
@@ -1501,7 +1501,7 @@
         :else
         nil)
 
-      (when (and dragging? (not slide?) (not dummy?))
+      (when (and dragging? (not slide?))
         (dnd-separator block false true))
 
       (when deadline
@@ -1528,17 +1528,17 @@
                  (str uuid "-" idx)))))])]]]))
 
 (rum/defc block-content-or-editor < rum/reactive
-  [config {:block/keys [uuid title body meta content dummy? page format repo children marker properties block-refs-count pre-block? idx] :as block} edit-input-id block-id slide? heading-level]
+  [config {:block/keys [uuid title body meta content page format repo children marker properties block-refs-count pre-block? idx] :as block} edit-input-id block-id slide? heading-level]
   (let [editor-box (get config :editor-box)
         edit? (state/sub [:editor/editing? edit-input-id])
-        editor-id (str "editor-" edit-input-id)]
+        editor-id (str "editor-" edit-input-id)
+        slide? (:slide? config)]
     (if (and edit? editor-box)
       [:div.editor-wrapper {:id editor-id}
        (editor-box {:block block
                     :block-id uuid
                     :block-parent-id block-id
                     :format format
-                    :dummy? dummy?
                     :heading-level heading-level
                     :on-hide (fn [value event]
                                (when (= event :esc)
@@ -1546,7 +1546,7 @@
                    edit-input-id
                    config)]
       [:div.flex.flex-row.block-content-wrapper
-       [:div.flex.flex-1
+       [:div.flex-1 {:style {:display (if (:slide? config) "block" "flex")}}
         (block-content config block edit-input-id block-id slide?)]
        [:div.flex.flex-row
         (when (and (:embed? config)
@@ -1592,7 +1592,6 @@
   (let [dragging? (rum/react *dragging?)]
     (when (and dragging?
                (not slide?)
-               (not (:block/dummy? block))
                (not (:block/pre-block? block)))
       (dnd-separator block top? false))))
 
@@ -1750,7 +1749,7 @@
    :should-update (fn [old-state new-state]
                     (not= (:block/content (second (:rum/args old-state)))
                           (:block/content (second (:rum/args new-state)))))}
-  [state config {:block/keys [uuid title body meta content dummy? page format repo children pre-block? top? properties refs path-refs heading-level level type] :as block}]
+  [state config {:block/keys [uuid title body meta content page format repo children pre-block? top? properties refs path-refs heading-level level type] :as block}]
   (let [blocks-container-id (:blocks-container-id config)
         heading? (and (= type :heading) heading-level (<= heading-level 6))
         *control-show? (get state ::control-show?)
@@ -1779,7 +1778,6 @@
        :data-refs-self data-refs-self
        :style {:position "relative"}
        :class (str uuid
-                   (when dummy? " dummy")
                    (when (and collapsed? has-child?) " collapsed")
                    (when pre-block? " pre-block"))
        :blockid (str uuid)
@@ -1800,7 +1798,7 @@
 
      [:div.flex.flex-row {:class (if heading? "items-center" "")}
       (when (not slide?)
-        (block-control config block uuid block-id body children dummy? collapsed? *ref-collapsed? *control-show?))
+        (block-control config block uuid block-id body children collapsed? *ref-collapsed? *control-show?))
 
       (let [edit-input-id (str "edit-block-" blocks-container-id "-" uuid)]
         (block-content-or-editor config block edit-input-id block-id slide? heading-level))]
@@ -2331,9 +2329,7 @@
              first-id (:block/uuid (first blocks))]
          (for [[idx item] (medley/indexed blocks)]
            (let [item (->
-                       (if (:block/dummy? item)
-                         item
-                         (dissoc item :block/meta))
+                       (dissoc item :block/meta)
                        (assoc :block/top? (zero? idx)))
                  config (assoc config :block/uuid (:block/uuid item))]
              (rum/with-key

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

@@ -365,3 +365,7 @@ a:hover > .bullet-container .bullet {
 .embed-header {
   font-weight: 600;
 }
+
+a.filter svg {
+    transform: scale(0.9);
+}

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

@@ -389,8 +389,7 @@
   (mixins/event-mixin setup-key-listener!)
   (shortcut/mixin :shortcut.handler/block-editing-only)
   lifecycle/lifecycle
-  [state {:keys [on-hide dummy? node format block block-parent-id heading-level]
-          :or   {dummy? false}
+  [state {:keys [on-hide node format block block-parent-id heading-level]
           :as   option} id config]
   (let [content (state/get-edit-content)
         heading-level (get state ::heading-level)]

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

@@ -102,7 +102,7 @@
           :options {:href (rfe/href :all-pages)}
           :icon svg/pages-sm})
 
-       (when current-repo
+       (when (and current-repo (not config/publishing?))
          {:title (t :all-files)
           :options {:href (rfe/href :all-files)}
           :icon svg/folder-sm})

+ 2 - 35
src/main/frontend/components/journal.cljs

@@ -25,44 +25,11 @@
             [frontend.handler.block :as block-handler]
             [frontend.text :as text]))
 
-(rum/defc blocks-inner < rum/static
-  {:did-mount (fn [state]
-                (let [[blocks page] (:rum/args state)
-                      first-title (second (first (:block/title (first blocks))))
-                      journal? (and (string? first-title)
-                                    (date/valid-journal-title? first-title))]
-                  (when (and journal?
-                             (= (string/lower-case first-title) (string/lower-case page)))
-                    (notification/show!
-                     [:div
-                      [:p
-                       (util/format
-                        "It seems that you have multiple journals for the same day \"%s\"."
-                        first-title)]
-                      (ui/button "Go to files"
-                                 :href "/all-files"
-                                 :on-click notification/clear!)]
-                     :error
-                     false)))
-                state)}
-  [blocks page document-mode?]
-  (let [config {:id page
-                :editor-box editor/box
-                :document/mode? document-mode?}]
-    (content/content
-     page
-     {:hiccup (block/->hiccup blocks config {})})))
-
 (rum/defc blocks-cp < rum/reactive db-mixins/query
   {}
   [repo page format]
-  (let [raw-blocks (db/get-page-blocks repo page)
-        document-mode? (state/sub :document/mode?)
-        blocks (->>
-                (block-handler/with-dummy-block raw-blocks format nil {:journal? true
-                                                                       :page-name page})
-                (db/with-block-refs-count repo))]
-    (blocks-inner blocks page document-mode?)))
+  (when-let [page-e (db/pull [:block/name (string/lower-case page)])]
+    (page/page-blocks-cp repo page-e {})))
 
 (rum/defc journal-cp < rum/reactive
   [[title format]]

+ 160 - 165
src/main/frontend/components/page.cljs

@@ -61,7 +61,8 @@
   [state]
   (let [blocks (nth (:rum/args state) 1)
         block (first blocks)]
-    (when (:block/dummy? block)
+    (when (and (= (count blocks) 1)
+               (string/blank? (:block/content block)))
       (editor-handler/edit-block! block :max (:block/format block) (:block/uuid block))))
   state)
 
@@ -99,17 +100,34 @@
           block? (util/uuid-string? page-name)
           block-id (and block? (uuid page-name))
           raw-page-blocks (get-blocks repo page-name page-original-name block? block-id)
-          page-blocks (block-handler/with-dummy-block raw-page-blocks format
-                        (if (empty? raw-page-blocks)
-                          {:block/page {:db/id (:db/id page-e)}
-                           :block/file {:db/id (:db/id (:block/file page-e))}})
-                        {:journal? journal?
-                         :page-name page-name})
+          page-e (if (and page-e (:db/id page-e))
+                   {:db/id (:db/id page-e)}
+                   page-e)
+          page-blocks (cond
+                        (seq raw-page-blocks)
+                        raw-page-blocks
+
+                        page-e
+                        (let [empty-block {:block/uuid (db/new-block-id)
+                                           :block/left page-e
+                                           :block/format format
+                                           :block/content ""
+                                           :block/parent page-e
+                                           :block/unordered true
+                                           :block/page page-e}]
+                          (when (db/page-empty? repo (:db/id page-e))
+                            (db/transact! [empty-block]))
+                          [empty-block])
+
+                        :else
+                        nil)
+          document-mode? (state/sub :document/mode?)
           hiccup-config (merge
                          {:id (if block? (str block-id) page-name)
                           :block? block?
                           :editor-box editor/box
-                          :page page}
+                          :page page
+                          :document/mode? document-mode?}
                          config)
           hiccup-config (common-handler/config-with-document-mode hiccup-config)
           hiccup (block/->hiccup page-blocks hiccup-config {})]
@@ -248,149 +266,151 @@
               page (if block?
                      (->> (:db/id (:block/page (db/entity repo [:block/uuid block-id])))
                           (db/entity repo))
-                     (db/entity repo [:block/name page-name]))
-             ;; TODO: replace page with frontend.format.block/page->map
-             page (if page page (do
-                                  (db/transact! repo [{:block/name page-name
-                                                       :block/original-name path-page-name
-                                                       :block/uuid (db/new-block-id)}])
-                                  (db/entity repo [:block/name page-name])))
-             {:keys [title] :as properties} (:block/properties page)
-             page-name (:block/name page)
-             page-original-name (:block/original-name page)
-             title (or title page-original-name page-name)
-             today? (and
-                     journal?
-                     (= page-name (string/lower-case (date/journal-name))))
-             developer-mode? (state/sub [:ui/developer-mode?])
-             public? (true? (:public properties))]
-         [:div.flex-1.page.relative (if (seq (:block/tags page))
-                                      (let [page-names (model/get-page-names-by-ids (map :db/id (:block/tags page)))]
-                                        {:data-page-tags (text/build-data-value page-names)})
-                                      {})
-          [:div.relative
-           (when (and (not sidebar?)
-                      (not block?))
-             [:div.flex.flex-row.space-between
-              [:div.flex-1.flex-row
-               [:a {:on-click (fn [e]
-                                (.preventDefault e)
-                                (when (gobj/get e "shiftKey")
-                                  (when-let [page (db/pull repo '[*] [:block/name page-name])]
-                                    (state/sidebar-add-block!
-                                     repo
-                                     (:db/id page)
-                                     :page
-                                     {:page page}))))}
-                [:h1.title {:style {:margin-left -2}}
-                 (if page-original-name
-                   (if (and (string/includes? page-original-name "[[")
-                            (string/includes? page-original-name "]]"))
-                     (let [ast (mldoc/->edn page-original-name (mldoc/default-config format))]
-                       (block/markup-element-cp {} (ffirst ast)))
-                     page-original-name)
-                   (or
-                    page-name
-                    path-page-name))]]]
-              (when (not config/publishing?)
-                (let [contents? (= (string/lower-case (str page-name)) "contents")
-                      links (fn [] (->>
-                                   [(when-not contents?
-                                      {:title   (t :page/add-to-contents)
-                                       :options {:on-click (fn [] (page-handler/handle-add-page-to-contents! page-original-name))}})
-
-                                    {:title "Go to presentation mode"
-                                     :options {:on-click (fn []
-                                                           (state/sidebar-add-block!
-                                                            repo
-                                                            (:db/id page)
-                                                            :page-presentation
-                                                            {:page page}))}}
-                                    (when-not contents?
-                                      {:title   (t :page/rename)
-                                       :options {:on-click #(state/set-modal! (rename-page-dialog title page-name))}})
-
-                                    (when-let [file-path (and (util/electron?) (page-handler/get-page-file-path))]
-                                      [{:title   (t :page/open-in-finder)
-                                        :options {:on-click #(js/window.apis.showItemInFolder file-path)}}
-                                       {:title   (t :page/open-with-default-app)
-                                        :options {:on-click #(js/window.apis.openPath file-path)}}])
-
-                                    (when-not contents?
-                                      {:title   (t :page/delete)
-                                       :options {:on-click #(state/set-modal! (delete-page-dialog page-name))}})
-
-                                    (when (state/get-current-page)
-                                      {:title   (t :export)
-                                       :options {:on-click #(state/set-modal! export/export-page)}})
-
-                                    (when (util/electron?)
-                                      {:title   (t (if public? :page/make-private :page/make-public))
-                                       :options {:on-click
-                                                 (fn []
-                                                   (page-handler/update-public-attribute!
-                                                    page-name
-                                                    (if public? false true))
-                                                   (state/close-modal!))}})
-
-                                    (when developer-mode?
-                                      {:title   "(Dev) Show page data"
-                                       :options {:on-click (fn []
-                                                             (let [page-data (with-out-str (pprint/pprint (db/pull (:db/id page))))]
-                                                               (println page-data)
-                                                               (notification/show!
-                                                                [:div
-                                                                 [:pre.code page-data]
-                                                                 [:br]
-                                                                 (ui/button "Copy to clipboard"
-                                                                   :on-click #(.writeText js/navigator.clipboard page-data))]
-                                                                :success
-                                                                false)))}})]
-                                   (flatten)
-                                   (remove nil?)))]
-                  [:div.flex.flex-row
-                   [:a.opacity-30.hover:opacity-100.page-op.mr-1
-                    {:title "Search in current page"
-                     :on-click #(route-handler/go-to-search! :page)}
-                    svg/search]
-                   (ui/dropdown-with-links
-                    (fn [{:keys [toggle-fn]}]
-                      [:a.cp__vertial-menu-button
-                       {:title    "More options"
-                        :on-click toggle-fn}
-                       (svg/vertical-dots nil)])
-                    links
-                    {:modal-class (util/hiccup->class
-                                   "origin-top-right.absolute.right-0.top-10.mt-2.rounded-md.shadow-lg.whitespace-no-wrap.dropdown-overflow-auto.page-drop-options")
-                     :z-index     1})]))])
-           [:div
-            (when (and block? (not sidebar?))
-              (let [config {:id "block-parent"
-                            :block? true}]
-                [:div.mb-4
-                 (block/block-parents config repo block-id format)]))
-
-            ;; blocks
-            (let [page (if block?
-                         (db/entity repo [:block/uuid block-id])
-                         page)]
-              (page-blocks-cp repo page {:sidebar? sidebar?}))]]
+                     (do
+                       (when-not (db/entity repo [:block/name page-name])
+                         (db/transact! repo [{:block/name page-name
+                                              :block/original-name path-page-name
+                                              :block/uuid (db/new-block-id)}]))
+                       (db/pull [:block/name page-name])))
+              _ (when (and (not block?) (db/page-empty? (state/get-current-repo) (:db/id page)))
+                  (page-handler/create! page-name {:page-map page
+                                                   :redirect? false}))
+              {:keys [title] :as properties} (:block/properties page)
+              page-name (:block/name page)
+              page-original-name (:block/original-name page)
+              title (or title page-original-name page-name)
+              today? (and
+                      journal?
+                      (= page-name (string/lower-case (date/journal-name))))
+              developer-mode? (state/sub [:ui/developer-mode?])
+              public? (true? (:public properties))]
+          [:div.flex-1.page.relative (if (seq (:block/tags page))
+                                       (let [page-names (model/get-page-names-by-ids (map :db/id (:block/tags page)))]
+                                         {:data-page-tags (text/build-data-value page-names)})
+                                       {})
+           [:div.relative
+            (when (and (not sidebar?)
+                       (not block?))
+              [:div.flex.flex-row.space-between
+               [:div.flex-1.flex-row
+                [:a {:on-click (fn [e]
+                                 (.preventDefault e)
+                                 (when (gobj/get e "shiftKey")
+                                   (when-let [page (db/pull repo '[*] [:block/name page-name])]
+                                     (state/sidebar-add-block!
+                                      repo
+                                      (:db/id page)
+                                      :page
+                                      {:page page}))))}
+                 [:h1.title {:style {:margin-left -2}}
+                  (if page-original-name
+                    (if (and (string/includes? page-original-name "[[")
+                             (string/includes? page-original-name "]]"))
+                      (let [ast (mldoc/->edn page-original-name (mldoc/default-config format))]
+                        (block/markup-element-cp {} (ffirst ast)))
+                      page-original-name)
+                    (or
+                     page-name
+                     path-page-name))]]]
+               (when (not config/publishing?)
+                 (let [contents? (= (string/lower-case (str page-name)) "contents")
+                       links (fn [] (->>
+                                    [(when-not contents?
+                                       {:title   (t :page/add-to-contents)
+                                        :options {:on-click (fn [] (page-handler/handle-add-page-to-contents! page-original-name))}})
+
+                                     {:title "Go to presentation mode"
+                                      :options {:on-click (fn []
+                                                            (state/sidebar-add-block!
+                                                             repo
+                                                             (:db/id page)
+                                                             :page-presentation
+                                                             {:page page}))}}
+                                     (when-not contents?
+                                       {:title   (t :page/rename)
+                                        :options {:on-click #(state/set-modal! (rename-page-dialog title page-name))}})
+
+                                     (when-let [file-path (and (util/electron?) (page-handler/get-page-file-path))]
+                                       [{:title   (t :page/open-in-finder)
+                                         :options {:on-click #(js/window.apis.showItemInFolder file-path)}}
+                                        {:title   (t :page/open-with-default-app)
+                                         :options {:on-click #(js/window.apis.openPath file-path)}}])
+
+                                     (when-not contents?
+                                       {:title   (t :page/delete)
+                                        :options {:on-click #(state/set-modal! (delete-page-dialog page-name))}})
+
+                                     (when (state/get-current-page)
+                                       {:title   (t :export)
+                                        :options {:on-click #(state/set-modal! export/export-page)}})
+
+                                     (when (util/electron?)
+                                       {:title   (t (if public? :page/make-private :page/make-public))
+                                        :options {:on-click
+                                                  (fn []
+                                                    (page-handler/update-public-attribute!
+                                                     page-name
+                                                     (if public? false true))
+                                                    (state/close-modal!))}})
+
+                                     (when developer-mode?
+                                       {:title   "(Dev) Show page data"
+                                        :options {:on-click (fn []
+                                                              (let [page-data (with-out-str (pprint/pprint (db/pull (:db/id page))))]
+                                                                (println page-data)
+                                                                (notification/show!
+                                                                 [:div
+                                                                  [:pre.code page-data]
+                                                                  [:br]
+                                                                  (ui/button "Copy to clipboard"
+                                                                    :on-click #(.writeText js/navigator.clipboard page-data))]
+                                                                 :success
+                                                                 false)))}})]
+                                    (flatten)
+                                    (remove nil?)))]
+                   [:div.flex.flex-row
+                    [:a.opacity-30.hover:opacity-100.page-op.mr-1
+                     {:title "Search in current page"
+                      :on-click #(route-handler/go-to-search! :page)}
+                     svg/search]
+                    (ui/dropdown-with-links
+                     (fn [{:keys [toggle-fn]}]
+                       [:a.cp__vertial-menu-button
+                        {:title    "More options"
+                         :on-click toggle-fn}
+                        (svg/vertical-dots nil)])
+                     links
+                     {:modal-class (util/hiccup->class
+                                    "origin-top-right.absolute.right-0.top-10.mt-2.rounded-md.shadow-lg.whitespace-no-wrap.dropdown-overflow-auto.page-drop-options")
+                      :z-index     1})]))])
+            [:div
+             (when (and block? (not sidebar?))
+               (let [config {:id "block-parent"
+                             :block? true}]
+                 [:div.mb-4
+                  (block/block-parents config repo block-id format)]))
+
+             ;; blocks
+             (let [page (if block?
+                          (db/entity repo [:block/uuid block-id])
+                          page)]
+               (page-blocks-cp repo page {:sidebar? sidebar?}))]]
 
            (when-not block?
              (today-queries repo today? sidebar?))
 
            (tagged-pages repo page-name)
 
-          ;; referenced blocks
+           ;; referenced blocks
            [:div {:key "page-references"}
             (rum/with-key
               (reference/references route-page-name false)
               (str route-page-name "-refs"))]
 
-          ;; TODO: or we can lazy load them
-          (when-not sidebar?
-            [:div {:key "page-unlinked-references"}
-             (reference/unlinked-references route-page-name)])])))))
+           ;; TODO: or we can lazy load them
+           (when-not sidebar?
+             [:div {:key "page-unlinked-references"}
+              (reference/unlinked-references route-page-name)])])))))
 
 (defonce layout (atom [js/window.outerWidth js/window.outerHeight]))
 
@@ -465,28 +485,3 @@
                       page]]
                 [:td [:span.text-gray-500.text-sm
                       (t :file/no-data)]]])]]))])))
-
-(rum/defcs new < rum/reactive
-  (rum/local "" ::title)
-  (mixins/event-mixin
-   (fn [state]
-     (mixins/on-enter state
-                      :node (gdom/getElement "page-title")
-                      :on-enter (fn []
-                                  (let [title @(get state ::title)]
-                                    (when-not (string/blank? title)
-                                      (page-handler/create! title)))))))
-  [state]
-  (rum/with-context [[t] i18n/*tongue-context*]
-    (let [title (get state ::title)]
-      [:div#page-new.flex-1.flex-col {:style {:flex-wrap "wrap"}}
-       [:div.mt-10.mb-2 {:style {:font-size "1.5rem"}}
-        (t :page/new-title)]
-       [:input#page-title.focus:outline-none.ml-1
-        {:style {:border "none"
-                 :font-size "1.8rem"
-                 :max-width 300}
-         :auto-focus true
-         :auto-complete "off"
-         :on-change (fn [e]
-                      (reset! title (util/evalue e)))}]])))

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

@@ -112,7 +112,7 @@
               [:h2.font-bold.opacity-50 (let []
                                           (str n-ref " Linked Reference"
                                                (if (> n-ref 1) "s")))]
-              [:a.opacity-50.hover:opacity-100
+              [:a.opacity-50.hover:opacity-100.filter
                {:title "Filter"
                 :on-click #(state/set-modal! (filter-dialog filters-atom references page-name))}
                (svg/filter-icon (cond

+ 1 - 7
src/main/frontend/components/right_sidebar.cljs

@@ -95,13 +95,7 @@
   [repo idx db-id block-type block-data t]
   (case block-type
     :contents
-    [[:a {:on-click (fn [e]
-                      (util/stop e)
-                      (if-not (db/entity [:block/name "contents"])
-                        (page-handler/create! "contents")
-                        (route-handler/redirect! {:to          :page
-                                                  :path-params {:name "contents"}})))}
-      (t :right-side-bar/contents)]
+    [(t :right-side-bar/contents)
      (contents)]
 
     :recent

+ 0 - 5
src/main/frontend/components/sidebar.css

@@ -167,10 +167,6 @@
     background: var(--ls-right-sidebar-code-bg-color);
   }
 
-  pre.CodeMirror-line {
-    background: #fff;
-  }
-
   .references {
     margin-left: 12px;
   }
@@ -210,4 +206,3 @@
     overflow-y: overlay;
   }
 }
-

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

@@ -91,8 +91,8 @@
            {:on-click page-handler/ls-dir-files!})
          [:div
           [:h1.title "Open a local directory"]
-          [:p "Logseq supports both Markdown and Org-mode, you can open an existing directory or creating a new one. Your data will be stored only on this device."]
-          [:p "After you opened your directory, it will create three sub-directories in that directory:"]
+          [:p "Logseq supports both Markdown and Org-mode. You can open an existing directory or create a new one on your device, a directory is also known simply as a folder. Your data will be stored only on this device."]
+          [:p "After you have opened your directory, it will create three folders in that directory:"]
           [:ul
            [:li "/journals - store your journal pages"]
            [:li "/pages - store the other pages"]

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

@@ -42,9 +42,9 @@
   get-block-parent get-block-parents parents-collapsed? get-block-referenced-blocks
   get-block-children-ids get-block-immediate-children get-block-page
   get-blocks-contents get-custom-css
-  get-date-scheduled-or-deadlines get-db-type get-empty-pages get-file
+  get-date-scheduled-or-deadlines get-db-type get-file
   get-file-blocks get-file-contents get-file-last-modified-at get-file-no-sub get-file-page get-file-page-id file-exists?
-  get-file-pages get-files get-files-blocks get-files-full get-files-that-referenced-page get-journals-length
+  get-file-pages get-files get-files-blocks get-files-full get-journals-length
   get-latest-journals get-matched-blocks get-page get-page-alias get-page-alias-names get-page-blocks get-page-linked-refs-refed-pages
   get-page-blocks-count get-page-blocks-no-cache get-page-file get-page-format get-page-properties
   get-page-referenced-blocks get-page-referenced-pages get-page-unlinked-references get-page-referenced-blocks-no-cache

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

@@ -5,5 +5,6 @@
   (mapv (fn [p]
           {:block/name (string/lower-case p)
            :block/original-name p
-           :block/journal? false})
+           :block/journal? false
+           :block/uuid (random-uuid)})
         #{"NOW" "LATER" "DOING" "DONE" "IN-PROGRESS" "TODO" "WAIT" "WAITING" "A" "B" "C"}))

+ 0 - 4
src/main/frontend/db/migrate.cljs

@@ -4,8 +4,6 @@
             [frontend.db-schema :as db-schema]
             [frontend.state :as state]))
 
-(defonce debug-db (atom nil))
-
 (defn- migrate-attribute
   [f]
   (if (and (keyword? f) (= "page" (namespace f)))
@@ -27,7 +25,5 @@
 
 (defn migrate
   [repo db]
-  (prn "Migrate DB")
-  (reset! debug-db db)
   (state/pub-event! [:graph/migrated repo])
   (with-schema db db-schema/schema))

+ 13 - 41
src/main/frontend/db/model.cljs

@@ -452,6 +452,10 @@
   (when-let [db (conn/get-conn repo)]
     (count (d/datoms db :avet :block/page page-id))))
 
+(defn page-empty?
+  [repo page-id]
+  (zero? (get-page-blocks-count repo page-id)))
+
 (defn get-block-parent
   ([block-id]
    (get-block-parent (state/get-current-repo) block-id))
@@ -765,23 +769,6 @@
      (distinct))))
 
 ;; Ignore files with empty blocks for now
-(defn get-empty-pages
-  [repo]
-  (when-let [conn (conn/get-conn repo)]
-    (->
-     (d/q
-      '[:find ?page
-        :where
-        [?p :block/name ?page]
-        (not [?p :block/file])]
-      conn)
-     (db-utils/seq-flatten)
-     (distinct))))
-
-(defn page-empty?
-  [repo page]
-  (nil? (:block/file (db-utils/entity repo [:block/name (string/lower-case page)]))))
-
 (defn get-pages-relation
   [repo with-journal?]
   (when-let [conn (conn/get-conn repo)]
@@ -950,22 +937,6 @@
                (sort-by-left-recursive)
                db-utils/group-by-page))))))
 
-(defn get-files-that-referenced-page
-  [page-id]
-  (when-let [repo (state/get-current-repo)]
-    (when-let [db (conn/get-conn repo)]
-      (->> (d/q
-            '[:find ?path
-              :in $ ?page-id
-              :where
-              [?block :block/refs ?page-id]
-              [?block :block/page ?p]
-              [?p :block/file ?f]
-              [?f :file/path ?path]]
-            db
-            page-id)
-           (db-utils/seq-flatten)))))
-
 (defn get-page-unlinked-references
   [page]
   (when-let [repo (state/get-current-repo)]
@@ -1151,14 +1122,15 @@
             filtered-db (d/filter db
                                   (fn [db datom]
                                     (let [ns (namespace (:a datom))]
-                                      (or
-                                       (not (exported-namespace? ns))
-                                       ;; (and (= ns "page")
-                                       ;;      (contains? public-pages (:e datom)))
-                                       (and (= ns "block")
-                                            (or
-                                             (contains? public-pages (:e datom))
-                                             (contains? public-pages (:db/id (:block/page (d/entity db (:e datom)))))))))))
+                                      (and
+                                       (not (contains? #{:block/file} (:a datom)))
+                                       (not= ns "file")
+                                       (or
+                                        (not (exported-namespace? ns))
+                                        (and (= ns "block")
+                                             (or
+                                              (contains? public-pages (:e datom))
+                                              (contains? public-pages (:db/id (:block/page (d/entity db (:e datom))))))))))))
             datoms (d/datoms filtered-db :eavt)
             assets (get-assets datoms)]
         [@(d/conn-from-datoms datoms db-schema/schema) assets]))))

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

@@ -32,7 +32,7 @@
 
 (defn save-block
   [conn block-m]
-  (let [tx (-> (dissoc block-m :block/children :block/dummy? :block/level :block/meta)
+  (let [tx (-> (dissoc block-m :block/children :block/level :block/meta)
              (util/remove-nils))
         block-id (:block/uuid block-m)]
     (d/transact! conn [tx])

+ 1 - 0
src/main/frontend/db/query_dsl.cljs

@@ -17,6 +17,7 @@
             [frontend.template :as template]
             [frontend.util.property :as property]))
 
+
 ;; Query fields:
 
 ;; and

+ 1 - 6
src/main/frontend/db/utils.cljs

@@ -42,11 +42,6 @@
              (group-by :block/page))
     blocks))
 
-(defn group-by-file
-  [blocks]
-  (some->> blocks
-           (group-by :block/file)))
-
 (defn get-tx-id [tx-report]
   (get-in tx-report [:tempids :db/current-tx]))
 
@@ -83,7 +78,7 @@
        (d/pull conn
                selector
                eid)
-       (catch js/Error e
+       (catch js/Error _e
          nil)))))
 
 (defn pull-many

+ 55 - 0
src/main/frontend/extensions/calc.cljc

@@ -0,0 +1,55 @@
+(ns frontend.extensions.calc
+  (:refer-clojure :exclude [eval])
+  (:require #?(:clj [clojure.java.io :as io])
+            #?(:cljs [shadow.resource :as rc])
+            [clojure.string :as str]
+            [clojure.edn :as edn]
+            [clojure.test :as test :refer [deftest testing is are]]
+            [frontend.util :as util]
+            #?(:clj [instaparse.core :as insta]
+               :cljs [instaparse.core :as insta :refer-macros [defparser]])))
+
+#?(:clj (def parse (insta/parser (io/resource "grammar/calc.bnf")))
+   :cljs (defparser parse (rc/inline "grammar/calc.bnf")))
+
+(defn new-env [] (atom {}))
+
+(defn eval
+  ([ast] (eval (new-env) ast))
+  ([env ast]
+   (doall
+    (insta/transform
+     {:number     edn/read-string
+      :expr       identity
+      :add        +
+      :sub        -
+      :mul        *
+      :div        /
+      :pow        (fn [a b]
+                    #?(:clj (java.lang.Math/pow a b) :cljs (js/Math.pow a b)))
+      :log        (fn [a]
+                    #?(:clj (java.lang.Math/log10 a) :cljs (js/Math.log10 a)))
+      :ln         (fn [a]
+                    #?(:clj (java.lang.Math/log a) :cljs (js/Math.log a)))
+      :sin        (fn [a]
+                    #?(:clj (java.lang.Math/sin a) :cljs (js/Math.sin a)))
+      :cos        (fn [a]
+                    #?(:clj (java.lang.Math/cos a) :cljs (js/Math.cos a)))
+      :tan        (fn [a]
+                    #?(:clj (java.lang.Math/tan a) :cljs (js/Math.tan a)))
+      :atan       (fn [a]
+                    #?(:clj (java.lang.Math/atan a) :cljs (js/Math.atan a)))
+      :asin       (fn [a]
+                    #?(:clj (java.lang.Math/asin a) :cljs (js/Math.asin a)))
+      :acos       (fn [a]
+                    #?(:clj (java.lang.Math/acos a) :cljs (js/Math.acos a)))
+      :assignment (fn [var val]
+                    (swap! env assoc var val)
+                    val)
+      :toassign   str/trim
+      :variable   (fn [var]
+                    (let [var (str/trim var)]
+                      (or (get @env var)
+                          (throw (ex-info (util/format "Can't find variable %s" var)
+                                          {:var var})))))}
+     ast))))

+ 3 - 1
src/main/frontend/extensions/code.cljs

@@ -107,7 +107,8 @@
 (defn render!
   [state]
   (let [editor-atom (:editor-atom state)
-        esc-pressed? (atom nil)]
+        esc-pressed? (atom nil)
+        dark? (state/dark?)]
     (if @editor-atom
       (let [editor @editor-atom
             doc (.getDoc editor)
@@ -126,6 +127,7 @@
                     (when textarea
                       (from-textarea textarea
                                      #js {:mode mode
+                                          :theme (if dark? "solarized dark" "solarized")
                                           :matchBrackets lisp?
                                           :autoCloseBrackets true
                                           :lineNumbers true

+ 6 - 286
src/main/frontend/extensions/code.css

@@ -3,19 +3,19 @@
   z-index: 0;
 
   &-lang {
-    @apply absolute top-0 right-0 p-1 text-sm;
+    @apply absolute right-0 p-1 text-sm;
+    top: 3px;
     z-index: 1;
-    background: rgba(255, 255, 255, .6);
+    background: var(--ls-secondary-background-color);
   }
 
   > .CodeMirror {
     z-index: 0;
     height: auto;
-    padding: 4px 0;
     margin-top: 4px;
     margin-bottom: 6px;
     font-family: Fira Code, Monaco, Menlo, Consolas, 'COURIER NEW', monospace;
-    max-width: 86vw;
+    max-width: var(--ls-main-content-max-width);
     border-radius: 2px;
     line-height: 1.45em;
 
@@ -28,289 +28,9 @@
     pre.CodeMirror-line {
       box-shadow: none !important;
     }
-  }
-}
-
-html[data-theme='dark'] {
-
-  .CodeMirror {
-    color: #839496;
-    background-color: var(--ls-secondary-background-color);
-
-    .CodeMirror-widget {
-      text-shadow: none;
-    }
-
-    .cm-keyword {
-      color: #cb4b16
-    }
-
-    .cm-atom {
-      color: #d33682;
-    }
-
-    .cm-number {
-      color: #d33682;
-    }
-
-    .cm-def {
-      color: #2aa198;
-    }
-
-    .cm-variable {
-      color: #268bd2;
-    }
-
-    .cm-variable-2 {
-      color: #b58900;
-    }
-
-    .cm-variable-3 {
-      color: #6c71c4;
-    }
-
-    .cm-property {
-      color: #2aa198;
-    }
-
-    .cm-operator {
-      color: #6c71c4;
-    }
-
-    .cm-comment {
-      color: #586e75;
-      font-style: italic;
-    }
-
-    .cm-string {
-      color: #859900;
-    }
-
-    .cm-string-2 {
-      color: #b58900;
-    }
-
-    .cm-meta {
-      color: #859900;
-    }
-
-    .cm-qualifier {
-      color: #b58900;
-    }
-
-    .cm-builtin {
-      color: #d33682;
-    }
-
-    .cm-bracket {
-      color: #cb4b16;
-    }
-
-    .CodeMirror-matchingbracket {
-      color: #859900;
-    }
-
-    .CodeMirror-nonmatchingbracket {
-      color: #dc322f;
-    }
-
-    .cm-tag {
-      color: #93a1a1
-    }
-
-    .cm-attribute {
-      color: #2aa198;
-    }
-
-    .cm-header {
-      color: #586e75;
-    }
-
-    .cm-quote {
-      color: #93a1a1;
-    }
-
-    .cm-hr {
-      color: transparent;
-      border-top: 1px solid #586e75;
-      display: block;
-    }
-
-    .cm-link {
-      color: #93a1a1;
-      cursor: pointer;
-    }
-
-    .cm-special {
-      color: #6c71c4;
-    }
-
-    .cm-em {
-      color: #999;
-      text-decoration: underline;
-      text-decoration-style: dotted;
-    }
-
-    .cm-strong {
-      color: #eee;
-    }
-
-    .cm-tab:before {
-      content: "➤"; /*visualize tab character*/
-      color: #586e75;
-      position: absolute;
-    }
-
-    .cm-error,
-    .cm-invalidchar {
-      color: #586e75;
-      border-bottom: 1px dotted #dc322f;
-    }
-
-    .CodeMirror-selected {
-      background: var(--ls-primary-background-color);
-    }
 
-    .CodeMirror-gutters {
-      border-right: 1px solid;
-    }
-
-    .CodeMirror-gutters {
-      background-color: var(--ls-secondary-background-color);
-      border-color: transparent;
-    }
-
-    pre.CodeMirror-line {
-      background-color: transparent !important;
-    }
-
-    .CodeMirror-linenumber {
-      text-shadow: #021014 0 -1px;
-    }
-
-    /* Common */
-
-    .CodeMirror-linenumber {
-      color: #586e75;
-      padding: 0 5px;
-    }
-
-    .CodeMirror-gutter .CodeMirror-gutter-text {
-      color: #586e75;
-    }
-
-    .CodeMirror-lines .CodeMirror-cursor {
-      border-left: 1px solid #819090;
-    }
-
-    /*
-    Active line. Negative margin compensates left padding of the text in the
-    view-port
-    */
-
-    .CodeMirror-activeline-background {
-      background: rgba(255, 255, 255, 0.10);
+    .CodeMirror-hscrollbar {
+        cursor: pointer;
     }
   }
-
-  .cp__right-sidebar .CodeMirror {
-    background-color: var(--ls-primary-background-color);
-  }
-
-  .cp__right-sidebar .CodeMirror .CodeMirror-gutters {
-    background-color: var(--ls-primary-background-color);
-  }
-
-  .cp__right-sidebar .CodeMirror .CodeMirror-selected {
-    background: var(--ls-secondary-background-color);
-  }
-
-  .extensions__code-lang {
-    background-color: rgba(0, 0, 0, .25);
-  }
 }
-
-html[data-theme='light'] {
-  .CodeMirror {
-    background: var(--ls-secondary-background-color);
-    color: #202020;
-
-    .CodeMirror-selected {
-      background: var(--ls-tertiary-background-color);
-    }
-
-    .CodeMirror-gutters {
-      background: #f5f5f5;
-      border-right: 0;
-    }
-
-    .CodeMirror-linenumber {
-      color: #b0b0b0;
-    }
-
-    .CodeMirror-cursor {
-      border-left: 1px solid #505050 !important;
-    }
-
-    span.cm-comment {
-      color: #8f5536;
-    }
-
-    span.cm-atom {
-      color: #aa759f;
-    }
-
-    span.cm-number {
-      color: #aa759f;
-    }
-
-    span.cm-property, span.cm-attribute {
-      color: #90a959;
-    }
-
-    span.cm-keyword {
-      color: #ac4142;
-    }
-
-    span.cm-string {
-      color: #eaaf5a;
-    }
-
-    span.cm-variable {
-      color: #8ca553;
-    }
-
-    span.cm-variable-2 {
-      color: #6a9fb5;
-    }
-
-    span.cm-def {
-      color: #d28445;
-    }
-
-    span.cm-bracket {
-      color: #202020;
-    }
-
-    span.cm-tag {
-      color: #ac4142;
-    }
-
-    span.cm-link {
-      color: #aa759f;
-    }
-
-    span.cm-error {
-      background: #ac4142;
-      color: #505050;
-    }
-
-    .CodeMirror-activeline-background {
-      background: var(--ls-tertiary-background-color) !important;
-    }
-
-    .CodeMirror-matchingbracket {
-      color: #1bff01 !important;
-    }
-  }
-}

+ 0 - 33
src/main/frontend/handler/block.cljs

@@ -28,39 +28,6 @@
            block)]
     @ids))
 
-;; TODO: should we remove this dummy block and use the page's root block instead?
-(defn with-dummy-block
-  ([blocks format]
-   (with-dummy-block blocks format {} {}))
-  ([blocks format default-option {:keys [journal? page-name]
-                                  :or {journal? false}}]
-   (let [format (or format (state/get-preferred-format) :markdown)
-         blocks (vec blocks)]
-     (if (seq blocks)
-       blocks
-       (let [page-block (when page-name (db/pull [:block/name (string/lower-case page-name)]))
-             create-title-property? (util/create-title-property? page-name)
-             content (if create-title-property?
-                       (let [title (or (:block/original-name page-block)
-                                       (:block/name page-block))
-                             properties (common-handler/get-page-default-properties title)]
-                         (property/build-properties-str format properties))
-                       "")
-             page-id {:db/id (:db/id page-block)}
-             dummy (merge {:block/uuid (db/new-block-id)
-                           :block/left page-id
-                           :block/parent page-id
-                           :block/page page-id
-                           :block/title ""
-                           :block/content content
-                           :block/format format
-                           :block/dummy? true}
-                          default-option)
-             dummy (if (:db/id (:block/file dummy))
-                     dummy
-                     (dissoc dummy :block/file))]
-         [dummy])))))
-
 (defn filter-blocks
   [repo ref-blocks filters group-by-page?]
   (let [ref-pages (->> (if group-by-page?

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

@@ -31,7 +31,7 @@
           (db/transact! repo
                         [{:file/path path
                           :block/name file
-                          :block/file [:file/path path]
+                          :block/file {:file/path path}
                           :block/journal? false}]))
          (p/catch (fn [error]
                     (prn "Write file failed, path: " path ", data: " data)

+ 71 - 92
src/main/frontend/handler/editor.cljs

@@ -143,14 +143,12 @@
     (state/set-cursor-range! (util/caret-range node))))
 
 (defn restore-cursor-pos!
-  ([id markup]
-   (restore-cursor-pos! id markup false))
-  ([id markup dummy?]
-   (when-let [node (gdom/getElement (str id))]
-     (when-let [cursor-range (state/get-cursor-range)]
-       (when-let [range cursor-range]
-         (let [pos (diff/find-position markup range)]
-           (util/set-caret-pos! node pos)))))))
+  [id markup]
+  (when-let [node (gdom/getElement (str id))]
+    (when-let [cursor-range (state/get-cursor-range)]
+      (when-let [range cursor-range]
+        (let [pos (diff/find-position markup range)]
+          (util/set-caret-pos! node pos))))))
 
 (defn highlight-block!
   [block-uuid]
@@ -288,7 +286,7 @@
 
 (defn- wrap-parse-block
   [{:block/keys [content format parent left page uuid pre-block?] :as block}]
-  (let [block (or (db/pull (:db/id block)) block)
+  (let [block (or (and (:db/id block) (db/pull (:db/id block))) block)
         properties (:block/properties block)
         real-content (:block/content block)
         content (if (and (seq properties) real-content (not= real-content content))
@@ -330,10 +328,10 @@
                         (select-keys properties property/built-in-properties)
                         (:block/properties block))]
     (-> block
-       (dissoc :block/top?
-               :block/block-refs-count)
-       (assoc :block/content content
-              :block/properties new-properties))))
+        (dissoc :block/top?
+                :block/block-refs-count)
+        (assoc :block/content content
+               :block/properties new-properties))))
 
 (defn- save-block-inner!
   [repo block value {:keys [refresh?]
@@ -390,7 +388,6 @@
   [config current-block new-block sibling?]
   (let [ref-top-block? (and (:ref? config)
                             (not (:ref-child? config)))
-        dummy? (:block/dummy? current-block)
         [current-node new-node]
         (mapv outliner-core/block [current-block new-block])
         has-children? (db/has-children? (state/get-current-repo)
@@ -464,7 +461,7 @@
 
 (defn insert-new-block-before-block-aux!
   [config
-   {:block/keys [uuid content repo format page dummy?]
+   {:block/keys [uuid content repo format page]
     db-id :db/id
     :as block}
    value
@@ -494,7 +491,7 @@
 
 (defn insert-new-block-aux!
   [config
-   {:block/keys [uuid content repo format page dummy?]
+   {:block/keys [uuid content repo format page]
     db-id :db/id
     :as block}
    value
@@ -522,7 +519,7 @@
                                  :data [current-block next-block]}]
                        (db/refresh! repo opts)))]
     (do
-      (if (or dummy? (:ref? config) (not sibling?))
+      (if (or (:ref? config) (not sibling?))
         (refresh-fn)
         (do
           (profile "update cache " (update-cache-for-block-insert! repo config block blocks))
@@ -543,14 +540,13 @@
 
 (defn get-state
   []
-  (let [[{:keys [on-hide block block-id block-parent-id dummy? format sidebar?]} id config] (state/get-editor-args)
+  (let [[{:keys [on-hide block block-id block-parent-id format sidebar?]} id config] (state/get-editor-args)
         node (gdom/getElement id)]
     (when node
       (let [value (gobj/get node "value")
             pos (gobj/get node "selectionStart")]
         {:config config
          :on-hide on-hide
-         :dummy? dummy?
          :sidebar? sidebar?
          :format format
          :id id
@@ -668,7 +664,7 @@
     content))
 
 (defn check
-  [{:block/keys [uuid marker content format dummy? repeated?] :as block}]
+  [{:block/keys [uuid marker content format repeated?] :as block}]
   (let [new-content (string/replace-first content marker "DONE")
         new-content (->
                      (if repeated?
@@ -678,7 +674,7 @@
     (save-block-if-changed! block new-content)))
 
 (defn uncheck
-  [{:block/keys [uuid marker content format dummy?] :as block}]
+  [{:block/keys [uuid marker content format] :as block}]
   (let [marker (if (= :now (state/get-preferred-workflow))
                  "LATER"
                  "TODO")
@@ -721,13 +717,13 @@
 
 
 (defn set-marker
-  [{:block/keys [uuid marker content format dummy? properties] :as block} new-marker]
+  [{:block/keys [uuid marker content format properties] :as block} new-marker]
   (let [new-content (-> (string/replace-first content marker new-marker)
                         (with-marker-time format new-marker))]
     (save-block-if-changed! block new-content)))
 
 (defn set-priority
-  [{:block/keys [uuid marker priority content dummy?] :as block} new-priority]
+  [{:block/keys [uuid marker priority content] :as block} new-priority]
   (let [new-content (string/replace-first content
                                           (util/format "[#%s]" priority)
                                           (util/format "[#%s]" new-priority))]
@@ -750,21 +746,21 @@
           (util/nth-safe blocks idx))))))
 
 (defn delete-block-aux!
-  [{:block/keys [uuid content repo refs] :as block} dummy? children?]
-  (when-not dummy?
-    (let [repo (or repo (state/get-current-repo))
-          block (db/pull repo '[*] [:block/uuid uuid])]
-      (when block
-        (->
-         (outliner-core/block block)
-         (outliner-core/delete-node children?))
-        (db/refresh! repo {:key :block/change :data [block]})))))
+  [{:block/keys [uuid content repo refs] :as block} children?]
+  (let [repo (or repo (state/get-current-repo))
+        block (db/pull repo '[*] [:block/uuid uuid])]
+    (when block
+      (->
+       (outliner-core/block block)
+       (outliner-core/delete-node children?))
+      (db/refresh! repo {:key :block/change :data [block]}))))
 
 (defn delete-block!
   ([repo e]
    (delete-block! repo e true))
   ([repo e delete-children?]
-   (let [{:keys [id block-id block-parent-id dummy? value format]} (get-state)]
+   (state/set-editor-op! :delete)
+   (let [{:keys [id block-id block-parent-id value format]} (get-state)]
     (when block-id
       (let [page-id (:db/id (:block/page (db/entity [:block/uuid block-id])))
             page-blocks-count (and page-id (db/get-page-blocks-count repo page-id))]
@@ -780,7 +776,7 @@
             (when-not (and has-children? left-has-children?)
               (let [block-parent (gdom/getElement block-parent-id)
                     sibling-block (get-prev-block-non-collapsed block-parent)]
-                (delete-block-aux! block dummy? delete-children?)
+                (delete-block-aux! block delete-children?)
                 (when (and repo sibling-block)
                   (when-let [sibling-block-id (dom/attr sibling-block "blockid")]
                     (when-let [block (db/pull repo '[*] [:block/uuid (uuid sibling-block-id)])]
@@ -795,7 +791,8 @@
                         (edit-block! block pos format id
                                      {:custom-content new-value
                                       :tail-len tail-len
-                                      :move-cursor? false}))))))))))))))
+                                      :move-cursor? false}))))))))))))
+   (state/set-editor-op! nil)))
 
 (defn- get-end-block-parent
   [end-block blocks]
@@ -830,7 +827,7 @@
       (let [start-node (outliner-core/block (first blocks))
             end-node (get-top-level-end-node blocks)]
         (if (= start-node end-node)
-          (delete-block-aux! (first blocks) false true)
+          (delete-block-aux! (first blocks) true)
           (when (outliner-core/delete-nodes start-node end-node lookup-refs)
             (let [opts {:key :block/change
                         :data blocks}]
@@ -1155,7 +1152,7 @@
   (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)
-      (delete-block-aux! block false true))))
+      (delete-block-aux! block true))))
 
 (defn clear-last-selected-block!
   []
@@ -1231,9 +1228,8 @@
                  (db-model/query-block-by-uuid block-or-uuid) block-or-uuid)
          format (:block/format block)]
      (save-block! {:block block :repo repo :format format} content)))
-  ([{:keys [format block repo dummy?] :as state} value]
-   (when (or (:db/id (db/entity repo [:block/uuid (:block/uuid block)]))
-             dummy?)
+  ([{:keys [format block repo] :as state} value]
+   (when (:db/id (db/entity repo [:block/uuid (:block/uuid block)]))
      (save-block-aux! block value format {}))))
 
 (defn save-current-block!
@@ -1269,8 +1265,7 @@
                     (or
                      (not= (string/trim db-content-without-heading)
                            (string/trim value))))
-               (save-block-aux! db-block value (:block/format db-block) opts))
-             )
+               (save-block-aux! db-block value (:block/format db-block) opts)))
            (catch js/Error error
              (log/error :save-block-failed error))))))))
 
@@ -1905,10 +1900,8 @@
 (defn- get-block-tree-insert-pos-at-point
   "return [target-block sibling? delete-editing-block? editing-block]"
   []
-  (when-let [editing-block (or (db/pull (:db/id (state/get-edit-block)))
-                               (when (:block/dummy? (state/get-edit-block)) (state/get-edit-block)))]
-    (let [dummy? (:block/dummy? editing-block)
-          input (gdom/getElement (state/get-edit-input-id))
+  (when-let [editing-block (db/pull (:db/id (state/get-edit-block)))]
+    (let [input (gdom/getElement (state/get-edit-input-id))
           pos (util/get-input-pos input)
           value (:value (get-state))
           [fst-block-text snd-block-text] (compute-fst-snd-block-text value pos)
@@ -1922,37 +1915,30 @@
           has-children? (db/has-children? (state/get-current-repo)
                                           (:block/uuid editing-block))
           collapsed? (:collapsed (:block/properties editing-block))]
-      (conj (match (mapv boolean [dummy? (seq fst-block-text) (seq snd-block-text)
+      (conj (match (mapv boolean [(seq fst-block-text) (seq snd-block-text)
                                   block-self? has-children? (= parent left) collapsed?])
-                   ;; if editing-block is dummy, insert after page-block
-                   [true false false _ _ _ _]
-                   [parent-block false true]
-
-                   [true _ _ _ _ _ _]
-                   [parent-block false false]
-
                    ;; when zoom at editing-block
-                   [false _ _ true _ _ _]
+                   [_ _ true _ _ _]
                    [editing-block false false]
 
                    ;; insert after editing-block
-                   [false true _ false true _ false]
+                   [true _ false true _ false]
                    [editing-block false false]
-                   [false true _ false true _ true]
+                   [true _ false true _ true]
                    [editing-block true false]
-                   [false true _ false false _ _]
+                   [true _ false false _ _]
                    [editing-block true false]
-                   [false false false false true _ false]
+                   [false false false true _ false]
                    [editing-block false false]
-                   [false false false false true _ true]
+                   [false false false true _ true]
                    [editing-block true false]
-                   [false false false false false _ _]
+                   [false false false false _ _]
                    [editing-block true true]
 
                    ;; insert before editing-block
-                   [false false true false _ true _]
+                   [false true false _ true _]
                    [parent-block false false]
-                   [false false true false _ false _]
+                   [false true false _ false _]
                    [left-block true false])
             editing-block))))
 
@@ -1979,38 +1965,32 @@
            new-content
            (->> new-content
                 (property/remove-property format "id")
-                (property/remove-property format "custom_id"))]
-       (conj {:block/uuid uuid
-              :block/page (select-keys page [:db/id])
-              :block/file (select-keys file [:db/id])
-              :block/format format
-              :block/properties (apply dissoc (:block/properties block)
-                                       (concat [:id :custom_id :custom-id]
-                                               exclude-properties))
-              :block/meta (dissoc (:block/meta block) :start-pos :end-pos)
-              :block/content new-content
-              :block/title new-title}
-             (dissoc block
-                     :block/pre-block?
-                     :block/uuid
-                     :block/page
-                     :block/file
-                     :db/id
-                     :block/left
-                     :block/parent
-                     :block/format
-                     :block/properties
-                     :block/meta
-                     :block/content
-                     :block/title))))))
+                (property/remove-property format "custom_id"))
+           m (merge (dissoc block
+                            :block/pre-block?
+                            :block/uuid
+                            :db/id
+                            :block/left
+                            :block/parent
+                            :block/file)
+                    {:block/uuid uuid
+                     :block/page (select-keys page [:db/id])
+                     :block/format format
+                     :block/properties (apply dissoc (:block/properties block)
+                                         (concat [:id :custom_id :custom-id]
+                                                 exclude-properties))
+                     :block/meta (dissoc (:block/meta block) :start-pos :end-pos)
+                     :block/content new-content
+                     :block/title new-title})]
+       (if file
+         (assoc m :block/file (select-keys file [:db/id]))
+         m)))))
 
 (defn- paste-block-tree-at-point
   ([tree exclude-properties] (paste-block-tree-at-point tree exclude-properties nil))
   ([tree exclude-properties content-update-fn]
    (let [repo (state/get-current-repo)
-         page (or (db/entity [:block/name (state/get-current-page)])
-                  (db/entity [:block/original-name (state/get-current-page)])
-                  (:block/page (db/entity (:db/id (state/get-edit-block)))))
+         page (:block/page (db/entity (:db/id (state/get-edit-block))))
          file (:block/file page)]
      (when-let [[target-block sibling? delete-editing-block? editing-block]
                 (get-block-tree-insert-pos-at-point)]
@@ -2270,7 +2250,7 @@
 
       :else
       (do
-        (delete-block-aux! next-block false false)
+        (delete-block-aux! next-block false)
         (state/set-edit-content! input-id (str value "" (:block/content next-block)))
         (util/move-cursor-to input current-pos)))))
 
@@ -2577,7 +2557,6 @@
         page (or (db/entity [:block/name (state/get-current-page)])
                  (db/entity [:block/original-name (state/get-current-page)])
                  (:block/page (db/entity (:db/id (state/get-edit-block)))))
-        file (:block/file page)
         copied-blocks (state/get-copied-blocks)
         copied-block-tree (:copy/block-tree copied-blocks)]
     (if (and

+ 1 - 1
src/main/frontend/handler/editor/keyboards.cljs

@@ -18,7 +18,7 @@
        (let [target (.-target e)]
          (if (d/has-class? target "bottom-action") ;; FIXME: not particular case
            (.preventDefault e)
-           (let [{:keys [on-hide format value block id repo dummy?]} (editor-handler/get-state)]
+           (let [{:keys [on-hide format value block id repo]} (editor-handler/get-state)]
              (when on-hide
                (on-hide value event))
              (when (contains? #{:esc :visibilitychange :click} event)

+ 4 - 4
src/main/frontend/handler/editor/lifecycle.cljs

@@ -14,12 +14,12 @@
 
 (defn did-mount!
   [state]
-  (let [[{:keys [dummy? format block-parent-id]} id] (:rum/args state)
+  (let [[{:keys [format block-parent-id]} id] (:rum/args state)
         content (get-in @state/state [:editor/content id])
         input (gdom/getElement id)]
     (when block-parent-id
       (state/set-editing-block-dom-id! block-parent-id))
-    (editor-handler/restore-cursor-pos! id content dummy?)
+    (editor-handler/restore-cursor-pos! id content)
 
     (when input
       (dnd/subscribe!
@@ -43,7 +43,7 @@
 
 (defn will-unmount
   [state]
-  (let [{:keys [id value format block repo dummy? config]} (get-state)
+  (let [{:keys [id value format block repo config]} (get-state)
         file? (:file? config)]
     (when-let [input (gdom/getElement id)]
       ;; (.removeEventListener input "paste" (fn [event]
@@ -58,7 +58,7 @@
               :upload-images))))
     (editor-handler/clear-when-saved!)
     ;; TODO: ugly
-    (when-not (contains? #{:insert :indent-outdent :auto-save :undo :redo} (state/get-editor-op))
+    (when-not (contains? #{:insert :indent-outdent :auto-save :undo :redo :delete} (state/get-editor-op))
       (editor-handler/save-block! (get-state) value)))
   state)
 

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

@@ -68,7 +68,7 @@
                             (swap! ref-pages set/union (set block-ref-pages)))
                           (-> block
                               (dissoc :ref-pages)
-                              (assoc :block/file [:file/path file]
+                              (assoc :block/file {:file/path file}
                                      :block/format format
                                      :block/page [:block/name (string/lower-case page)]
                                      :block/refs block-ref-pages
@@ -84,7 +84,7 @@
                           (util/remove-nils
                            (assoc
                             (block/page-name->map page false)
-                            :block/file [:file/path file]))
+                            :block/file {:file/path file}))
                           (seq properties)
                           (assoc :block/properties properties)
 

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

@@ -17,7 +17,8 @@
     (let [files (export/get-md-file-contents repo)]
       (when (seq files)
         (-> (p/all (for [[path content] files]
-                     (file/alter-file repo path content {:add-history? false})))
+                     (file/alter-file repo path content {:add-history? false
+                                                         :reset? false})))
             (p/then (fn []
                       (config-handler/set-config! :markdown/version 2)
 

+ 12 - 17
src/main/frontend/handler/page.cljs

@@ -65,33 +65,27 @@
 (defn create!
   ([title]
    (create! title {}))
-  ([title {:keys [redirect?]
+  ([title {:keys [redirect? page-map]
            :or {redirect? true}}]
    (let [title (string/trim title)
          page (string/lower-case title)
          format (state/get-preferred-format)
-         tx (-> (block/page-name->map title true)
-                (assoc :block/format format))
-         page-entity [:block/uuid (:block/uuid tx)]
+         tx (if page-map
+              page-map
+              (-> (block/page-name->map title true)
+                  (assoc :block/format format)))
+         page-entity (if (:block/uuid tx)
+                       [:block/uuid (:block/uuid tx)]
+                       (:db/id tx))
          create-title-property? (util/create-title-property? title)
          default-properties (default-properties-block title format page-entity)
-         empty-block {:block/uuid (db/new-block-id)
-                      :block/left [:block/uuid (:block/uuid default-properties)]
-                      :block/format format
-                      :block/content ""
-                      :block/parent page-entity
-                      :block/unordered true
-                      :block/page page-entity}
          txs (if create-title-property?
-               [tx default-properties empty-block]
+               [tx default-properties]
                [tx])]
      (db/transact! txs)
      (when redirect?
       (route-handler/redirect! {:to :page
-                                :path-params {:name page}})
-      (when create-title-property?
-        (js/setTimeout (fn []
-                        (editor-handler/edit-block! empty-block 0 format (:block/uuid empty-block))) 50))))))
+                                :path-params {:name page}})))))
 
 (defn page-add-property!
   [page-name key value]
@@ -390,7 +384,8 @@
 (defn get-pages-with-modified-at
   [repo]
   (->> (db/get-modified-pages repo)
-       (remove util/file-page?)))
+       (remove util/file-page?)
+       (remove util/uuid-string?)))
 
 (defn get-filters
   [page-name]

+ 10 - 2
src/main/frontend/modules/datascript_report/core.cljs

@@ -5,11 +5,19 @@
 
 (def keys-of-deleted-entity 1)
 
+(defn safe-pull
+  [db selector eid]
+  (try
+    (d/pull db selector eid)
+    (catch js/Error e
+      (js/console.error e)
+      nil)))
+
 (defn get-entity-from-db-after-or-before
   [db-before db-after db-id]
-  (let [r (d/pull db-after '[*] db-id)]
+  (let [r (safe-pull db-after '[*] db-id)]
     (if (= keys-of-deleted-entity (count r))
-      (let [r (d/pull db-before '[*] db-id)]
+      (let [r (safe-pull db-before '[*] db-id)]
         (when (= keys-of-deleted-entity (count r))
           ;; TODO: What can cause this happen?
           (log/error :outliner-pipeline/cannot-find-entity {:entity r}))

+ 16 - 14
src/main/frontend/modules/file/core.cljs

@@ -102,7 +102,7 @@
         (when chan-callback
           (chan-callback))))))
 
-(defn- create-file-if-not-exists!
+(defn- transact-file-tx-if-not-exists!
   [page ok-handler]
   (when-let [repo (state/get-current-repo)]
     (let [format (name (get page :block/format
@@ -121,18 +121,20 @@
                 (if (= format "markdown") "md" format))
           file-path (str "/" path)
           dir (config/get-repo-dir repo)]
-      (p/let [exists? (fs/file-exists? dir file-path)]
-        (if exists?
-          (notification/show!
-           [:p.content
-            (util/format "File %s already exists!" file-path)]
-           :error)
-          (let [file-path (config/get-file-path repo path)
-                tx [{:file/path file-path}
-                    {:block/name (:block/name page)
-                     :block/file [:file/path file-path]}]]
-            (db/transact! tx)
-            (when ok-handler (ok-handler))))))))
+      (let [file-path (config/get-file-path repo path)
+            page-blocks (db/get-page-blocks-no-cache (:block/name page))
+            file {:file/path file-path}
+            tx (->>
+                (concat
+                 [{:file/path file-path}
+                  {:block/name (:block/name page)
+                   :block/file file}]
+                 (map (fn [block] {:db/id (:db/id block)
+                                  :block/file file})
+                   page-blocks))
+                (remove nil?))]
+        (db/transact! tx)
+        (when ok-handler (ok-handler))))))
 
 (defn save-tree-aux!
   [page-block tree]
@@ -154,4 +156,4 @@
                    (:block/file (db-utils/entity page))))]
     (if file
       (ok-handler)
-      (create-file-if-not-exists! page-block ok-handler))))
+      (transact-file-tx-if-not-exists! page-block ok-handler))))

+ 1 - 1
src/main/frontend/modules/outliner/core.cljs

@@ -128,7 +128,7 @@
     (assert (ds/outliner-txs-state? txs-state)
             "db should be satisfied outliner-tx-state?")
     (let [m (-> (:data this)
-                (dissoc :block/children :block/dummy? :block/meta)
+                (dissoc :block/children :block/meta)
                 (util/remove-nils))
           other-tx (:db/other-tx m)]
       (when (seq other-tx)

+ 13 - 8
src/main/frontend/modules/outliner/datascript.cljc

@@ -8,7 +8,8 @@
                      [frontend.state :as state]
                      [frontend.util :as util :refer-macros [profile]]
                      [frontend.config :as config]
-                     [frontend.util :as util])))
+                     [frontend.util :as util]
+                     [lambdaisland.glogi :as log])))
 
 
 #?(:cljs
@@ -40,13 +41,17 @@
      ;; (util/pprint txs)
      (when (and (seq txs)
                 (not (:skip-transact? opts)))
-       (let [conn (conn/get-conn false)
-             editor-cursor (state/get-last-edit-block)
-             meta (merge opts {:editor-cursor editor-cursor})
-             rs (d/transact! conn txs meta)]
-         (when-not config/test?
-           (after-transact-pipelines rs))
-         rs))))
+       (try
+         (let [conn (conn/get-conn false)
+              editor-cursor (state/get-last-edit-block)
+              meta (merge opts {:editor-cursor editor-cursor})
+              rs (d/transact! conn txs meta)]
+          (when-not config/test?
+            (after-transact-pipelines rs))
+          rs)
+         (catch js/Error e
+           (log/error :exception e)
+           (throw e))))))
 
 #?(:clj
    (defmacro auto-transact!

+ 0 - 4
src/main/frontend/routes.cljs

@@ -37,10 +37,6 @@
     {:name :search
      :view search/more}]
 
-   ["/new-page"
-    {:name :new-page
-     :view page/new}]
-
    ["/page/:name"
     {:name :page
      :view page/page}]

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

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

+ 24 - 0
src/main/grammar/calc.bnf

@@ -0,0 +1,24 @@
+<start> = assignment | expr
+expr = add-sub
+<add-sub> = pow-log | mul-div | add | sub |  variable
+add = add-sub <'+'> mul-div
+sub = add-sub <'-'> mul-div
+<mul-div> = pow-log | mul | div
+mul = mul-div <'*'> pow-log
+div = mul-div <'/'> pow-log
+<trig> = sin | cos | tan | acos | asin | atan
+<pow-log> = term | pow | log | ln | trig
+pow = pow-log <'^'> term
+log = <#'\s*'> <'log('> expr <')'> <#'\s*'>
+ln = <#'\s*'> <'ln('> expr <')'> <#'\s*'>
+sin = <#'\s*'> <'sin('> expr <')'> <#'\s*'>
+cos = <#'\s*'> <'cos('> expr <')'> <#'\s*'>
+tan = <#'\s*'> <'tan('> expr <')'> <#'\s*'>
+atan = <#'\s*'> <'atan('> expr <')'> <#'\s*'>
+acos = <#'\s*'> <'acos('> expr <')'> <#'\s*'>
+asin = <#'\s*'> <'asin('> expr <')'> <#'\s*'>
+<term> = number | variable | <#'\s*'> <'('> expr <')'> <#'\s*'>
+number = #'\s*[0-9]+\.?[0-9]*()\s*'
+variable = #'\s*[a-zA-Z]+\s*'
+toassign =  #'\s*[a-zA-Z]+\s*'
+assignment = toassign <#'\s*'> <'='> <#'\s*'> expr

+ 4 - 4
src/test/frontend/db/query_dsl_test.cljs

@@ -7,7 +7,7 @@
             [frontend.db-schema :as schema]
             [frontend.handler.repo :as repo-handler]
             [promesa.core :as p]
-            [cljs.test :refer [deftest is are testing use-fixtures]]))
+            [cljs.test :refer [deftest is are testing use-fixtures run-tests]]))
 
 ;; TODO: quickcheck
 ;; 1. generate query filters
@@ -321,7 +321,7 @@ last-modified-at:: 1609084800002"}]]
       "(not [[page 1]])"
       {:query '([?b :block/uuid]
                 (not [?b :block/path-refs [:block/name "page 1"]]))
-       :count 25}))
+       :count 31}))
 
   (testing "Between query"
     (are [x y] (= (count-only x) y)
@@ -367,7 +367,7 @@ last-modified-at:: 1609084800002"}]]
                  (or
                   (and [?b :block/path-refs [:block/name "page 1"]])
                   (and [?b :block/path-refs [:block/name "page 2"]]))))
-       :count 28})
+       :count 34})
 
     ;; FIXME: not working
     ;; (are [x y] (= (q-count x) y)
@@ -458,7 +458,7 @@ last-modified-at:: 1609084800002"}]]
              (import-test-data!))
    :after config/destroy-test-db!})
 
-#_(cljs.test/test-ns 'frontend.db.query-dsl-test)
+#_(run-tests)
 
 (comment
   (require '[clojure.pprint :as pprint])

+ 87 - 0
src/test/frontend/extensions/calc_test.cljc

@@ -0,0 +1,87 @@
+(ns frontend.extensions.calc-test
+  (:require [clojure.test :as test :refer [deftest testing is are]]
+            [frontend.extensions.calc :as calc]))
+
+(defn run [expr]
+  {:pre [(string? expr)]}
+  (first (calc/eval (calc/parse expr))))
+
+(deftest basic-arithmetic
+  (testing "numbers are parsed as expected"
+    (are [value expr] (= value (run expr))
+      1          "1"
+      1          "   1  "
+      98123      "98123"
+      1.0        " 1.0 "
+      22.1124131 "22.1124131"
+      100.01231  " 100.01231 "))
+  (testing "basic operations work"
+    (are [value expr] (= value (run expr))
+      1             "1 + 0"
+      1             "1 + 1 - 1 "
+      3             "1+2"
+      3             " 1 +2 "
+      1             "(2-1 ) "
+      211           "100  + 111"
+      0             "1 + 2 + 3 + 4 + 5 -1-2-3-4-5"
+      1             "1 * 1"
+      2             "1*2"
+      9             " 3 *3"
+      1             " 2 * 3 / 3 / 2"
+      #?(:clj 1/2
+         :cljs 0.5) " 1 / 2"
+      0.5           " 1/ 2.0"))
+  (testing "power"
+    (are [value expr] (= value (run expr))
+      1.0   "1 ^ 0"
+      4.0   "2^2 "
+      27.0  " 3^ 3"
+      16.0  "2 ^ 2 ^ 2"
+      256.0 "4.000 ^ 4.0"))
+  (testing "operator precedence"
+    (are [value expr] (= value (run expr))
+      1     "1 + 0 * 2"
+      1     "2 * 1 - 1 "
+      4     "8 / 4 + 2 * 1 - 25 * 0 / 1"
+      14.0  "3 *2 ^ 2 + 1 * 2"
+      74.0  "((3*2) ^ 2 + 1) * 2"
+      432.0 "(3*2) ^ (2 + 1) * 2"
+      97.0  "(2 * 3) * 2 ^ (2 * 2) + 1"
+      4.0   "2 * 3 / 2 ^ 2 * 2 + 1"))
+  (testing "scientific functions"
+    (are [value expr] (= value (run expr))
+      1.0  "cos( 0 * 1 )"
+      0.0  "sin( 1 -1 )"
+      0.0  "atan(tan(0))"
+      1.0  "sin(asin(0)) + 1"
+      0.0  "acos(cos(0))"
+      5.0  "2 * log(10) + 3"
+      10.0 "ln(1) + 10")))
+
+(deftest variables
+  (testing "variables can be remembered"
+    (are [final-env expr] (let [env (calc/new-env)]
+                            (calc/eval env (calc/parse expr))
+                            (= final-env @env))
+      {"a" 1}        "a = 1"
+      {"variable" 1} "variable = 1 + 0 * 2"
+      {"x" 1}        "x= 2 * 1 - 1 "
+      {"y" 4}        "y =8 / 4 + 2 * 1 - 25 * 0 / 1"
+      {"zzz" 14.0}   "zzz=3 *2 ^ 2 + 1 * 2"
+      {"foo" 74.0}   "foo = (((3*2) ^ 2 + 1) * 2)"))
+  (testing "variables can be reused"
+    (are [final-env exprs] (let [env (calc/new-env)]
+                            (doseq [expr exprs]
+                              (calc/eval env (calc/parse expr)))
+                            (= final-env @env))
+      {"a" 1 "b" 2}          ["a = 1" "b = a + 1"]
+      {"variable" 1 "x" 0.0} ["variable = 1 + 0 * 2" "x = log(variable)"]
+      {"x" 1 "u" 23 "v" 24}  ["x= 2 * 1 - 1 " "23 + 54" "u= 23" "v = x + u"]))
+  (testing "variables can be rewritten"
+    (are [final-env exprs] (let [env (calc/new-env)]
+                             (doseq [expr exprs]
+                               (calc/eval env (calc/parse expr)))
+                             (= final-env @env))
+      {"a" 2}              ["a = 1" "a = 2"]
+      {"a" 2 "b" 2}        ["a = 1" "b = a + 1" "a = b"]
+      {"variable" 1 "x" 0} ["variable = 1 + 0 * 2" "x = log(variable)" "x = variable - 1"])))

+ 1 - 0
tailwind.all.css

@@ -8,6 +8,7 @@
 @import "resources/css/excalidraw.min.css";
 @import "resources/css/katex.min.css";
 @import "resources/css/codemirror.min.css";
+@import "resources/css/codemirror.solarized.css";
 @import "resources/css/animation.css";
 @import "resources/css/table.css";
 @import "resources/css/datepicker.css";

+ 4 - 4
yarn.lock

@@ -3917,10 +3917,10 @@ mkdirp@^0.5.4, mkdirp@~0.5.1:
   dependencies:
     minimist "^1.2.5"
 
[email protected]2:
-  version "0.6.22"
-  resolved "https://registry.yarnpkg.com/mldoc/-/mldoc-0.6.22.tgz#78c11db4547d7df0e15ecdef51b0eb84248ed1a8"
-  integrity sha512-kUZ3iYmF05ztML0rhQPKd8x/4c/zQlCCZKZV6BxbDXSwEaTp137xTKeJ6tK6Z70wmNK3SWRRgrYDB+TFCEoDTw==
[email protected]3:
+  version "0.6.23"
+  resolved "https://registry.yarnpkg.com/mldoc/-/mldoc-0.6.23.tgz#4e6de4a31921eeeddb87ef1d3164eb5e27c051ec"
+  integrity sha512-UuE3NGuUthMak8tMqno8EHEy9WkQt1uVmKIuNFdM8vfkLgy8unV/IHZiLPJwvayaiSRfx+sXt16w3LDmZL+WxQ==
   dependencies:
     yargs "^12.0.2"