Browse Source

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

Charlie 4 years ago
parent
commit
5df70519c1

+ 1 - 0
.gitignore

@@ -33,3 +33,4 @@ resources/electron.js
 .lsp/
 /libs/dist/
 charlie/
+.vscode

+ 71 - 0
CODEBASE_OVERVIEW.MD

@@ -0,0 +1,71 @@
+# Logseq Codebase Overview
+
+This document helps you understand more about how Logseq works. To contribute, read the [README](https://github.com/logseq/logseq) first.
+
+## Tech Stack
+
+### Clojure/ClojureScript
+
+Nowadays compile-to-js are common practice. With the advent of web assembly, you can use almost any language to write browser apps. The Logseq app mostly uses Clojure.
+
+Simply put, Clojure is a dynamic typing functional programming language, with Lisp's syntax, running on the JVM. ClojureScript is just Clojure compiling to JavaScript.
+
+Clojure is easy to learn, you can pick it up pretty quickly following the [official guide](https://clojure.org/guides/learn/syntax).
+
+Logseq chose ClojureScript not only because of all the [benefits](https://clojure.org/about/rationale) of the language itself but also because of its awesome ecosystem, such as the [DataScript](https://github.com/tonsky/datascript) library. More on that later.
+
+### Build Tools
+
+Shadow-cljs is a tool that helps compiling the ClojureScript code to JavaScript. In addition, it supports more handy features like live reload, code splitting, REPL, etc.
+
+For other tasks like bundling static resources and building the desktop app, which is not covered by shadow-cljs, Logseq uses the good old [Gulp](https://gulpjs.com).
+
+### React & Rum
+
+[React](https://reactjs.org/) is a library for building data-driven UI declaratively. Comparing to the imperative ways (such as DOM manipulation or using jQuery), it's simpler and easier to code correctly.
+
+[Rum](https://github.com/tonsky/rum) is a React wrapper in ClojureScript. More than just providing the familiar React APIs, Rum adds many Clojure flavors to React, especially on the state management part. As a result, if you have experience with React, read Rum's [README]((https://github.com/tonsky/rum) before diving into the code.
+
+### DataScript
+
+[DataScript](https://github.com/tonsky/datascript) is an in-memory database that implements the [Datalog](https://en.wikipedia.org/wiki/Datalog) logic programming language. Datalog is very different from and much more expressive than the more common SQL and NoSQL query languages. Many users have implemented interesting features on top of Logseq just by utilizing the rich query language. Get started with Datalog with this [tutorial](http://www.learndatalogtoday.org/)
+
+## Important Folders and Files
+
+After cloning the [Logseq repository](https://github.com/logseq/logseq), there are some folders and files that deserve extra attention.
+
+- Config files are located at the root directory. `package.json` contains the JavaScript dependencies while `deps.edn` contains their Clojure counterparts. `shadow-cljs.edn` and `gulpfile.js` contain all the build scripts.
+
+- `/public` and `/resources` contain all the static assets
+
+- `/src` is where most of the code locates.
+
+  - `/src/electron` and `/src/main/electron` contains code specific to the desktop app.
+
+  - `/src/test` contains all the test and `/src/dev-cljs` contains some development utilities.
+
+  - `/src/main/frontend` contains code that powers the Logseq editor. Folders and files inside are organized by features or functions. For example, `components` contains all the UI components and `handler` contains all the event-handling code. You can explore on your own interest.
+
+## Data Flow
+
+### Application State
+
+Most of Logseq's application state is divided into two parts. Document-related state (all your pages, blocks, and contents) is stored in DataScript. UI-related state (such as the current editing block) is kept in Clojure's [atom](https://clojure.org/reference/atoms). We then use Rum's reactive component to subscribe to these states. React efficiently re-renders after state changes.
+
+### When the App Starts
+
+Logseq loads files from your computer or the cloud, depending on your usage. The files are then parsed (and might be decrypted) and stored in DataScript. Other UI-related states are initialized. React components render for the first time. Event handlers are registered.
+
+### When you Type Something in the Document
+
+It's the typical flow of an event-driven GUI application. Various handlers (which are just functions) are listening for events like drag and drop, edit, format, and so on. When you start typing, the handler for editing blocks is called. It does three things:
+
+- Save your work to the disk or the cloud, so you won't lose them in case of an emergent power off.
+- Update the UI state.
+- Run transactions to update the DataScript database. Since other parts of the app may use data that are affected by the change, we need to rebuild the database query cache.
+
+After the change changes, React will dutifully refresh the screen.
+
+## Architecture
+
+Logseq has undergone a heavy refactoring, results in a much more robust and clear architecture. Read [this article](https://logseq.github.io/#/page/The%20Refactoring%20Of%20Logseq) written by the main contributor to the refactoring for a detailed tour.

+ 5 - 0
README.md

@@ -13,6 +13,9 @@ A local-first, non-linear, outliner notebook for organizing and sharing your per
 
 Use it to organize your todo list, to write your journals, or to record your unique life.
 
+## [Download our free Desktop app](https://github.com/logseq/logseq/releases)
+[Sponsor our contributors on Open Collective](https://opencollective.com/logseq), Logseq will move to Stripe later!
+
 ## Why Logseq?
 
 [Logseq](https://logseq.com) is a platform for knowledge management and collaboration. It focuses on privacy, longevity, and [user control](https://www.gnu.org/philosophy/free-sw.en.html).
@@ -74,6 +77,8 @@ Logseq is also made possible by the following projects:
 
 The following is for developers and designers who want to build and run Logseq locally and contribute to this project.
 
+We also have [a dedicated page](https://github.com/logseq/logseq/blob/master/CODEBASE_OVERVIEW.md) for LogSeq's codebase overview.
+
 ## Set up development environment
 
 ### 1. Requirements

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

@@ -104,14 +104,6 @@ http://ethanschoonover.com/solarized/img/solarized-palette.png
 /* 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;

+ 3 - 1
resources/package.json

@@ -10,6 +10,7 @@
     "electron:debug": "electron-forge start --inspect-electron",
     "electron:make": "electron-forge make",
     "electron:publish:github": "electron-forge publish",
+    "rebuild:better-sqlite3": "electron-rebuild -v 12 -f -w better-sqlite3",
     "postinstall": "install-app-deps"
   },
   "config": {
@@ -36,6 +37,7 @@
     "@electron-forge/maker-zip": "^6.0.0-beta.54",
     "electron": "12.0.4",
     "electron-builder": "^22.10.5",
-    "electron-forge-maker-appimage": "trusktr/electron-forge-maker-appimage#patch-1"
+    "electron-forge-maker-appimage": "trusktr/electron-forge-maker-appimage#patch-1",
+    "electron-rebuild": "^2.3.5"
   }
 }

+ 6 - 1
src/electron/electron/updater.cljs

@@ -56,6 +56,11 @@
        (emit "checking-for-update" nil)
        (-> (p/let
             [artifact (get-latest-artifact-info repo)
+
+             artifact (when-let [remote-version (and artifact (re-find #"\d+\.\d+\.\d+" (:url artifact)))]
+                        (if (and (. semver valid remote-version)
+                                 (. semver lt electron-version remote-version)) artifact))
+
              url (if-not artifact (do (emit "update-not-available" nil) (throw nil)) (:url artifact))
              _ (if url (emit "update-available" (bean/->js artifact)) (throw (js/Error. "download url not exists")))
                ;; start download FIXME: user's preference about auto download
@@ -108,7 +113,7 @@
   [repo]
   (when (.valid semver electron-version)
     (p/let [info (get-latest-artifact-info repo)]
-      (when-let [remote-version (and info (re-find #"\d+.\d+.\d+" (:url info)))]
+      (when-let [remote-version (and info (re-find #"\d+\.\d+\.\d+" (:url info)))]
         (if (and (. semver valid remote-version)
                  (. semver lt electron-version remote-version))
 

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

@@ -2008,7 +2008,7 @@
        [:div.custom-query.mt-2 (get config :attr {})
         (when-not (and built-in? (empty? result))
           (ui/foldable
-           [:div.opacity-70
+           [:div.opacity-70.custom-query-title
             title]
            (cond
              (and (seq result) view-f)

+ 5 - 3
src/main/frontend/components/lazy_editor.cljs

@@ -1,7 +1,8 @@
 (ns frontend.components.lazy-editor
   (:require [rum.core :as rum]
             [shadow.lazy :as lazy]
-            [frontend.ui :as ui]))
+            [frontend.ui :as ui]
+            [frontend.state :as state]))
 
 (def lazy-editor (lazy/loadable frontend.extensions.code/editor))
 
@@ -14,7 +15,8 @@
                               (reset! loaded? true)))
                  state)}
   [config id attr code options]
-  (let [loaded? (rum/react loaded?)]
+  (let [loaded? (rum/react loaded?)
+        theme (state/sub :ui/theme)]
     (if loaded?
-      (@lazy-editor config id attr code options)
+      (@lazy-editor config id attr code theme options)
       (ui/loading "CodeMirror"))))

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

@@ -308,7 +308,9 @@
         :db-restoring? db-restoring?
         :sidebar-open? sidebar-open?
         :system-theme? system-theme?
-        :on-click      editor-handler/unhighlight-blocks!}
+        :on-click      #(do
+                          (editor-handler/unhighlight-blocks!)
+                          (util/fix-open-external-with-shift! %))}
 
        [:div.theme-inner
         (sidebar-mobile-sidebar

+ 12 - 1
src/main/frontend/db/query_react.cljs

@@ -3,7 +3,9 @@
   (:require [datascript.core :as d]
             [frontend.db.utils :as db-utils :refer [date->int]]
             [frontend.db.model :as model]
+            [frontend.debug :as debug]
             [cljs-time.core :as t]
+            [cljs-time.coerce :as tc]
             [frontend.state :as state]
             [clojure.string :as string]
             [cljs.reader :as reader]
@@ -17,6 +19,10 @@
 (defn- resolve-input
   [input]
   (cond
+    (= :right-now-ms input) (util/time-ms)
+    (= :start-of-today-ms input) (util/today-at-local-ms 0 0 0 0)
+    (= :end-of-today-ms input) (util/today-at-local-ms 24 0 0 0)
+
     (= :today input)
     (date->int (t/today))
     (= :yesterday input)
@@ -103,12 +109,17 @@
 
 (defn react-query
   [repo {:keys [query inputs] :as query'} query-opts]
+  (debug/pprint "================")
+  (debug/pprint "Use the following to debug your datalog queries:")
+  (debug/pprint query')
   (try
     (let [query (resolve-query query)
           inputs (map resolve-input inputs)
           repo (or repo (state/get-current-repo))
           k [:custom query']]
+      (debug/pprint "inputs (post-resolution):" inputs)
+      (debug/pprint "query-opts:" query-opts)
       (apply react/q repo k query-opts query inputs))
     (catch js/Error e
-      (println "Custom query failed: " {:query query'})
+      (debug/pprint "Custom query failed: " {:query query'})
       (js/console.dir e))))

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

@@ -113,8 +113,9 @@
       (let [editor @editor-atom
             doc (.getDoc editor)
             code (nth (:rum/args state) 3)]
-        (.setValue doc code))
-      (let [[config id attr code] (:rum/args state)
+        (.setValue doc code)
+        @editor-atom)
+      (let [[config id attr code theme] (:rum/args state)
             original-mode (get attr :data-lang)
             mode (or original-mode "javascript")
             clojure? (contains? #{"clojure" "clj" "text/x-clojure" "cljs" "cljc"} mode)
@@ -127,7 +128,7 @@
                     (when textarea
                       (from-textarea textarea
                                      #js {:mode mode
-                                          :theme (if dark? "solarized dark" "solarized")
+                                          :theme (str "solarized " theme)
                                           :matchBrackets lisp?
                                           :autoCloseBrackets true
                                           :lineNumbers true
@@ -171,9 +172,10 @@
                 (load-and-render! state)
                 state)
    :did-update (fn [state]
-                 (load-and-render! state)
+                 (when-let [editor @(:editor-atom state)]
+                   (.setOption editor "theme" (str "solarized " (nth (state :rum/args) 4))))
                  state)}
-  [state config id attr code options]
+  [state config id attr code theme options]
   [:div.extensions__code
    [:div.extensions__code-lang
     (let [mode (string/lower-case (get attr :data-lang "javascript"))]

+ 15 - 0
src/main/frontend/util.cljc

@@ -907,6 +907,13 @@
   []
   #?(:cljs (tc/to-long (cljs-time.core/now))))
 
+;; Returns the milliseconds representation of the provided time, in the local timezone.
+;; For example, if you run this function at 10pm EDT in the EDT timezone on May 31st,
+;; it will return 1622433600000, which is equivalent to Mon May 31 2021 00 :00:00.
+#?(:cljs
+   (defn today-at-local-ms [hours mins secs millisecs]
+     (.setHours (js/Date. (.now js/Date)) hours mins secs millisecs)))
+
 (defn d
   [k f]
   (let [result (atom nil)]
@@ -1382,6 +1389,14 @@
                    (count val))]
        (.setRangeText input "" current (inc idx)))))
 
+#?(:cljs
+   (defn fix-open-external-with-shift!
+     [^js/MouseEvent e]
+     (when (and (.-shiftKey e) win32? (electron?)
+                (= (string/lower-case (.. e -target -nodeName)) "a")
+                (string/starts-with? (.. e -target -href) "file:"))
+       (.preventDefault e))))
+
 (defn classnames
   "Like react classnames utility:
 

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

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