Browse Source

Merge branch 'feat/db' of https://github.com/logseq/logseq into feat/db

Konstantinos Kaloutas 1 year ago
parent
commit
f7ac0629c2

+ 1 - 1
.github/workflows/deploy-db-pages.yml

@@ -38,7 +38,7 @@ jobs:
       - name: Build Released-Web
         run: |
           yarn gulp:build && clojure -M:cljs release app  --config-merge '{:compiler-options {:source-map-include-sources-content false :source-map-detail-level :symbols}}'
-          rsync -avz --exclude node_modules --exclude '*.js.map' --exclude android --exclude ios ./static/ ./public/static/
+          rsync -avz --exclude node_modules --exclude android --exclude ios ./static/ ./public/static/
           ls -lR ./public
 
       - name: Publish to Cloudflare Pages

+ 17 - 17
deps/outliner/src/logseq/outliner/core.cljs

@@ -18,7 +18,7 @@
             [logseq.db.sqlite.util :as sqlite-util]
             [cljs.pprint :as pprint]))
 
-(def block-map
+(def ^:private block-map
   (mu/optional-keys
    [:map
     [:db/id :int]
@@ -28,12 +28,12 @@
     [:block/parent :map]
     [:block/page :map]]))
 
-(def block-map-or-entity
+(def ^:private block-map-or-entity
   [:or [:fn de/entity?] block-map])
 
-(defrecord Block [data])
+(defrecord ^:api Block [data])
 
-(defn block
+(defn ^:api block
   [db m]
   (assert (or (map? m) (de/entity? m)) (common-util/format "block data must be map or entity, got: %s %s" (type m) m))
   (let [e (if (or (de/entity? m)
@@ -48,11 +48,11 @@
                        :block/uuid (:block/uuid entity)))))]
     (->Block e)))
 
-(defn get-data
+(defn ^:api get-data
   [block]
   (:data block))
 
-(defn get-block-by-id
+(defn- get-block-by-id
   [db id]
   (let [r (ldb/get-by-id db (outliner-u/->block-lookup-ref id))]
     (when r (->Block r))))
@@ -74,7 +74,7 @@
                 (assoc :block/created-at updated-at))]
     block))
 
-(defn block-with-updated-at
+(defn ^:api block-with-updated-at
   [block]
   (let [updated-at (common-util/time-ms)]
     (assoc block :block/updated-at updated-at)))
@@ -192,7 +192,7 @@
                                       merge-tx))))))
      (reset! (:editor/create-page? @state/state) false))))
 
-(defn rebuild-block-refs
+(defn ^:api rebuild-block-refs
   [repo conn date-formatter block new-properties & {:keys [skip-content-parsing?]}]
   (let [db @conn
         property-key-refs (keys new-properties)
@@ -400,7 +400,7 @@
           children (ldb/get-block-immediate-children @conn parent-id)]
       (map #(block @conn %) children))))
 
-(defn get-right-sibling
+(defn ^:api get-right-sibling
   [db db-id]
   (when db-id
     (ldb/get-right-sibling db db-id)))
@@ -537,7 +537,7 @@
         (mapcat #(tree-seq map? children-key %))
         (map #(dissoc % :block/children)))))
 
-(defn save-block
+(defn ^:api save-block
   "Save the `block`."
   [repo conn date-formatter block']
   {:pre [(map? block')]}
@@ -577,7 +577,7 @@
 
 ;;; ### insert-blocks, delete-blocks, move-blocks
 
-(defn fix-top-level-blocks
+(defn ^:api fix-top-level-blocks
   "Blocks with :block/level"
   [blocks]
   (let [top-level-blocks (filter #(= (:block/level %) 1) blocks)
@@ -694,7 +694,7 @@
       result)))
 
 
-(defn blocks-with-level
+(defn ^:api blocks-with-level
   "Calculate `:block/level` for all the `blocks`. Blocks should be sorted already."
   [blocks]
   {:pre [(seq blocks)]}
@@ -723,7 +723,7 @@
               m' (vec (conj m block))]
           (recur m' (rest blocks)))))))
 
-(defn ^:large-vars/cleanup-todo insert-blocks
+(defn- ^:large-vars/cleanup-todo insert-blocks
   "Insert blocks as children (or siblings) of target-node.
   Args:
     `conn`: db connection.
@@ -844,7 +844,7 @@
                                   non-consecutive-blocks)))) page-blocks)
        (remove nil?)))))
 
-(defn delete-block
+(defn ^:api delete-block
   "Delete block from the tree."
   [repo conn txs-state node {:keys [children? children-check? date-formatter]
                         :or {children-check? true}}]
@@ -933,7 +933,7 @@
                 (:db/id target-block))
              sibling?)))
 
-(defn move-blocks
+(defn- move-blocks
   "Move `blocks` to `target-block` as siblings or children."
   [repo conn blocks target-block {:keys [_sibling? _up? outliner-op _indent?]
                                   :as opts}]
@@ -979,7 +979,7 @@
                 {:tx-data full-tx
                  :tx-meta tx-meta}))))))))
 
-(defn move-blocks-up-down
+(defn- move-blocks-up-down
   "Move blocks up/down."
   [repo conn blocks up?]
   {:pre [(seq blocks) (boolean? up?)]}
@@ -1013,7 +1013,7 @@
           (move-blocks repo conn blocks right (merge opts {:sibling? sibling?
                                                            :up? up?})))))))
 
-(defn ^:large-vars/cleanup-todo indent-outdent-blocks
+(defn- ^:large-vars/cleanup-todo indent-outdent-blocks
   "Indent or outdent `blocks`."
   [repo conn blocks indent? & {:keys [get-first-block-original logical-outdenting?]}]
   {:pre [(seq blocks) (boolean? indent?)]}

+ 1 - 1
deps/outliner/src/logseq/outliner/datascript.cljs

@@ -27,7 +27,7 @@
                                        v)))
                     x)))))
 
-(defn update-refs-and-macros
+(defn- update-refs-and-macros
   "When a block is deleted, refs are updated and macros associated with the block are deleted"
   [txs db repo opts set-state-fn]
   (if (= :delete-blocks (:outliner-op opts))

+ 2 - 2
deps/outliner/src/logseq/outliner/transaction.cljc

@@ -4,12 +4,12 @@
   #?(:cljs (:require-macros [logseq.outliner.transaction]))
   #?(:cljs (:require [malli.core :as m])))
 
-(def transact-opts [:or :symbol :map])
+(def ^:private transact-opts [:or :symbol :map])
 
 #?(:org.babashka/nbb nil
    :cljs (m/=> transact! [:=> [:cat transact-opts :any] :any]))
 
-(defmacro transact!
+(defmacro ^:api transact!
   "Batch all the transactions in `body` to a single transaction, Support nested transact! calls.
   Currently there are no options, it'll execute body and collect all transaction data generated by body.
   If no transactions are included in `body`, it does not save a transaction.

+ 3 - 3
deps/outliner/src/logseq/outliner/tree.cljs

@@ -80,7 +80,7 @@
       (assoc root' :block/children children)
       root')))
 
-(defn block-entity->map
+(defn ^:api block-entity->map
   [e]
   (cond-> {:db/id (:db/id e)
            :block/uuid (:block/uuid e)
@@ -93,7 +93,7 @@
     (:block/children e)
     (assoc :block/children (:block/children e))))
 
-(defn filter-top-level-blocks
+(defn ^:api filter-top-level-blocks
   [blocks]
   (let [id->blocks (zipmap (map :db/id blocks) blocks)]
     (filter #(nil?
@@ -121,7 +121,7 @@
                     (if sorted-nested-children [parent sorted-nested-children] [parent])))
         parents))
 
-(defn sort-blocks
+(defn ^:api sort-blocks
   "sort blocks by parent & left"
   [blocks-exclude-root root]
   (let [parent-groups (atom (group-by :block/parent blocks-exclude-root))]

+ 1 - 1
deps/outliner/src/logseq/outliner/util.cljs

@@ -7,7 +7,7 @@
    [datascript.impl.entity :as e]
    [logseq.common.util :as common-util]))
 
-(defn block-id?
+(defn- block-id?
   [id]
   (or
    (number? id)

+ 3 - 2
deps/publishing/src/logseq/publishing.cljs

@@ -20,7 +20,7 @@ can be passed:
 * :db-graph? - Boolean which indicates if graph is db based
 * :default-notification-fn - Configure how errors are reported when creating the export.
   Default is to throw an exception when it occurs."
-  [db static-dir graph-dir output-dir {:keys [notification-fn]
+  [db static-dir graph-dir output-dir {:keys [notification-fn dev?]
                                        :or {notification-fn default-notification-fn}
                                        :as options}]
   (let [options' (cond-> options
@@ -30,4 +30,5 @@ can be passed:
                    (assoc-in [:app-state :ui/radix-color] (:ui/radix-color options)))
         {:keys [html asset-filenames]} (publish-html/build-html db options')]
     (publish-export/create-export html static-dir graph-dir output-dir {:asset-filenames asset-filenames
-                                                                        :notification-fn notification-fn})))
+                                                                        :notification-fn notification-fn
+                                                                        :dev? dev?})))

+ 18 - 9
deps/publishing/src/logseq/publishing/export.cljs

@@ -22,21 +22,30 @@
 
 (defn- cleanup-js-dir
   "Moves used js files to the correct dir and removes unused js files"
-  [output-static-dir]
+  [output-static-dir source-static-dir {:keys [dev?]}]
   (let [publishing-dir (node-path/join output-static-dir "js" "publishing")]
     (p/let [_ (p/all (map (fn [file]
                             (fs/rmSync (node-path/join output-static-dir "js" file) #js {:force true}))
                           js-files))
+            _ (when dev?
+                (fse/remove (node-path/join output-static-dir "js" "cljs-runtime")))
             _ (p/all (map (fn [file]
-                            (fs/renameSync
-                             (node-path/join publishing-dir file)
-                             (node-path/join output-static-dir "js" file)))
+                            (if dev?
+                              (fs/symlinkSync
+                               (node-path/join source-static-dir "js" "publishing" file)
+                               (node-path/join output-static-dir "js" file))
+                              (fs/renameSync
+                               (node-path/join publishing-dir file)
+                               (node-path/join output-static-dir "js" file))))
                           js-files))
+            _ (when dev?
+                (fs/symlinkSync (node-path/join source-static-dir "js" "publishing" "cljs-runtime")
+                                (node-path/join output-static-dir "js" "cljs-runtime")))
             ;; remove publishing-dir
-            _ (p/all (map (fn [file]
-                            (fs/rmSync (node-path/join publishing-dir file)))
-                          (fs/readdirSync publishing-dir)))
-            _ (fs/rmdirSync publishing-dir)
+            _ (when-not dev? (p/all (map (fn [file]
+                                           (fs/rmSync (node-path/join publishing-dir file)))
+                                         (fs/readdirSync publishing-dir))))
+            _ (when-not dev? (fs/rmdirSync publishing-dir))
             ;; remove source map files
             _ (p/all (map (fn [file]
                             (fs/rmSync (node-path/join output-static-dir "js" (str file ".map")) #js {:force true}))
@@ -90,7 +99,7 @@
                 _ (fs/writeFileSync (node-path/join output-static-dir "css" "custom.css") custom-css)
                 custom-js (if (fs/existsSync custom-js-path) (str (fs/readFileSync custom-js-path)) "")
                 _ (fs/writeFileSync (node-path/join output-static-dir "js" "custom.js") custom-js)
-                _ (cleanup-js-dir output-static-dir)]
+                _ (cleanup-js-dir output-static-dir static-dir options)]
                (notification-fn {:type "success"
                                  :payload (str "Export public pages and publish assets to " output-dir " successfully 🎉")}))
         (p/catch (fn [error]

+ 3 - 0
deps/publishing/src/logseq/publishing/html.cljs

@@ -122,6 +122,9 @@ necessary db filtering"
         }
       }(window.location))"]
             ;; TODO: should make this configurable
+            [:script {:src "static/js/react.production.min.js"}]
+            [:script {:src "static/js/react-dom.production.min.js"}]
+            [:script {:src "static/js/ui.js"}]
             [:script {:src "static/js/shared.js"}]
             [:script {:src "static/js/main.js"}]
             [:script {:src "static/js/interact.min.js"}]

+ 6 - 2
docs/dev-practices.md

@@ -320,10 +320,14 @@ point out:
   ```sh
   # One time setup
   $ cd scripts && yarn install && cd -
-  # Build the export
+  # Build a release export
   $ bb dev:publishing /path/to/graph-dir tmp/publish
+  # OR build a dev export with `clojure -M:cljs watch publishing` and then
+  $ bb dev:publishing /path/to/graph-dir tmp/publish --dev
+
   # View the app in a browser
-  $ open tmp/publish/index.html
+  $ python3 -m http.server 8080 -d tmp/db-publish &; open http://localhost:8080
+
   ```
 
 There are also some tasks under `nbb:` which are useful for inspecting database

+ 4 - 4
scripts/src/logseq/tasks/dev/publishing.cljs

@@ -21,7 +21,7 @@
                        output-path
                        {:repo-config repo-config :ui/theme "dark" :ui/radix-color :purple})))
 
-(defn- publish-db-graph [static-dir graph-dir output-path]
+(defn- publish-db-graph [static-dir graph-dir output-path opts]
   (let [db-name (node-path/basename graph-dir)
         conn (sqlite-db/open-db! (node-path/dirname graph-dir) db-name)
         repo-config (-> (d/q '[:find ?content
@@ -33,16 +33,16 @@
                        static-dir
                        graph-dir
                        output-path
-                       {:repo-config repo-config :db-graph? true :ui/theme "dark" :ui/radix-color :cyan})))
+                       (merge opts {:repo-config repo-config :db-graph? true :ui/theme "dark" :ui/radix-color :cyan}))))
 
 (defn -main
   [& args]
-  (when-not (= 3 (count args))
+  (when (< (count args) 3)
     (println "Usage: $0 STATIC-DIR GRAPH-DIR OUT-DIR")
     (js/process.exit 1))
   (let [[static-dir graph-dir output-path]
         ;; Offset relative paths since they are run in a different directory than user is in
         (map #(if (node-path/isAbsolute %) % (node-path/resolve ".." %)) args)]
     (if (sqlite-cli/db-graph-directory? graph-dir)
-      (publish-db-graph static-dir graph-dir output-path)
+      (publish-db-graph static-dir graph-dir output-path {:dev? (contains? (set args) "--dev")})
       (publish-file-graph static-dir graph-dir output-path))))

+ 2 - 1
shadow-cljs.edn

@@ -43,7 +43,8 @@
                            :externs ["datascript/externs.js"
                                      "externs.js"]
                            :warnings {:fn-deprecated false
-                                      :redef false}}
+                                      :redef false}
+                           :cross-chunk-method-motion false}
         :build-hooks [(shadow.hooks/git-revision-hook "--long --always --dirty")]
         :closure-defines {goog.debug.LOGGING_ENABLED true
                           frontend.modules.instrumentation.sentry/SENTRY-DSN #shadow/env "LOGSEQ_SENTRY_DSN"

+ 3 - 2
src/main/frontend/components/class.cljs

@@ -61,12 +61,13 @@
 
 (rum/defcs configure < rum/reactive
   "Configure a class page"
-  [state page]
+  [state page {:keys [show-title?]
+               :or {show-title? true}}]
   (let [page-id (:db/id page)
         page (when page-id (db/sub-block page-id))]
     (when page
       [:div.property-configure.grid.gap-2
-       [:h1.title.mb-4 "Configure class"]
+       (when show-title? [:h1.title.mb-4 "Configure class"])
 
        [:div.grid.grid-cols-5.gap-1.items-center.class-parent
         [:div.col-span-2 "Parent class:"]

+ 149 - 115
src/main/frontend/components/db_based/page.cljs

@@ -12,38 +12,41 @@
             [frontend.handler.property.util :as pu]
             [frontend.handler.db-based.property.util :as db-pu]
             [frontend.ui :as ui]
-            [frontend.util :as util]
             [frontend.state :as state]
-            [rum.core :as rum]))
+            [rum.core :as rum]
+            [logseq.shui.ui :as shui-ui]
+            [frontend.util :as util]
+            [clojure.set :as set]
+            [clojure.string :as string]))
 
 (rum/defc page-properties < rum/reactive
-  [page {:keys [configure? show-page-properties?]}]
+  [page {:keys [configure? mode]}]
   (let [types (:block/type page)
         class? (contains? types "class")
+        property? (contains? types "property")
         edit-input-id-prefix (str "edit-block-" (:block/uuid page))
         configure-opts {:selected? false
                         :page-configure? true}
         has-viewable-properties? (db-property-handler/block-has-viewable-properties? page)
         has-class-properties? (seq (:properties (:block/schema page)))]
-    (when (or configure? has-viewable-properties? has-class-properties?)
-      [:div.ls-page-properties.mb-4 {:style {:padding 2}}
+    (when (or configure? has-viewable-properties? has-class-properties? property?)
+      [:div.ls-page-properties.mb-4
        (if configure?
          (cond
-           (and class? (not show-page-properties?) (not has-class-properties?))
-           [:div
-            [:div.mb-1 "Class properties:"]
-            (component-block/db-properties-cp {:editor-box editor/box}
-                                              page
-                                              (str edit-input-id-prefix "-schema")
-                                              (assoc configure-opts :class-schema? true))]
+           (and class? has-class-properties? (= :class mode))
+           nil
+
+           (and class? (not has-class-properties?))
+           (component-block/db-properties-cp {:editor-box editor/box}
+                                             page
+                                             (str edit-input-id-prefix "-schema")
+                                             (assoc configure-opts :class-schema? true))
 
            (not (db-property-handler/block-has-viewable-properties? page))
-           [:div
-            [:div.mb-1 "Page properties:"]
-            (component-block/db-properties-cp {:editor-box editor/box}
-                                              page
-                                              (str edit-input-id-prefix "-page")
-                                              (assoc configure-opts :class-schema? false))])
+           (component-block/db-properties-cp {:editor-box editor/box}
+                                             page
+                                             (str edit-input-id-prefix "-page")
+                                             (assoc configure-opts :class-schema? false)))
          (if config/publishing?
            [:div.flex.flex-col.gap-4
             (when has-viewable-properties?
@@ -88,7 +91,7 @@
 
 (rum/defc icon-row < rum/reactive
   [page]
-  [:div.grid.grid-cols-5.gap-1.items-center.leading-8
+  [:div.grid.grid-cols-5.gap-1.items-center
    [:label.col-span-2 "Icon:"]
    (let [icon-value (pu/get-block-property-value page :icon)]
      [:div.col-span-3.flex.flex-row.items-center.gap-2
@@ -109,85 +112,43 @@
                             :title "Delete this icon"}
         (ui/icon "X")])])])
 
-(rum/defcs page-configure-inner <
-  (rum/local false ::show-page-properties?)
-  {:will-unmount (fn [state]
-                   (let [on-unmount (nth (:rum/args state) 1)]
-                     (on-unmount)))}
-  [state page _on-unmount opts]
-  (let [*show-page-properties? (::show-page-properties? state)
+(rum/defc tags
+  [page]
+  (let [tags-property (pu/get-property :tags)]
+    (pv/property-value page tags-property
+                       (map :block/uuid (:block/tags page))
+                       {:page-cp (fn [config page]
+                                   (component-block/page-cp (assoc config :tag? true) page))})))
+
+(rum/defc tags-row < rum/reactive
+  [page]
+  [:div.grid.grid-cols-5.gap-1.items-center
+   [:label.col-span-2 "Tags:"]
+   [:div.col-span-3.flex.flex-row.items-center.gap-2
+    (tags page)]])
+
+(rum/defcs page-configure < rum/reactive
+  [state page *mode]
+  (let [*mode *mode
+        mode (rum/react *mode)
         types (:block/type page)
         class? (contains? types "class")
         property? (contains? types "property")
-        class-or-property? (or class? property?)
-        page-opts {:configure? true
-                   :show-page-properties? @*show-page-properties?}]
-    [:div.flex.flex-col.justify-between.p-4 {:style {:min-width 700
-                                                     :min-height 400}}
-     [:div.flex.flex-col.gap-2
-      (cond
-        (not class-or-property?)
-        (when (and (not class?)
-                   (not property?))
-          [:<>
-           (icon-row page)
-           (page-properties page page-opts)])
-
-        @*show-page-properties?
-        (page-properties page page-opts)
-
-        :else
-        [:<>
-         (when class?
-           (class-component/configure page))
-         (when class?
-           (icon-row page))
-         (when class?
-           (page-properties page page-opts))
-         (when (and property? (not class?))
-           [:h2.title "Configure property"])
-         (when property?
-           (property-component/property-config page page (assoc opts
-                                                                :inline-text component-block/inline-text)))])]
-
-     (when (and class-or-property?
-                (not (db-property-handler/block-has-viewable-properties? page))
-                (not config/publishing?)
-                (empty? (:properties (:block/schema page))))
-       [:a.fade-link.flex.flex-row.items-center.gap-1.text-sm
-        {:on-click #(swap! *show-page-properties? not)}
-        (ui/icon (if @*show-page-properties?
-                   "arrow-narrow-left"
-                   "arrow-narrow-right"))
-        (if @*show-page-properties?
-          "Back"
-          "Edit page properties")])]))
-
-(rum/defc page-configure
-  [page *hover? *configuring?]
-  (when (or @*hover? (and config/publishing? (some #{"class" "property"} (:block/type page))))
-    (let [toggle-fn' (fn [toggle-fn]
-                       (fn []
-                         (toggle-fn)
-                         (reset! *configuring? true)))]
-      (ui/dropdown
-       (fn [{:keys [toggle-fn]}]
-         [:a.fade-link.flex.flex-row.items-center
-          {:on-click (toggle-fn' toggle-fn)}
-          [:div.mr-1.text-sm (if-let [block-type (and config/publishing?
-                                                      (some #{"class" "property"} (:block/type page)))]
-                               (str "More info on this " block-type)
-                               "Configure")]])
-       (fn [{:keys [toggle-fn]}]
-         (page-configure-inner
-          page
-          (fn []
-            (reset! *configuring? false)
-            (reset! *hover? false))
-          {:toggle-fn toggle-fn}))
-
-       {:modal-class (util/hiccup->class
-                      "origin-top-right.absolute.left-0.mt-2.rounded-md.shadow-lg")}))))
+        page-opts {:configure? true}]
+    (when (nil? mode)
+      (reset! *mode (cond
+                      property? :property
+                      class? :class
+                      :else :page)))
+    [:div.flex.flex-col.gap-1
+     (if (= mode :property)
+       (property-component/property-config page page {:inline-text component-block/inline-text})
+       [:<>
+        (when (= mode :class)
+          (class-component/configure page {:show-title? false}))
+        (when-not config/publishing? (tags-row page))
+        (when-not config/publishing? (icon-row page))
+        (page-properties page (assoc page-opts :mode mode))])]))
 
 (rum/defc page-properties-react < rum/reactive
   [page* page-opts]
@@ -197,23 +158,96 @@
               (some #{"class" "property"} (:block/type page)))
       (page-properties page page-opts))))
 
-(rum/defc page-tags <
-  [page tags-property *hover? *configuring?]
-  (let [toggle-fn' (fn [toggle-fn]
-                     (fn []
-                       (toggle-fn)
-                       (swap! *configuring? not)))]
-    (ui/dropdown
-     (fn [{:keys [toggle-fn]}]
-       [:a.fade-link.flex.flex-row.items-center
-        {:on-click (toggle-fn' toggle-fn)}
-        [:div.ml-1.text-sm "Set tags"]])
-     (fn [{:keys [toggle-fn]}]
-       (pv/property-value page tags-property nil {:on-chosen (toggle-fn' toggle-fn)
-                                                  :dropdown? false}))
-     {:modal-class (util/hiccup->class
-                    "origin-top-right.absolute.left-0.mt-2.rounded-md.shadow-lg")
-      :on-toggle (fn [value]
-                   (when (false? value)
-                     (reset! *configuring? false)
-                     (reset! *hover? false)))})))
+(rum/defc mode-switch < rum/reactive
+  [types *mode]
+  (let [current-mode (rum/react *mode)
+        property? (contains? types "property")
+        class? (contains? types "class")
+        modes (->
+               (cond
+                 (and property? class?)
+                 ["Property" "Class"]
+                 property?
+                 ["Property"]
+                 class?
+                 ["Class"]
+                 :else
+                 [])
+               (conj "Page"))]
+    [:div.flex.flex-row.items-center.gap-1
+     (for [mode modes]
+       (let [mode' (keyword (string/lower-case mode))
+             selected? (= mode' current-mode)]
+         (shui-ui/button {:variant (if selected? :outline :ghost) :size :sm
+                          :on-click (if config/publishing?
+                                      util/stop-propagation
+                                      (fn [e]
+                                        (util/stop-propagation e)
+                                        (reset! *mode mode')))}
+                         mode)))]))
+
+(rum/defcs page-info < rum/reactive
+  (rum/local false ::hover?)
+  (rum/local nil ::mode)
+  {:init (if config/publishing?
+           (fn [state]
+             (let [page* (first (:rum/args state))
+                   page (db/sub-block (:db/id page*))]
+               (assoc state
+                      ::collapsed?
+                      (atom (not (seq (set/intersection #{"class" "property"} (:block/type page))))))))
+           (fn [state]
+             (let [page (first (:rum/args state))
+                   properties (:block/properties page)]
+               (assoc state ::collapsed? (atom (empty? properties))))))}
+  [state page *hover-title?]
+  (let [page (db/sub-block (:db/id page))
+        *collapsed? (::collapsed? state)
+        *hover? (::hover? state)
+        *mode (::mode state)
+        types (:block/type page)
+        hover-title? (rum/react *hover-title?)
+        collapsed? (rum/react *collapsed?)
+        has-tags? (seq (:block/tags page))
+        hover-or-expanded? (or @*hover? hover-title? (not collapsed?))]
+    (when (if config/publishing?
+            ;; Since publishing is read-only, hide this component if it has no info to show
+            ;; as it creates a fair amount of empty vertical space
+            (or has-tags? (some? types))
+            true)
+     [:div.page-info {:on-mouse-over #(reset! *hover? true)
+                      :on-mouse-leave #(reset! *hover? false)}
+      (when (or hover-or-expanded? has-tags?)
+        [:div.fade-in.p-2 (cond-> {}
+                            (or @*hover? (not collapsed?))
+                            (assoc :class "border rounded"))
+         [:div.info-title.cursor {:on-click
+                                  (if config/publishing?
+                                    (fn [_]
+                                      (when (seq (set/intersection #{"class" "empty"} types))
+                                        (swap! *collapsed? not)))
+                                    #(swap! *collapsed? not))}
+          [:div.flex.flex-row.items-center.gap-2.justify-between
+           [:div.flex.flex-row.items-center.gap-2
+            (if collapsed?
+              [:<>
+               (shui-ui/button {:variant :ghost :size :sm :class "fade-link"}
+                               (ui/icon "tags"))
+               [:div {:on-click util/stop-propagation}
+                (tags page)]]
+              [:div.flex.flex-row.items-center.gap-1
+               (shui-ui/button {:variant :ghost :size :sm :class "fade-link"}
+                               (ui/icon "info-circle"))
+               [:a.text-sm.font-medium.fade-link
+                "Configure:"]
+               (mode-switch types *mode)])]
+           (when (or @*hover? (not collapsed?))
+             (shui-ui/button
+              {:variant :ghost :size :sm :class "fade-link"}
+              (ui/icon (if collapsed?
+                         "chevron-down"
+                         "chevron-up"))))]]
+
+         (when-not collapsed?
+           [:div.py-2.px-4
+            (page-configure page *mode)])])])))

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

@@ -177,7 +177,8 @@
      [:button.flex {:on-click #(when-not disabled? (toggle-fn))}
       (if icon-value
         (icon icon-value)
-        [:span.bullet-container.cursor [:span.bullet]])])
+        [:div.opacity-50.text-sm
+         "Empty"])])
    (if config/publishing?
      (constantly [])
      (fn [{:keys [toggle-fn]}]

+ 65 - 71
src/main/frontend/components/page.cljs

@@ -10,7 +10,6 @@
             [frontend.components.reference :as reference]
             [frontend.components.scheduled-deadlines :as scheduled]
             [frontend.components.icon :as icon-component]
-            [frontend.components.property.value :as pv]
             [frontend.components.db-based.page :as db-page]
             [frontend.handler.property.util :as pu]
             [frontend.handler.db-based.property :as db-property-handler]
@@ -312,24 +311,20 @@
 (rum/defcs ^:large-vars/cleanup-todo page-title < rum/reactive
   (rum/local false ::edit?)
   (rum/local "" ::input-value)
-  (rum/local false ::hover?)
-  (rum/local false ::configuring?)
   {:init (fn [state]
            (let [page-name (first (:rum/args state))
                  original-name (:block/original-name (db/entity [:block/name (util/page-name-sanity-lc page-name)]))
                  *title-value (atom original-name)]
              (assoc state ::title-value *title-value)))}
-  [state page-name {:keys [fmt-journal? preview?]}]
+  [state page-name {:keys [fmt-journal? preview? *hover?]}]
   (when page-name
     (let [page (when page-name (db/entity [:block/name page-name]))
           page (db/sub-block (:db/id page))
           title (:block/original-name page)]
       (when title
         (let [icon (pu/lookup (:block/properties page) :icon)
-              *hover? (::hover? state)
               *title-value (get state ::title-value)
               *edit? (get state ::edit?)
-              *configuring? (::configuring? state)
               *input-value (get state ::input-value)
               repo (state/get-current-repo)
               hls-page? (pdf-utils/hls-file? title)
@@ -341,12 +336,10 @@
                         (date/journal-title->custom-format title)
                         title))
               old-name (or title page-name)
-              db-based? (config/db-based-graph? repo)
-              tags-property (db/entity [:block/name "tags"])]
+              db-based? (config/db-based-graph? repo)]
           [:div.ls-page-title.flex.flex-1.flex-row.flex-wrap.w-full.relative.items-center.gap-2
            {:on-mouse-over #(reset! *hover? true)
-            :on-mouse-out #(when-not @*configuring?
-                             (reset! *hover? false))}
+            :on-mouse-out #(reset! *hover? false)}
            (when icon
              [:div.page-icon {:on-mouse-down util/stop-propagation}
               (if (and (map? icon) db-based?)
@@ -358,66 +351,61 @@
                                                              (:block/uuid page)
                                                              {:properties {icon-property-id icon}})))})
                 icon)])
-           [:div.flex.flex-1.flex-row.flex-wrap.items-center.gap-4
-            [:h1.page-title.flex-1.cursor-pointer.gap-1
-             {:class (when-not whiteboard-page? "title")
-              :on-mouse-down (fn [e]
-                               (when (util/right-click? e)
-                                 (state/set-state! :page-title/context {:page page-name})))
-              :on-click (fn [e]
-                          (when-not (= (.-nodeName (.-target e)) "INPUT")
-                            (.preventDefault e)
-                            (if (gobj/get e "shiftKey")
-                              (when-let [page (db/pull repo '[*] [:block/name page-name])]
-                                (state/sidebar-add-block!
-                                 repo
-                                 (:db/id page)
-                                 :page))
-                              (when (and (not hls-page?)
-                                         (not fmt-journal?)
-                                         (not config/publishing?)
-                                         (not (and (contains? (:block/type page) "property")
-                                                   (contains? db-property/built-in-properties-keys-str page-name))))
-                                (reset! *input-value (if untitled? "" old-name))
-                                (reset! *edit? true)))))}
-
-             (if @*edit?
-               (page-title-editor page {:*title-value *title-value
-                                        :*edit? *edit?
-                                        :*input-value *input-value
-                                        :page-name page-name
-                                        :old-name old-name
-                                        :untitled? untitled?
-                                        :whiteboard-page? whiteboard-page?
-                                        :preview? preview?})
-               [:span.title.block
-                {:on-click (fn []
-                             (when (and (state/home?) (not preview?))
-                               (route-handler/redirect-to-page! page-name)))
-                 :data-value @*input-value
-                 :data-ref   page-name
-                 :style      {:opacity (when @*edit? 0)}}
-                (let [nested? (and (string/includes? title page-ref/left-brackets)
-                                   (string/includes? title page-ref/right-brackets))]
-                  (cond untitled? [:span.opacity-50 (t :untitled)]
-                        nested? (component-block/map-inline {} (gp-mldoc/inline->edn title (mldoc/get-default-config
-                                                                                            (:block/format page))))
-                        :else title))])]
-            (when (and db-based? (seq (:block/tags page)))
-              [:div.page-tags.ml-4
-               (pv/property-value page tags-property (map :block/uuid (:block/tags page))
-                                  {:page-cp (fn [config page]
-                                              (component-block/page-cp (assoc config :tag? true) page))})])]
-
-           (when (and db-based? (not whiteboard-page?))
-             [:div.absolute.bottom-2.left-0
-              [:div.page-add-tags.flex.flex-row.items-center.flex-wrap.gap-2.ml-2
-               (when (and (empty? (:block/tags page)) @*hover? (not config/publishing?))
-                 (db-page/page-tags page tags-property *hover? *configuring?))
-
-               (when (or (some #(contains? #{"class" "property"} %) (:block/type page))
-                         (not (db-property-handler/block-has-viewable-properties? page)))
-                 (db-page/page-configure page *hover? *configuring?))]])])))))
+           [:h1.page-title.flex-1.cursor-pointer.gap-1
+            {:class (when-not whiteboard-page? "title")
+             :on-mouse-down (fn [e]
+                              (when (util/right-click? e)
+                                (state/set-state! :page-title/context {:page page-name})))
+             :on-click (fn [e]
+                         (when-not (= (.-nodeName (.-target e)) "INPUT")
+                           (.preventDefault e)
+                           (if (gobj/get e "shiftKey")
+                             (when-let [page (db/pull repo '[*] [:block/name page-name])]
+                               (state/sidebar-add-block!
+                                repo
+                                (:db/id page)
+                                :page))
+                             (when (and (not hls-page?)
+                                        (not fmt-journal?)
+                                        (not config/publishing?)
+                                        (not (and (contains? (:block/type page) "property")
+                                                  (contains? db-property/built-in-properties-keys-str page-name))))
+                               (reset! *input-value (if untitled? "" old-name))
+                               (reset! *edit? true)))))}
+
+            (if @*edit?
+              (page-title-editor page {:*title-value *title-value
+                                       :*edit? *edit?
+                                       :*input-value *input-value
+                                       :page-name page-name
+                                       :old-name old-name
+                                       :untitled? untitled?
+                                       :whiteboard-page? whiteboard-page?
+                                       :preview? preview?})
+              [:span.title.block
+               {:on-click (fn []
+                            (when (and (state/home?) (not preview?))
+                              (route-handler/redirect-to-page! page-name)))
+                :data-value @*input-value
+                :data-ref   page-name
+                :style      {:opacity (when @*edit? 0)}}
+               (let [nested? (and (string/includes? title page-ref/left-brackets)
+                                  (string/includes? title page-ref/right-brackets))]
+                 (cond untitled? [:span.opacity-50 (t :untitled)]
+                       nested? (component-block/map-inline {} (gp-mldoc/inline->edn title (mldoc/get-default-config
+                                                                                           (:block/format page))))
+                       :else title))])]
+
+           ;; (when (and db-based? (not whiteboard-page?))
+           ;;   [:div.absolute.bottom-2.left-0
+           ;;    [:div.page-add-tags.flex.flex-row.items-center.flex-wrap.gap-2.ml-2
+           ;;     (when (and (empty? (:block/tags page)) @*hover? (not config/publishing?))
+           ;;       (db-page/page-tags page tags-property *hover? *configuring?))
+
+           ;;     (when (or (some #(contains? #{"class" "property"} %) (:block/type page))
+           ;;               (not (db-property-handler/block-has-viewable-properties? page)))
+           ;;       (db-page/page-configure page *hover? *configuring?))]])
+           ])))))
 
 (defn- page-mouse-over
   [e *control-show? *all-collapsed?]
@@ -472,6 +460,7 @@
   (rum/local false ::all-collapsed?)
   (rum/local false ::control-show?)
   (rum/local nil   ::current-page)
+  (rum/local false ::hover-title?)
   [state {:keys [repo page-name preview? sidebar?] :as option}]
   (when-let [path-page-name (get-path-page-name state page-name)]
     (let [current-repo (state/sub :git/current-repo)
@@ -524,13 +513,18 @@
                  (when (and (not whiteboard?) original-name)
                    (page-title page-name {:journal? journal?
                                           :fmt-journal? fmt-journal?
-                                          :preview? preview?})))
+                                          :preview? preview?
+                                          :*hover? (::hover-title? state)})))
                (when (not config/publishing?)
                  (when config/lsp-enabled?
                    [:div.flex.flex-row
                     (plugins/hook-ui-slot :page-head-actions-slotted nil)
                     (plugins/hook-ui-items :pagebar)]))])
 
+            (when db-based?
+              [:div.pb-4
+               (db-page/page-info page (::hover-title? state))])
+
             [:div
              (when (and block? (not sidebar?) (not whiteboard?))
                (let [config {:id "block-parent"

+ 7 - 3
src/main/frontend/components/page.css

@@ -239,9 +239,8 @@
 .ls-page-title {
   @apply rounded-sm;
 
-  padding: 5px 8px;
-  padding-bottom: 2rem;
-  margin: 0 -6px 12px -6px;
+  padding: 5px 8px 12px 8px;
+  margin: 0 -6px;
 
   &.title {
     margin-bottom: 12px;
@@ -408,3 +407,8 @@ html.is-native-ios {
 .ring-none {
     @apply focus:ring-0 focus:ring-offset-0;
 }
+
+.page-info {
+    min-height: 46px;
+    margin-left: -21px;
+}

+ 0 - 1
src/main/frontend/components/property.css

@@ -1,6 +1,5 @@
 .property-configure {
     min-width: 32rem;
-    padding: 2px;
 
     .form-input, .form-select {
         line-height: 1rem;

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

@@ -269,7 +269,7 @@
                             e
                             (content/page-title-custom-context-menu-content page-name))
                            (state/set-state! :page-title/context nil))}
-       (page/page-title page-name {:*configure-show? (atom false)})]
+       (page/page-title page-name {:*hover? (atom false)})]
 
       [:div.whiteboard-page-refs
        (references-count page-name

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

@@ -140,8 +140,8 @@
   [repo]
   (if @*publishing?
     (p/let [^object DB (.-DB ^object (.-oo1 ^object @*sqlite))
-            db (new DB "/db.sqlite" "ct")
-            search-db (new DB "/search-db.sqlite" "ct")]
+            db (new DB "/db.sqlite" "c")
+            search-db (new DB "/search-db.sqlite" "c")]
       [db search-db])
     (p/let [^js pool (<get-opfs-pool repo)
             capacity (.getCapacity pool)

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

@@ -811,7 +811,7 @@
 (defmethod handle :class/configure [[_ page]]
   (state/set-modal!
    #(vector :<>
-            (class-component/configure page)
+            (class-component/configure page {})
             (db-page/page-properties page {:configure? true}))
    {:id :page-configure
     :label "page-configure"

+ 0 - 1
src/resources/dicts/es.edn

@@ -395,7 +395,6 @@
  :linked-references/filter-includes                 "Incluye: "
  :linked-references/filter-search                   "Buscar en las páginas vinculadas"
  :linked-references/reference-count (fn [filtered-count total] (str (when filtered-count (str filtered-count " de ")) total (if (= total 1) " Referencia vinculada" " Referencias vinculadas")))
- :linked-references/unexpected-error                "Referencias vinculadas: Error inesperado. Reindexa tu gráfico primero."
  :notification/clear-all                            "Limpiar todo"
  :on-boarding/add-graph                             "Añadir grafo"
  :on-boarding/command-palette-quick-tour            "Tour rápido para acostumbrarse"

+ 0 - 1
src/resources/dicts/nb-no.edn

@@ -776,7 +776,6 @@
  :linked-references/filter-heading "Filter"
  :linked-references/filter-includes "Inkluderer: "
  :linked-references/reference-count (fn [filtered-count total] (str (when filtered-count (str filtered-count " av ")) total (if (= total 1) " Lenket Referanse" " Lenkede Referanser")))
- :linked-references/unexpected-error "Lenkede Referanser: Uventet feil. Vennligst re-indekser din graf først."
  :pdf/auto-open-context-menu "Auto-åpne kontekstmeny for valg"
  :unlinked-references/reference-count (fn [total] (str total (if (= total 1) " Ulenket Referanse" " Ulenkede Referanser")))
  :whiteboard/reference-count (fn [refs-count] (if (= refs-count 1) "Referanse" "Referanser"))}

+ 1 - 4
src/test/frontend/handler/export_test.cljs

@@ -1,12 +1,9 @@
 (ns frontend.handler.export-test
-  (:require [cljs.test :refer [are async deftest is use-fixtures]]
-            [clojure.edn :as edn]
+  (:require [cljs.test :refer [are async deftest use-fixtures]]
             [clojure.string :as string]
-            [frontend.handler.export :as export]
             [frontend.handler.export.text :as export-text]
             [frontend.state :as state]
             [frontend.test.helper :as test-helper :include-macros true :refer [deftest-async]]
-            [logseq.db.frontend.default :as default-db]
             [promesa.core :as p]))
 
 (def test-files

+ 1 - 2
src/test/frontend/worker/rtc/asset_sync_effects_test.cljs

@@ -1,8 +1,7 @@
 (ns frontend.worker.rtc.asset-sync-effects-test
   "This ns include tests abouts asset-sync with other components.
   These tests need to start the asset-sync-loop."
-  (:require [cljs.core.async :as async :refer [<! >! go timeout]]
-            [clojure.test :as t :refer [deftest is use-fixtures]]
+  (:require [clojure.test :as t :refer [deftest is use-fixtures]]
             [frontend.test.helper :include-macros true :as test-helper]
             [frontend.worker.rtc.fixture :as rtc-fixture]
             [spy.core :as spy]))

+ 1 - 1
typos.toml

@@ -18,4 +18,4 @@ fom = "fom"
 tne = "tne"
 Damon = "Damon"
 [files]
-extend-exclude = ["resources/*", "src/resources/*", "scripts/resources/*"]
+extend-exclude = ["resources/*", "src/resources/*", "scripts/resources/*", "e2e-tests/plugin/lsplugin.user.js"]