From bc4790541696a27978642736f6502fde413362b5 Mon Sep 17 00:00:00 2001 From: Gabriel Horner Date: Mon, 31 Mar 2025 10:44:17 -0400 Subject: [PATCH 01/20] fix: can't navigate to journal page in file graphs Occurred when home page was configured Fixes https://test.logseq.com/#/page/67ea96a6-067c-4569-a02a-6ff8d43619a0 --- src/main/frontend/components/page.cljs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/frontend/components/page.cljs b/src/main/frontend/components/page.cljs index 13ef877453..16ba6dedca 100644 --- a/src/main/frontend/components/page.cljs +++ b/src/main/frontend/components/page.cljs @@ -414,7 +414,8 @@ :preview? preview?}) [:span.title.block {:on-click (fn [] - (when (and (state/home?) (not preview?)) + (when (and (not preview?) + (contains? #{:home :all-journals} (get-in (state/get-route-match) [:data :name]))) (route-handler/redirect-to-page! (:block/uuid page)))) :data-value @*input-value :data-ref (:block/title page) From 1e29c9ee15ec95858e78a34d948ff514548f53a1 Mon Sep 17 00:00:00 2001 From: Gabriel Horner Date: Mon, 31 Mar 2025 17:04:45 -0400 Subject: [PATCH 02/20] enhance: add sqlite.build support for properties on property values Needed for export in https://github.com/logseq/db-test/issues/242 --- .../logseq/db/frontend/property/build.cljs | 57 +++++++++++-------- deps/db/src/logseq/db/sqlite/build.cljs | 50 +++++++++------- deps/db/test/logseq/db/sqlite/build_test.cljs | 20 ++++++- 3 files changed, 80 insertions(+), 47 deletions(-) diff --git a/deps/db/src/logseq/db/frontend/property/build.cljs b/deps/db/src/logseq/db/frontend/property/build.cljs index 6a1eb70ef5..4201b73901 100644 --- a/deps/db/src/logseq/db/frontend/property/build.cljs +++ b/deps/db/src/logseq/db/frontend/property/build.cljs @@ -65,26 +65,30 @@ (into [property-tx] (closed-values->blocks property)))) -(defn- build-property-value-block +(defn build-property-value-block "Builds a property value entity given a block map/entity, a property entity or ident and its property value" - [block property value & {:keys [block-uuid]}] + [block property value & {:keys [block-uuid property-value-properties]}] (let [block-id (or (:db/id block) (:db/ident block))] - (-> (merge - {:block/uuid (or block-uuid (common-uuid/gen-uuid)) - :block/page (if (:block/page block) - (:db/id (:block/page block)) + (cond-> + (merge + {:block/uuid (or block-uuid (common-uuid/gen-uuid)) + :block/page (if (:block/page block) + (:db/id (:block/page block)) ;; page block - block-id) - :block/parent block-id - :logseq.property/created-from-property (if (= (:db/ident property) :logseq.property/default-value) - block-id - (or (:db/id property) {:db/ident (:db/ident property)})) - :block/order (db-order/gen-key)} - (if (db-property-type/property-value-content? (:logseq.property/type property) property) - {:logseq.property/value value} - {:block/title value})) - common-util/block-with-timestamps))) + block-id) + :block/parent block-id + :logseq.property/created-from-property (if (= (:db/ident property) :logseq.property/default-value) + block-id + (or (:db/id property) {:db/ident (:db/ident property)})) + :block/order (db-order/gen-key)} + (if (db-property-type/property-value-content? (:logseq.property/type property) property) + {:logseq.property/value value} + {:block/title value})) + true + common-util/block-with-timestamps + property-value-properties + (merge property-value-properties)))) (defn build-property-values-tx-m "Builds a map of property names to their property value blocks to be @@ -99,7 +103,7 @@ (let [block' (if (:db/id block) block (assoc block :db/id [:block/uuid (:block/uuid block)]))] (->> properties (map (fn [[k v]] - (let [property-map (if (map? k) k {:db/ident k}) + (let [{:keys [property-value-properties] :as property-map} (if (map? k) k {:db/ident k}) gen-uuid-value-prefix (when pure? (or (:db/ident block) (:block/uuid block)))] (assert (:db/ident property-map) "Key in map must have a :db/ident") @@ -108,15 +112,20 @@ (if (set? v) (set (map #(build-property-value-block block' property-map % - (when pure? - {:block-uuid - (common-uuid/gen-uuid :builtin-block-uuid (str gen-uuid-value-prefix "-" %))})) + (cond-> {} + property-value-properties + (assoc :property-value-properties property-value-properties) + pure? + (assoc :block-uuid + (common-uuid/gen-uuid :builtin-block-uuid (str gen-uuid-value-prefix "-" %))))) v)) (build-property-value-block block' property-map v - (when pure? - {:block-uuid - (common-uuid/gen-uuid - :builtin-block-uuid (str gen-uuid-value-prefix "-" v))})))]))) + (cond-> {} + property-value-properties + (assoc :property-value-properties property-value-properties) + pure? + (assoc :block-uuid + (common-uuid/gen-uuid :builtin-block-uuid (str gen-uuid-value-prefix "-" v))))))]))) (into {})))) (defn build-properties-with-ref-values diff --git a/deps/db/src/logseq/db/sqlite/build.cljs b/deps/db/src/logseq/db/sqlite/build.cljs index 2b3887bd64..25caaf8dba 100644 --- a/deps/db/src/logseq/db/sqlite/build.cljs +++ b/deps/db/src/logseq/db/sqlite/build.cljs @@ -99,6 +99,27 @@ "Provides the next temp :db/id to use in a create-graph transact!" #(swap! current-db-id dec)) +(defn- build-property-map-for-pvalue-tx [k v new-block properties-config all-idents] + (if-let [built-in-type (get-in db-property/built-in-properties [k :schema :type])] + (if (and (db-property-type/value-ref-property-types built-in-type) + ;; closed values are referenced by their :db/ident so no need to create values + (not (get-in db-property/built-in-properties [k :closed-values]))) + {:db/ident k + :logseq.property/type built-in-type} + (when-let [built-in-type' (get (or (:build/properties-ref-types new-block) + ;; Reasonable default for properties like logseq.property/default-value + {:entity :number}) + built-in-type)] + {:db/ident k + :logseq.property/type built-in-type'})) + (when (and (db-property-type/value-ref-property-types (get-in properties-config [k :logseq.property/type])) + ;; TODO: Support translate-property-value without this hack + (not (vector? v))) + (let [prop-type (get-in properties-config [k :logseq.property/type])] + {:db/ident (get-ident all-idents k) + :original-property-id k + :logseq.property/type prop-type})))) + (defn- ->property-value-tx-m "Given a new block and its properties, creates a map of properties which have values of property value tx. This map is used for both creating the new property values and then adding them to a block. @@ -106,28 +127,13 @@ [new-block properties properties-config all-idents] (->> properties (keep (fn [[k v]] - (if-let [built-in-type (get-in db-property/built-in-properties [k :schema :type])] - (if (and (db-property-type/value-ref-property-types built-in-type) - ;; closed values are referenced by their :db/ident so no need to create values - (not (get-in db-property/built-in-properties [k :closed-values]))) - (let [property-map {:db/ident k - :logseq.property/type built-in-type}] - [property-map v]) - (when-let [built-in-type' (get (or (:build/properties-ref-types new-block) - ;; Reasonable default for properties like logseq.property/default-value - {:entity :number}) - built-in-type)] - (let [property-map {:db/ident k - :logseq.property/type built-in-type'}] - [property-map v]))) - (when (and (db-property-type/value-ref-property-types (get-in properties-config [k :logseq.property/type])) - ;; TODO: Support translate-property-value without this hack - (not (vector? v))) - (let [prop-type (get-in properties-config [k :logseq.property/type]) - property-map {:db/ident (get-ident all-idents k) - :original-property-id k - :logseq.property/type prop-type}] - [property-map v]))))) + (when-let [property-map (build-property-map-for-pvalue-tx k v new-block properties-config all-idents)] + [(cond-> property-map + (and (:build/property-value v) (:build/properties v)) + (assoc :property-value-properties (:build/properties v))) + (if (:build/property-value v) + (or (:logseq.property/value v) (:block/title v)) + v)]))) (db-property-build/build-property-values-tx-m new-block))) (defn- extract-basic-content-refs diff --git a/deps/db/test/logseq/db/sqlite/build_test.cljs b/deps/db/test/logseq/db/sqlite/build_test.cljs index 0e7e3d883c..6149ca931a 100644 --- a/deps/db/test/logseq/db/sqlite/build_test.cljs +++ b/deps/db/test/logseq/db/sqlite/build_test.cljs @@ -205,4 +205,22 @@ (->> (d/q '[:find [?b ...] :in $ ?page-id :where [?b :block/page ?page-id]] @conn [:block/uuid property-uuid]) (map #(:block/title (d/entity @conn %))))) - "Property page has correct blocks"))) \ No newline at end of file + "Property page has correct blocks"))) + +(deftest property-value-properties + (let [conn (db-test/create-conn-with-blocks + [{:page {:block/title "page1"} + :blocks [{:block/title "Todo query", + :build/tags [:logseq.class/Query], + :build/properties + {:logseq.property/query + {:build/property-value :block + :block/title "{:query (task Todo)}" + :build/properties + {:logseq.property.code/lang "clojure" + :logseq.property.node/display-type :code}}}}]}])] + (is (= {:logseq.property.node/display-type :code + :logseq.property.code/lang "clojure"} + (-> (db-test/find-block-by-content @conn "{:query (task Todo)}") + db-test/readable-properties + (dissoc :logseq.property/created-from-property)))))) \ No newline at end of file From 29ac5a9c187d67e97691a2b4509fa84f0b509158 Mon Sep 17 00:00:00 2001 From: Gabriel Horner Date: Mon, 31 Mar 2025 18:24:11 -0400 Subject: [PATCH 03/20] enhance: export+import property value properties For https://github.com/logseq/db-test/issues/242. Also added timestamp support for proprety values --- deps/db/src/logseq/db/sqlite/build.cljs | 6 ++-- deps/db/src/logseq/db/sqlite/export.cljs | 33 +++++++++++++++---- .../db/test/logseq/db/sqlite/export_test.cljs | 11 ++++++- 3 files changed, 40 insertions(+), 10 deletions(-) diff --git a/deps/db/src/logseq/db/sqlite/build.cljs b/deps/db/src/logseq/db/sqlite/build.cljs index 25caaf8dba..6637a2affe 100644 --- a/deps/db/src/logseq/db/sqlite/build.cljs +++ b/deps/db/src/logseq/db/sqlite/build.cljs @@ -129,8 +129,10 @@ (keep (fn [[k v]] (when-let [property-map (build-property-map-for-pvalue-tx k v new-block properties-config all-idents)] [(cond-> property-map - (and (:build/property-value v) (:build/properties v)) - (assoc :property-value-properties (:build/properties v))) + (and (:build/property-value v) + (or (:build/properties v) (seq (select-keys v [:block/created-at :block/updated-at])))) + (assoc :property-value-properties + (merge (:build/properties v) (select-keys v [:block/created-at :block/updated-at])))) (if (:build/property-value v) (or (:logseq.property/value v) (:block/title v)) v)]))) diff --git a/deps/db/src/logseq/db/sqlite/export.cljs b/deps/db/src/logseq/db/sqlite/export.cljs index 27c2d39cbd..8b59ce6bd3 100644 --- a/deps/db/src/logseq/db/sqlite/export.cljs +++ b/deps/db/src/logseq/db/sqlite/export.cljs @@ -12,7 +12,8 @@ [logseq.db.frontend.entity-plus :as entity-plus] [logseq.db.frontend.entity-util :as entity-util] [logseq.db.frontend.property :as db-property] - [logseq.db.sqlite.build :as sqlite-build])) + [logseq.db.sqlite.build :as sqlite-build] + [medley.core :as medley])) ;; Export fns ;; ========== @@ -37,9 +38,11 @@ {:build/journal (:block/journal-day page-entity)} {:block/title (block-title page-entity)})) +(declare buildable-properties) + (defn- buildable-property-value-entity "Converts property value to a buildable version" - [property-ent pvalue {:keys [property-value-uuids?]}] + [db property-ent pvalue {:keys [property-value-uuids?] :as options}] (cond (and (not property-value-uuids?) (ldb/internal-page? pvalue)) ;; Should page properties be pulled here? [:build/page (cond-> (shallow-copy-page pvalue) @@ -55,9 +58,25 @@ ;; Use metadata distinguish from block references that don't exist like closed values ^::existing-property-value? [:block/uuid (:block/uuid pvalue)]) (or (:db/ident pvalue) - ;; nbb-compatible version of db-property/property-value-content - (or (block-title pvalue) - (:logseq.property/value pvalue)))))) + (let [ent-properties* (->> (apply dissoc (db-property/properties pvalue) + :logseq.property/value :logseq.property/created-from-property + db-property/public-db-attribute-properties) + ;; TODO: Allow user properties when sqlite.build supports it + (medley/filter-keys db-property/internal-property?)) + ent-properties (when (and (not (:block/closed-value-property pvalue)) (seq ent-properties*)) + ;; TODO: Support closed values as needed + (buildable-properties db ent-properties* {} options))] + (if (seq ent-properties) + (cond-> + {:build/property-value :block + :block/title (or (block-title pvalue) + (:logseq.property/value pvalue)) + :build/properties ent-properties} + (:include-timestamps? options) + (merge (select-keys pvalue [:block/created-at :block/updated-at]))) + ;; nbb-compatible version of db-property/property-value-content + (or (block-title pvalue) + (:logseq.property/value pvalue)))))))) (defn- buildable-properties "Originally copied from db-test/readable-properties. Modified so that property values are @@ -74,10 +93,10 @@ (throw (ex-info (str "No closed value found for content: " (pr-str (db-property/property-value-content v))) {:properties properties-config}))) (cond (de/entity? v) - (buildable-property-value-entity (d/entity db k) v options) + (buildable-property-value-entity db (d/entity db k) v options) (and (set? v) (every? de/entity? v)) (let [property-ent (d/entity db k)] - (set (map #(buildable-property-value-entity property-ent % options) v))) + (set (map #(buildable-property-value-entity db property-ent % options) v))) :else v))])) (into {}))) diff --git a/deps/db/test/logseq/db/sqlite/export_test.cljs b/deps/db/test/logseq/db/sqlite/export_test.cljs index ed75c41dcd..90d9e50de1 100644 --- a/deps/db/test/logseq/db/sqlite/export_test.cljs +++ b/deps/db/test/logseq/db/sqlite/export_test.cljs @@ -590,7 +590,16 @@ :user.property/default-closed [:block/uuid closed-value-uuid] :user.property/date [:block/uuid journal-uuid]}} {:block/title "b2" :build/properties {:user.property/node #{[:block/uuid page-object-uuid]}}} - {:block/title "b3" :build/properties {:user.property/node #{[:block/uuid page-object-uuid]}}}]} + {:block/title "b3" :build/properties {:user.property/node #{[:block/uuid page-object-uuid]}}} + {:block/title "Example advanced query", + :build/tags [:logseq.class/Query], + :build/properties + {:logseq.property/query + {:build/property-value :block + :block/title "{:query (task Todo)}" + :build/properties + {:logseq.property.code/lang "clojure" + :logseq.property.node/display-type :code}}}}]} {:page {:block/title "page object" :block/uuid page-object-uuid :build/keep-uuid? true} From b342d122f212003cfc4a487cbfef3a1008470fa2 Mon Sep 17 00:00:00 2001 From: rcmerci Date: Tue, 1 Apr 2025 16:36:26 +0800 Subject: [PATCH 04/20] enhance(rtc): adjust some properties' rtc config --- deps/db/src/logseq/db/frontend/property.cljs | 28 ++++++-------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/deps/db/src/logseq/db/frontend/property.cljs b/deps/db/src/logseq/db/frontend/property.cljs index 4bfa4b9372..d5e440fce5 100644 --- a/deps/db/src/logseq/db/frontend/property.cljs +++ b/deps/db/src/logseq/db/frontend/property.cljs @@ -430,6 +430,9 @@ {:type :coll :hide? true :public? false} + ;; ignore this property when rtc, + ;; since users frequently click the sort button to view table content temporarily, + ;; but this action does not need to be synchronized with other clients. :rtc {:rtc/ignore-attr-when-init-upload true :rtc/ignore-attr-when-init-download true :rtc/ignore-attr-when-syncing true}} @@ -438,47 +441,32 @@ :schema {:type :map :hide? true - :public? false} - :rtc {:rtc/ignore-attr-when-init-upload true - :rtc/ignore-attr-when-init-download true - :rtc/ignore-attr-when-syncing true}} + :public? false}} :logseq.property.table/hidden-columns {:title "View hidden columns" :schema {:type :keyword :cardinality :many :hide? true - :public? false} - :rtc {:rtc/ignore-attr-when-init-upload true - :rtc/ignore-attr-when-init-download true - :rtc/ignore-attr-when-syncing true}} + :public? false}} :logseq.property.table/ordered-columns {:title "View ordered columns" :schema {:type :coll :hide? true - :public? false} - :rtc {:rtc/ignore-attr-when-init-upload true - :rtc/ignore-attr-when-init-download true - :rtc/ignore-attr-when-syncing true}} + :public? false}} :logseq.property.table/sized-columns {:title "View columns settings" :schema {:type :map :hide? true - :public? false} - :rtc {:rtc/ignore-attr-when-init-upload true - :rtc/ignore-attr-when-init-download true - :rtc/ignore-attr-when-syncing true}} + :public? false}} :logseq.property.table/pinned-columns {:title "Table view pinned columns" :schema {:type :property :cardinality :many :hide? true - :public? false} - :rtc {:rtc/ignore-attr-when-init-upload true - :rtc/ignore-attr-when-init-download true - :rtc/ignore-attr-when-syncing true}} + :public? false}} :logseq.property/view-for {:title "This view belongs to" :schema {:type :node From ad64632c1ba739640b4befa56eba6a245cd6f268 Mon Sep 17 00:00:00 2001 From: Gabriel Horner Date: Tue, 1 Apr 2025 11:43:43 -0400 Subject: [PATCH 05/20] enhance: export tags for property values --- .../logseq/db/frontend/property/build.cljs | 14 +++--- deps/db/src/logseq/db/sqlite/build.cljs | 13 +++--- deps/db/src/logseq/db/sqlite/export.cljs | 16 ++++--- deps/db/test/logseq/db/sqlite/build_test.cljs | 43 ++++++++++++++----- .../db/test/logseq/db/sqlite/export_test.cljs | 8 +++- 5 files changed, 65 insertions(+), 29 deletions(-) diff --git a/deps/db/src/logseq/db/frontend/property/build.cljs b/deps/db/src/logseq/db/frontend/property/build.cljs index 4201b73901..49bba4a866 100644 --- a/deps/db/src/logseq/db/frontend/property/build.cljs +++ b/deps/db/src/logseq/db/frontend/property/build.cljs @@ -67,8 +67,10 @@ (defn build-property-value-block "Builds a property value entity given a block map/entity, a property entity or - ident and its property value" - [block property value & {:keys [block-uuid property-value-properties]}] + ident and its property value. Takes the following options: + * :block-uuid - :block/uuid for property value entity + * :properties - Additional properties and attributes to add to entity" + [block property value & {:keys [block-uuid properties]}] (let [block-id (or (:db/id block) (:db/ident block))] (cond-> (merge @@ -87,8 +89,8 @@ {:block/title value})) true common-util/block-with-timestamps - property-value-properties - (merge property-value-properties)))) + properties + (merge properties)))) (defn build-property-values-tx-m "Builds a map of property names to their property value blocks to be @@ -114,7 +116,7 @@ block' property-map % (cond-> {} property-value-properties - (assoc :property-value-properties property-value-properties) + (assoc :properties property-value-properties) pure? (assoc :block-uuid (common-uuid/gen-uuid :builtin-block-uuid (str gen-uuid-value-prefix "-" %))))) @@ -122,7 +124,7 @@ (build-property-value-block block' property-map v (cond-> {} property-value-properties - (assoc :property-value-properties property-value-properties) + (assoc :properties property-value-properties) pure? (assoc :block-uuid (common-uuid/gen-uuid :builtin-block-uuid (str gen-uuid-value-prefix "-" v))))))]))) diff --git a/deps/db/src/logseq/db/sqlite/build.cljs b/deps/db/src/logseq/db/sqlite/build.cljs index 6637a2affe..eac593ca6d 100644 --- a/deps/db/src/logseq/db/sqlite/build.cljs +++ b/deps/db/src/logseq/db/sqlite/build.cljs @@ -128,11 +128,14 @@ (->> properties (keep (fn [[k v]] (when-let [property-map (build-property-map-for-pvalue-tx k v new-block properties-config all-idents)] - [(cond-> property-map - (and (:build/property-value v) - (or (:build/properties v) (seq (select-keys v [:block/created-at :block/updated-at])))) - (assoc :property-value-properties - (merge (:build/properties v) (select-keys v [:block/created-at :block/updated-at])))) + [(let [pvalue-attrs (when (:build/property-value v) + (merge (:build/properties v) + {:block/tags (mapv #(hash-map :db/ident (get-ident all-idents %)) + (:build/tags v))} + (select-keys v [:block/created-at :block/updated-at])))] + (cond-> property-map + (and (:build/property-value v) (seq pvalue-attrs)) + (assoc :property-value-properties pvalue-attrs))) (if (:build/property-value v) (or (:logseq.property/value v) (:block/title v)) v)]))) diff --git a/deps/db/src/logseq/db/sqlite/export.cljs b/deps/db/src/logseq/db/sqlite/export.cljs index 8b59ce6bd3..0a36b64cd1 100644 --- a/deps/db/src/logseq/db/sqlite/export.cljs +++ b/deps/db/src/logseq/db/sqlite/export.cljs @@ -66,12 +66,16 @@ ent-properties (when (and (not (:block/closed-value-property pvalue)) (seq ent-properties*)) ;; TODO: Support closed values as needed (buildable-properties db ent-properties* {} options))] - (if (seq ent-properties) - (cond-> - {:build/property-value :block - :block/title (or (block-title pvalue) - (:logseq.property/value pvalue)) - :build/properties ent-properties} + (if (or (seq ent-properties) (seq (:block/tags pvalue))) + (cond-> {:build/property-value :block + :block/title (or (block-title pvalue) + (:logseq.property/value pvalue))} + (seq (:block/tags pvalue)) + (assoc :build/tags (->build-tags (:block/tags pvalue))) + + (seq ent-properties) + (assoc :build/properties ent-properties) + (:include-timestamps? options) (merge (select-keys pvalue [:block/created-at :block/updated-at]))) ;; nbb-compatible version of db-property/property-value-content diff --git a/deps/db/test/logseq/db/sqlite/build_test.cljs b/deps/db/test/logseq/db/sqlite/build_test.cljs index 6149ca931a..c61f67d39b 100644 --- a/deps/db/test/logseq/db/sqlite/build_test.cljs +++ b/deps/db/test/logseq/db/sqlite/build_test.cljs @@ -207,20 +207,41 @@ (map #(:block/title (d/entity @conn %))))) "Property page has correct blocks"))) -(deftest property-value-properties +(deftest property-value-with-properties-and-tags (let [conn (db-test/create-conn-with-blocks - [{:page {:block/title "page1"} - :blocks [{:block/title "Todo query", - :build/tags [:logseq.class/Query], - :build/properties - {:logseq.property/query - {:build/property-value :block - :block/title "{:query (task Todo)}" - :build/properties - {:logseq.property.code/lang "clojure" - :logseq.property.node/display-type :code}}}}]}])] + {:properties {:p1 {:logseq.property/type :default}} + :classes {:C1 {}} + :pages-and-blocks + [{:page {:block/title "page1"} + :blocks [{:block/title "block has pvalue with built-in tag" + :build/properties + {:p1 {:build/property-value :block + :block/title "t1" + :build/tags [:logseq.class/Task]}}} + {:block/title "block has pvalue with user tag" + :build/properties + {:p1 {:build/property-value :block + :block/title "u1" + :build/tags [:C1]}}} + {:block/title "Todo query", + :build/tags [:logseq.class/Query], + :build/properties + {:logseq.property/query + {:build/property-value :block + :block/title "{:query (task Todo)}" + :build/properties + {:logseq.property.code/lang "clojure" + :logseq.property.node/display-type :code}}}}]}]})] (is (= {:logseq.property.node/display-type :code :logseq.property.code/lang "clojure"} (-> (db-test/find-block-by-content @conn "{:query (task Todo)}") + db-test/readable-properties + (dissoc :logseq.property/created-from-property)))) + (is (= {:block/tags [:logseq.class/Task]} + (-> (db-test/find-block-by-content @conn "t1") + db-test/readable-properties + (dissoc :logseq.property/created-from-property)))) + (is (= {:block/tags [:user.class/C1]} + (-> (db-test/find-block-by-content @conn "u1") db-test/readable-properties (dissoc :logseq.property/created-from-property)))))) \ No newline at end of file diff --git a/deps/db/test/logseq/db/sqlite/export_test.cljs b/deps/db/test/logseq/db/sqlite/export_test.cljs index 90d9e50de1..f6701d8355 100644 --- a/deps/db/test/logseq/db/sqlite/export_test.cljs +++ b/deps/db/test/logseq/db/sqlite/export_test.cljs @@ -599,7 +599,13 @@ :block/title "{:query (task Todo)}" :build/properties {:logseq.property.code/lang "clojure" - :logseq.property.node/display-type :code}}}}]} + :logseq.property.node/display-type :code}}}} + {:block/title "block has property value with tags and properties" + :build/properties + {:user.property/url + {:build/property-value :block + :block/title "https://example.com" + :build/tags [:user.class/MyClass]}}}]} {:page {:block/title "page object" :block/uuid page-object-uuid :build/keep-uuid? true} From a1bd56790620c25e68259e4be88662df655b1eb2 Mon Sep 17 00:00:00 2001 From: Gabriel Horner Date: Tue, 1 Apr 2025 13:42:42 -0400 Subject: [PATCH 06/20] fix: build+export of closed :many property values --- deps/db/src/logseq/db/sqlite/build.cljs | 9 ++++--- deps/db/src/logseq/db/sqlite/export.cljs | 27 +++++++++++-------- .../db/test/logseq/db/sqlite/export_test.cljs | 3 ++- 3 files changed, 24 insertions(+), 15 deletions(-) diff --git a/deps/db/src/logseq/db/sqlite/build.cljs b/deps/db/src/logseq/db/sqlite/build.cljs index eac593ca6d..22a071d2be 100644 --- a/deps/db/src/logseq/db/sqlite/build.cljs +++ b/deps/db/src/logseq/db/sqlite/build.cljs @@ -99,7 +99,10 @@ "Provides the next temp :db/id to use in a create-graph transact!" #(swap! current-db-id dec)) -(defn- build-property-map-for-pvalue-tx [k v new-block properties-config all-idents] +(defn- build-property-map-for-pvalue-tx + "Returns a property map if the given property pair should have a property value entity constructured + or nil if it should not. Property maps must at least contain the :db/ident and :logseq.property/type keys" + [k v new-block properties-config all-idents] (if-let [built-in-type (get-in db-property/built-in-properties [k :schema :type])] (if (and (db-property-type/value-ref-property-types built-in-type) ;; closed values are referenced by their :db/ident so no need to create values @@ -113,8 +116,8 @@ {:db/ident k :logseq.property/type built-in-type'})) (when (and (db-property-type/value-ref-property-types (get-in properties-config [k :logseq.property/type])) - ;; TODO: Support translate-property-value without this hack - (not (vector? v))) + ;; Don't build property value entity if values are :block/uuid refs + (if (set? v) (not (vector? (first v))) (not (vector? v)))) (let [prop-type (get-in properties-config [k :logseq.property/type])] {:db/ident (get-ident all-idents k) :original-property-id k diff --git a/deps/db/src/logseq/db/sqlite/export.cljs b/deps/db/src/logseq/db/sqlite/export.cljs index 0a36b64cd1..c4acd0fe4c 100644 --- a/deps/db/src/logseq/db/sqlite/export.cljs +++ b/deps/db/src/logseq/db/sqlite/export.cljs @@ -42,7 +42,7 @@ (defn- buildable-property-value-entity "Converts property value to a buildable version" - [db property-ent pvalue {:keys [property-value-uuids?] :as options}] + [db property-ent pvalue properties-config {:keys [property-value-uuids?] :as options}] (cond (and (not property-value-uuids?) (ldb/internal-page? pvalue)) ;; Should page properties be pulled here? [:build/page (cond-> (shallow-copy-page pvalue) @@ -64,8 +64,7 @@ ;; TODO: Allow user properties when sqlite.build supports it (medley/filter-keys db-property/internal-property?)) ent-properties (when (and (not (:block/closed-value-property pvalue)) (seq ent-properties*)) - ;; TODO: Support closed values as needed - (buildable-properties db ent-properties* {} options))] + (buildable-properties db ent-properties* properties-config options))] (if (or (seq ent-properties) (seq (:block/tags pvalue))) (cond-> {:build/property-value :block :block/title (or (block-title pvalue) @@ -89,18 +88,24 @@ (->> ent-properties (map (fn [[k v]] [k - (if (and (:block/closed-value-property v) (not (db-property/logseq-property? k))) - (if-let [closed-uuid (some #(when (= (:value %) (db-property/property-value-content v)) - (:uuid %)) - (get-in properties-config [k :build/closed-values]))] - [:block/uuid closed-uuid] - (throw (ex-info (str "No closed value found for content: " (pr-str (db-property/property-value-content v))) {:properties properties-config}))) + ;; handle user closed value properties. built-ins have idents and shouldn't be handled here + (if (and (not (db-property/logseq-property? k)) + (or (:block/closed-value-property v) + (and (set? v) (:block/closed-value-property (first v))))) + (let [find-closed-uuid (fn [val] + (or (some #(when (= (:value %) (db-property/property-value-content val)) + (:uuid %)) + (get-in properties-config [k :build/closed-values])) + (throw (ex-info (str "No closed value found for content: " (pr-str (db-property/property-value-content val))) {:properties properties-config}))))] + (if (set? v) + (set (map #(vector :block/uuid (find-closed-uuid %)) v)) + [:block/uuid (find-closed-uuid v)])) (cond (de/entity? v) - (buildable-property-value-entity db (d/entity db k) v options) + (buildable-property-value-entity db (d/entity db k) v properties-config options) (and (set? v) (every? de/entity? v)) (let [property-ent (d/entity db k)] - (set (map #(buildable-property-value-entity db property-ent % options) v))) + (set (map #(buildable-property-value-entity db property-ent % properties-config options) v))) :else v))])) (into {}))) diff --git a/deps/db/test/logseq/db/sqlite/export_test.cljs b/deps/db/test/logseq/db/sqlite/export_test.cljs index f6701d8355..1b214470f5 100644 --- a/deps/db/test/logseq/db/sqlite/export_test.cljs +++ b/deps/db/test/logseq/db/sqlite/export_test.cljs @@ -558,6 +558,7 @@ :logseq.property/default-value 42})} :user.property/default-closed {:logseq.property/type :default + :db/cardinality :db.cardinality/many :build/closed-values [{:value "joy" :uuid closed-value-uuid} {:value "sad" :uuid (random-uuid)}]} :user.property/checkbox {:logseq.property/type :checkbox} @@ -587,7 +588,7 @@ :user.property/node #{[:block/uuid page-pvalue-uuid]}}} :blocks [{:block/title "b1" :build/properties {:user.property/num 1 - :user.property/default-closed [:block/uuid closed-value-uuid] + :user.property/default-closed #{[:block/uuid closed-value-uuid]} :user.property/date [:block/uuid journal-uuid]}} {:block/title "b2" :build/properties {:user.property/node #{[:block/uuid page-object-uuid]}}} {:block/title "b3" :build/properties {:user.property/node #{[:block/uuid page-object-uuid]}}} From 798928aed545dd06aaa2032e95336bbcccc60147 Mon Sep 17 00:00:00 2001 From: Gabriel Horner Date: Tue, 1 Apr 2025 14:56:38 -0400 Subject: [PATCH 07/20] refactor: clean up mutually recursive fns Better to localize dependent fns than to make them circular dependencies --- deps/db/src/logseq/db/sqlite/export.cljs | 123 ++++++++++++----------- 1 file changed, 63 insertions(+), 60 deletions(-) diff --git a/deps/db/src/logseq/db/sqlite/export.cljs b/deps/db/src/logseq/db/sqlite/export.cljs index c4acd0fe4c..4a5c3fa56e 100644 --- a/deps/db/src/logseq/db/sqlite/export.cljs +++ b/deps/db/src/logseq/db/sqlite/export.cljs @@ -38,77 +38,80 @@ {:build/journal (:block/journal-day page-entity)} {:block/title (block-title page-entity)})) -(declare buildable-properties) - -(defn- buildable-property-value-entity - "Converts property value to a buildable version" - [db property-ent pvalue properties-config {:keys [property-value-uuids?] :as options}] - (cond (and (not property-value-uuids?) (ldb/internal-page? pvalue)) +(defn- build-pvalue-entity-for-build-page + [pvalue] + (cond (ldb/internal-page? pvalue) ;; Should page properties be pulled here? [:build/page (cond-> (shallow-copy-page pvalue) (seq (:block/tags pvalue)) (assoc :build/tags (->build-tags (:block/tags pvalue))))] - (and (not property-value-uuids?) (entity-util/journal? pvalue)) - [:build/page {:build/journal (:block/journal-day pvalue)}] - :else - (if (contains? #{:node :date} (:logseq.property/type property-ent)) - ;; Idents take precedence over uuid because they are keep data graph-agnostic - (if (:db/ident pvalue) - (:db/ident pvalue) - ;; Use metadata distinguish from block references that don't exist like closed values - ^::existing-property-value? [:block/uuid (:block/uuid pvalue)]) - (or (:db/ident pvalue) - (let [ent-properties* (->> (apply dissoc (db-property/properties pvalue) - :logseq.property/value :logseq.property/created-from-property - db-property/public-db-attribute-properties) - ;; TODO: Allow user properties when sqlite.build supports it - (medley/filter-keys db-property/internal-property?)) - ent-properties (when (and (not (:block/closed-value-property pvalue)) (seq ent-properties*)) - (buildable-properties db ent-properties* properties-config options))] - (if (or (seq ent-properties) (seq (:block/tags pvalue))) - (cond-> {:build/property-value :block - :block/title (or (block-title pvalue) - (:logseq.property/value pvalue))} - (seq (:block/tags pvalue)) - (assoc :build/tags (->build-tags (:block/tags pvalue))) + (entity-util/journal? pvalue) + [:build/page {:build/journal (:block/journal-day pvalue)}])) - (seq ent-properties) - (assoc :build/properties ent-properties) +(defn- build-pvalue-entity-default [ent-properties pvalue options] + (if (or (seq ent-properties) (seq (:block/tags pvalue))) + (cond-> {:build/property-value :block + :block/title (or (block-title pvalue) + (:logseq.property/value pvalue))} + (seq (:block/tags pvalue)) + (assoc :build/tags (->build-tags (:block/tags pvalue))) - (:include-timestamps? options) - (merge (select-keys pvalue [:block/created-at :block/updated-at]))) - ;; nbb-compatible version of db-property/property-value-content - (or (block-title pvalue) - (:logseq.property/value pvalue)))))))) + (seq ent-properties) + (assoc :build/properties ent-properties) + + (:include-timestamps? options) + (merge (select-keys pvalue [:block/created-at :block/updated-at]))) + ;; nbb-compatible version of db-property/property-value-content + (or (block-title pvalue) + (:logseq.property/value pvalue)))) (defn- buildable-properties "Originally copied from db-test/readable-properties. Modified so that property values are valid sqlite.build EDN" [db ent-properties properties-config options] - (->> ent-properties - (map (fn [[k v]] - [k - ;; handle user closed value properties. built-ins have idents and shouldn't be handled here - (if (and (not (db-property/logseq-property? k)) - (or (:block/closed-value-property v) - (and (set? v) (:block/closed-value-property (first v))))) - (let [find-closed-uuid (fn [val] - (or (some #(when (= (:value %) (db-property/property-value-content val)) - (:uuid %)) - (get-in properties-config [k :build/closed-values])) - (throw (ex-info (str "No closed value found for content: " (pr-str (db-property/property-value-content val))) {:properties properties-config}))))] - (if (set? v) - (set (map #(vector :block/uuid (find-closed-uuid %)) v)) - [:block/uuid (find-closed-uuid v)])) - (cond - (de/entity? v) - (buildable-property-value-entity db (d/entity db k) v properties-config options) - (and (set? v) (every? de/entity? v)) - (let [property-ent (d/entity db k)] - (set (map #(buildable-property-value-entity db property-ent % properties-config options) v))) - :else - v))])) - (into {}))) + (letfn [(build-pvalue-entity + [db' property-ent pvalue properties-config' {:keys [property-value-uuids?] :as options'}] + (if-let [build-page (and (not property-value-uuids?) (build-pvalue-entity-for-build-page pvalue))] + build-page + (if (contains? #{:node :date} (:logseq.property/type property-ent)) + ;; Idents take precedence over uuid because they are keep data graph-agnostic + (if (:db/ident pvalue) + (:db/ident pvalue) + ;; Use metadata distinguish from block references that don't exist like closed values + ^::existing-property-value? [:block/uuid (:block/uuid pvalue)]) + (or (:db/ident pvalue) + (let [ent-properties* (->> (apply dissoc (db-property/properties pvalue) + :logseq.property/value :logseq.property/created-from-property + db-property/public-db-attribute-properties) + ;; TODO: Allow user properties when sqlite.build supports it + (medley/filter-keys db-property/internal-property?)) + ent-properties (when (and (not (:block/closed-value-property pvalue)) (seq ent-properties*)) + (buildable-properties db' ent-properties* properties-config' options'))] + (build-pvalue-entity-default ent-properties pvalue options'))))))] + (->> ent-properties + (map (fn [[k v]] + [k + ;; handle user closed value properties. built-ins have idents and shouldn't be handled here + (if (and (not (db-property/logseq-property? k)) + (or (:block/closed-value-property v) + (and (set? v) (:block/closed-value-property (first v))))) + (let [find-closed-uuid (fn [val] + (or (some #(when (= (:value %) (db-property/property-value-content val)) + (:uuid %)) + (get-in properties-config [k :build/closed-values])) + (throw (ex-info (str "No closed value found for content: " (pr-str (db-property/property-value-content val))) {:properties properties-config}))))] + (if (set? v) + (set (map #(vector :block/uuid (find-closed-uuid %)) v)) + [:block/uuid (find-closed-uuid v)])) + (cond + (de/entity? v) + (build-pvalue-entity db (d/entity db k) v properties-config options) + (and (set? v) (every? de/entity? v)) + (let [property-ent (d/entity db k)] + (set (map #(build-pvalue-entity db property-ent % properties-config options) v))) + :else + v))])) + (into {})))) (defn- build-export-properties "The caller of this fn is responsible for building :build/:property-classes unless shallow-copy?" From 7c583c489256e3aa47a8ad13a8bd5fe349696111 Mon Sep 17 00:00:00 2001 From: Gabriel Horner Date: Wed, 2 Apr 2025 08:27:52 -0400 Subject: [PATCH 08/20] fix: export edn displayed success when errors happened Also fix unreferenced :block/alias uuids as found in https://github.com/logseq/db-test/issues/243 --- deps/db/src/logseq/db/sqlite/export.cljs | 5 +- .../frontend/handler/db_based/export.cljs | 51 +++++++++---------- src/main/frontend/worker/db_worker.cljs | 3 +- 3 files changed, 31 insertions(+), 28 deletions(-) diff --git a/deps/db/src/logseq/db/sqlite/export.cljs b/deps/db/src/logseq/db/sqlite/export.cljs index 4a5c3fa56e..9e4907ff1e 100644 --- a/deps/db/src/logseq/db/sqlite/export.cljs +++ b/deps/db/src/logseq/db/sqlite/export.cljs @@ -733,7 +733,10 @@ ;; Only looks one-level deep in properties e.g. not inside :build/page ;; Doesn't find :block/link refs ref-uuids - (->> (concat (mapcat get-pvalue-uuids (vals classes)) + (->> (concat (mapcat #(map second (:block/alias %)) (vals classes)) + (mapcat #(map second (:block/alias %)) (vals properties)) + (mapcat #(map second (:block/alias (:page %))) pages-and-blocks) + (mapcat get-pvalue-uuids (vals classes)) (mapcat get-pvalue-uuids (vals properties)) (mapcat (comp get-pvalue-uuids :page) pages-and-blocks) (mapcat #(sqlite-build/extract-from-blocks (:blocks %) get-pvalue-uuids) pages-and-blocks)) diff --git a/src/main/frontend/handler/db_based/export.cljs b/src/main/frontend/handler/db_based/export.cljs index 8fb19ae11b..34ba2f0daf 100644 --- a/src/main/frontend/handler/db_based/export.cljs +++ b/src/main/frontend/handler/db_based/export.cljs @@ -17,10 +17,10 @@ (state/get-current-repo) {:export-type :block :block-id [:block/uuid block-uuid]}) pull-data (with-out-str (pprint/pprint result))] - (.writeText js/navigator.clipboard pull-data) - (println pull-data) - (notification/show! "Copied block's data!" :success)) - + (when-not (= :export-edn-error result) + (.writeText js/navigator.clipboard pull-data) + (println pull-data) + (notification/show! "Copied block's data!" :success))) (notification/show! "No block found" :warning))) (defn export-view-nodes-data [nodes] @@ -29,9 +29,10 @@ (state/get-current-repo) {:export-type :view-nodes :node-ids block-uuids}) pull-data (with-out-str (pprint/pprint result))] - (.writeText js/navigator.clipboard pull-data) - (println pull-data) - (notification/show! "Copied block's data!" :success)))) + (when-not (= :export-edn-error result) + (.writeText js/navigator.clipboard pull-data) + (println pull-data) + (notification/show! "Copied view nodes' data!" :success))))) (defn ^:export export-page-data [] (if-let [page-id (page-util/get-current-page-id)] @@ -39,9 +40,10 @@ (state/get-current-repo) {:export-type :page :page-id page-id}) pull-data (with-out-str (pprint/pprint result))] - (.writeText js/navigator.clipboard pull-data) - (println pull-data) - (notification/show! "Copied page's data!" :success)) + (when-not (= :export-edn-error result) + (.writeText js/navigator.clipboard pull-data) + (println pull-data) + (notification/show! "Copied page's data!" :success))) (notification/show! "No page found" :warning))) (defn ^:export export-graph-ontology-data [] @@ -49,19 +51,12 @@ (state/get-current-repo) {:export-type :graph-ontology}) pull-data (with-out-str (pprint/pprint result))] - (.writeText js/navigator.clipboard pull-data) - (println pull-data) - (js/console.log (str "Exported " (count (:classes result)) " classes and " - (count (:properties result)) " properties")) - (notification/show! "Copied graphs's ontology data!" :success))) - -(defn- export-graph-edn-data [] - (p/let [result (state/> edn-str + (p/let [result (state/> pull-data js/encodeURIComponent (str "data:text/edn;charset=utf-8,")) filename (file-name repo :edn)] diff --git a/src/main/frontend/worker/db_worker.cljs b/src/main/frontend/worker/db_worker.cljs index 7136f64f82..4aaa8ed89c 100644 --- a/src/main/frontend/worker/db_worker.cljs +++ b/src/main/frontend/worker/db_worker.cljs @@ -718,7 +718,8 @@ (js/console.error "export-edn error: " e) (worker-util/post-message :notification ["An unexpected error occurred during export. See the javascript console for details." - :error]))))) + :error]) + :export-edn-error)))) (comment (def-thread-api :general/dangerousRemoveAllDbs From 7c38daef244917935344ca2647ec7c7f8b756270 Mon Sep 17 00:00:00 2001 From: Gabriel Horner Date: Wed, 2 Apr 2025 09:56:25 -0400 Subject: [PATCH 09/20] fix: block/alias failing partial exports e.g. ontology and page exports. Also remove unused fn option --- deps/db/src/logseq/db/sqlite/export.cljs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/deps/db/src/logseq/db/sqlite/export.cljs b/deps/db/src/logseq/db/sqlite/export.cljs index 9e4907ff1e..cb236fcdbe 100644 --- a/deps/db/src/logseq/db/sqlite/export.cljs +++ b/deps/db/src/logseq/db/sqlite/export.cljs @@ -160,8 +160,7 @@ (defn- build-export-class "The caller of this fn is responsible for building any classes or properties from this fn unless shallow-copy?" - [class-ent {:keys [include-parents? include-uuid? shallow-copy? include-timestamps?] - :or {include-parents? true}}] + [class-ent {:keys [include-uuid? shallow-copy? include-timestamps? include-alias?]}] (cond-> (select-keys class-ent [:block/title :block/collapsed?]) include-uuid? (assoc :block/uuid (:block/uuid class-ent) :build/keep-uuid? true) @@ -170,11 +169,10 @@ (and (:logseq.property.class/properties class-ent) (not shallow-copy?)) (assoc :build/class-properties (mapv :db/ident (:logseq.property.class/properties class-ent))) - (and (not shallow-copy?) (:block/alias class-ent)) + (and (not shallow-copy?) include-alias? (:block/alias class-ent)) (assoc :block/alias (set (map #(vector :block/uuid (:block/uuid %)) (:block/alias class-ent)))) ;; It's caller's responsibility to ensure parent is included in final export - (and include-parents? - (not shallow-copy?) + (and (not shallow-copy?) (:logseq.property/parent class-ent) (not= :logseq.class/Root (:db/ident (:logseq.property/parent class-ent)))) (assoc :build/class-parent @@ -418,7 +416,7 @@ (merge {::block (:node node-export)} block-export))) -(defn- build-page-blocks-export [db page-entity {:keys [properties classes blocks ontology-page?] :as options}] +(defn- build-page-blocks-export [db page-entity {:keys [properties classes blocks ontology-page? include-alias?] :as options}] (let [options' (cond-> (dissoc options :classes :blocks :graph-ontology) (:exclude-ontology? options) (assoc :properties (get-in options [:graph-ontology :properties]))) @@ -431,7 +429,7 @@ (:node page-ent-export) (merge (dissoc (:node page-ent-export) :block/title) (shallow-copy-page page-entity) - (when (:block/alias page-entity) + (when (and include-alias? (:block/alias page-entity)) {:block/alias (set (map #(vector :block/uuid (:block/uuid %)) (:block/alias page-entity)))}))) page-blocks-export {:pages-and-blocks [{:page page :blocks blocks}] :properties properties @@ -675,7 +673,8 @@ * :exclude-built-in-pages? - When set, built-in pages are excluded from export * :exclude-files? - When set, files are excluded from export" [db {:keys [exclude-files?] :as options*}] - (let [options (merge options* {:property-value-uuids? true}) + (let [options (merge options* {:property-value-uuids? true + :include-alias? true}) content-ref-uuids (get-graph-content-ref-uuids db options) ontology-options (merge options {:include-uuid? true}) ontology-export (build-graph-ontology-export db ontology-options) From c6af79a260fa07c5cde355358a7f0a5c8f25b415 Mon Sep 17 00:00:00 2001 From: Gabriel Horner Date: Wed, 2 Apr 2025 10:25:30 -0400 Subject: [PATCH 10/20] enhance: export property aliases --- deps/db/src/logseq/db/sqlite/export.cljs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/deps/db/src/logseq/db/sqlite/export.cljs b/deps/db/src/logseq/db/sqlite/export.cljs index cb236fcdbe..eb3d1629e8 100644 --- a/deps/db/src/logseq/db/sqlite/export.cljs +++ b/deps/db/src/logseq/db/sqlite/export.cljs @@ -115,7 +115,7 @@ (defn- build-export-properties "The caller of this fn is responsible for building :build/:property-classes unless shallow-copy?" - [db user-property-idents {:keys [include-properties? include-timestamps? include-uuid? shallow-copy?] :as options}] + [db user-property-idents {:keys [include-properties? include-timestamps? include-uuid? shallow-copy? include-alias?] :as options}] (let [properties-config-by-ent (->> user-property-idents (map (fn [ident] @@ -129,6 +129,8 @@ (assoc :block/uuid (:block/uuid property) :build/keep-uuid? true) include-timestamps? (merge (select-keys property [:block/created-at :block/updated-at])) + (and (not shallow-copy?) include-alias? (:block/alias property)) + (assoc :block/alias (set (map #(vector :block/uuid (:block/uuid %)) (:block/alias property)))) (and (not shallow-copy?) (:logseq.property/classes property)) (assoc :build/property-classes (mapv :db/ident (:logseq.property/classes property))) (seq closed-values) From a3dfa00ccfaae43350c8a443663c76bffb6c9176 Mon Sep 17 00:00:00 2001 From: Gabriel Horner Date: Wed, 2 Apr 2025 12:12:31 -0400 Subject: [PATCH 11/20] chore: Add optional verbose test runner for graph-parser dep --- deps/graph-parser/nbb.edn | 5 ++--- deps/graph-parser/package.json | 3 ++- deps/graph-parser/test/logseq/graph_parser/test_runner.cljs | 6 ++++++ 3 files changed, 10 insertions(+), 4 deletions(-) create mode 100644 deps/graph-parser/test/logseq/graph_parser/test_runner.cljs diff --git a/deps/graph-parser/nbb.edn b/deps/graph-parser/nbb.edn index 98eda58b65..95d1f22920 100644 --- a/deps/graph-parser/nbb.edn +++ b/deps/graph-parser/nbb.edn @@ -2,9 +2,8 @@ :deps {logseq/common {:local/root "../common"} - logseq/db {:local/root "../db"} - io.github.nextjournal/nbb-test-runner - {:git/sha "60ed57aa04bca8d604f5ba6b28848bd887109347"}}} + {:git/sha "60ed57aa04bca8d604f5ba6b28848bd887109347"} + io.github.pez/baldr {:mvn/version "1.0.9"}}} \ No newline at end of file diff --git a/deps/graph-parser/package.json b/deps/graph-parser/package.json index 755796f272..75db1d83de 100644 --- a/deps/graph-parser/package.json +++ b/deps/graph-parser/package.json @@ -10,6 +10,7 @@ "mldoc": "^1.5.9" }, "scripts": { - "test": "nbb-logseq -cp test:../outliner/src -m nextjournal.test-runner" + "test": "nbb-logseq -cp test:../outliner/src -m nextjournal.test-runner", + "test-v": "nbb-logseq -cp test:../outliner/src -m logseq.graph-parser.test-runner" } } diff --git a/deps/graph-parser/test/logseq/graph_parser/test_runner.cljs b/deps/graph-parser/test/logseq/graph_parser/test_runner.cljs new file mode 100644 index 0000000000..50f8d1d3f1 --- /dev/null +++ b/deps/graph-parser/test/logseq/graph_parser/test_runner.cljs @@ -0,0 +1,6 @@ +(ns logseq.graph-parser.test-runner + "Test runner which enables https://github.com/PEZ/baldr by default" + (:require [nextjournal.test-runner :as next-runner] + [pez.baldr])) + +(def -main next-runner/-main) From 26cbd74cd1b0b6fa2bce655e45935cfd9839a646 Mon Sep 17 00:00:00 2001 From: Gabriel Horner Date: Thu, 3 Apr 2025 15:29:33 -0400 Subject: [PATCH 12/20] enhance(perf): speed up db graph importer for larger graphs Use atom cache based on tx instead of full db lookup to create all-existing-page-uuids --- .../src/logseq/graph_parser/exporter.cljs | 65 +++++++++++++------ 1 file changed, 44 insertions(+), 21 deletions(-) diff --git a/deps/graph-parser/src/logseq/graph_parser/exporter.cljs b/deps/graph-parser/src/logseq/graph_parser/exporter.cljs index 1676bf7a86..1b34019ee9 100644 --- a/deps/graph-parser/src/logseq/graph_parser/exporter.cljs +++ b/deps/graph-parser/src/logseq/graph_parser/exporter.cljs @@ -918,30 +918,41 @@ (dissoc :block/whiteboard?) (update-page-tags db user-options per-file-state all-idents))) +(defn- get-page-parents + "Like ldb/get-page-parents but using all-existing-page-uuids" + [node all-existing-page-uuids] + (let [get-parent (fn get-parent [n] + (when (:block/uuid (:logseq.property/parent n)) + (or (get all-existing-page-uuids (:block/uuid (:logseq.property/parent n))) + (throw (ex-info (str "No parent page found for " (pr-str (:block/uuid (:logseq.property/parent n)))) + {:node n})))))] + (when-let [parent (get-parent node)] + (loop [current-parent parent + parents' []] + (if (and current-parent (not (contains? parents' current-parent))) + (recur (get-parent current-parent) + (conj parents' current-parent)) + (vec (reverse parents'))))))) + (defn- get-all-existing-page-uuids "Returns a map of unique page names mapped to their uuids. The page names are in a format that is compatible with extract/extract e.g. namespace pages have their full hierarchy in the name" - [db classes-from-property-parents] - (->> db - ;; don't fetch built-in as that would give the wrong entity if a user used - ;; a db-only built-in property name e.g. description - (d/q '[:find [?b ...] - :where [?b :block/name] [(missing? $ ?b :logseq.property/built-in?)]]) - (map #(d/entity db %)) - (map #(vector - (if-let [parents (and (or (ldb/internal-page? %) (ldb/class? %)) + [classes-from-property-parents all-existing-page-uuids] + (->> all-existing-page-uuids + (map (fn [[_ p]] + (vector + (if-let [parents (and (or (contains? (:block/tags p) :logseq.class/Tag) + (contains? (:block/tags p) :logseq.class/Page)) ;; These classes have parents now but don't in file graphs (and in extract) - (not (contains? classes-from-property-parents (:block/title %))) - (->> (ldb/get-page-parents %) - (remove (fn [e] (= :logseq.class/Root (:db/ident e)))) - seq))] + (not (contains? classes-from-property-parents (:block/title p))) + (get-page-parents p all-existing-page-uuids))] ;; Build a :block/name for namespace pages that matches data from extract/extract - (string/join ns-util/namespace-char (map :block/name (conj (vec parents) %))) - (:block/name %)) - (or (:block/uuid %) - (throw (ex-info (str "No uuid for existing page " (pr-str (:block/name %))) - (select-keys % [:block/name :block/tags])))))) + (string/join ns-util/namespace-char (map :block/name (conj (vec parents) p))) + (:block/name p)) + (or (:block/uuid p) + (throw (ex-info (str "No uuid for existing page " (pr-str (:block/name p))) + (select-keys p [:block/name :block/tags]))))))) (into {}))) (defn- build-existing-page @@ -1008,8 +1019,9 @@ (not (:block/file %)))) ;; remove file path relative (map #(dissoc % :block/file))) - ;; Fetch all named ents once per import file to speed up named lookups - all-existing-page-uuids (get-all-existing-page-uuids @conn @(:classes-from-property-parents import-state)) + ;; Build all named ents once per import file to speed up named lookups + all-existing-page-uuids (get-all-existing-page-uuids @(:classes-from-property-parents import-state) + @(:all-existing-page-uuids import-state)) all-pages (map #(modify-page-tx % all-existing-page-uuids) all-pages*) all-new-page-uuids (->> all-pages (remove #(all-existing-page-uuids (or (::original-name %) (:block/name %)))) @@ -1119,6 +1131,8 @@ ;; Map of property names (keyword) and their current schemas (map of qualified properties). ;; Used for adding schemas to properties and detecting changes across a property's usage :property-schemas (atom {}) + ;; Indexes all created pages by uuid. Index is used to fetch all parents of a page + :all-existing-page-uuids (atom {}) ;; Map of property or class names (keyword) to db-ident keywords :all-idents (atom {}) ;; Set of children pages turned into classes by :property-parent-classes option @@ -1312,6 +1326,12 @@ classes-tx) retract-page-tag-from-existing-pages)})) +(defn- save-from-tx + "Save importer state from given txs" + [txs {:keys [import-state]}] + (when-let [nodes (seq (filter :block/name txs))] + (swap! (:all-existing-page-uuids import-state) merge (into {} (map (juxt :block/uuid identity) nodes))))) + (defn add-file-to-db-graph "Parse file and save parsed data to the given db graph. Options available: @@ -1351,6 +1371,7 @@ ;; _ (when (seq property-pages-tx) (cljs.pprint/pprint {:property-pages-tx property-pages-tx})) ;; Necessary to transact new property entities first so that block+page properties can be transacted next main-props-tx-report (d/transact! conn property-pages-tx {::new-graph? true ::path file}) + _ (save-from-tx property-pages-tx options) classes-tx @(:classes-tx tx-options) {:keys [retract-page-tags-tx] pages-tx'' :pages-tx} (clean-extra-invalid-tags @conn pages-tx' classes-tx existing-pages) @@ -1376,11 +1397,13 @@ ;; [whiteboard-pages pages-index page-properties-tx property-page-properties-tx pages-tx' classes-tx blocks-index blocks-tx])) ;; _ (when (not (seq whiteboard-pages)) (cljs.pprint/pprint {#_:property-pages-tx #_property-pages-tx :pages-tx pages-tx :tx tx'})) main-tx-report (d/transact! conn tx' {::new-graph? true ::path file}) + _ (save-from-tx tx' options) upstream-properties-tx (build-upstream-properties-tx @conn @(:upstream-properties tx-options) (:import-state options) log-fn) ;; _ (when (seq upstream-properties-tx) (cljs.pprint/pprint {:upstream-properties-tx upstream-properties-tx})) - upstream-tx-report (when (seq upstream-properties-tx) (d/transact! conn upstream-properties-tx {::new-graph? true ::path file}))] + upstream-tx-report (when (seq upstream-properties-tx) (d/transact! conn upstream-properties-tx {::new-graph? true ::path file})) + _ (save-from-tx upstream-properties-tx options)] ;; Return all tx-reports that occurred in this fn as UI needs to know what changed [main-props-tx-report main-tx-report upstream-tx-report])) From f4c5e6b0bb5ad616096e2df85bc60e276063b3c2 Mon Sep 17 00:00:00 2001 From: Gabriel Horner Date: Thu, 3 Apr 2025 16:59:39 -0400 Subject: [PATCH 13/20] Add time assertion on largest test import to prevent regression --- .../test/logseq/graph_parser/exporter_test.cljs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs b/deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs index 5d3eb2162e..6fbc8e11a1 100644 --- a/deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs +++ b/deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs @@ -130,11 +130,18 @@ (deftest-async ^:integration export-docs-graph-with-convert-all-tags (p/let [file-graph-dir "test/resources/docs-0.10.9" + start-time (cljs.core/system-time) _ (docs-graph-helper/clone-docs-repo-if-not-exists file-graph-dir "v0.10.9") conn (db-test/create-conn) _ (db-pipeline/add-listener conn) {:keys [import-state]} - (import-file-graph-to-db file-graph-dir conn {:convert-all-tags? true})] + (import-file-graph-to-db file-graph-dir conn {:convert-all-tags? true}) + end-time (cljs.core/system-time)] + + ;; Add multiplicative factor for CI as it runs about twice as slow + (let [max-time (-> 15 (* (if js/process.env.CI 2 1)))] + (is (< (-> end-time (- start-time) (/ 1000)) max-time) + (str "Importing large graph takes less than " max-time "s"))) (is (empty? (map :entity (:errors (db-validate/validate-db! @conn)))) "Created graph has no validation errors") From cf114803ffbb66e003eeaabaa4945855053ca5ea Mon Sep 17 00:00:00 2001 From: Gabriel Horner Date: Thu, 3 Apr 2025 17:11:06 -0400 Subject: [PATCH 14/20] remove silent failing of CI jobs Can't debug failures if we can't see their output e.g. https://github.com/logseq/logseq/actions/runs/14252668697/job/39948918589?pr=11807 --- .github/workflows/build.yml | 6 +++--- .github/workflows/db.yml | 6 +++--- .github/workflows/graph-parser.yml | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cb47d7cc79..7ae1d6bd64 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -112,13 +112,13 @@ jobs: run: clojure -M:clj-kondo --parallel --lint src - name: Carve lint for unused vars - run: bb lint:carve 2>/dev/null + run: bb lint:carve - name: Lint for vars that are too large - run: bb lint:large-vars 2>/dev/null + run: bb lint:large-vars - name: Lint for namespaces that aren't documented - run: bb lint:ns-docstrings 2>/dev/null + run: bb lint:ns-docstrings - name: Lint invalid translation entries run: bb lang:validate-translations diff --git a/.github/workflows/db.yml b/.github/workflows/db.yml index d3e807a253..8361d161aa 100644 --- a/.github/workflows/db.yml +++ b/.github/workflows/db.yml @@ -86,13 +86,13 @@ jobs: run: clojure -M:clj-kondo --lint src test - name: Carve lint for unused vars - run: bb lint:carve 2>/dev/null + run: bb lint:carve - name: Lint for vars that are too large - run: bb lint:large-vars 2>/dev/null + run: bb lint:large-vars - name: Lint datalog rules run: bb lint:rules - name: Lint for namespaces that aren't documented - run: bb lint:ns-docstrings 2>/dev/null + run: bb lint:ns-docstrings diff --git a/.github/workflows/graph-parser.yml b/.github/workflows/graph-parser.yml index 44909f28b1..2ac539af19 100644 --- a/.github/workflows/graph-parser.yml +++ b/.github/workflows/graph-parser.yml @@ -108,10 +108,10 @@ jobs: run: clojure -M:clj-kondo --parallel --lint src test - name: Carve lint for unused vars - run: bb lint:carve 2>/dev/null + run: bb lint:carve - name: Lint for vars that are too large - run: bb lint:large-vars 2>/dev/null + run: bb lint:large-vars - name: Lint for namespaces that aren't documented - run: bb lint:ns-docstrings 2>/dev/null + run: bb lint:ns-docstrings From 445be61fcf14c58847a23ba2c813fb405935b534 Mon Sep 17 00:00:00 2001 From: Gabriel Horner Date: Fri, 4 Apr 2025 11:07:05 -0400 Subject: [PATCH 15/20] enhance: Add EDN export to page+block menus for db graphs for more user-friendly use of the feature and to provide consistency. Addresses a request in #alpha-db-feedback --- deps/db/src/logseq/db/sqlite/export.cljs | 3 ++- src/main/frontend/components/content.cljs | 6 +++-- src/main/frontend/components/export.cljs | 29 ++++++++++++++++++--- src/main/frontend/components/page_menu.cljs | 3 ++- 4 files changed, 34 insertions(+), 7 deletions(-) diff --git a/deps/db/src/logseq/db/sqlite/export.cljs b/deps/db/src/logseq/db/sqlite/export.cljs index eb3d1629e8..a0bcddc830 100644 --- a/deps/db/src/logseq/db/sqlite/export.cljs +++ b/deps/db/src/logseq/db/sqlite/export.cljs @@ -769,7 +769,8 @@ (build-block-export db (:block-id options)) :page (build-page-export db (:page-id options)) - :view-nodes + ;; Different export types for different features as their needs may diverge + (:view-nodes :selected-nodes) (build-view-nodes-export db (:node-ids options)) :graph-ontology (build-graph-ontology-export db {}) diff --git a/src/main/frontend/components/content.cljs b/src/main/frontend/components/content.cljs index 18f059235d..ed1da6f026 100644 --- a/src/main/frontend/components/content.cljs +++ b/src/main/frontend/components/content.cljs @@ -81,7 +81,8 @@ (let [block-uuids (state/get-selection-block-ids)] (shui/popup-hide!) (shui/dialog-open! - #(export/export-blocks block-uuids {:whiteboard? false}))))} + #(export/export-blocks block-uuids {:whiteboard? false + :export-type :selected-nodes}))))} (t :content/copy-export-as)) (shui/dropdown-menu-item @@ -260,7 +261,8 @@ {:key "Copy as" :on-click (fn [_] (shui/dialog-open! - #(export/export-blocks [block-id] {:whiteboard? false})))} + #(export/export-blocks [block-id] {:whiteboard? false + :export-type :block})))} (t :content/copy-export-as)) (when-not property-default-value? diff --git a/src/main/frontend/components/export.cljs b/src/main/frontend/components/export.cljs index e6bccf48b2..7158316c27 100644 --- a/src/main/frontend/components/export.cljs +++ b/src/main/frontend/components/export.cljs @@ -1,6 +1,7 @@ (ns frontend.components.export (:require ["/frontend/utils" :as utils] [cljs-time.core :as t] + [cljs.pprint :as pprint] [frontend.config :as config] [frontend.context.i18n :refer [t]] [frontend.db :as db] @@ -159,6 +160,20 @@ current-repo block-uuids-or-page-name {:remove-options text-remove-options :other-options text-other-options}) ""))) +(defn- Date: Fri, 4 Apr 2025 14:43:45 -0400 Subject: [PATCH 16/20] enhance: add export+import of selected nodes Fixes https://github.com/logseq/db-test/issues/232 --- deps/db/src/logseq/db/sqlite/export.cljs | 37 +++++++++++++++---- .../db/test/logseq/db/sqlite/export_test.cljs | 37 +++++++++++++++++++ 2 files changed, 67 insertions(+), 7 deletions(-) diff --git a/deps/db/src/logseq/db/sqlite/export.cljs b/deps/db/src/logseq/db/sqlite/export.cljs index a0bcddc830..088934c742 100644 --- a/deps/db/src/logseq/db/sqlite/export.cljs +++ b/deps/db/src/logseq/db/sqlite/export.cljs @@ -470,7 +470,9 @@ page-export (finalize-export-maps db page-export* uuid-block-export content-ref-export)] page-export)) -(defn build-view-nodes-export* [db nodes opts] +(defn- build-nodes-export + "Export a mix of pages and blocks" + [db nodes opts] (let [node-pages (filter entity-util/page? nodes) pages-export (merge @@ -483,9 +485,7 @@ (->> node-blocks (group-by :block/page) (map (fn [[parent-page-ent blocks]] - (merge (build-blocks-export db - (sort-by :block/order blocks) - (merge opts {:include-children? false})) + (merge (build-blocks-export db (sort-by :block/order blocks) opts) {:page (shallow-copy-page parent-page-ent)})))) pages-to-blocks-export {:properties (apply merge (map :properties pages-to-blocks)) @@ -505,7 +505,29 @@ {:keys [content-ref-uuids content-ref-ents] :as content-ref-export} (build-content-ref-export db (into nodes property-value-ents)) {:keys [pvalue-uuids] :as nodes-export} - (build-view-nodes-export* db nodes {:include-uuid-fn content-ref-uuids}) + (build-nodes-export db nodes {:include-uuid-fn content-ref-uuids :include-children? false}) + uuid-block-export (build-uuid-block-export db pvalue-uuids content-ref-ents {}) + view-nodes-export (finalize-export-maps db nodes-export uuid-block-export content-ref-export)] + view-nodes-export)) + +(defn- build-selected-nodes-export + "Exports given nodes selected by a user. Nodes can be a mix of blocks and pages" + [db eids] + (let [top-level-nodes (map #(d/entity db %) eids) + children-nodes (->> top-level-nodes + ;; Remove pages b/c when selected their children are not highlighted + (remove entity-util/page?) + (mapcat #(rest (ldb/get-block-and-children db (:block/uuid %)))) + (remove :logseq.property/created-from-property)) + nodes (concat top-level-nodes children-nodes) + property-value-ents (mapcat #(->> (apply dissoc (db-property/properties %) db-property/public-db-attribute-properties) + vals + (filter de/entity?)) + nodes) + {:keys [content-ref-uuids content-ref-ents] :as content-ref-export} + (build-content-ref-export db (into nodes property-value-ents)) + {:keys [pvalue-uuids] :as nodes-export} + (build-nodes-export db nodes {:include-uuid-fn content-ref-uuids :include-children? true}) uuid-block-export (build-uuid-block-export db pvalue-uuids content-ref-ents {}) view-nodes-export (finalize-export-maps db nodes-export uuid-block-export content-ref-export)] view-nodes-export)) @@ -769,9 +791,10 @@ (build-block-export db (:block-id options)) :page (build-page-export db (:page-id options)) - ;; Different export types for different features as their needs may diverge - (:view-nodes :selected-nodes) + :view-nodes (build-view-nodes-export db (:node-ids options)) + :selected-nodes + (build-selected-nodes-export db (:node-ids options)) :graph-ontology (build-graph-ontology-export db {}) :graph diff --git a/deps/db/test/logseq/db/sqlite/export_test.cljs b/deps/db/test/logseq/db/sqlite/export_test.cljs index 1b214470f5..37c18c755c 100644 --- a/deps/db/test/logseq/db/sqlite/export_test.cljs +++ b/deps/db/test/logseq/db/sqlite/export_test.cljs @@ -531,6 +531,43 @@ (is (= (expand-properties (:properties original-data)) (:properties imported-nodes))) (is (= (expand-classes (:classes original-data)) (:classes imported-nodes))))) +(deftest import-selected-nodes + (let [original-data + ;; Test a mix of pages and blocks + {:properties {:user.property/p1 {:logseq.property/type :default}} + :classes {:user.class/class1 {}} + :pages-and-blocks [{:page {:block/title "page1"} + :blocks [{:block/title "b1" + :build/properties {:user.property/p1 "ok"} + :build/children [{:block/title "b2"}]} + {:block/title "b3" + :build/tags [:user.class/class1] + :build/children [{:block/title "b4"}]}]} + {:page {:block/title "page2"} + :blocks [{:block/title "dont export"}]}]} + conn (db-test/create-conn-with-blocks original-data) + get-node-ids (fn [db] + (->> [(db-test/find-block-by-content db "b1") + (db-test/find-page-by-title db "b3") + (db-test/find-page-by-title db "page2")] + (remove nil?) + (mapv #(vector :block/uuid (:block/uuid %))))) + conn2 (db-test/create-conn) + {:keys [init-tx block-props-tx] :as _txs} + (-> (sqlite-export/build-export @conn {:export-type :selected-nodes :node-ids (get-node-ids @conn)}) + (sqlite-export/build-import @conn2 {})) + ;; _ (cljs.pprint/pprint _txs) + _ (d/transact! conn2 init-tx) + _ (d/transact! conn2 block-props-tx) + _ (validate-db @conn2) + imported-nodes (sqlite-export/build-export @conn2 {:export-type :selected-nodes :node-ids (get-node-ids @conn2)})] + + (is (= (set (->> (:pages-and-blocks original-data) + (map #(if (= (get-in % [:page :block/title]) "page2") (dissoc % :blocks) %)))) + (set (:pages-and-blocks imported-nodes)))) + (is (= (expand-properties (:properties original-data)) (:properties imported-nodes))) + (is (= (expand-classes (:classes original-data)) (:classes imported-nodes))))) + (defn- build-original-graph-data [& {:keys [exclude-namespaces?]}] (let [internal-block-uuid (random-uuid) From 99331545c4da4e2e4a5b2a4549f9974347f659ba Mon Sep 17 00:00:00 2001 From: Gabriel Horner Date: Fri, 4 Apr 2025 15:12:11 -0400 Subject: [PATCH 17/20] enhance: give partial exports reliable page sort order Could be useful as partial exports are used more often --- deps/db/src/logseq/db/sqlite/export.cljs | 30 +++++++++++-------- .../db/test/logseq/db/sqlite/export_test.cljs | 26 +++++++++------- 2 files changed, 33 insertions(+), 23 deletions(-) diff --git a/deps/db/src/logseq/db/sqlite/export.cljs b/deps/db/src/logseq/db/sqlite/export.cljs index 088934c742..b5e839c7a7 100644 --- a/deps/db/src/logseq/db/sqlite/export.cljs +++ b/deps/db/src/logseq/db/sqlite/export.cljs @@ -395,12 +395,26 @@ :classes (apply merge (map :classes uuid-block-pages)) :pages-and-blocks (mapv #(select-keys % [:page :blocks]) uuid-block-pages)})) +(defn sort-pages-and-blocks + "Provide a reliable sort order since this tends to be large. Helps with diffing + and readability" + [pages-and-blocks] + (vec + (sort-by #(or (get-in % [:page :block/title]) + (some-> (get-in % [:page :build/journal]) str) + (str (get-in % [:page :block/uuid]))) + pages-and-blocks))) + (defn- finalize-export-maps - "Given final export maps, merges them, adds any missing class parents and merges those in" + "Given final export maps, merges them, adds any missing class parents and merges those in. + If :pages-and-blocks exist, sorts them in order to have reliable sort order" [db & export-maps] (let [final-export* (apply merge-export-maps export-maps) - class-parents-export (some->> (:classes final-export*) (build-class-parents-export db))] - (merge-export-maps final-export* class-parents-export))) + class-parents-export (some->> (:classes final-export*) (build-class-parents-export db)) + merged-map (merge-export-maps final-export* class-parents-export)] + (cond-> merged-map + (:pages-and-blocks merged-map) + (update :pages-and-blocks sort-pages-and-blocks)))) (defn- build-block-export "Exports block for given block eid" @@ -660,16 +674,6 @@ :blocks (sqlite-build/update-each-block blocks remove-uuid-if-not-ref)}) pages-and-blocks)))))) -(defn sort-pages-and-blocks - "Provide a reliable sort order since this tends to be large. Helps with diffing - and readability" - [pages-and-blocks] - (vec - (sort-by #(or (get-in % [:page :block/title]) - (some-> (get-in % [:page :build/journal]) str) - (str (get-in % [:page :block/uuid]))) - pages-and-blocks))) - (defn- add-ontology-for-include-namespaces "Adds :properties to export for given namespace parents. Current use case is for :exclude-namespaces so no need to add :classes yet" diff --git a/deps/db/test/logseq/db/sqlite/export_test.cljs b/deps/db/test/logseq/db/sqlite/export_test.cljs index 37c18c755c..cf02c5655f 100644 --- a/deps/db/test/logseq/db/sqlite/export_test.cljs +++ b/deps/db/test/logseq/db/sqlite/export_test.cljs @@ -57,17 +57,21 @@ (sqlite-export/build-export @import-conn {:export-type :page :page-id (:db/id page2)}))) (defn- import-second-time-assertions [conn conn2 page-title original-data - & {:keys [transform-expected-blocks] + & {:keys [transform-expected-blocks build-journal] :or {transform-expected-blocks (fn [bs] (into bs bs))}}] (let [page (db-test/find-page-by-title @conn2 page-title) imported-page (export-page-and-import-to-another-graph conn conn2 page-title) updated-page (db-test/find-page-by-title @conn2 page-title) expected-page-and-blocks - (update-in (:pages-and-blocks original-data) [0 :blocks] transform-expected-blocks)] + (update-in (:pages-and-blocks original-data) [0 :blocks] transform-expected-blocks) + filter-imported-page (if build-journal + #(= build-journal (get-in % [:page :build/journal])) + #(= (get-in % [:page :block/title]) page-title))] + (assert (first expected-page-and-blocks)) ;; Assume first page is one being imported for now (is (= (first expected-page-and-blocks) - (first (:pages-and-blocks imported-page))) + (first (filter filter-imported-page (:pages-and-blocks imported-page)))) "Blocks are appended to existing page") (is (= (:block/created-at page) (:block/created-at updated-page)) "Existing page didn't get re-created") @@ -323,7 +327,8 @@ (is (= (-> (:pages-and-blocks original-data) (medley/dissoc-in [1 :blocks 0 :build/properties]) ;; shallow block means this page doesn't get included - butlast) + butlast + sort-pages-and-blocks) (:pages-and-blocks imported-page)) "Page's blocks are imported") @@ -412,7 +417,7 @@ (is (= (:pages-and-blocks original-data) (:pages-and-blocks imported-page)) "Page's blocks are imported") - (import-second-time-assertions conn conn2 journal-title original-data))) + (import-second-time-assertions conn conn2 journal-title original-data {:build-journal 20250210}))) (deftest import-page-with-different-property-types (let [block-object-uuid (random-uuid) @@ -458,7 +463,8 @@ "Page's classes are imported") (is (= (-> (:pages-and-blocks original-data) ;; adjust shallow block - (medley/dissoc-in [1 :blocks 0 :build/tags])) + (medley/dissoc-in [1 :blocks 0 :build/tags]) + sort-pages-and-blocks) (:pages-and-blocks imported-page)) "Page's blocks are imported") @@ -527,7 +533,7 @@ imported-nodes (sqlite-export/build-export @conn2 {:export-type :view-nodes :node-ids (get-node-ids @conn2)})] - (is (= (:pages-and-blocks original-data) (:pages-and-blocks imported-nodes))) + (is (= (sort-pages-and-blocks (:pages-and-blocks original-data)) (:pages-and-blocks imported-nodes))) (is (= (expand-properties (:properties original-data)) (:properties imported-nodes))) (is (= (expand-classes (:classes original-data)) (:classes imported-nodes))))) @@ -562,9 +568,9 @@ _ (validate-db @conn2) imported-nodes (sqlite-export/build-export @conn2 {:export-type :selected-nodes :node-ids (get-node-ids @conn2)})] - (is (= (set (->> (:pages-and-blocks original-data) - (map #(if (= (get-in % [:page :block/title]) "page2") (dissoc % :blocks) %)))) - (set (:pages-and-blocks imported-nodes)))) + (is (= (->> (:pages-and-blocks original-data) + (map #(if (= (get-in % [:page :block/title]) "page2") (dissoc % :blocks) %))) + (:pages-and-blocks imported-nodes))) (is (= (expand-properties (:properties original-data)) (:properties imported-nodes))) (is (= (expand-classes (:classes original-data)) (:classes imported-nodes))))) From c9a87a0e37eca5a9c24da9404a78a2b9a9a67188 Mon Sep 17 00:00:00 2001 From: charlie Date: Mon, 7 Apr 2025 19:13:00 +0800 Subject: [PATCH 18/20] chore(dev): upgrade capacitor to latest --- android/app/capacitor.build.gradle | 4 +- android/build.gradle | 4 +- android/gradle/wrapper/gradle-wrapper.jar | Bin 59536 -> 43583 bytes .../gradle/wrapper/gradle-wrapper.properties | 4 +- android/gradlew | 44 +- android/gradlew.bat | 37 +- android/variables.gradle | 22 +- gulpfile.js | 2 +- ios/App/Podfile | 2 +- ios/App/Podfile.lock | 131 ++++++ package.json | 388 +++++++++--------- 11 files changed, 397 insertions(+), 241 deletions(-) create mode 100644 ios/App/Podfile.lock diff --git a/android/app/capacitor.build.gradle b/android/app/capacitor.build.gradle index de5663b84e..6eafa22a46 100644 --- a/android/app/capacitor.build.gradle +++ b/android/app/capacitor.build.gradle @@ -2,8 +2,8 @@ android { compileOptions { - sourceCompatibility JavaVersion.VERSION_17 - targetCompatibility JavaVersion.VERSION_17 + sourceCompatibility JavaVersion.VERSION_21 + targetCompatibility JavaVersion.VERSION_21 } } diff --git a/android/build.gradle b/android/build.gradle index 632d121927..c6bd5595f2 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -8,8 +8,8 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:8.1.1' - classpath 'com.google.gms:google-services:4.3.15' + classpath 'com.android.tools.build:gradle:8.7.2' + classpath 'com.google.gms:google-services:4.4.2' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/android/gradle/wrapper/gradle-wrapper.jar b/android/gradle/wrapper/gradle-wrapper.jar index 7454180f2ae8848c63b8b4dea2cb829da983f2fa..a4b76b9530d66f5e68d973ea569d8e19de379189 100644 GIT binary patch literal 43583 zcma&N1CXTcmMvW9vTb(Rwr$&4wr$(C?dmSu>@vG-+vuvg^_??!{yS%8zW-#zn-LkA z5&1^$^{lnmUON?}LBF8_K|(?T0Ra(xUH{($5eN!MR#ZihR#HxkUPe+_R8Cn`RRs(P z_^*#_XlXmGv7!4;*Y%p4nw?{bNp@UZHv1?Um8r6)Fei3p@ClJn0ECfg1hkeuUU@Or zDaPa;U3fE=3L}DooL;8f;P0ipPt0Z~9P0)lbStMS)ag54=uL9ia-Lm3nh|@(Y?B`; zx_#arJIpXH!U{fbCbI^17}6Ri*H<>OLR%c|^mh8+)*h~K8Z!9)DPf zR2h?lbDZQ`p9P;&DQ4F0sur@TMa!Y}S8irn(%d-gi0*WxxCSk*A?3lGh=gcYN?FGl z7D=Js!i~0=u3rox^eO3i@$0=n{K1lPNU zwmfjRVmLOCRfe=seV&P*1Iq=^i`502keY8Uy-WNPwVNNtJFx?IwAyRPZo2Wo1+S(xF37LJZ~%i)kpFQ3Fw=mXfd@>%+)RpYQLnr}B~~zoof(JVm^^&f zxKV^+3D3$A1G;qh4gPVjhrC8e(VYUHv#dy^)(RoUFM?o%W-EHxufuWf(l*@-l+7vt z=l`qmR56K~F|v<^Pd*p~1_y^P0P^aPC##d8+HqX4IR1gu+7w#~TBFphJxF)T$2WEa zxa?H&6=Qe7d(#tha?_1uQys2KtHQ{)Qco)qwGjrdNL7thd^G5i8Os)CHqc>iOidS} z%nFEDdm=GXBw=yXe1W-ShHHFb?Cc70+$W~z_+}nAoHFYI1MV1wZegw*0y^tC*s%3h zhD3tN8b=Gv&rj}!SUM6|ajSPp*58KR7MPpI{oAJCtY~JECm)*m_x>AZEu>DFgUcby z1Qaw8lU4jZpQ_$;*7RME+gq1KySGG#Wql>aL~k9tLrSO()LWn*q&YxHEuzmwd1?aAtI zBJ>P=&$=l1efe1CDU;`Fd+_;&wI07?V0aAIgc(!{a z0Jg6Y=inXc3^n!U0Atk`iCFIQooHqcWhO(qrieUOW8X(x?(RD}iYDLMjSwffH2~tB z)oDgNBLB^AJBM1M^c5HdRx6fBfka`(LD-qrlh5jqH~);#nw|iyp)()xVYak3;Ybik z0j`(+69aK*B>)e_p%=wu8XC&9e{AO4c~O1U`5X9}?0mrd*m$_EUek{R?DNSh(=br# z#Q61gBzEpmy`$pA*6!87 zSDD+=@fTY7<4A?GLqpA?Pb2z$pbCc4B4zL{BeZ?F-8`s$?>*lXXtn*NC61>|*w7J* z$?!iB{6R-0=KFmyp1nnEmLsA-H0a6l+1uaH^g%c(p{iT&YFrbQ$&PRb8Up#X3@Zsk zD^^&LK~111%cqlP%!_gFNa^dTYT?rhkGl}5=fL{a`UViaXWI$k-UcHJwmaH1s=S$4 z%4)PdWJX;hh5UoK?6aWoyLxX&NhNRqKam7tcOkLh{%j3K^4Mgx1@i|Pi&}<^5>hs5 zm8?uOS>%)NzT(%PjVPGa?X%`N2TQCKbeH2l;cTnHiHppPSJ<7y-yEIiC!P*ikl&!B z%+?>VttCOQM@ShFguHVjxX^?mHX^hSaO_;pnyh^v9EumqSZTi+#f&_Vaija0Q-e*| z7ulQj6Fs*bbmsWp{`auM04gGwsYYdNNZcg|ph0OgD>7O}Asn7^Z=eI>`$2*v78;sj-}oMoEj&@)9+ycEOo92xSyY344^ z11Hb8^kdOvbf^GNAK++bYioknrpdN>+u8R?JxG=!2Kd9r=YWCOJYXYuM0cOq^FhEd zBg2puKy__7VT3-r*dG4c62Wgxi52EMCQ`bKgf*#*ou(D4-ZN$+mg&7$u!! z-^+Z%;-3IDwqZ|K=ah85OLwkO zKxNBh+4QHh)u9D?MFtpbl)us}9+V!D%w9jfAMYEb>%$A;u)rrI zuBudh;5PN}_6J_}l55P3l_)&RMlH{m!)ai-i$g)&*M`eN$XQMw{v^r@-125^RRCF0 z^2>|DxhQw(mtNEI2Kj(;KblC7x=JlK$@78`O~>V!`|1Lm-^JR$-5pUANAnb(5}B}JGjBsliK4& zk6y(;$e&h)lh2)L=bvZKbvh@>vLlreBdH8No2>$#%_Wp1U0N7Ank!6$dFSi#xzh|( zRi{Uw%-4W!{IXZ)fWx@XX6;&(m_F%c6~X8hx=BN1&q}*( zoaNjWabE{oUPb!Bt$eyd#$5j9rItB-h*5JiNi(v^e|XKAj*8(k<5-2$&ZBR5fF|JA z9&m4fbzNQnAU}r8ab>fFV%J0z5awe#UZ|bz?Ur)U9bCIKWEzi2%A+5CLqh?}K4JHi z4vtM;+uPsVz{Lfr;78W78gC;z*yTch~4YkLr&m-7%-xc ztw6Mh2d>_iO*$Rd8(-Cr1_V8EO1f*^@wRoSozS) zy1UoC@pruAaC8Z_7~_w4Q6n*&B0AjOmMWa;sIav&gu z|J5&|{=a@vR!~k-OjKEgPFCzcJ>#A1uL&7xTDn;{XBdeM}V=l3B8fE1--DHjSaxoSjNKEM9|U9#m2<3>n{Iuo`r3UZp;>GkT2YBNAh|b z^jTq-hJp(ebZh#Lk8hVBP%qXwv-@vbvoREX$TqRGTgEi$%_F9tZES@z8Bx}$#5eeG zk^UsLBH{bc2VBW)*EdS({yw=?qmevwi?BL6*=12k9zM5gJv1>y#ML4!)iiPzVaH9% zgSImetD@dam~e>{LvVh!phhzpW+iFvWpGT#CVE5TQ40n%F|p(sP5mXxna+Ev7PDwA zamaV4m*^~*xV+&p;W749xhb_X=$|LD;FHuB&JL5?*Y2-oIT(wYY2;73<^#46S~Gx| z^cez%V7x$81}UWqS13Gz80379Rj;6~WdiXWOSsdmzY39L;Hg3MH43o*y8ibNBBH`(av4|u;YPq%{R;IuYow<+GEsf@R?=@tT@!}?#>zIIn0CoyV!hq3mw zHj>OOjfJM3F{RG#6ujzo?y32m^tgSXf@v=J$ELdJ+=5j|=F-~hP$G&}tDZsZE?5rX ztGj`!S>)CFmdkccxM9eGIcGnS2AfK#gXwj%esuIBNJQP1WV~b~+D7PJTmWGTSDrR` zEAu4B8l>NPuhsk5a`rReSya2nfV1EK01+G!x8aBdTs3Io$u5!6n6KX%uv@DxAp3F@{4UYg4SWJtQ-W~0MDb|j-$lwVn znAm*Pl!?Ps&3wO=R115RWKb*JKoexo*)uhhHBncEDMSVa_PyA>k{Zm2(wMQ(5NM3# z)jkza|GoWEQo4^s*wE(gHz?Xsg4`}HUAcs42cM1-qq_=+=!Gk^y710j=66(cSWqUe zklbm8+zB_syQv5A2rj!Vbw8;|$@C!vfNmNV!yJIWDQ>{+2x zKjuFX`~~HKG~^6h5FntRpnnHt=D&rq0>IJ9#F0eM)Y-)GpRjiN7gkA8wvnG#K=q{q z9dBn8_~wm4J<3J_vl|9H{7q6u2A!cW{bp#r*-f{gOV^e=8S{nc1DxMHFwuM$;aVI^ zz6A*}m8N-&x8;aunp1w7_vtB*pa+OYBw=TMc6QK=mbA-|Cf* zvyh8D4LRJImooUaSb7t*fVfih<97Gf@VE0|z>NcBwBQze);Rh!k3K_sfunToZY;f2 z^HmC4KjHRVg+eKYj;PRN^|E0>Gj_zagfRbrki68I^#~6-HaHg3BUW%+clM1xQEdPYt_g<2K+z!$>*$9nQ>; zf9Bei{?zY^-e{q_*|W#2rJG`2fy@{%6u0i_VEWTq$*(ZN37|8lFFFt)nCG({r!q#9 z5VK_kkSJ3?zOH)OezMT{!YkCuSSn!K#-Rhl$uUM(bq*jY? zi1xbMVthJ`E>d>(f3)~fozjg^@eheMF6<)I`oeJYx4*+M&%c9VArn(OM-wp%M<-`x z7sLP1&3^%Nld9Dhm@$3f2}87!quhI@nwd@3~fZl_3LYW-B?Ia>ui`ELg z&Qfe!7m6ze=mZ`Ia9$z|ARSw|IdMpooY4YiPN8K z4B(ts3p%2i(Td=tgEHX z0UQ_>URBtG+-?0E;E7Ld^dyZ;jjw0}XZ(}-QzC6+NN=40oDb2^v!L1g9xRvE#@IBR zO!b-2N7wVfLV;mhEaXQ9XAU+>=XVA6f&T4Z-@AX!leJ8obP^P^wP0aICND?~w&NykJ#54x3_@r7IDMdRNy4Hh;h*!u(Ol(#0bJdwEo$5437-UBjQ+j=Ic>Q2z` zJNDf0yO6@mr6y1#n3)s(W|$iE_i8r@Gd@!DWDqZ7J&~gAm1#~maIGJ1sls^gxL9LLG_NhU!pTGty!TbhzQnu)I*S^54U6Yu%ZeCg`R>Q zhBv$n5j0v%O_j{QYWG!R9W?5_b&67KB$t}&e2LdMvd(PxN6Ir!H4>PNlerpBL>Zvyy!yw z-SOo8caEpDt(}|gKPBd$qND5#a5nju^O>V&;f890?yEOfkSG^HQVmEbM3Ugzu+UtH zC(INPDdraBN?P%kE;*Ae%Wto&sgw(crfZ#Qy(<4nk;S|hD3j{IQRI6Yq|f^basLY; z-HB&Je%Gg}Jt@={_C{L$!RM;$$|iD6vu#3w?v?*;&()uB|I-XqEKqZPS!reW9JkLewLb!70T7n`i!gNtb1%vN- zySZj{8-1>6E%H&=V}LM#xmt`J3XQoaD|@XygXjdZ1+P77-=;=eYpoEQ01B@L*a(uW zrZeZz?HJsw_4g0vhUgkg@VF8<-X$B8pOqCuWAl28uB|@r`19DTUQQsb^pfqB6QtiT z*`_UZ`fT}vtUY#%sq2{rchyfu*pCg;uec2$-$N_xgjZcoumE5vSI{+s@iLWoz^Mf; zuI8kDP{!XY6OP~q5}%1&L}CtfH^N<3o4L@J@zg1-mt{9L`s^z$Vgb|mr{@WiwAqKg zp#t-lhrU>F8o0s1q_9y`gQNf~Vb!F%70f}$>i7o4ho$`uciNf=xgJ>&!gSt0g;M>*x4-`U)ysFW&Vs^Vk6m%?iuWU+o&m(2Jm26Y(3%TL; zA7T)BP{WS!&xmxNw%J=$MPfn(9*^*TV;$JwRy8Zl*yUZi8jWYF>==j~&S|Xinsb%c z2?B+kpet*muEW7@AzjBA^wAJBY8i|#C{WtO_or&Nj2{=6JTTX05}|H>N2B|Wf!*3_ z7hW*j6p3TvpghEc6-wufFiY!%-GvOx*bZrhZu+7?iSrZL5q9}igiF^*R3%DE4aCHZ zqu>xS8LkW+Auv%z-<1Xs92u23R$nk@Pk}MU5!gT|c7vGlEA%G^2th&Q*zfg%-D^=f z&J_}jskj|Q;73NP4<4k*Y%pXPU2Thoqr+5uH1yEYM|VtBPW6lXaetokD0u z9qVek6Q&wk)tFbQ8(^HGf3Wp16gKmr>G;#G(HRBx?F`9AIRboK+;OfHaLJ(P>IP0w zyTbTkx_THEOs%Q&aPrxbZrJlio+hCC_HK<4%f3ZoSAyG7Dn`=X=&h@m*|UYO-4Hq0 z-Bq&+Ie!S##4A6OGoC~>ZW`Y5J)*ouaFl_e9GA*VSL!O_@xGiBw!AF}1{tB)z(w%c zS1Hmrb9OC8>0a_$BzeiN?rkPLc9%&;1CZW*4}CDDNr2gcl_3z+WC15&H1Zc2{o~i) z)LLW=WQ{?ricmC`G1GfJ0Yp4Dy~Ba;j6ZV4r{8xRs`13{dD!xXmr^Aga|C=iSmor% z8hi|pTXH)5Yf&v~exp3o+sY4B^^b*eYkkCYl*T{*=-0HniSA_1F53eCb{x~1k3*`W zr~};p1A`k{1DV9=UPnLDgz{aJH=-LQo<5%+Em!DNN252xwIf*wF_zS^!(XSm(9eoj z=*dXG&n0>)_)N5oc6v!>-bd(2ragD8O=M|wGW z!xJQS<)u70m&6OmrF0WSsr@I%T*c#Qo#Ha4d3COcX+9}hM5!7JIGF>7<~C(Ear^Sn zm^ZFkV6~Ula6+8S?oOROOA6$C&q&dp`>oR-2Ym3(HT@O7Sd5c~+kjrmM)YmgPH*tL zX+znN>`tv;5eOfX?h{AuX^LK~V#gPCu=)Tigtq9&?7Xh$qN|%A$?V*v=&-2F$zTUv z`C#WyIrChS5|Kgm_GeudCFf;)!WH7FI60j^0o#65o6`w*S7R@)88n$1nrgU(oU0M9 zx+EuMkC>(4j1;m6NoGqEkpJYJ?vc|B zOlwT3t&UgL!pX_P*6g36`ZXQ; z9~Cv}ANFnJGp(;ZhS(@FT;3e)0)Kp;h^x;$*xZn*k0U6-&FwI=uOGaODdrsp-!K$Ac32^c{+FhI-HkYd5v=`PGsg%6I`4d9Jy)uW0y%) zm&j^9WBAp*P8#kGJUhB!L?a%h$hJgQrx!6KCB_TRo%9{t0J7KW8!o1B!NC)VGLM5! zpZy5Jc{`r{1e(jd%jsG7k%I+m#CGS*BPA65ZVW~fLYw0dA-H_}O zrkGFL&P1PG9p2(%QiEWm6x;U-U&I#;Em$nx-_I^wtgw3xUPVVu zqSuKnx&dIT-XT+T10p;yjo1Y)z(x1fb8Dzfn8e yu?e%!_ptzGB|8GrCfu%p?(_ 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 GZ~q4|mN%#X literal 59536 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 QuEJmfE|#`Dqx|px0L@K;Y5)KL diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index 3796d3cd3c..c1d5e01859 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-all.zip +networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/android/gradlew b/android/gradlew index 1b6c787337..f5feea6d6b 100755 --- a/android/gradlew +++ b/android/gradlew @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -80,13 +82,12 @@ do esac done -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -APP_NAME="Gradle" +# This is normally unused +# shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -133,22 +134,29 @@ location of your Java installation." fi else JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac case $MAX_FD in #( '' | soft) :;; #( *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -193,11 +201,15 @@ if "$cygwin" || "$msys" ; then done fi -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ @@ -205,6 +217,12 @@ set -- \ org.gradle.wrapper.GradleWrapperMain \ "$@" +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + # Use "xargs" to parse quoted args. # # With -n1 it outputs one arg per line, with the quotes and backslashes removed. diff --git a/android/gradlew.bat b/android/gradlew.bat index ac1b06f938..9b42019c79 100644 --- a/android/gradlew.bat +++ b/android/gradlew.bat @@ -13,8 +13,10 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,7 +27,8 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -40,13 +43,13 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute +if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -56,11 +59,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -75,13 +78,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal diff --git a/android/variables.gradle b/android/variables.gradle index df5c0f24d8..b319e0e7d1 100644 --- a/android/variables.gradle +++ b/android/variables.gradle @@ -1,16 +1,16 @@ ext { - minSdkVersion = 22 - compileSdkVersion = 33 - targetSdkVersion = 33 - androidxActivityVersion = '1.7.0' - androidxAppCompatVersion = '1.6.1' + minSdkVersion = 23 + compileSdkVersion = 35 + targetSdkVersion = 35 + androidxActivityVersion = '1.9.2' + androidxAppCompatVersion = '1.7.0' androidxCoordinatorLayoutVersion = '1.2.0' - androidxCoreVersion = '1.10.0' - androidxFragmentVersion = '1.5.6' + androidxCoreVersion = '1.15.0' + androidxFragmentVersion = '1.8.4' junitVersion = '4.13.2' - androidxJunitVersion = '1.1.5' - androidxEspressoCoreVersion = '3.5.1' + androidxJunitVersion = '1.2.1' + androidxEspressoCoreVersion = '3.6.1' cordovaAndroidVersion = '10.1.1' - coreSplashScreenVersion = '1.0.0' - androidxWebkitVersion = '1.6.1' + coreSplashScreenVersion = '1.0.1' + androidxWebkitVersion = '1.12.1' } diff --git a/gulpfile.js b/gulpfile.js index 1ae0c46ab4..ad347250f0 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -161,7 +161,7 @@ const common = { stdio: 'inherit', }) - cp.execSync(`npx cap run ${mode} --external`, { + cp.execSync(`npx cap run ${mode}`, { stdio: 'inherit', env: Object.assign(process.env, { LOGSEQ_APP_SERVER_URL, diff --git a/ios/App/Podfile b/ios/App/Podfile index c931f25369..a9fcddd9aa 100644 --- a/ios/App/Podfile +++ b/ios/App/Podfile @@ -1,6 +1,6 @@ require_relative '../../node_modules/@capacitor/ios/scripts/pods_helpers' -platform :ios, '13.0' +platform :ios, '14.0' use_frameworks! # workaround to avoid Xcode caching of Pods that requires diff --git a/ios/App/Podfile.lock b/ios/App/Podfile.lock new file mode 100644 index 0000000000..b1c5bf28a7 --- /dev/null +++ b/ios/App/Podfile.lock @@ -0,0 +1,131 @@ +PODS: + - AgeEncryption (1.0.6) + - Alamofire (5.6.2) + - AWSCore (2.27.10) + - AWSS3 (2.27.10): + - AWSCore (= 2.27.10) + - Capacitor (7.2.0): + - CapacitorCordova + - CapacitorActionSheet (7.0.1): + - Capacitor + - CapacitorApp (7.0.1): + - Capacitor + - CapacitorCamera (7.0.1): + - Capacitor + - CapacitorClipboard (7.0.1): + - Capacitor + - CapacitorCordova (7.2.0) + - CapacitorFilesystem (7.0.1): + - Capacitor + - CapacitorHaptics (7.0.1): + - Capacitor + - CapacitorKeyboard (7.0.1): + - Capacitor + - CapacitorShare (7.0.1): + - Capacitor + - CapacitorSplashScreen (7.0.1): + - Capacitor + - CapacitorStatusBar (7.0.1): + - Capacitor + - CapacitorVoiceRecorder (5.0.0): + - Capacitor + - CapawesomeCapacitorBackgroundTask (7.0.1): + - Capacitor + - CapgoCapacitorNavigationBar (7.1.2): + - Capacitor + - LogseqCapacitorFileSync (5.0.2): + - AgeEncryption (~> 1.0.6) + - Alamofire + - AWSS3 + - Capacitor + - SendIntent (0.0.1): + - Capacitor + +DEPENDENCIES: + - "Capacitor (from `../../node_modules/@capacitor/ios`)" + - "CapacitorActionSheet (from `../../node_modules/@capacitor/action-sheet`)" + - "CapacitorApp (from `../../node_modules/@capacitor/app`)" + - "CapacitorCamera (from `../../node_modules/@capacitor/camera`)" + - "CapacitorClipboard (from `../../node_modules/@capacitor/clipboard`)" + - "CapacitorCordova (from `../../node_modules/@capacitor/ios`)" + - "CapacitorFilesystem (from `../../node_modules/@capacitor/filesystem`)" + - "CapacitorHaptics (from `../../node_modules/@capacitor/haptics`)" + - "CapacitorKeyboard (from `../../node_modules/@capacitor/keyboard`)" + - "CapacitorShare (from `../../node_modules/@capacitor/share`)" + - "CapacitorSplashScreen (from `../../node_modules/@capacitor/splash-screen`)" + - "CapacitorStatusBar (from `../../node_modules/@capacitor/status-bar`)" + - CapacitorVoiceRecorder (from `../../node_modules/capacitor-voice-recorder`) + - "CapawesomeCapacitorBackgroundTask (from `../../node_modules/@capawesome/capacitor-background-task`)" + - "CapgoCapacitorNavigationBar (from `../../node_modules/@capgo/capacitor-navigation-bar`)" + - "LogseqCapacitorFileSync (from `../../node_modules/@logseq/capacitor-file-sync`)" + - SendIntent (from `../../node_modules/send-intent`) + +SPEC REPOS: + trunk: + - AgeEncryption + - Alamofire + - AWSCore + - AWSS3 + +EXTERNAL SOURCES: + Capacitor: + :path: "../../node_modules/@capacitor/ios" + CapacitorActionSheet: + :path: "../../node_modules/@capacitor/action-sheet" + CapacitorApp: + :path: "../../node_modules/@capacitor/app" + CapacitorCamera: + :path: "../../node_modules/@capacitor/camera" + CapacitorClipboard: + :path: "../../node_modules/@capacitor/clipboard" + CapacitorCordova: + :path: "../../node_modules/@capacitor/ios" + CapacitorFilesystem: + :path: "../../node_modules/@capacitor/filesystem" + CapacitorHaptics: + :path: "../../node_modules/@capacitor/haptics" + CapacitorKeyboard: + :path: "../../node_modules/@capacitor/keyboard" + CapacitorShare: + :path: "../../node_modules/@capacitor/share" + CapacitorSplashScreen: + :path: "../../node_modules/@capacitor/splash-screen" + CapacitorStatusBar: + :path: "../../node_modules/@capacitor/status-bar" + CapacitorVoiceRecorder: + :path: "../../node_modules/capacitor-voice-recorder" + CapawesomeCapacitorBackgroundTask: + :path: "../../node_modules/@capawesome/capacitor-background-task" + CapgoCapacitorNavigationBar: + :path: "../../node_modules/@capgo/capacitor-navigation-bar" + LogseqCapacitorFileSync: + :path: "../../node_modules/@logseq/capacitor-file-sync" + SendIntent: + :path: "../../node_modules/send-intent" + +SPEC CHECKSUMS: + AgeEncryption: 23c203675d5e4883a18b133ab1d5e61ff29e0c18 + Alamofire: d368e1ff8a298e6dde360e35a3e68e6c610e7204 + AWSCore: dbad318b7ff0fd86fb8387d3ad1f30049c05bc58 + AWSS3: 0cf2cedb263368f624ca721e5c56a8cb59fc44bc + Capacitor: 106e7a4205f4618d582b886a975657c61179138d + CapacitorActionSheet: 24609588961cc27c87e8b033be92b5eee65b5d4c + CapacitorApp: d63334c052278caf5d81585d80b21905c6f93f39 + CapacitorCamera: eb8687d8687fed853598ec9460d94bcd5e16babe + CapacitorClipboard: b98aead5dc7ec595547fc2c5d75bacd2ae3338bc + CapacitorCordova: 5967b9ba03915ef1d585469d6e31f31dc49be96f + CapacitorFilesystem: 307f97c27a265edf8396a1c9c235592fd8572fe3 + CapacitorHaptics: 70e47470fa1a6bd6338cd102552e3846b7f9a1b3 + CapacitorKeyboard: 969647d0ca2e5c737d7300088e2517aa832434e2 + CapacitorShare: 58d6c2da63b093e8693287b2d36db92435538435 + CapacitorSplashScreen: 19cd3573e57507e02d6f34597a8c421e00931487 + CapacitorStatusBar: 275cbf2f4dfc00388f519ef80c7ec22edda342c9 + CapacitorVoiceRecorder: 872ea857b497ce2c71afe3e4eb5de0a74290c0db + CapawesomeCapacitorBackgroundTask: 834d797abc9933fac4354490d1a2f3c0e389b98d + CapgoCapacitorNavigationBar: 3a0e93a40b7da3d3cb74c0410bb761a1525406be + LogseqCapacitorFileSync: 541dcd5492b8aeb2257cce3d18bb9ed5800cfcad + SendIntent: 0a17b6984c4f27e9dfa56513267ba2c044a5a6c8 + +PODFILE CHECKSUM: d9e4df9f9578f817a4148a5e29ee928f271e4cc8 + +COCOAPODS: 1.11.2 diff --git a/package.json b/package.json index 061ed18498..d5ac8f8724 100644 --- a/package.json +++ b/package.json @@ -1,196 +1,196 @@ { - "name": "logseq", - "version": "0.0.1", - "private": true, - "main": "static/electron.js", - "devDependencies": { - "@axe-core/playwright": "=4.4.4", - "@capacitor/cli": "^5.0.0", - "@playwright/test": "=1.51.0", - "@tailwindcss/aspect-ratio": "0.4.2", - "@tailwindcss/forms": "0.5.3", - "@tailwindcss/typography": "0.5.7", - "@types/gulp": "^4.0.7", - "autoprefixer": "^10.4.13", - "cross-env": "^7.0.3", - "cssnano": "^5.1.13", - "del": "^6.0.0", - "glob": "9.0.0", - "gulp": "^4.0.2", - "gulp-replace": "^1.1.4", - "gulp-postcss": "^10.0.0", - "ip": "1.1.9", - "semver": "7.5.2", - "karma": "^6.4.4", - "karma-chrome-launcher": "^3.2.0", - "karma-cljs-test": "^0.1.0", - "npm-run-all": "^4.1.5", - "playwright": "=1.51.0", - "postcss": "^8.4.47", - "postcss-cli": "10.0.0", - "postcss-functions": "^4.0.2", - "postcss-import": "15.0.0", - "postcss-import-ext-glob": "2.0.1", - "postcss-nested": "6.0.0", - "purgecss": "4.0.2", - "shadow-cljs": "2.26.0", - "stylelint": "^13.8.0", - "stylelint-config-standard": "^20.0.0", - "tailwindcss": "3.3.5", - "tailwindcss-animate": "^1.0.7", - "typescript": "^4.4.3" - }, - "scripts": { - "watch": "run-p gulp:watch cljs:watch", - "electron-watch": "run-p gulp:watch cljs:electron-watch", - "app-watch": "run-p gulp:watch cljs:app-watch", - "release": "run-s gulp:build cljs:release", - "release-app": "run-s gulp:build cljs:release-app", - "dev-release-app": "run-s gulp:build cljs:dev-release-app", - "dev-electron-app": "gulp electron", - "release-electron": "run-s gulp:build && gulp electronMaker", - "debug-electron": "cd static/ && yarn electron:debug", - "e2e-test": "cross-env DEBUG=pw:api CI=true npx playwright test --reporter github", - "run-android-release": "yarn clean && yarn release-app && rm -rf ./public/static && rm -rf ./static/js/*.map && mv static ./public && npx cap sync android && npx cap run android", - "run-ios-release": "yarn clean && yarn release-app && rm -rf ./public/static && rm -rf ./static/js/*.map && mv static ./public && npx cap sync ios && npx cap run ios", - "clean": "gulp clean", - "test": "run-s cljs:test cljs:run-test", - "report": "run-s cljs:report", - "style:lint": "stylelint \"src/**/*.css\"", - "gulp:watch": "gulp watch", - "gulp:build": "cross-env NODE_ENV=production gulp build", - "css:build": "postcss tailwind.all.css -o static/css/style.css --verbose --env production", - "css:watch": "cross-env TAILWIND_MODE=watch postcss tailwind.all.css -o static/css/style.css --verbose --watch", - "cljs:watch": "clojure -M:cljs watch app electron", - "cljs:watch-storybook": "clojure -M:cljs watch stories-dev", - "cljs:app-watch": "clojure -M:cljs watch app", - "cljs:electron-watch": "clojure -M:cljs watch app electron --config-merge \"{:asset-path \\\"./js\\\"}\"", - "cljs:release": "clojure -M:cljs release app publishing electron", - "cljs:release-electron": "clojure -M:cljs release app electron --debug && clojure -M:cljs release publishing", - "cljs:release-app": "clojure -M:cljs release app", - "cljs:release-publishing": "clojure -M:cljs release publishing", - "cljs:test": "clojure -M:test compile test", - "cljs:run-test": "node static/tests.js", - "cljs:dev-release-app": "clojure -M:cljs release app --config-merge \"{:closure-defines {frontend.config/DEV-RELEASE true}}\"", - "cljs:dev-release-electron": "clojure -M:cljs release app electron --debug --config-merge \"{:closure-defines {frontend.config/DEV-RELEASE true}}\" && clojure -M:cljs release publishing", - "cljs:debug": "clojure -M:cljs release app --debug", - "cljs:report": "clojure -M:cljs run shadow.cljs.build-report app report.html", - "cljs:build-electron": "clojure -A:cljs compile app electron", - "cljs:lint": "clojure -M:clj-kondo --parallel --lint src --cache false", - "ios:dev": "cross-env PLATFORM=ios gulp cap", - "android:dev": "cross-env PLATFORM=android gulp cap", - "tldraw:build": "yarn --cwd packages/tldraw install", - "amplify:build": "yarn --cwd packages/amplify install", - "ui:build": "yarn --cwd packages/ui install", - "postinstall": "yarn tldraw:build && yarn amplify:build && yarn ui:build" - }, - "dependencies": { - "@capacitor/action-sheet": "^5.0.7", - "@capacitor/android": "^5.0.0", - "@capacitor/app": "^5.0.0", - "@capacitor/camera": "^5.0.0", - "@capacitor/clipboard": "^5.0.0", - "@capacitor/core": "^5.0.0", - "@capacitor/filesystem": "^5.0.0", - "@capacitor/haptics": "^5.0.0", - "@capacitor/ios": "^5.0.0", - "@capacitor/keyboard": "^5.0.0", - "@capacitor/share": "^5.0.0", - "@capacitor/splash-screen": "^5.0.0", - "@capacitor/status-bar": "^5.0.0", - "@capawesome/capacitor-background-task": "^5.0.0", - "@capgo/capacitor-navigation-bar": "^6.0.0", - "@dnd-kit/core": "^6.0.8", - "@dnd-kit/sortable": "^7.0.2", - "@emoji-mart/data": "^1.1.2", - "@emoji-mart/react": "^1.1.1", - "@excalidraw/excalidraw": "0.16.1", - "@glidejs/glide": "^3.6.0", - "@highlightjs/cdn-assets": "10.4.1", - "@isomorphic-git/lightning-fs": "^4.6.0", - "@js-joda/core": "3.2.0", - "@js-joda/locale_en-us": "3.1.1", - "@js-joda/timezone": "2.5.0", - "@logseq/capacitor-file-sync": "5.0.2", - "@logseq/diff-merge": "0.2.2", - "@logseq/react-tweet-embed": "1.3.1-1", - "@logseq/sqlite-wasm": "=0.1.0", - "@radix-ui/colors": "^0.1.8", - "@sentry/react": "^6.18.2", - "@sentry/tracing": "^6.18.2", - "@tabler/icons-react": "^2.47.0", - "@tabler/icons-webfont": "^2.47.0", - "@tippyjs/react": "4.2.5", - "bignumber.js": "^9.0.2", - "capacitor-voice-recorder": "^5.0.0", - "check-password-strength": "2.0.7", - "chokidar": "3.5.1", - "chrono-node": "2.2.4", - "codemirror": "5.65.18", - "comlink": "^4.4.1", - "d3-force": "3.0.0", - "diff": "5.0.0", - "dompurify": "2.4.0", - "electron": "35.0.1", - "electron-dl": "^4.0.0", - "emoji-mart": "^5.5.2", - "fs": "0.0.1-security", - "fs-extra": "9.1.0", - "fuse.js": "6.4.6", - "grapheme-splitter": "1.0.4", - "graphology": "0.20.0", - "html2canvas": "^1.4.1", - "ignore": "5.1.8", - "inter-ui": "^3.19.3", - "interactjs": "^1.10.17", - "jszip": "3.8.0", - "katex": "^0.16.10", - "marked": "^5.1.2", - "mldoc": "^1.5.9", - "path": "0.12.7", - "path-complete-extname": "1.0.0", - "pdfjs-dist": "^3.9.179", - "photoswipe": "^5.3.7", - "pixi-graph-fork": "0.2.0", - "pixi.js": "6.2.0", - "posthog-js": "1.10.2", - "prop-types": "^15.7.2", - "react": "17.0.2", - "react-dom": "17.0.2", - "react-grid-layout": "0.16.6", - "react-intersection-observer": "^9.3.5", - "react-resize-context": "3.0.0", - "react-textarea-autosize": "8.3.3", - "react-tippy": "1.4.0", - "react-transition-group": "4.3.0", - "react-virtuoso": "4.12.5", - "remove-accents": "0.4.2", - "reveal.js": "^4.5.0", - "sanitize-filename": "1.6.3", - "send-intent": "^5.0.0", - "shepherd.js": "^9.1.0", - "tailwind-capitalize-first-letter": "^1.0.4", - "threads": "1.6.5", - "url": "^0.11.0", - "yargs-parser": "20.2.4" - }, - "resolutions": { - "pixi-graph-fork/@pixi/app": "6.2.0", - "pixi-graph-fork/@pixi/constants": "6.2.0", - "pixi-graph-fork/@pixi/core": "6.2.0", - "pixi-graph-fork/@pixi/display": "6.2.0", - "pixi-graph-fork/@pixi/graphics": "6.2.0", - "pixi-graph-fork/@pixi/interaction": "6.2.0", - "pixi-graph-fork/@pixi/loaders": "6.2.0", - "pixi-graph-fork/@pixi/ticker": "6.2.0", - "pixi-graph-fork/@pixi/sprite": "6.2.0", - "pixi-graph-fork/@pixi/text": "6.2.0", - "pixi-graph-fork/@pixi/text-bitmap": "6.2.0", - "pixi-graph-fork/@pixi/utils": "6.2.0", - "pixi-graph-fork/@pixi/runner": "6.2.0", - "pixi-graph-fork/@pixi/mesh": "6.2.0", - "pixi-graph-fork/@pixi/settings": "6.2.0", - "pixi-graph-fork/@pixi/mixin-get-child-by-name": "6.2.0", - "pixi-graph-fork/@pixi/math": "6.2.0" - } + "name": "logseq", + "version": "0.0.1", + "private": true, + "main": "static/electron.js", + "devDependencies": { + "@axe-core/playwright": "=4.4.4", + "@capacitor/cli": "^7.0.0", + "@playwright/test": "=1.51.0", + "@tailwindcss/aspect-ratio": "0.4.2", + "@tailwindcss/forms": "0.5.3", + "@tailwindcss/typography": "0.5.7", + "@types/gulp": "^4.0.7", + "autoprefixer": "^10.4.13", + "cross-env": "^7.0.3", + "cssnano": "^5.1.13", + "del": "^6.0.0", + "glob": "9.0.0", + "gulp": "^4.0.2", + "gulp-postcss": "^10.0.0", + "gulp-replace": "^1.1.4", + "ip": "1.1.9", + "karma": "^6.4.4", + "karma-chrome-launcher": "^3.2.0", + "karma-cljs-test": "^0.1.0", + "npm-run-all": "^4.1.5", + "playwright": "=1.51.0", + "postcss": "^8.4.47", + "postcss-cli": "10.0.0", + "postcss-functions": "^4.0.2", + "postcss-import": "15.0.0", + "postcss-import-ext-glob": "2.0.1", + "postcss-nested": "6.0.0", + "purgecss": "4.0.2", + "semver": "7.5.2", + "shadow-cljs": "2.26.0", + "stylelint": "^13.8.0", + "stylelint-config-standard": "^20.0.0", + "tailwindcss": "3.3.5", + "tailwindcss-animate": "^1.0.7", + "typescript": "^4.4.3" + }, + "scripts": { + "watch": "run-p gulp:watch cljs:watch", + "electron-watch": "run-p gulp:watch cljs:electron-watch", + "app-watch": "run-p gulp:watch cljs:app-watch", + "release": "run-s gulp:build cljs:release", + "release-app": "run-s gulp:build cljs:release-app", + "dev-release-app": "run-s gulp:build cljs:dev-release-app", + "dev-electron-app": "gulp electron", + "release-electron": "run-s gulp:build && gulp electronMaker", + "debug-electron": "cd static/ && yarn electron:debug", + "e2e-test": "cross-env DEBUG=pw:api CI=true npx playwright test --reporter github", + "run-android-release": "yarn clean && yarn release-app && rm -rf ./public/static && rm -rf ./static/js/*.map && mv static ./public && npx cap sync android && npx cap run android", + "run-ios-release": "yarn clean && yarn release-app && rm -rf ./public/static && rm -rf ./static/js/*.map && mv static ./public && npx cap sync ios && npx cap run ios", + "clean": "gulp clean", + "test": "run-s cljs:test cljs:run-test", + "report": "run-s cljs:report", + "style:lint": "stylelint \"src/**/*.css\"", + "gulp:watch": "gulp watch", + "gulp:build": "cross-env NODE_ENV=production gulp build", + "css:build": "postcss tailwind.all.css -o static/css/style.css --verbose --env production", + "css:watch": "cross-env TAILWIND_MODE=watch postcss tailwind.all.css -o static/css/style.css --verbose --watch", + "cljs:watch": "clojure -M:cljs watch app electron", + "cljs:watch-storybook": "clojure -M:cljs watch stories-dev", + "cljs:app-watch": "clojure -M:cljs watch app", + "cljs:electron-watch": "clojure -M:cljs watch app electron --config-merge \"{:asset-path \\\"./js\\\"}\"", + "cljs:release": "clojure -M:cljs release app publishing electron", + "cljs:release-electron": "clojure -M:cljs release app electron --debug && clojure -M:cljs release publishing", + "cljs:release-app": "clojure -M:cljs release app", + "cljs:release-publishing": "clojure -M:cljs release publishing", + "cljs:test": "clojure -M:test compile test", + "cljs:run-test": "node static/tests.js", + "cljs:dev-release-app": "clojure -M:cljs release app --config-merge \"{:closure-defines {frontend.config/DEV-RELEASE true}}\"", + "cljs:dev-release-electron": "clojure -M:cljs release app electron --debug --config-merge \"{:closure-defines {frontend.config/DEV-RELEASE true}}\" && clojure -M:cljs release publishing", + "cljs:debug": "clojure -M:cljs release app --debug", + "cljs:report": "clojure -M:cljs run shadow.cljs.build-report app report.html", + "cljs:build-electron": "clojure -A:cljs compile app electron", + "cljs:lint": "clojure -M:clj-kondo --parallel --lint src --cache false", + "ios:dev": "cross-env PLATFORM=ios gulp cap", + "android:dev": "cross-env PLATFORM=android gulp cap", + "tldraw:build": "yarn --cwd packages/tldraw install", + "amplify:build": "yarn --cwd packages/amplify install", + "ui:build": "yarn --cwd packages/ui install", + "postinstall": "yarn tldraw:build && yarn amplify:build && yarn ui:build" + }, + "dependencies": { + "@capacitor/action-sheet": "^7.0.0", + "@capacitor/android": "^7.0.0", + "@capacitor/app": "^7.0.0", + "@capacitor/camera": "^7.0.0", + "@capacitor/clipboard": "^7.0.0", + "@capacitor/core": "^7.0.0", + "@capacitor/filesystem": "^7.0.0", + "@capacitor/haptics": "^7.0.0", + "@capacitor/ios": "^7.0.0", + "@capacitor/keyboard": "^7.0.0", + "@capacitor/share": "^7.0.0", + "@capacitor/splash-screen": "^7.0.0", + "@capacitor/status-bar": "^7.0.0", + "@capawesome/capacitor-background-task": "7.0.1", + "@capgo/capacitor-navigation-bar": "7.1.2", + "@dnd-kit/core": "^6.0.8", + "@dnd-kit/sortable": "^7.0.2", + "@emoji-mart/data": "^1.1.2", + "@emoji-mart/react": "^1.1.1", + "@excalidraw/excalidraw": "0.16.1", + "@glidejs/glide": "^3.6.0", + "@highlightjs/cdn-assets": "10.4.1", + "@isomorphic-git/lightning-fs": "^4.6.0", + "@js-joda/core": "3.2.0", + "@js-joda/locale_en-us": "3.1.1", + "@js-joda/timezone": "2.5.0", + "@logseq/capacitor-file-sync": "5.0.2", + "@logseq/diff-merge": "0.2.2", + "@logseq/react-tweet-embed": "1.3.1-1", + "@logseq/sqlite-wasm": "=0.1.0", + "@radix-ui/colors": "^0.1.8", + "@sentry/react": "^6.18.2", + "@sentry/tracing": "^6.18.2", + "@tabler/icons-react": "^2.47.0", + "@tabler/icons-webfont": "^2.47.0", + "@tippyjs/react": "4.2.5", + "bignumber.js": "^9.0.2", + "capacitor-voice-recorder": "^5.0.0", + "check-password-strength": "2.0.7", + "chokidar": "3.5.1", + "chrono-node": "2.2.4", + "codemirror": "5.65.18", + "comlink": "^4.4.1", + "d3-force": "3.0.0", + "diff": "5.0.0", + "dompurify": "2.4.0", + "electron": "35.0.1", + "electron-dl": "^4.0.0", + "emoji-mart": "^5.5.2", + "fs": "0.0.1-security", + "fs-extra": "9.1.0", + "fuse.js": "6.4.6", + "grapheme-splitter": "1.0.4", + "graphology": "0.20.0", + "html2canvas": "^1.4.1", + "ignore": "5.1.8", + "inter-ui": "^3.19.3", + "interactjs": "^1.10.17", + "jszip": "3.8.0", + "katex": "^0.16.10", + "marked": "^5.1.2", + "mldoc": "^1.5.9", + "path": "0.12.7", + "path-complete-extname": "1.0.0", + "pdfjs-dist": "^3.9.179", + "photoswipe": "^5.3.7", + "pixi-graph-fork": "0.2.0", + "pixi.js": "6.2.0", + "posthog-js": "1.10.2", + "prop-types": "^15.7.2", + "react": "17.0.2", + "react-dom": "17.0.2", + "react-grid-layout": "0.16.6", + "react-intersection-observer": "^9.3.5", + "react-resize-context": "3.0.0", + "react-textarea-autosize": "8.3.3", + "react-tippy": "1.4.0", + "react-transition-group": "4.3.0", + "react-virtuoso": "4.12.5", + "remove-accents": "0.4.2", + "reveal.js": "^4.5.0", + "sanitize-filename": "1.6.3", + "send-intent": "^5.0.0", + "shepherd.js": "^9.1.0", + "tailwind-capitalize-first-letter": "^1.0.4", + "threads": "1.6.5", + "url": "^0.11.0", + "yargs-parser": "20.2.4" + }, + "resolutions": { + "pixi-graph-fork/@pixi/app": "6.2.0", + "pixi-graph-fork/@pixi/constants": "6.2.0", + "pixi-graph-fork/@pixi/core": "6.2.0", + "pixi-graph-fork/@pixi/display": "6.2.0", + "pixi-graph-fork/@pixi/graphics": "6.2.0", + "pixi-graph-fork/@pixi/interaction": "6.2.0", + "pixi-graph-fork/@pixi/loaders": "6.2.0", + "pixi-graph-fork/@pixi/ticker": "6.2.0", + "pixi-graph-fork/@pixi/sprite": "6.2.0", + "pixi-graph-fork/@pixi/text": "6.2.0", + "pixi-graph-fork/@pixi/text-bitmap": "6.2.0", + "pixi-graph-fork/@pixi/utils": "6.2.0", + "pixi-graph-fork/@pixi/runner": "6.2.0", + "pixi-graph-fork/@pixi/mesh": "6.2.0", + "pixi-graph-fork/@pixi/settings": "6.2.0", + "pixi-graph-fork/@pixi/mixin-get-child-by-name": "6.2.0", + "pixi-graph-fork/@pixi/math": "6.2.0" + } } From 48591f0085ac47dad916ffcd52c9c7095cc3c723 Mon Sep 17 00:00:00 2001 From: charlie Date: Mon, 7 Apr 2025 19:19:32 +0800 Subject: [PATCH 19/20] chore(dev): restore package.json style --- package.json | 388 +++++++++++++++++++++++++-------------------------- 1 file changed, 194 insertions(+), 194 deletions(-) diff --git a/package.json b/package.json index d5ac8f8724..62eb6f3c03 100644 --- a/package.json +++ b/package.json @@ -1,196 +1,196 @@ { - "name": "logseq", - "version": "0.0.1", - "private": true, - "main": "static/electron.js", - "devDependencies": { - "@axe-core/playwright": "=4.4.4", - "@capacitor/cli": "^7.0.0", - "@playwright/test": "=1.51.0", - "@tailwindcss/aspect-ratio": "0.4.2", - "@tailwindcss/forms": "0.5.3", - "@tailwindcss/typography": "0.5.7", - "@types/gulp": "^4.0.7", - "autoprefixer": "^10.4.13", - "cross-env": "^7.0.3", - "cssnano": "^5.1.13", - "del": "^6.0.0", - "glob": "9.0.0", - "gulp": "^4.0.2", - "gulp-postcss": "^10.0.0", - "gulp-replace": "^1.1.4", - "ip": "1.1.9", - "karma": "^6.4.4", - "karma-chrome-launcher": "^3.2.0", - "karma-cljs-test": "^0.1.0", - "npm-run-all": "^4.1.5", - "playwright": "=1.51.0", - "postcss": "^8.4.47", - "postcss-cli": "10.0.0", - "postcss-functions": "^4.0.2", - "postcss-import": "15.0.0", - "postcss-import-ext-glob": "2.0.1", - "postcss-nested": "6.0.0", - "purgecss": "4.0.2", - "semver": "7.5.2", - "shadow-cljs": "2.26.0", - "stylelint": "^13.8.0", - "stylelint-config-standard": "^20.0.0", - "tailwindcss": "3.3.5", - "tailwindcss-animate": "^1.0.7", - "typescript": "^4.4.3" - }, - "scripts": { - "watch": "run-p gulp:watch cljs:watch", - "electron-watch": "run-p gulp:watch cljs:electron-watch", - "app-watch": "run-p gulp:watch cljs:app-watch", - "release": "run-s gulp:build cljs:release", - "release-app": "run-s gulp:build cljs:release-app", - "dev-release-app": "run-s gulp:build cljs:dev-release-app", - "dev-electron-app": "gulp electron", - "release-electron": "run-s gulp:build && gulp electronMaker", - "debug-electron": "cd static/ && yarn electron:debug", - "e2e-test": "cross-env DEBUG=pw:api CI=true npx playwright test --reporter github", - "run-android-release": "yarn clean && yarn release-app && rm -rf ./public/static && rm -rf ./static/js/*.map && mv static ./public && npx cap sync android && npx cap run android", - "run-ios-release": "yarn clean && yarn release-app && rm -rf ./public/static && rm -rf ./static/js/*.map && mv static ./public && npx cap sync ios && npx cap run ios", - "clean": "gulp clean", - "test": "run-s cljs:test cljs:run-test", - "report": "run-s cljs:report", - "style:lint": "stylelint \"src/**/*.css\"", - "gulp:watch": "gulp watch", - "gulp:build": "cross-env NODE_ENV=production gulp build", - "css:build": "postcss tailwind.all.css -o static/css/style.css --verbose --env production", - "css:watch": "cross-env TAILWIND_MODE=watch postcss tailwind.all.css -o static/css/style.css --verbose --watch", - "cljs:watch": "clojure -M:cljs watch app electron", - "cljs:watch-storybook": "clojure -M:cljs watch stories-dev", - "cljs:app-watch": "clojure -M:cljs watch app", - "cljs:electron-watch": "clojure -M:cljs watch app electron --config-merge \"{:asset-path \\\"./js\\\"}\"", - "cljs:release": "clojure -M:cljs release app publishing electron", - "cljs:release-electron": "clojure -M:cljs release app electron --debug && clojure -M:cljs release publishing", - "cljs:release-app": "clojure -M:cljs release app", - "cljs:release-publishing": "clojure -M:cljs release publishing", - "cljs:test": "clojure -M:test compile test", - "cljs:run-test": "node static/tests.js", - "cljs:dev-release-app": "clojure -M:cljs release app --config-merge \"{:closure-defines {frontend.config/DEV-RELEASE true}}\"", - "cljs:dev-release-electron": "clojure -M:cljs release app electron --debug --config-merge \"{:closure-defines {frontend.config/DEV-RELEASE true}}\" && clojure -M:cljs release publishing", - "cljs:debug": "clojure -M:cljs release app --debug", - "cljs:report": "clojure -M:cljs run shadow.cljs.build-report app report.html", - "cljs:build-electron": "clojure -A:cljs compile app electron", - "cljs:lint": "clojure -M:clj-kondo --parallel --lint src --cache false", - "ios:dev": "cross-env PLATFORM=ios gulp cap", - "android:dev": "cross-env PLATFORM=android gulp cap", - "tldraw:build": "yarn --cwd packages/tldraw install", - "amplify:build": "yarn --cwd packages/amplify install", - "ui:build": "yarn --cwd packages/ui install", - "postinstall": "yarn tldraw:build && yarn amplify:build && yarn ui:build" - }, - "dependencies": { - "@capacitor/action-sheet": "^7.0.0", - "@capacitor/android": "^7.0.0", - "@capacitor/app": "^7.0.0", - "@capacitor/camera": "^7.0.0", - "@capacitor/clipboard": "^7.0.0", - "@capacitor/core": "^7.0.0", - "@capacitor/filesystem": "^7.0.0", - "@capacitor/haptics": "^7.0.0", - "@capacitor/ios": "^7.0.0", - "@capacitor/keyboard": "^7.0.0", - "@capacitor/share": "^7.0.0", - "@capacitor/splash-screen": "^7.0.0", - "@capacitor/status-bar": "^7.0.0", - "@capawesome/capacitor-background-task": "7.0.1", - "@capgo/capacitor-navigation-bar": "7.1.2", - "@dnd-kit/core": "^6.0.8", - "@dnd-kit/sortable": "^7.0.2", - "@emoji-mart/data": "^1.1.2", - "@emoji-mart/react": "^1.1.1", - "@excalidraw/excalidraw": "0.16.1", - "@glidejs/glide": "^3.6.0", - "@highlightjs/cdn-assets": "10.4.1", - "@isomorphic-git/lightning-fs": "^4.6.0", - "@js-joda/core": "3.2.0", - "@js-joda/locale_en-us": "3.1.1", - "@js-joda/timezone": "2.5.0", - "@logseq/capacitor-file-sync": "5.0.2", - "@logseq/diff-merge": "0.2.2", - "@logseq/react-tweet-embed": "1.3.1-1", - "@logseq/sqlite-wasm": "=0.1.0", - "@radix-ui/colors": "^0.1.8", - "@sentry/react": "^6.18.2", - "@sentry/tracing": "^6.18.2", - "@tabler/icons-react": "^2.47.0", - "@tabler/icons-webfont": "^2.47.0", - "@tippyjs/react": "4.2.5", - "bignumber.js": "^9.0.2", - "capacitor-voice-recorder": "^5.0.0", - "check-password-strength": "2.0.7", - "chokidar": "3.5.1", - "chrono-node": "2.2.4", - "codemirror": "5.65.18", - "comlink": "^4.4.1", - "d3-force": "3.0.0", - "diff": "5.0.0", - "dompurify": "2.4.0", - "electron": "35.0.1", - "electron-dl": "^4.0.0", - "emoji-mart": "^5.5.2", - "fs": "0.0.1-security", - "fs-extra": "9.1.0", - "fuse.js": "6.4.6", - "grapheme-splitter": "1.0.4", - "graphology": "0.20.0", - "html2canvas": "^1.4.1", - "ignore": "5.1.8", - "inter-ui": "^3.19.3", - "interactjs": "^1.10.17", - "jszip": "3.8.0", - "katex": "^0.16.10", - "marked": "^5.1.2", - "mldoc": "^1.5.9", - "path": "0.12.7", - "path-complete-extname": "1.0.0", - "pdfjs-dist": "^3.9.179", - "photoswipe": "^5.3.7", - "pixi-graph-fork": "0.2.0", - "pixi.js": "6.2.0", - "posthog-js": "1.10.2", - "prop-types": "^15.7.2", - "react": "17.0.2", - "react-dom": "17.0.2", - "react-grid-layout": "0.16.6", - "react-intersection-observer": "^9.3.5", - "react-resize-context": "3.0.0", - "react-textarea-autosize": "8.3.3", - "react-tippy": "1.4.0", - "react-transition-group": "4.3.0", - "react-virtuoso": "4.12.5", - "remove-accents": "0.4.2", - "reveal.js": "^4.5.0", - "sanitize-filename": "1.6.3", - "send-intent": "^5.0.0", - "shepherd.js": "^9.1.0", - "tailwind-capitalize-first-letter": "^1.0.4", - "threads": "1.6.5", - "url": "^0.11.0", - "yargs-parser": "20.2.4" - }, - "resolutions": { - "pixi-graph-fork/@pixi/app": "6.2.0", - "pixi-graph-fork/@pixi/constants": "6.2.0", - "pixi-graph-fork/@pixi/core": "6.2.0", - "pixi-graph-fork/@pixi/display": "6.2.0", - "pixi-graph-fork/@pixi/graphics": "6.2.0", - "pixi-graph-fork/@pixi/interaction": "6.2.0", - "pixi-graph-fork/@pixi/loaders": "6.2.0", - "pixi-graph-fork/@pixi/ticker": "6.2.0", - "pixi-graph-fork/@pixi/sprite": "6.2.0", - "pixi-graph-fork/@pixi/text": "6.2.0", - "pixi-graph-fork/@pixi/text-bitmap": "6.2.0", - "pixi-graph-fork/@pixi/utils": "6.2.0", - "pixi-graph-fork/@pixi/runner": "6.2.0", - "pixi-graph-fork/@pixi/mesh": "6.2.0", - "pixi-graph-fork/@pixi/settings": "6.2.0", - "pixi-graph-fork/@pixi/mixin-get-child-by-name": "6.2.0", - "pixi-graph-fork/@pixi/math": "6.2.0" - } + "name": "logseq", + "version": "0.0.1", + "private": true, + "main": "static/electron.js", + "devDependencies": { + "@axe-core/playwright": "=4.4.4", + "@capacitor/cli": "7.2.0", + "@playwright/test": "=1.51.0", + "@tailwindcss/aspect-ratio": "0.4.2", + "@tailwindcss/forms": "0.5.3", + "@tailwindcss/typography": "0.5.7", + "@types/gulp": "^4.0.7", + "autoprefixer": "^10.4.13", + "cross-env": "^7.0.3", + "cssnano": "^5.1.13", + "del": "^6.0.0", + "glob": "9.0.0", + "gulp": "^4.0.2", + "gulp-replace": "^1.1.4", + "gulp-postcss": "^10.0.0", + "ip": "1.1.9", + "semver": "7.5.2", + "karma": "^6.4.4", + "karma-chrome-launcher": "^3.2.0", + "karma-cljs-test": "^0.1.0", + "npm-run-all": "^4.1.5", + "playwright": "=1.51.0", + "postcss": "^8.4.47", + "postcss-cli": "10.0.0", + "postcss-functions": "^4.0.2", + "postcss-import": "15.0.0", + "postcss-import-ext-glob": "2.0.1", + "postcss-nested": "6.0.0", + "purgecss": "4.0.2", + "shadow-cljs": "2.26.0", + "stylelint": "^13.8.0", + "stylelint-config-standard": "^20.0.0", + "tailwindcss": "3.3.5", + "tailwindcss-animate": "^1.0.7", + "typescript": "^4.4.3" + }, + "scripts": { + "watch": "run-p gulp:watch cljs:watch", + "electron-watch": "run-p gulp:watch cljs:electron-watch", + "app-watch": "run-p gulp:watch cljs:app-watch", + "release": "run-s gulp:build cljs:release", + "release-app": "run-s gulp:build cljs:release-app", + "dev-release-app": "run-s gulp:build cljs:dev-release-app", + "dev-electron-app": "gulp electron", + "release-electron": "run-s gulp:build && gulp electronMaker", + "debug-electron": "cd static/ && yarn electron:debug", + "e2e-test": "cross-env DEBUG=pw:api CI=true npx playwright test --reporter github", + "run-android-release": "yarn clean && yarn release-app && rm -rf ./public/static && rm -rf ./static/js/*.map && mv static ./public && npx cap sync android && npx cap run android", + "run-ios-release": "yarn clean && yarn release-app && rm -rf ./public/static && rm -rf ./static/js/*.map && mv static ./public && npx cap sync ios && npx cap run ios", + "clean": "gulp clean", + "test": "run-s cljs:test cljs:run-test", + "report": "run-s cljs:report", + "style:lint": "stylelint \"src/**/*.css\"", + "gulp:watch": "gulp watch", + "gulp:build": "cross-env NODE_ENV=production gulp build", + "css:build": "postcss tailwind.all.css -o static/css/style.css --verbose --env production", + "css:watch": "cross-env TAILWIND_MODE=watch postcss tailwind.all.css -o static/css/style.css --verbose --watch", + "cljs:watch": "clojure -M:cljs watch app electron", + "cljs:watch-storybook": "clojure -M:cljs watch stories-dev", + "cljs:app-watch": "clojure -M:cljs watch app", + "cljs:electron-watch": "clojure -M:cljs watch app electron --config-merge \"{:asset-path \\\"./js\\\"}\"", + "cljs:release": "clojure -M:cljs release app publishing electron", + "cljs:release-electron": "clojure -M:cljs release app electron --debug && clojure -M:cljs release publishing", + "cljs:release-app": "clojure -M:cljs release app", + "cljs:release-publishing": "clojure -M:cljs release publishing", + "cljs:test": "clojure -M:test compile test", + "cljs:run-test": "node static/tests.js", + "cljs:dev-release-app": "clojure -M:cljs release app --config-merge \"{:closure-defines {frontend.config/DEV-RELEASE true}}\"", + "cljs:dev-release-electron": "clojure -M:cljs release app electron --debug --config-merge \"{:closure-defines {frontend.config/DEV-RELEASE true}}\" && clojure -M:cljs release publishing", + "cljs:debug": "clojure -M:cljs release app --debug", + "cljs:report": "clojure -M:cljs run shadow.cljs.build-report app report.html", + "cljs:build-electron": "clojure -A:cljs compile app electron", + "cljs:lint": "clojure -M:clj-kondo --parallel --lint src --cache false", + "ios:dev": "cross-env PLATFORM=ios gulp cap", + "android:dev": "cross-env PLATFORM=android gulp cap", + "tldraw:build": "yarn --cwd packages/tldraw install", + "amplify:build": "yarn --cwd packages/amplify install", + "ui:build": "yarn --cwd packages/ui install", + "postinstall": "yarn tldraw:build && yarn amplify:build && yarn ui:build" + }, + "dependencies": { + "@capacitor/action-sheet": "7.0.1", + "@capacitor/android": "7.2.0", + "@capacitor/app": "7.0.1", + "@capacitor/camera": "7.0.1", + "@capacitor/clipboard": "7.0.1", + "@capacitor/core": "7.2.0", + "@capacitor/filesystem": "7.0.1", + "@capacitor/haptics": "7.0.1", + "@capacitor/ios": "7.2.0", + "@capacitor/keyboard": "7.0.1", + "@capacitor/share": "7.0.1", + "@capacitor/splash-screen": "7.0.1", + "@capacitor/status-bar": "7.0.1", + "@capawesome/capacitor-background-task": "7.0.1", + "@capgo/capacitor-navigation-bar": "7.1.2", + "@dnd-kit/core": "^6.0.8", + "@dnd-kit/sortable": "^7.0.2", + "@emoji-mart/data": "^1.1.2", + "@emoji-mart/react": "^1.1.1", + "@excalidraw/excalidraw": "0.16.1", + "@glidejs/glide": "^3.6.0", + "@highlightjs/cdn-assets": "10.4.1", + "@isomorphic-git/lightning-fs": "^4.6.0", + "@js-joda/core": "3.2.0", + "@js-joda/locale_en-us": "3.1.1", + "@js-joda/timezone": "2.5.0", + "@logseq/capacitor-file-sync": "5.0.2", + "@logseq/diff-merge": "0.2.2", + "@logseq/react-tweet-embed": "1.3.1-1", + "@logseq/sqlite-wasm": "=0.1.0", + "@radix-ui/colors": "^0.1.8", + "@sentry/react": "^6.18.2", + "@sentry/tracing": "^6.18.2", + "@tabler/icons-react": "^2.47.0", + "@tabler/icons-webfont": "^2.47.0", + "@tippyjs/react": "4.2.5", + "bignumber.js": "^9.0.2", + "capacitor-voice-recorder": "^5.0.0", + "check-password-strength": "2.0.7", + "chokidar": "3.5.1", + "chrono-node": "2.2.4", + "codemirror": "5.65.18", + "comlink": "^4.4.1", + "d3-force": "3.0.0", + "diff": "5.0.0", + "dompurify": "2.4.0", + "electron": "35.0.1", + "electron-dl": "^4.0.0", + "emoji-mart": "^5.5.2", + "fs": "0.0.1-security", + "fs-extra": "9.1.0", + "fuse.js": "6.4.6", + "grapheme-splitter": "1.0.4", + "graphology": "0.20.0", + "html2canvas": "^1.4.1", + "ignore": "5.1.8", + "inter-ui": "^3.19.3", + "interactjs": "^1.10.17", + "jszip": "3.8.0", + "katex": "^0.16.10", + "marked": "^5.1.2", + "mldoc": "^1.5.9", + "path": "0.12.7", + "path-complete-extname": "1.0.0", + "pdfjs-dist": "^3.9.179", + "photoswipe": "^5.3.7", + "pixi-graph-fork": "0.2.0", + "pixi.js": "6.2.0", + "posthog-js": "1.10.2", + "prop-types": "^15.7.2", + "react": "17.0.2", + "react-dom": "17.0.2", + "react-grid-layout": "0.16.6", + "react-intersection-observer": "^9.3.5", + "react-resize-context": "3.0.0", + "react-textarea-autosize": "8.3.3", + "react-tippy": "1.4.0", + "react-transition-group": "4.3.0", + "react-virtuoso": "4.12.5", + "remove-accents": "0.4.2", + "reveal.js": "^4.5.0", + "sanitize-filename": "1.6.3", + "send-intent": "^5.0.0", + "shepherd.js": "^9.1.0", + "tailwind-capitalize-first-letter": "^1.0.4", + "threads": "1.6.5", + "url": "^0.11.0", + "yargs-parser": "20.2.4" + }, + "resolutions": { + "pixi-graph-fork/@pixi/app": "6.2.0", + "pixi-graph-fork/@pixi/constants": "6.2.0", + "pixi-graph-fork/@pixi/core": "6.2.0", + "pixi-graph-fork/@pixi/display": "6.2.0", + "pixi-graph-fork/@pixi/graphics": "6.2.0", + "pixi-graph-fork/@pixi/interaction": "6.2.0", + "pixi-graph-fork/@pixi/loaders": "6.2.0", + "pixi-graph-fork/@pixi/ticker": "6.2.0", + "pixi-graph-fork/@pixi/sprite": "6.2.0", + "pixi-graph-fork/@pixi/text": "6.2.0", + "pixi-graph-fork/@pixi/text-bitmap": "6.2.0", + "pixi-graph-fork/@pixi/utils": "6.2.0", + "pixi-graph-fork/@pixi/runner": "6.2.0", + "pixi-graph-fork/@pixi/mesh": "6.2.0", + "pixi-graph-fork/@pixi/settings": "6.2.0", + "pixi-graph-fork/@pixi/mixin-get-child-by-name": "6.2.0", + "pixi-graph-fork/@pixi/math": "6.2.0" + } } From 70e2012e29295bf9b41292f5cef0eca947affcea Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Mon, 7 Apr 2025 10:27:26 +0800 Subject: [PATCH 20/20] bump React to 18 --- deps.edn | 3 +- deps/shui/deps.edn | 4 +- deps/shui/src/logseq/shui/demo.cljs | 11 +- deps/shui/src/logseq/shui/demo2.cljs | 465 +++++++++--------- deps/shui/src/logseq/shui/dialog/core.cljs | 30 +- .../shui/src/logseq/shui}/hooks.cljs | 20 +- deps/shui/src/logseq/shui/select/multi.cljs | 227 ++++----- deps/shui/src/logseq/shui/table/core.cljc | 15 +- deps/shui/src/logseq/shui/toaster/core.cljs | 33 +- package.json | 4 +- src/main/frontend/components/all_pages.cljs | 2 +- src/main/frontend/components/assets.cljs | 2 +- src/main/frontend/components/block.cljs | 2 +- src/main/frontend/components/bug_report.cljs | 2 +- src/main/frontend/components/cmdk/core.cljs | 2 +- .../frontend/components/cmdk/list_item.cljs | 2 +- src/main/frontend/components/container.cljs | 2 +- src/main/frontend/components/dnd.cljs | 2 +- src/main/frontend/components/editor.cljs | 2 +- .../frontend/components/file_based/block.cljs | 2 +- .../frontend/components/file_based/git.cljs | 10 +- src/main/frontend/components/file_sync.cljs | 2 +- src/main/frontend/components/handbooks.cljs | 2 +- src/main/frontend/components/header.cljs | 2 +- src/main/frontend/components/icon.cljs | 2 +- src/main/frontend/components/imports.cljs | 2 +- src/main/frontend/components/objects.cljs | 2 +- src/main/frontend/components/page.cljs | 2 +- src/main/frontend/components/plugins.cljs | 2 +- .../frontend/components/plugins_settings.cljs | 2 +- src/main/frontend/components/property.cljs | 2 +- .../frontend/components/property/config.cljs | 2 +- .../frontend/components/property/value.cljs | 18 +- src/main/frontend/components/query.cljs | 2 +- .../frontend/components/query/builder.cljs | 2 +- .../frontend/components/right_sidebar.cljs | 2 +- src/main/frontend/components/server.cljs | 2 +- src/main/frontend/components/settings.cljs | 2 +- src/main/frontend/components/shortcut.cljs | 2 +- src/main/frontend/components/theme.cljs | 2 +- src/main/frontend/components/user/login.cljs | 2 +- src/main/frontend/components/views.cljs | 2 +- .../frontend/extensions/handbooks/core.cljs | 2 +- src/main/frontend/extensions/pdf/core.cljs | 2 +- src/main/frontend/extensions/pdf/toolbar.cljs | 2 +- src/main/frontend/extensions/tldraw.cljs | 2 +- src/main/frontend/extensions/zotero.cljs | 2 +- src/main/frontend/mobile/graph_picker.cljs | 2 +- src/main/frontend/rum.cljs | 2 +- src/main/frontend/ui.cljs | 2 +- yarn.lock | 29 +- 51 files changed, 479 insertions(+), 466 deletions(-) rename {src/main/frontend => deps/shui/src/logseq/shui}/hooks.cljs (74%) diff --git a/deps.edn b/deps.edn index 2f62a543c9..fa2087345a 100644 --- a/deps.edn +++ b/deps.edn @@ -1,7 +1,8 @@ {:paths ["src/main" "src/electron" "src/resources"] :deps {org.clojure/clojure {:mvn/version "1.11.1"} - rum/rum {:mvn/version "0.12.9"} + rum/rum {:git/url "https://github.com/logseq/rum" ;; fork + :sha "5d672bf84ed944414b9f61eeb83808ead7be9127"} datascript/datascript {:git/url "https://github.com/logseq/datascript" ;; fork :sha "1f84d10df4970f054489b0ee78799f64b8dd4ee2"} diff --git a/deps/shui/deps.edn b/deps/shui/deps.edn index fdb9142c2f..8206a42d82 100644 --- a/deps/shui/deps.edn +++ b/deps/shui/deps.edn @@ -3,6 +3,8 @@ {org.clojure/clojure {:mvn/version "1.11.1"} org.clojure/clojurescript {:mvn/version "1.11.132"} funcool/promesa {:mvn/version "11.0.678"} - rum/rum {:mvn/version "0.12.9"} + rum/rum {:git/url "https://github.com/logseq/rum" ;; fork + :sha "5d672bf84ed944414b9f61eeb83808ead7be9127"} + medley/medley {:mvn/version "1.4.0"} cljs-bean/cljs-bean {:mvn/version "1.5.0"}}} diff --git a/deps/shui/src/logseq/shui/demo.cljs b/deps/shui/src/logseq/shui/demo.cljs index 861f734699..0619a1b07d 100644 --- a/deps/shui/src/logseq/shui/demo.cljs +++ b/deps/shui/src/logseq/shui/demo.cljs @@ -1,10 +1,11 @@ (ns logseq.shui.demo - (:require [rum.core :as rum] - [logseq.shui.ui :as ui] - [dommy.core :refer-macros [sel1]] + (:require [dommy.core :refer-macros [sel1]] + [logseq.shui.dialog.core :as dialog-core] [logseq.shui.form.core :refer [yup yup-resolver] :as form-core] + [logseq.shui.hooks :as hooks] + [logseq.shui.ui :as ui] [promesa.core :as p] - [logseq.shui.dialog.core :as dialog-core])) + [rum.core :as rum])) (rum/defc section-item [title children] @@ -511,7 +512,7 @@ [] (let [el-ref (rum/use-ref nil)] - (rum/use-effect! + (hooks/use-effect! (fn [] (let [^js container (get-main-scroll-container) ^js el (rum/deref el-ref) diff --git a/deps/shui/src/logseq/shui/demo2.cljs b/deps/shui/src/logseq/shui/demo2.cljs index 33b277c990..95737ac900 100644 --- a/deps/shui/src/logseq/shui/demo2.cljs +++ b/deps/shui/src/logseq/shui/demo2.cljs @@ -1,23 +1,24 @@ (ns logseq.shui.demo2 - (:require [clojure.string :as string] - [rum.core :as rum] - [logseq.shui.ui :as ui] - [logseq.shui.popup.core :refer [install-popups update-popup! get-popup]] - [logseq.shui.select.multi :refer [x-select-content]] + (:require [cljs-bean.core :as bean] + [clojure.string :as string] [frontend.components.icon :refer [emojis-cp emojis icon-search]] [frontend.storage :as storage] - [cljs-bean.core :as bean] - [promesa.core :as p])) + [logseq.shui.hooks :as hooks] + [logseq.shui.popup.core :refer [install-popups update-popup! get-popup]] + [logseq.shui.select.multi :refer [x-select-content]] + [logseq.shui.ui :as ui] + [promesa.core :as p] + [rum.core :as rum])) (defn do-fetch! ([action] (do-fetch! action nil)) ([action query-str] (-> (js/window.fetch - (str "https://movies-api14.p.rapidapi.com/" (name action) (when query-str (str "?" query-str))) - #js {:method "GET" - :headers #js {:X-RapidAPI-Key "808ffd08c0mshc67d496f6024b46p164350jsn7b35179966c9", - :X-RapidAPI-Host "movies-api14.p.rapidapi.com"}}) - (p/then #(.json %))))) + (str "https://movies-api14.p.rapidapi.com/" (name action) (when query-str (str "?" query-str))) + #js {:method "GET" + :headers #js {:X-RapidAPI-Key "808ffd08c0mshc67d496f6024b46p164350jsn7b35179966c9", + :X-RapidAPI-Host "movies-api14.p.rapidapi.com"}}) + (p/then #(.json %))))) (rum/defc multi-select-demo [] @@ -35,96 +36,96 @@ rm-item! (fn [item-or-id] (set-selected-items! - (remove #(or (= item-or-id %) - (= item-or-id (str (:id %)))) - selected-items))) + (remove #(or (= item-or-id %) + (= item-or-id (str (:id %)))) + selected-items))) add-item! (fn [item] (set-selected-items! (conj selected-items item))) [open? set-open!] (rum/use-state false)] - (rum/use-effect! - (fn [] - (storage/set :ls-demo-multi-selected-items selected-items)) - [selected-items]) + (hooks/use-effect! + (fn [] + (storage/set :ls-demo-multi-selected-items selected-items)) + [selected-items]) (ui/card - (ui/card-header - (ui/card-title "Search Movies") - (ui/card-description "x multiselect for the remote items")) - (ui/card-content + (ui/card-header + (ui/card-title "Search Movies") + (ui/card-description "x multiselect for the remote items")) + (ui/card-content ;; Basic - (ui/dropdown-menu - {:open open?} + (ui/dropdown-menu + {:open open?} ;; trigger - (ui/dropdown-menu-trigger - [:div.border.p-2.rounded.w-full.cursor-pointer.flex.items-center.gap-1.flex-wrap - {:on-click (fn [^js e] - (let [^js target (.-target e)] - (if-let [^js c (some-> target (.closest ".close"))] - (some-> (.-dataset c) (.-k) (rm-item!)) - (set-open! true))))} - (for [{:keys [id original_title class poster_path]} selected-items] - (ui/badge {:variant :secondary :class (str class " group relative")} - [:span.flex.items-center.gap-1.flex-nowrap - [:img {:src poster_path :class "w-[16px] scale-75"}] - [:b original_title]] - (ui/button - {:variant :destructive - :size :icon - :data-k id - :class "!rounded-full !h-4 !w-4 absolute top-[-7px] right-[-3px] group-hover:visible invisible close"} - (ui/tabler-icon "x" {:size 12})))) - (ui/button {:variant :link :size :sm} "+")]) + (ui/dropdown-menu-trigger + [:div.border.p-2.rounded.w-full.cursor-pointer.flex.items-center.gap-1.flex-wrap + {:on-click (fn [^js e] + (let [^js target (.-target e)] + (if-let [^js c (some-> target (.closest ".close"))] + (some-> (.-dataset c) (.-k) (rm-item!)) + (set-open! true))))} + (for [{:keys [id original_title class poster_path]} selected-items] + (ui/badge {:variant :secondary :class (str class " group relative")} + [:span.flex.items-center.gap-1.flex-nowrap + [:img {:src poster_path :class "w-[16px] scale-75"}] + [:b original_title]] + (ui/button + {:variant :destructive + :size :icon + :data-k id + :class "!rounded-full !h-4 !w-4 absolute top-[-7px] right-[-3px] group-hover:visible invisible close"} + (ui/tabler-icon "x" {:size 12})))) + (ui/button {:variant :link :size :sm} "+")]) ;; content - (x-select-content items selected-items - {;; test item render - :open? open? - :close! #(set-open! false) - :search-enabled? true - :search-key q - :search-fn (fn [items] - (when (not fetching?) items)) - :on-search-key-change (fn [v] - (set-q! v) - (if (string/blank? v) - (set-items! []) - (when (not fetching?) - (set-fetching? true) - (-> (do-fetch! :search (str "query=" v)) - (p/then #(when-let [ret (bean/->clj %)] - (when-let [items (:contents ret)] - (set-items! (map (fn [item] (assoc item :id (:_id item))) (take 12 items)))))) - (p/finally #(set-fetching? false)))))) + (x-select-content items selected-items + {;; test item render + :open? open? + :close! #(set-open! false) + :search-enabled? true + :search-key q + :search-fn (fn [items] + (when (not fetching?) items)) + :on-search-key-change (fn [v] + (set-q! v) + (if (string/blank? v) + (set-items! []) + (when (not fetching?) + (set-fetching? true) + (-> (do-fetch! :search (str "query=" v)) + (p/then #(when-let [ret (bean/->clj %)] + (when-let [items (:contents ret)] + (set-items! (map (fn [item] (assoc item :id (:_id item))) (take 12 items)))))) + (p/finally #(set-fetching? false)))))) - :item-render (fn [item {:keys [selected?]}] - (if item - (ui/dropdown-menu-checkbox-item - {:checked selected? - :on-click (fn [] - (if selected? - (rm-item! item) - (add-item! item)) + :item-render (fn [item {:keys [selected?]}] + (if item + (ui/dropdown-menu-checkbox-item + {:checked selected? + :on-click (fn [] + (if selected? + (rm-item! item) + (add-item! item)) ;(set-open! false) - )} - [:div.flex.items-center.gap-2 - [:span [:img {:src (:poster_path item) - :class "w-[20px]"}]] - [:span.flex.flex-col - [:b (:original_title item)] - [:small.opacity-50 - {:class "text-[10px]"} - (:release_date item)]]]) - (ui/dropdown-menu-separator))) + )} + [:div.flex.items-center.gap-2 + [:span [:img {:src (:poster_path item) + :class "w-[20px]"}]] + [:span.flex.flex-col + [:b (:original_title item)] + [:small.opacity-50 + {:class "text-[10px]"} + (:release_date item)]]]) + (ui/dropdown-menu-separator))) - :head-render (fn [] (when (and fetching? (not (string/blank? q))) - [:b.flex.items-center.justify-center.py-4 - (ui/tabler-icon "loader" {:class "animate-spin"})])) + :head-render (fn [] (when (and fetching? (not (string/blank? q))) + [:b.flex.items-center.justify-center.py-4 + (ui/tabler-icon "loader" {:class "animate-spin"})])) ;:foot-render (fn [] [:b "footer"]) - :content-props - {:align "start" - :class "w-80"}}))))) + :content-props + {:align "start" + :class "w-80"}}))))) [:hr] @@ -146,50 +147,50 @@ [open? set-open!] (rum/use-state false)] (ui/card - (ui/card-header - (ui/card-title "Basic") - (ui/card-description "x multiselect for shui")) - (ui/card-content - [:label.block.flex.items-center.pb-3.cursor-pointer - (ui/checkbox {:checked search? - :on-click #(set-search? (not search?))}) - [:small.pl-2 "Enable basic search input"]] + (ui/card-header + (ui/card-title "Basic") + (ui/card-description "x multiselect for shui")) + (ui/card-content + [:label.block.flex.items-center.pb-3.cursor-pointer + (ui/checkbox {:checked search? + :on-click #(set-search? (not search?))}) + [:small.pl-2 "Enable basic search input"]] ;; Basic - (ui/dropdown-menu - {:open open?} + (ui/dropdown-menu + {:open open?} ;; trigger - (ui/dropdown-menu-trigger - [:p.border.p-2.rounded.w-full.cursor-pointer - {:on-click #(set-open! true)} - (for [{:keys [key value class]} selected-items] - (ui/badge {:variant :secondary :class class} (str "#" key " " value))) - (ui/button {:variant :link :size :sm} "+")]) + (ui/dropdown-menu-trigger + [:p.border.p-2.rounded.w-full.cursor-pointer + {:on-click #(set-open! true)} + (for [{:keys [key value class]} selected-items] + (ui/badge {:variant :secondary :class class} (str "#" key " " value))) + (ui/button {:variant :link :size :sm} "+")]) ;; content - (x-select-content items selected-items - {:close! #(set-open! false) - :search-enabled? search? - :search-key-render (fn [q {:keys [items]}] - (when (and (not (string/blank? q)) - (not (seq items))) - [:b.flex.items-center.justify-center.py-4.gap-2.font-normal.opacity-80 - (ui/tabler-icon "lemon") [:small "No fruits!"]])) - :on-chosen on-chosen - :value-render (fn [v {:keys [selected?]}] - (if selected? - [:b.text-red-800 v] - [:b.text-green-800 v])) - :content-props - {:class "w-48"}}))))) + (x-select-content items selected-items + {:close! #(set-open! false) + :search-enabled? search? + :search-key-render (fn [q {:keys [items]}] + (when (and (not (string/blank? q)) + (not (seq items))) + [:b.flex.items-center.justify-center.py-4.gap-2.font-normal.opacity-80 + (ui/tabler-icon "lemon") [:small "No fruits!"]])) + :on-chosen on-chosen + :value-render (fn [v {:keys [selected?]}] + (if selected? + [:b.text-red-800 v] + [:b.text-green-800 v])) + :content-props + {:class "w-48"}}))))) [:hr] (let [[items set-items!] (rum/use-state - [{:key 1 :value "Apple" :class "bg-gray-800 text-gray-50"} - {:key 2 :value "Orange" :class "bg-orange-700 text-gray-50"} - nil - {:key 3 :value "Pear"} - {:key 4 :value "Banana" :class "bg-yellow-700 text-gray-700"}]) + [{:key 1 :value "Apple" :class "bg-gray-800 text-gray-50"} + {:key 2 :value "Orange" :class "bg-orange-700 text-gray-50"} + nil + {:key 3 :value "Pear"} + {:key 4 :value "Banana" :class "bg-yellow-700 text-gray-700"}]) [selected-items set-selected-items!] (rum/use-state [(last items) (first items)]) @@ -202,55 +203,54 @@ [open? set-open!] (rum/use-state false)] (ui/card - (ui/card-header - (ui/card-title "Search & Custom") - (ui/card-description "x multiselect for shui")) - (ui/card-content + (ui/card-header + (ui/card-title "Search & Custom") + (ui/card-description "x multiselect for shui")) + (ui/card-content ;; Basic - (ui/dropdown-menu - {:open open?} + (ui/dropdown-menu + {:open open?} ;; trigger - (ui/dropdown-menu-trigger - [:p.border.p-2.rounded.w-full.cursor-pointer - {:on-click #(set-open! true)} - (for [{:keys [key value class]} selected-items] - (ui/badge {:variant :secondary :class class} (str "#" key " " value))) - (ui/button {:variant :link :size :sm} "+")]) + (ui/dropdown-menu-trigger + [:p.border.p-2.rounded.w-full.cursor-pointer + {:on-click #(set-open! true)} + (for [{:keys [key value class]} selected-items] + (ui/badge {:variant :secondary :class class} (str "#" key " " value))) + (ui/button {:variant :link :size :sm} "+")]) ;; content - (x-select-content items selected-items - {;; test item render - :open? open? - :close! #(set-open! false) - :search-enabled? true - :item-render (fn [item {:keys [selected?]}] - (if item - (ui/dropdown-menu-checkbox-item - {:checked selected? - :on-click (fn [] - (if selected? - (rm-item! item) - (add-item! item)))} - (:value item)) - (ui/dropdown-menu-separator))) + (x-select-content items selected-items + {;; test item render + :open? open? + :close! #(set-open! false) + :search-enabled? true + :item-render (fn [item {:keys [selected?]}] + (if item + (ui/dropdown-menu-checkbox-item + {:checked selected? + :on-click (fn [] + (if selected? + (rm-item! item) + (add-item! item)))} + (:value item)) + (ui/dropdown-menu-separator))) - :search-key-render - (fn [k {:keys [items x-item exist-fn]}] - (when (and - (not (string/blank? k)) - (not (exist-fn))) - (x-item - {:on-click (fn [] - (ui/toast! (str "Create: " k) :warning) - (set-open! false))} - (str "+ create: " k)))) + :search-key-render + (fn [k {:keys [items x-item exist-fn]}] + (when (and + (not (string/blank? k)) + (not (exist-fn))) + (x-item + {:on-click (fn [] + (ui/toast! (str "Create: " k) :warning) + (set-open! false))} + (str "+ create: " k)))) ;:head-render (fn [] [:b "header"]) ;:foot-render (fn [] [:b "footer"]) - :content-props - {:align "start" - :class "w-48"}}))))) - ]) + :content-props + {:align "start" + :class "w-48"}})))))]) (rum/defc icon-picker-demo [] @@ -282,27 +282,27 @@ [:a.underline {:on-click #(ui/popup-show! % - (fn [_config] - [:div.max-h-72.overflow-auto.p-1 - (emojis-cp (take 80 emojis) - {:on-chosen - (fn [_ t] - (set-emoji! t) - (ui/popup-hide-all!))})]) - {:content-props {:class "w-72 p-0"} - :as-dropdown? true})} + (fn [_config] + [:div.max-h-72.overflow-auto.p-1 + (emojis-cp (take 80 emojis) + {:on-chosen + (fn [_ t] + (set-emoji! t) + (ui/popup-hide-all!))})]) + {:content-props {:class "w-72 p-0"} + :as-dropdown? true})} (if emoji [:strong.px-1.text-6xl [:em-emoji emoji]] "emoji :O")] "."])] [:<> (emoji-picker nil) [:p.py-4 (ui/button - {:variant :secondary - :on-click #(ui/popup-show! % - (fn [] - [:p.p-4 - (emoji-picker true)]))} - "Play a nested x popup.")] + {:variant :secondary + :on-click #(ui/popup-show! % + (fn [] + [:p.p-4 + (emoji-picker true)]))} + "Play a nested x popup.")] [:p.py-4 (let [gen-content @@ -312,60 +312,60 @@ (emoji-picker true) [:strong.px-1.text-6xl q]])] (ui/input - {:placeholder "Select a fruit." - :ref *q-ref - :value q - :on-change (fn [^js e] - (let [val (.-value (.-target e))] - (set-q! val) - (update-popup! :select-a-fruit-input [:content] (gen-content val)))) - :class "w-1/5" - :on-focus (fn [^js e] - (let [id :select-a-fruit-input - [_ popup] (get-popup id)] - (if (not popup) - (ui/popup-show! (.-target e) - (gen-content q) - {:id id - :align "start" - :content-props - {:class "x-input-popup-content" - :onPointerDownOutside - (fn [^js e] - (js/console.log "===>> onPointerDownOutside:" e (rum/deref *q-ref)) - (when-let [q-ref (rum/deref *q-ref)] - (let [^js target (or (.-relatedTarget e) - (.-target e))] - (js/console.log "t:" target) - (when (and - (not (.contains q-ref target)) - (not (.closest target ".x-input-popup-content"))) - (ui/popup-hide! id))))) - :onOpenAutoFocus #(.preventDefault %)}}) + {:placeholder "Select a fruit." + :ref *q-ref + :value q + :on-change (fn [^js e] + (let [val (.-value (.-target e))] + (set-q! val) + (update-popup! :select-a-fruit-input [:content] (gen-content val)))) + :class "w-1/5" + :on-focus (fn [^js e] + (let [id :select-a-fruit-input + [_ popup] (get-popup id)] + (if (not popup) + (ui/popup-show! (.-target e) + (gen-content q) + {:id id + :align "start" + :content-props + {:class "x-input-popup-content" + :onPointerDownOutside + (fn [^js e] + (js/console.log "===>> onPointerDownOutside:" e (rum/deref *q-ref)) + (when-let [q-ref (rum/deref *q-ref)] + (let [^js target (or (.-relatedTarget e) + (.-target e))] + (js/console.log "t:" target) + (when (and + (not (.contains q-ref target)) + (not (.closest target ".x-input-popup-content"))) + (ui/popup-hide! id))))) + :onOpenAutoFocus #(.preventDefault %)}}) ;; update content - (update-popup! id [:content] - (gen-content q))))) + (update-popup! id [:content] + (gen-content q))))) ;:on-blur (fn [^js e] ; (let [^js target (.-relatedTarget e)] ; (js/console.log "==>>>" target) ; (when-not (.closest target ".x-input-popup-content") ; (hide-x-popup! :select-a-fruit-input)))) - }))] + }))] [:div.w-full.p-4.border.rounded.dotted.h-48.mt-8.bg-gray-02 {:on-click #(ui/popup-show! % - (->> (range 8) - (map (fn [it] - (ui/dropdown-menu-item - {:on-select (fn [] - (ui/toast! it) - (ui/popup-hide-all!))} - [:strong it])))) - {:as-dropdown? true - :content-props {:class "w-48"}}) + (->> (range 8) + (map (fn [it] + (ui/dropdown-menu-item + {:on-select (fn [] + (ui/toast! it) + (ui/popup-hide-all!))} + [:strong it])))) + {:as-dropdown? true + :content-props {:class "w-48"}}) :on-context-menu #(ui/popup-show! % - [:h1.text-3xl.font-bold "hi x popup for custom context menu!"])}]])]) + [:h1.text-3xl.font-bold "hi x popup for custom context menu!"])}]])]) (rum/defc custom-trigger-content [] @@ -381,17 +381,16 @@ [:h1.text-3xl.font-bold.border-b.pb-4 "Sample dropdown/menu trigger"] [:div.py-4 (ui/dropdown-menu - (ui/dropdown-menu-trigger - {:as-child true} - (ui/trigger-child-wrap - {:class "border p-6 border"} - (custom-trigger-content))) - (ui/dropdown-menu-content - (ui/dropdown-menu-item "A item") - (ui/dropdown-menu-item "B item") - (ui/dropdown-menu-item "C item")))] - ]) + (ui/dropdown-menu-trigger + {:as-child true} + (ui/trigger-child-wrap + {:class "border p-6 border"} + (custom-trigger-content))) + (ui/dropdown-menu-content + (ui/dropdown-menu-item "A item") + (ui/dropdown-menu-item "B item") + (ui/dropdown-menu-item "C item")))]]) (rum/defc page [] - (sample-dropdown-trigger)) \ No newline at end of file + (sample-dropdown-trigger)) diff --git a/deps/shui/src/logseq/shui/dialog/core.cljs b/deps/shui/src/logseq/shui/dialog/core.cljs index 2ac95fdac0..659773f84f 100644 --- a/deps/shui/src/logseq/shui/dialog/core.cljs +++ b/deps/shui/src/logseq/shui/dialog/core.cljs @@ -1,11 +1,12 @@ (ns logseq.shui.dialog.core - (:require [rum.core :as rum] - [daiquiri.interpreter :refer [interpret]] - [medley.core :as medley] - [logseq.shui.util :as util] + (:require [daiquiri.interpreter :refer [interpret]] [logseq.shui.base.core :as base] [logseq.shui.form.core :as form] - [promesa.core :as p])) + [logseq.shui.hooks :as hooks] + [logseq.shui.util :as util] + [medley.core :as medley] + [promesa.core :as p] + [rum.core :as rum])) ;; provider (def dialog (util/lsui-wrap "Dialog")) @@ -130,10 +131,11 @@ :close :align :on-open-change :open? :root-props :content-props) props (assoc-in props [:overlay-props :data-align] (name (or align :center)))] - (rum/use-effect! + (hooks/use-effect! (fn [] (when (false? open?) - (js/setTimeout #(detach-modal! id) 128))) + (let [timeout (js/setTimeout #(detach-modal! id) 128)] + #(js/clearTimeout timeout)))) [open?]) (dialog @@ -171,10 +173,11 @@ (let [{:keys [id title description content footer deferred open?]} config props (dissoc config :id :title :description :content :footer :deferred :open? :alert?)] - (rum/use-effect! + (hooks/use-effect! (fn [] (when (false? open?) - (js/setTimeout #(detach-modal! id) 128))) + (let [timeout (js/setTimeout #(detach-modal! id) 128)] + #(js/clearTimeout timeout)))) [open?]) (alert-dialog @@ -209,14 +212,15 @@ *ok-ref (rum/use-ref nil) *reminder-ref (rum/use-ref nil)] - (rum/use-effect! + (hooks/use-effect! (fn [] (when ready? - (js/setTimeout - #(some-> (rum/deref *ok-ref) (.focus)) 128))) + (let [timeout (js/setTimeout + #(some-> (rum/deref *ok-ref) (.focus)) 128)] + #(js/clearTimeout timeout)))) [ready?]) - (rum/use-effect! + (hooks/use-effect! (fn [] (try (if-let [reminder-v (and reminder? (js/localStorage.getItem (str id)))] diff --git a/src/main/frontend/hooks.cljs b/deps/shui/src/logseq/shui/hooks.cljs similarity index 74% rename from src/main/frontend/hooks.cljs rename to deps/shui/src/logseq/shui/hooks.cljs index ff8e2a79c2..fba51400bd 100644 --- a/src/main/frontend/hooks.cljs +++ b/deps/shui/src/logseq/shui/hooks.cljs @@ -1,4 +1,4 @@ -(ns frontend.hooks +(ns logseq.shui.hooks "React custom hooks." (:refer-clojure :exclude [ref deref]) (:require [goog.functions :as gfun] @@ -24,17 +24,23 @@ "setup-fn will be invoked every render of component when no deps arg provided" ([setup-fn] (rum/use-effect! setup-fn)) ([setup-fn deps & {:keys [equal-fn]}] - (rum/use-effect! setup-fn (if (empty? deps) - deps - #js[(memo-deps equal-fn deps)])))) + (rum/use-effect! (fn [& deps] + (let [result (apply setup-fn deps)] + (when (fn? result) result))) + (if (empty? deps) + deps + #js[(memo-deps equal-fn deps)])))) #_{:clj-kondo/ignore [:discouraged-var]} (defn use-layout-effect! ([setup-fn] (rum/use-layout-effect! setup-fn)) ([setup-fn deps & {:keys [equal-fn]}] - (rum/use-layout-effect! setup-fn (if (empty? deps) - deps - #js[(memo-deps equal-fn deps)])))) + (rum/use-layout-effect! (fn [& deps] + (let [result (apply setup-fn deps)] + (when (fn? result) result))) + (if (empty? deps) + deps + #js[(memo-deps equal-fn deps)])))) #_{:clj-kondo/ignore [:discouraged-var]} (defn use-callback diff --git a/deps/shui/src/logseq/shui/select/multi.cljs b/deps/shui/src/logseq/shui/select/multi.cljs index 205f1fa879..13a98adf84 100644 --- a/deps/shui/src/logseq/shui/select/multi.cljs +++ b/deps/shui/src/logseq/shui/select/multi.cljs @@ -1,14 +1,15 @@ (ns logseq.shui.select.multi (:require [clojure.string :as string] - [rum.core :as rum] + [logseq.shui.form.core :as form] + [logseq.shui.hooks :as hooks] [logseq.shui.popup.core :as popup] - [logseq.shui.form.core :as form])) + [rum.core :as rum])) (defn- get-k [item] (if (map? item) (some->> ((juxt :id :key :label) item) - (remove nil?) - (first)) + (remove nil?) + (first)) item)) (defn- get-v @@ -21,27 +22,27 @@ (let [*el (rum/use-ref nil) [down set-down!] (rum/use-state 0)] - (rum/use-effect! - (fn [] - (when-let [^js item (and (> down 0) - (some-> (rum/deref *el) - (.closest ".head") - (.-nextSibling)))] - (some-> (if valid-search-key? (.-nextSibling item) item) - (.focus)))) - [down]) + (hooks/use-effect! + (fn [] + (when-let [^js item (and (> down 0) + (some-> (rum/deref *el) + (.closest ".head") + (.-nextSibling)))] + (some-> (if valid-search-key? (.-nextSibling item) item) + (.focus)))) + [down]) [:div.search-input {:ref *el} (form/input - (merge {:placeholder "search" - :on-key-up #(case (.-key %) - "ArrowDown" (set-down! (inc down)) - "ArrowUp" nil - "Enter" (when (fn? on-enter) (on-enter)) - :dune) - :auto-focus true} - input-props))])) + (merge {:placeholder "search" + :on-key-up #(case (.-key %) + "ArrowDown" (set-down! (inc down)) + "ArrowUp" nil + "Enter" (when (fn? on-enter) (on-enter)) + :dune) + :auto-focus true} + input-props))])) (defn- simple-search-fn [items q] @@ -49,8 +50,8 @@ (if (string/blank? q) items (filter #(some-> (get-v %) - (string/lower-case) - (string/includes? q)) items)))) + (string/lower-case) + (string/includes? q)) items)))) (rum/defc x-select-content [items selected-items & {:keys [on-chosen item-render value-render @@ -70,8 +71,8 @@ (when (and search-enabled? (not= "INPUT" (.-nodeName target))) ;; focus search input (some-> (get-content-el target) - (.querySelector "input") - (.focus)))) + (.querySelector "input") + (.focus)))) items (if search-enabled? (if (fn? search-fn) (search-fn items search-key1) @@ -79,100 +80,100 @@ items) close1! #(when (fn? close!) (close!))] - (rum/use-effect! - (fn [] - (when (fn? on-search-key-change) - (on-search-key-change search-key1'))) - [search-key1']) + (hooks/use-effect! + (fn [] + (when (fn? on-search-key-change) + (on-search-key-change search-key1'))) + [search-key1']) - (rum/use-effect! - (fn [] - (when-let [t (when (and search-enabled? (false? open?)) - (js/setTimeout #(set-search-key! "") 500))] - #(js/clearTimeout t))) - [open?]) + (hooks/use-effect! + (fn [] + (when-let [t (when (and search-enabled? (false? open?)) + (js/setTimeout #(set-search-key! "") 500))] + #(js/clearTimeout t))) + [open?]) (x-content - (merge - {:onInteractOutside close1! - :onEscapeKeyDown close1! - :on-key-down (fn [^js e] - (when-let [^js target (.-target e)] - (case (.-key e) - "ArrowUp" - (when (= (some-> (get-item-nodes target) (first)) - js/document.activeElement) - (focus-search-input! target)) - "l" (when (or (.-metaKey e) (.-ctrlKey e)) - (focus-search-input! target)) - :dune))) - :class (str (:class content-props) - " ui__multi-select-content" - (when valid-search-key? " has-search-key"))} - (dissoc content-props :class)) + (merge + {:onInteractOutside close1! + :onEscapeKeyDown close1! + :on-key-down (fn [^js e] + (when-let [^js target (.-target e)] + (case (.-key e) + "ArrowUp" + (when (= (some-> (get-item-nodes target) (first)) + js/document.activeElement) + (focus-search-input! target)) + "l" (when (or (.-metaKey e) (.-ctrlKey e)) + (focus-search-input! target)) + :dune))) + :class (str (:class content-props) + " ui__multi-select-content" + (when valid-search-key? " has-search-key"))} + (dissoc content-props :class)) ;; header - (when (or search-enabled? (fn? head-render)) - [:div.head - {:ref *head-ref} - (when search-enabled? - (search-input - {:value search-key1 - :on-key-down (fn [^js e] - (.stopPropagation e) - (case (.-key e) - "Escape" (if (string/blank? search-key1) - (some-> (.-target e) (.closest "[data-radix-menu-content]") (.focus)) - (set-search-key! "")) - :dune)) - :on-change #(set-search-key! (.-value (.-target %)))} + (when (or search-enabled? (fn? head-render)) + [:div.head + {:ref *head-ref} + (when search-enabled? + (search-input + {:value search-key1 + :on-key-down (fn [^js e] + (.stopPropagation e) + (case (.-key e) + "Escape" (if (string/blank? search-key1) + (some-> (.-target e) (.closest "[data-radix-menu-content]") (.focus)) + (set-search-key! "")) + :dune)) + :on-change #(set-search-key! (.-value (.-target %)))} - {:on-enter (fn [] - (when-let [head-el (and (not (string/blank? search-key1')) - (rum/deref *head-ref))] - (when-let [^js item (.-nextSibling head-el)] - (when (contains? #{"menuitemcheckbox" "menuitem"} - (.getAttribute item "role")) - (.click item))))) - :valid-search-key? valid-search-key?})) - (when head-render (head-render))]) + {:on-enter (fn [] + (when-let [head-el (and (not (string/blank? search-key1')) + (rum/deref *head-ref))] + (when-let [^js item (.-nextSibling head-el)] + (when (contains? #{"menuitemcheckbox" "menuitem"} + (.getAttribute item "role")) + (.click item))))) + :valid-search-key? valid-search-key?})) + (when head-render (head-render))]) ;; items - (for [item items - :let [selected? (some #(let [k (get-k item) - k' (get-k %)] - (or (= item %) - (and (not (nil? k)) - (not (nil? k')) - (= k k')))) - selected-items)]] - (if (fn? item-render) - (item-render item {:x-item x-item :selected? selected?}) - (let [k (get-k item) - v (get-v item)] - (when k - (let [opts {:selected? selected?} - on-click' (:on-click item-props) - on-click (fn [e] + (for [item items + :let [selected? (some #(let [k (get-k item) + k' (get-k %)] + (or (= item %) + (and (not (nil? k)) + (not (nil? k')) + (= k k')))) + selected-items)]] + (if (fn? item-render) + (item-render item {:x-item x-item :selected? selected?}) + (let [k (get-k item) + v (get-v item)] + (when k + (let [opts {:selected? selected?} + on-click' (:on-click item-props) + on-click (fn [e] ;; TODO: return value - (when (fn? on-click') (on-click' e)) - (when (fn? on-chosen) - (on-chosen item opts)))] - (x-item (merge {:data-k k :on-click on-click} item-props) - [:span.flex.items-center.gap-2.w-full - (form/checkbox {:checked selected?}) - (let [v' (if (fn? v) (v item opts) v)] - (if (fn? value-render) - (value-render v' (assoc opts :item item)) v'))])))))) + (when (fn? on-click') (on-click' e)) + (when (fn? on-chosen) + (on-chosen item opts)))] + (x-item (merge {:data-k k :on-click on-click} item-props) + [:span.flex.items-center.gap-2.w-full + (form/checkbox {:checked selected?}) + (let [v' (if (fn? v) (v item opts) v)] + (if (fn? value-render) + (value-render v' (assoc opts :item item)) v'))])))))) - (when (and search-enabled? - (fn? search-key-render)) - (let [exist-fn (fn [] - (and (not (string/blank? search-key1)) - (seq items) - (contains? (into #{} (map #(some-> (get-v %) (string/lower-case)) items)) - (string/lower-case search-key1))))] - (search-key-render search-key1 - {:items items :x-item x-item :exist-fn exist-fn}))) + (when (and search-enabled? + (fn? search-key-render)) + (let [exist-fn (fn [] + (and (not (string/blank? search-key1)) + (seq items) + (contains? (into #{} (map #(some-> (get-v %) (string/lower-case)) items)) + (string/lower-case search-key1))))] + (search-key-render search-key1 + {:items items :x-item x-item :exist-fn exist-fn}))) ;; footer - (when (fn? foot-render) - [:div.foot - (foot-render)])))) + (when (fn? foot-render) + [:div.foot + (foot-render)])))) diff --git a/deps/shui/src/logseq/shui/table/core.cljc b/deps/shui/src/logseq/shui/table/core.cljc index bab8430cec..fb9a35833f 100644 --- a/deps/shui/src/logseq/shui/table/core.cljc +++ b/deps/shui/src/logseq/shui/table/core.cljc @@ -2,6 +2,7 @@ "Table" (:require [clojure.set :as set] [dommy.core :refer-macros [sel1]] + [logseq.shui.hooks :as hooks] [logseq.shui.table.impl :as impl] [rum.core :as rum])) @@ -147,7 +148,7 @@ ;; FIXME: ux (defn- use-sticky-element! [^js/HTMLElement container target-ref] - (rum/use-effect! + (hooks/use-effect! (fn [] (let [^js el (rum/deref target-ref) ^js cls (.-classList el) @@ -189,7 +190,7 @@ ;; FIXME: another solution for the sticky header (defn- use-sticky-element2! [^js/HTMLDivElement target-ref] - (rum/use-effect! + (hooks/use-effect! (fn [] (let [^js target (rum/deref target-ref) ^js container (or (.closest target ".sidebar-item-list") (get-main-scroll-container)) @@ -205,7 +206,7 @@ update-target-top! (fn [] (when (not (.contains target-cls "ls-fixed")) (vreset! *el-top (+ (-> target (.getBoundingClientRect) (.-top)) - (.-scrollTop container))))) + (.-scrollTop container))))) update-footer! (fn [] (let [tw (.-scrollWidth table)] (when (and table-footer (number? tw) (> tw 0)) @@ -228,7 +229,7 @@ table-in-top (+ scroll-top head-height) table-bottom (.-bottom (.getBoundingClientRect table)) fixed? (and (> table-bottom (+ head-height 90)) - (> table-in-top @*el-top))] + (> table-in-top @*el-top))] (if fixed? (.add target-cls "ls-fixed") (.remove target-cls "ls-fixed")) @@ -236,7 +237,7 @@ target-observe-handle! (fn [^js _e] (when (not @*ticking?) (js/window.requestAnimationFrame - #(do (target-observe!) (vreset! *ticking? false))) + #(do (target-observe!) (vreset! *ticking? false))) (vreset! *ticking? true))) resize-observer (js/ResizeObserver. update-target!) page-resize-observer (js/ResizeObserver. (fn [] (update-target-top!)))] @@ -251,8 +252,8 @@ ;; teardown #(do (.removeEventListener container "scroll" target-observe!) - (.disconnect resize-observer) - (.disconnect page-resize-observer)))))) + (.disconnect resize-observer) + (.disconnect page-resize-observer)))))) [])) (rum/defc table-header < rum/static diff --git a/deps/shui/src/logseq/shui/toaster/core.cljs b/deps/shui/src/logseq/shui/toaster/core.cljs index 4115a1ddb1..db31b60867 100644 --- a/deps/shui/src/logseq/shui/toaster/core.cljs +++ b/deps/shui/src/logseq/shui/toaster/core.cljs @@ -1,8 +1,9 @@ (ns logseq.shui.toaster.core - (:require [rum.core :as rum] + (:require [cljs-bean.core :as bean] [daiquiri.interpreter :refer [interpret]] + [logseq.shui.hooks :as hooks] [logseq.shui.util :as util] - [cljs-bean.core :as bean])) + [rum.core :as rum])) (defonce ^:private Toaster (util/lsui-wrap "Toaster")) (defonce ^:private *toast (atom nil)) @@ -23,22 +24,22 @@ < rum/static [] (let [^js js-toast (js/window.LSUI.useToast)] - (rum/use-effect! - (fn [] - (reset! *toast {:toast (.-toast js-toast) - :dismiss (.-dismiss js-toast) - :update (.-update js-toast)}) - #()) - []) + (hooks/use-effect! + (fn [] + (reset! *toast {:toast (.-toast js-toast) + :dismiss (.-dismiss js-toast) + :update (.-update js-toast)}) + #()) + []) [:<> (Toaster)])) (defn update-html-props [v] (update-keys v - #(case % - :class :className - :for :htmlFor - %))) + #(case % + :class :className + :for :htmlFor + %))) (defn interpret-vals [config ks & args] @@ -46,7 +47,7 @@ (let [v (get config k) v (if (fn? v) (apply v args) v)] (if (vector? v) (assoc config k (interpret v)) config))) - config ks)) + config ks)) (defn toast! ([content-or-config] (toast! content-or-config :default nil)) @@ -56,12 +57,12 @@ (let [config (if (map? content-or-config) content-or-config (-> {:description content-or-config} - (merge (if (map? status) status {:variant status})))) + (merge (if (map? status) status {:variant status})))) config (update-html-props (merge config opts)) id (or (:id config) (gen-id)) config (assoc config :id id) config (interpret-vals config [:title :description :action :icon] - {:id id :dismiss! #(dismiss id) :update! #(toast! (assoc %1 :id id))})] + {:id id :dismiss! #(dismiss id) :update! #(toast! (assoc %1 :id id))})] (js->clj (toast (clj->js config)))) :exception))) diff --git a/package.json b/package.json index 62eb6f3c03..ee828984d0 100644 --- a/package.json +++ b/package.json @@ -155,8 +155,8 @@ "pixi.js": "6.2.0", "posthog-js": "1.10.2", "prop-types": "^15.7.2", - "react": "17.0.2", - "react-dom": "17.0.2", + "react": "18.3.1", + "react-dom": "18.3.1", "react-grid-layout": "0.16.6", "react-intersection-observer": "^9.3.5", "react-resize-context": "3.0.0", diff --git a/src/main/frontend/components/all_pages.cljs b/src/main/frontend/components/all_pages.cljs index 7ca57b286a..0a3335245e 100644 --- a/src/main/frontend/components/all_pages.cljs +++ b/src/main/frontend/components/all_pages.cljs @@ -7,9 +7,9 @@ [frontend.context.i18n :refer [t]] [frontend.db :as db] [frontend.handler.page :as page-handler] - [frontend.hooks :as hooks] [frontend.state :as state] [logseq.common.config :as common-config] + [logseq.shui.hooks :as hooks] [logseq.shui.ui :as shui] [promesa.core :as p] [rum.core :as rum])) diff --git a/src/main/frontend/components/assets.cljs b/src/main/frontend/components/assets.cljs index aab80d21a6..4f67f0e019 100644 --- a/src/main/frontend/components/assets.cljs +++ b/src/main/frontend/components/assets.cljs @@ -8,10 +8,10 @@ [frontend.context.i18n :refer [t]] [frontend.handler.assets :as assets-handler] [frontend.handler.notification :as notification] - [frontend.hooks :as hooks] [frontend.state :as state] [frontend.ui :as ui] [frontend.util :as util] + [logseq.shui.hooks :as hooks] [logseq.shui.ui :as shui] [medley.core :as medley] [promesa.core :as p] diff --git a/src/main/frontend/components/block.cljs b/src/main/frontend/components/block.cljs index 63042349fa..8dc3b98eb1 100644 --- a/src/main/frontend/components/block.cljs +++ b/src/main/frontend/components/block.cljs @@ -55,7 +55,6 @@ [frontend.handler.route :as route-handler] [frontend.handler.ui :as ui-handler] [frontend.handler.whiteboard :as whiteboard-handler] - [frontend.hooks :as hooks] [frontend.mixins :as mixins] [frontend.mobile.intent :as mobile-intent] [frontend.mobile.util :as mobile-util] @@ -87,6 +86,7 @@ [logseq.graph-parser.text :as text] [logseq.outliner.property :as outliner-property] [logseq.shui.dialog.core :as shui-dialog] + [logseq.shui.hooks :as hooks] [logseq.shui.ui :as shui] [medley.core :as medley] [promesa.core :as p] diff --git a/src/main/frontend/components/bug_report.cljs b/src/main/frontend/components/bug_report.cljs index 1b07c75163..daf4665197 100644 --- a/src/main/frontend/components/bug_report.cljs +++ b/src/main/frontend/components/bug_report.cljs @@ -3,9 +3,9 @@ [frontend.components.header :as header] [frontend.context.i18n :refer [t]] [frontend.handler.notification :as notification] - [frontend.hooks :as hooks] [frontend.ui :as ui] [frontend.util :as util] + [logseq.shui.hooks :as hooks] [reitit.frontend.easy :as rfe] [rum.core :as rum])) diff --git a/src/main/frontend/components/cmdk/core.cljs b/src/main/frontend/components/cmdk/core.cljs index db089e6309..74363c7ea2 100644 --- a/src/main/frontend/components/cmdk/core.cljs +++ b/src/main/frontend/components/cmdk/core.cljs @@ -18,7 +18,6 @@ [frontend.handler.page :as page-handler] [frontend.handler.route :as route-handler] [frontend.handler.whiteboard :as whiteboard-handler] - [frontend.hooks :as hooks] [frontend.mixins :as mixins] [frontend.modules.shortcut.core :as shortcut] [frontend.modules.shortcut.utils :as shortcut-utils] @@ -36,6 +35,7 @@ [logseq.common.util.block-ref :as block-ref] [logseq.db :as ldb] [logseq.graph-parser.text :as text] + [logseq.shui.hooks :as hooks] [logseq.shui.ui :as shui] [promesa.core :as p] [rum.core :as rum])) diff --git a/src/main/frontend/components/cmdk/list_item.cljs b/src/main/frontend/components/cmdk/list_item.cljs index 64cf853d3a..a78e1517c0 100644 --- a/src/main/frontend/components/cmdk/list_item.cljs +++ b/src/main/frontend/components/cmdk/list_item.cljs @@ -2,8 +2,8 @@ (:require ["remove-accents" :as remove-accents] [clojure.string :as string] - [frontend.hooks :as hooks] [goog.string :as gstring] + [logseq.shui.hooks :as hooks] [logseq.shui.ui :as shui] [rum.core :as rum])) diff --git a/src/main/frontend/components/container.cljs b/src/main/frontend/components/container.cljs index 97f132a52b..05b38fe705 100644 --- a/src/main/frontend/components/container.cljs +++ b/src/main/frontend/components/container.cljs @@ -31,7 +31,6 @@ [frontend.handler.route :as route-handler] [frontend.handler.user :as user-handler] [frontend.handler.whiteboard :as whiteboard-handler] - [frontend.hooks :as hooks] [frontend.mixins :as mixins] [frontend.mobile.action-bar :as action-bar] [frontend.mobile.footer :as footer] @@ -52,6 +51,7 @@ [logseq.common.util.namespace :as ns-util] [logseq.db :as ldb] [logseq.shui.dialog.core :as shui-dialog] + [logseq.shui.hooks :as hooks] [logseq.shui.popup.core :as shui-popup] [logseq.shui.toaster.core :as shui-toaster] [logseq.shui.ui :as shui] diff --git a/src/main/frontend/components/dnd.cljs b/src/main/frontend/components/dnd.cljs index 99fad02201..42936ba33c 100644 --- a/src/main/frontend/components/dnd.cljs +++ b/src/main/frontend/components/dnd.cljs @@ -3,9 +3,9 @@ ["@dnd-kit/sortable" :refer [useSortable arrayMove SortableContext verticalListSortingStrategy horizontalListSortingStrategy] :as sortable] ["@dnd-kit/utilities" :refer [CSS]] [cljs-bean.core :as bean] - [frontend.hooks :as hooks] [frontend.rum :as r] [frontend.state :as state] + [logseq.shui.hooks :as hooks] [rum.core :as rum])) (def dnd-context (r/adapt-class DndContext)) diff --git a/src/main/frontend/components/editor.cljs b/src/main/frontend/components/editor.cljs index 2500ab02b8..3dedb90643 100644 --- a/src/main/frontend/components/editor.cljs +++ b/src/main/frontend/components/editor.cljs @@ -19,7 +19,6 @@ [frontend.handler.paste :as paste-handler] [frontend.handler.property.util :as pu] [frontend.handler.search :as search-handler] - [frontend.hooks :as hooks] [frontend.mixins :as mixins] [frontend.search :refer [fuzzy-search]] [frontend.state :as state] @@ -34,6 +33,7 @@ [logseq.db :as ldb] [logseq.db.frontend.class :as db-class] [logseq.graph-parser.property :as gp-property] + [logseq.shui.hooks :as hooks] [logseq.shui.ui :as shui] [promesa.core :as p] [react-draggable] diff --git a/src/main/frontend/components/file_based/block.cljs b/src/main/frontend/components/file_based/block.cljs index cb28e367ad..47f435d2f3 100644 --- a/src/main/frontend/components/file_based/block.cljs +++ b/src/main/frontend/components/file_based/block.cljs @@ -4,12 +4,12 @@ [frontend.components.file-based.datetime :as datetime-comp] [frontend.handler.editor :as editor-handler] [frontend.handler.file-based.repeated :as repeated] - [frontend.hooks :as hooks] [frontend.state :as state] [frontend.ui :as ui] [frontend.util :as util] [frontend.util.file-based.clock :as clock] [frontend.util.file-based.drawer :as drawer] + [logseq.shui.hooks :as hooks] [logseq.shui.ui :as shui] [reitit.frontend.easy :as rfe] [rum.core :as rum])) diff --git a/src/main/frontend/components/file_based/git.cljs b/src/main/frontend/components/file_based/git.cljs index 8ec6fb0f7d..898c178728 100644 --- a/src/main/frontend/components/file_based/git.cljs +++ b/src/main/frontend/components/file_based/git.cljs @@ -2,10 +2,10 @@ (:require [clojure.string :as string] [frontend.handler.file-based.file :as file-handler] [frontend.handler.shell :as shell] - [frontend.hooks :as hooks] [frontend.state :as state] [frontend.ui :as ui] [frontend.util :as util] + [logseq.shui.hooks :as hooks] [promesa.core :as p] [rum.core :as rum])) @@ -73,7 +73,7 @@ (ui/button "Revert" :on-click (fn [] (file-handler/alter-file (state/get-current-repo) - path - content - {:re-render-root? true - :skip-compare? true})))]]])) + path + content + {:re-render-root? true + :skip-compare? true})))]]])) diff --git a/src/main/frontend/components/file_sync.cljs b/src/main/frontend/components/file_sync.cljs index f9dbfef8fd..8ce9624b5a 100644 --- a/src/main/frontend/components/file_sync.cljs +++ b/src/main/frontend/components/file_sync.cljs @@ -19,7 +19,6 @@ [frontend.handler.page :as page-handler] [frontend.handler.repo :as repo-handler] [frontend.handler.user :as user-handler] - [frontend.hooks :as hooks] [frontend.mobile.util :as mobile-util] [frontend.state :as state] [frontend.storage :as storage] @@ -29,6 +28,7 @@ [frontend.util.persist-var :as persist-var] [goog.functions :refer [debounce]] [logseq.common.util :as common-util] + [logseq.shui.hooks :as hooks] [logseq.shui.ui :as shui] [promesa.core :as p] [reitit.frontend.easy :as rfe] diff --git a/src/main/frontend/components/handbooks.cljs b/src/main/frontend/components/handbooks.cljs index 5f94b87571..04ce0e5abc 100644 --- a/src/main/frontend/components/handbooks.cljs +++ b/src/main/frontend/components/handbooks.cljs @@ -1,9 +1,9 @@ (ns frontend.components.handbooks (:require ;[shadow.lazy :as lazy] [frontend.extensions.handbooks.core :as handbooks] - [frontend.hooks :as hooks] [frontend.modules.layout.core :as layout] [frontend.state :as state] + [logseq.shui.hooks :as hooks] [rum.core :as rum])) #_:clj-kondo/ignore diff --git a/src/main/frontend/components/header.cljs b/src/main/frontend/components/header.cljs index 22dee5b5ac..9fee9e87f6 100644 --- a/src/main/frontend/components/header.cljs +++ b/src/main/frontend/components/header.cljs @@ -23,7 +23,6 @@ [frontend.handler.plugin :as plugin-handler] [frontend.handler.route :as route-handler] [frontend.handler.user :as user-handler] - [frontend.hooks :as hooks] [frontend.mobile.util :as mobile-util] [frontend.state :as state] [frontend.storage :as storage] @@ -31,6 +30,7 @@ [frontend.util :as util] [frontend.version :refer [version]] [logseq.db :as ldb] + [logseq.shui.hooks :as hooks] [logseq.shui.ui :as shui] [logseq.shui.util :as shui-util] [missionary.core :as m] diff --git a/src/main/frontend/components/icon.cljs b/src/main/frontend/components/icon.cljs index f0885835d7..cf7b2b8c00 100644 --- a/src/main/frontend/components/icon.cljs +++ b/src/main/frontend/components/icon.cljs @@ -6,7 +6,6 @@ [clojure.string :as string] [frontend.config :as config] [frontend.handler.property.util :as pu] - [frontend.hooks :as hooks] [frontend.search :as search] [frontend.storage :as storage] [frontend.ui :as ui] @@ -14,6 +13,7 @@ [goog.functions :refer [debounce]] [goog.object :as gobj] [logseq.db :as ldb] + [logseq.shui.hooks :as hooks] [logseq.shui.ui :as shui] [medley.core :as medley] [promesa.core :as p] diff --git a/src/main/frontend/components/imports.cljs b/src/main/frontend/components/imports.cljs index 164c8f255b..158e1b577f 100644 --- a/src/main/frontend/components/imports.cljs +++ b/src/main/frontend/components/imports.cljs @@ -18,7 +18,6 @@ [frontend.handler.repo :as repo-handler] [frontend.handler.route :as route-handler] [frontend.handler.ui :as ui-handler] - [frontend.hooks :as hooks] [frontend.persist-db.browser :as db-browser] [frontend.state :as state] [frontend.ui :as ui] @@ -32,6 +31,7 @@ [logseq.graph-parser.exporter :as gp-exporter] [logseq.shui.dialog.core :as shui-dialog] [logseq.shui.form.core :as form-core] + [logseq.shui.hooks :as hooks] [logseq.shui.ui :as shui] [promesa.core :as p] [rum.core :as rum])) diff --git a/src/main/frontend/components/objects.cljs b/src/main/frontend/components/objects.cljs index 1224bc666a..98b508df41 100644 --- a/src/main/frontend/components/objects.cljs +++ b/src/main/frontend/components/objects.cljs @@ -8,7 +8,6 @@ [frontend.db.model :as db-model] [frontend.db.react :as react] [frontend.handler.editor :as editor-handler] - [frontend.hooks :as hooks] [frontend.mixins :as mixins] [frontend.modules.outliner.op :as outliner-op] [frontend.modules.outliner.ui :as ui-outliner-tx] @@ -16,6 +15,7 @@ [logseq.db.frontend.entity-util :as entity-util] [logseq.db.frontend.property :as db-property] [logseq.outliner.property :as outliner-property] + [logseq.shui.hooks :as hooks] [logseq.shui.ui :as shui] [promesa.core :as p] [rum.core :as rum])) diff --git a/src/main/frontend/components/page.cljs b/src/main/frontend/components/page.cljs index 16ba6dedca..dace7ed470 100644 --- a/src/main/frontend/components/page.cljs +++ b/src/main/frontend/components/page.cljs @@ -33,7 +33,6 @@ [frontend.handler.notification :as notification] [frontend.handler.page :as page-handler] [frontend.handler.route :as route-handler] - [frontend.hooks :as hooks] [frontend.mixins :as mixins] [frontend.mobile.util :as mobile-util] [frontend.rum :as frontend-rum] @@ -46,6 +45,7 @@ [logseq.common.util.page-ref :as page-ref] [logseq.db :as ldb] [logseq.graph-parser.mldoc :as gp-mldoc] + [logseq.shui.hooks :as hooks] [logseq.shui.ui :as shui] [promesa.core :as p] [reitit.frontend.easy :as rfe] diff --git a/src/main/frontend/components/plugins.cljs b/src/main/frontend/components/plugins.cljs index 730808d42a..ac67dd6b2d 100644 --- a/src/main/frontend/components/plugins.cljs +++ b/src/main/frontend/components/plugins.cljs @@ -12,7 +12,6 @@ [frontend.handler.plugin :as plugin-handler] [frontend.handler.plugin-config :as plugin-config-handler] [frontend.handler.ui :as ui-handler] - [frontend.hooks :as hooks] [frontend.mixins :as mixins] [frontend.rum :as rum-utils] [frontend.search :as search] @@ -20,6 +19,7 @@ [frontend.storage :as storage] [frontend.ui :as ui] [frontend.util :as util] + [logseq.shui.hooks :as hooks] [logseq.shui.ui :as shui] [promesa.core :as p] [rum.core :as rum])) diff --git a/src/main/frontend/components/plugins_settings.cljs b/src/main/frontend/components/plugins_settings.cljs index d8277c6d30..130fb606b9 100644 --- a/src/main/frontend/components/plugins_settings.cljs +++ b/src/main/frontend/components/plugins_settings.cljs @@ -3,10 +3,10 @@ [frontend.components.lazy-editor :as lazy-editor] [frontend.handler.notification :as notification] [frontend.handler.plugin :as plugin-handler] - [frontend.hooks :as hooks] [frontend.ui :as ui] [frontend.util :as util] [goog.functions :refer [debounce]] + [logseq.shui.hooks :as hooks] [logseq.shui.ui :as shui] [rum.core :as rum])) diff --git a/src/main/frontend/components/property.cljs b/src/main/frontend/components/property.cljs index 0776ec5d0b..840826b769 100644 --- a/src/main/frontend/components/property.cljs +++ b/src/main/frontend/components/property.cljs @@ -18,7 +18,6 @@ [frontend.handler.property :as property-handler] [frontend.handler.property.util :as pu] [frontend.handler.route :as route-handler] - [frontend.hooks :as hooks] [frontend.mixins :as mixins] [frontend.modules.shortcut.core :as shortcut] [frontend.state :as state] @@ -31,6 +30,7 @@ [logseq.db.frontend.property.type :as db-property-type] [logseq.outliner.core :as outliner-core] [logseq.outliner.property :as outliner-property] + [logseq.shui.hooks :as hooks] [logseq.shui.ui :as shui] [promesa.core :as p] [rum.core :as rum])) diff --git a/src/main/frontend/components/property/config.cljs b/src/main/frontend/components/property/config.cljs index 597a6cb6ee..41bb7250f9 100644 --- a/src/main/frontend/components/property/config.cljs +++ b/src/main/frontend/components/property/config.cljs @@ -15,7 +15,6 @@ [frontend.handler.db-based.property :as db-property-handler] [frontend.handler.property :as property-handler] [frontend.handler.route :as route-handler] - [frontend.hooks :as hooks] [frontend.state :as state] [frontend.ui :as ui] [frontend.util :as util] @@ -25,6 +24,7 @@ [logseq.db.frontend.property :as db-property] [logseq.db.frontend.property.type :as db-property-type] [logseq.outliner.core :as outliner-core] + [logseq.shui.hooks :as hooks] [logseq.shui.popup.core :as shui-popup] [logseq.shui.ui :as shui] [promesa.core :as p] diff --git a/src/main/frontend/components/property/value.cljs b/src/main/frontend/components/property/value.cljs index 813c0e699b..c02bad5f23 100644 --- a/src/main/frontend/components/property/value.cljs +++ b/src/main/frontend/components/property/value.cljs @@ -23,7 +23,6 @@ [frontend.handler.property :as property-handler] [frontend.handler.property.util :as pu] [frontend.handler.route :as route-handler] - [frontend.hooks :as hooks] [frontend.modules.outliner.ui :as ui-outliner-tx] [frontend.search :as search] [frontend.state :as state] @@ -38,6 +37,7 @@ [logseq.db.frontend.property :as db-property] [logseq.db.frontend.property.type :as db-property-type] [logseq.outliner.property :as outliner-property] + [logseq.shui.hooks :as hooks] [logseq.shui.ui :as shui] [promesa.core :as p] [rum.core :as rum])) @@ -1340,12 +1340,12 @@ value-cp)))]] (if show-tooltip? (shui/tooltip-provider - (shui/tooltip - {:delayDuration 1200} - (shui/tooltip-trigger - {:onFocusCapture #(util/stop-propagation %) - :as-child true} - value-cp) - (shui/tooltip-content - (str "Change " (:block/title property))))) + (shui/tooltip + {:delayDuration 1200} + (shui/tooltip-trigger + {:onFocusCapture #(util/stop-propagation %) + :as-child true} + value-cp) + (shui/tooltip-content + (str "Change " (:block/title property))))) value-cp)))))) diff --git a/src/main/frontend/components/query.cljs b/src/main/frontend/components/query.cljs index d3c88b9927..b585485f4b 100644 --- a/src/main/frontend/components/query.cljs +++ b/src/main/frontend/components/query.cljs @@ -10,12 +10,12 @@ [frontend.db-mixins :as db-mixins] [frontend.extensions.sci :as sci] [frontend.handler.editor :as editor-handler] - [frontend.hooks :as hooks] [frontend.state :as state] [frontend.ui :as ui] [frontend.util :as util] [lambdaisland.glogi :as log] [logseq.db :as ldb] + [logseq.shui.hooks :as hooks] [rum.core :as rum])) (defn- built-in-custom-query? diff --git a/src/main/frontend/components/query/builder.cljs b/src/main/frontend/components/query/builder.cljs index 8fc4985d02..04e7b0c94c 100644 --- a/src/main/frontend/components/query/builder.cljs +++ b/src/main/frontend/components/query/builder.cljs @@ -11,7 +11,6 @@ [frontend.db.query-dsl :as query-dsl] [frontend.handler.editor :as editor-handler] [frontend.handler.query.builder :as query-builder] - [frontend.hooks :as hooks] [frontend.mixins :as mixins] [frontend.state :as state] [frontend.ui :as ui] @@ -23,6 +22,7 @@ [logseq.db.frontend.property.type :as db-property-type] [logseq.db.sqlite.util :as sqlite-util] [logseq.graph-parser.db :as gp-db] + [logseq.shui.hooks :as hooks] [logseq.shui.ui :as shui] [promesa.core :as p] [rum.core :as rum])) diff --git a/src/main/frontend/components/right_sidebar.cljs b/src/main/frontend/components/right_sidebar.cljs index 7d84710a58..8e15efb3cb 100644 --- a/src/main/frontend/components/right_sidebar.cljs +++ b/src/main/frontend/components/right_sidebar.cljs @@ -16,11 +16,11 @@ [frontend.handler.editor :as editor-handler] [frontend.handler.route :as route-handler] [frontend.handler.ui :as ui-handler] - [frontend.hooks :as hooks] [frontend.state :as state] [frontend.ui :as ui] [frontend.util :as util] [logseq.db :as ldb] + [logseq.shui.hooks :as hooks] [logseq.shui.ui :as shui] [medley.core :as medley] [reitit.frontend.easy :as rfe] diff --git a/src/main/frontend/components/server.cljs b/src/main/frontend/components/server.cljs index 4089ff830f..9ddb0b6930 100644 --- a/src/main/frontend/components/server.cljs +++ b/src/main/frontend/components/server.cljs @@ -3,10 +3,10 @@ [clojure.string :as string] [electron.ipc :as ipc] [frontend.handler.notification :as notification] - [frontend.hooks :as hooks] [frontend.state :as state] [frontend.ui :as ui] [frontend.util :as util] + [logseq.shui.hooks :as hooks] [logseq.shui.ui :as shui] [medley.core :as medley] [promesa.core :as p] diff --git a/src/main/frontend/components/settings.cljs b/src/main/frontend/components/settings.cljs index e701dadf2e..ec633b381f 100644 --- a/src/main/frontend/components/settings.cljs +++ b/src/main/frontend/components/settings.cljs @@ -21,7 +21,6 @@ [frontend.handler.route :as route-handler] [frontend.handler.ui :as ui-handler] [frontend.handler.user :as user-handler] - [frontend.hooks :as hooks] [frontend.mobile.util :as mobile-util] [frontend.modules.instrumentation.core :as instrument] [frontend.modules.shortcut.data-helper :as shortcut-helper] @@ -34,6 +33,7 @@ [goog.object :as gobj] [goog.string :as gstring] [logseq.db :as ldb] + [logseq.shui.hooks :as hooks] [logseq.shui.ui :as shui] [promesa.core :as p] [reitit.frontend.easy :as rfe] diff --git a/src/main/frontend/components/shortcut.cljs b/src/main/frontend/components/shortcut.cljs index c7cf1fd5ff..07fbfc0d1b 100644 --- a/src/main/frontend/components/shortcut.cljs +++ b/src/main/frontend/components/shortcut.cljs @@ -3,7 +3,6 @@ [clojure.string :as string] [frontend.context.i18n :refer [t]] [frontend.handler.notification :as notification] - [frontend.hooks :as hooks] [frontend.modules.shortcut.config :as shortcut-config] [frontend.modules.shortcut.core :as shortcut] [frontend.modules.shortcut.data-helper :as dh] @@ -14,6 +13,7 @@ [frontend.util :as util] [goog.events :as events] [logseq.shui.dialog.core :as shui-dialog] + [logseq.shui.hooks :as hooks] [logseq.shui.ui :as shui] [promesa.core :as p] [rum.core :as rum]) diff --git a/src/main/frontend/components/theme.cljs b/src/main/frontend/components/theme.cljs index 28f1fbdde4..2dddab49c7 100644 --- a/src/main/frontend/components/theme.cljs +++ b/src/main/frontend/components/theme.cljs @@ -8,12 +8,12 @@ [frontend.handler.plugin-config :as plugin-config-handler] [frontend.handler.route :as route-handler] [frontend.handler.ui :as ui-handler] - [frontend.hooks :as hooks] [frontend.rum :refer [use-mounted]] [frontend.state :as state] [frontend.storage :as storage] [frontend.ui :as ui] [frontend.util :as util] + [logseq.shui.hooks :as hooks] [logseq.shui.ui :as shui] [rum.core :as rum])) diff --git a/src/main/frontend/components/user/login.cljs b/src/main/frontend/components/user/login.cljs index 94224f6faf..bf1746db2b 100644 --- a/src/main/frontend/components/user/login.cljs +++ b/src/main/frontend/components/user/login.cljs @@ -6,10 +6,10 @@ [frontend.handler.notification :as notification] [frontend.handler.route :as route-handler] [frontend.handler.user :as user] - [frontend.hooks :as hooks] [frontend.modules.shortcut.core :as shortcut] [frontend.rum :refer [adapt-class]] [frontend.state :as state] + [logseq.shui.hooks :as hooks] [logseq.shui.ui :as shui] [rum.core :as rum])) diff --git a/src/main/frontend/components/views.cljs b/src/main/frontend/components/views.cljs index 4e788a153f..708d300b2b 100644 --- a/src/main/frontend/components/views.cljs +++ b/src/main/frontend/components/views.cljs @@ -26,7 +26,6 @@ [frontend.handler.property :as property-handler] [frontend.handler.property.util :as pu] [frontend.handler.ui :as ui-handler] - [frontend.hooks :as hooks] [frontend.mixins :as mixins] [frontend.state :as state] [frontend.ui :as ui] @@ -36,6 +35,7 @@ [logseq.db :as ldb] [logseq.db.frontend.property :as db-property] [logseq.db.frontend.property.type :as db-property-type] + [logseq.shui.hooks :as hooks] [logseq.shui.table.core :as table-core] [logseq.shui.ui :as shui] [promesa.core :as p] diff --git a/src/main/frontend/extensions/handbooks/core.cljs b/src/main/frontend/extensions/handbooks/core.cljs index 84aa1eec78..eb82f2ffb3 100644 --- a/src/main/frontend/extensions/handbooks/core.cljs +++ b/src/main/frontend/extensions/handbooks/core.cljs @@ -9,7 +9,6 @@ [frontend.extensions.lightbox :as lightbox] [frontend.extensions.video.youtube :as youtube] [frontend.handler.notification :as notification] - [frontend.hooks :as hooks] [frontend.modules.shortcut.config :as shortcut-config] [frontend.rum :as r] [frontend.search :as search] @@ -17,6 +16,7 @@ [frontend.storage :as storage] [frontend.ui :as ui] [frontend.util :as util] + [logseq.shui.hooks :as hooks] [medley.core :as medley] [promesa.core :as p] [rum.core :as rum])) diff --git a/src/main/frontend/extensions/pdf/core.cljs b/src/main/frontend/extensions/pdf/core.cljs index 8654efe783..eb562ade5f 100644 --- a/src/main/frontend/extensions/pdf/core.cljs +++ b/src/main/frontend/extensions/pdf/core.cljs @@ -14,13 +14,13 @@ [frontend.extensions.pdf.windows :as pdf-windows] [frontend.handler.notification :as notification] [frontend.handler.property :as property-handler] - [frontend.hooks :as hooks] [frontend.modules.shortcut.core :as shortcut] [frontend.rum :refer [use-atom]] [frontend.state :as state] [frontend.ui :as ui] [frontend.util :as util] [goog.functions :refer [debounce]] + [logseq.shui.hooks :as hooks] [logseq.shui.ui :as shui] [medley.core :as medley] [promesa.core :as p] diff --git a/src/main/frontend/extensions/pdf/toolbar.cljs b/src/main/frontend/extensions/pdf/toolbar.cljs index a6c37a547f..d86a3cb841 100644 --- a/src/main/frontend/extensions/pdf/toolbar.cljs +++ b/src/main/frontend/extensions/pdf/toolbar.cljs @@ -13,13 +13,13 @@ [frontend.extensions.pdf.windows :refer [resolve-own-container] :as pdf-windows] [frontend.handler.assets :as assets-handler] [frontend.handler.notification :as notification] - [frontend.hooks :as hooks] [frontend.rum :refer [use-atom]] [frontend.state :as state] [frontend.storage :as storage] [frontend.ui :as ui] [frontend.util :as util] [logseq.publishing.db :as publish-db] + [logseq.shui.hooks :as hooks] [logseq.shui.ui :as shui] [promesa.core :as p] [rum.core :as rum])) diff --git a/src/main/frontend/extensions/tldraw.cljs b/src/main/frontend/extensions/tldraw.cljs index eeaa593fea..3cd86495dd 100644 --- a/src/main/frontend/extensions/tldraw.cljs +++ b/src/main/frontend/extensions/tldraw.cljs @@ -19,7 +19,6 @@ [frontend.handler.page :as page-handler] [frontend.handler.route :as route-handler] [frontend.handler.whiteboard :as whiteboard-handler] - [frontend.hooks :as hooks] [frontend.rum :as r] [frontend.search :as search] [frontend.state :as state] @@ -28,6 +27,7 @@ [frontend.util.text :as text-util] [goog.object :as gobj] [logseq.common.util :as common-util] + [logseq.shui.hooks :as hooks] [logseq.shui.ui :as shui] [promesa.core :as p] [rum.core :as rum])) diff --git a/src/main/frontend/extensions/zotero.cljs b/src/main/frontend/extensions/zotero.cljs index 241ee60fc8..c1c6c05159 100644 --- a/src/main/frontend/extensions/zotero.cljs +++ b/src/main/frontend/extensions/zotero.cljs @@ -9,11 +9,11 @@ [frontend.extensions.zotero.setting :as setting] [frontend.handler.notification :as notification] [frontend.handler.route :as route-handler] - [frontend.hooks :as hooks] [frontend.state :as state] [frontend.ui :as ui] [frontend.util :as util] [goog.dom :as gdom] + [logseq.shui.hooks :as hooks] [logseq.shui.ui :as shui] [promesa.core :as p] [rum.core :as rum])) diff --git a/src/main/frontend/mobile/graph_picker.cljs b/src/main/frontend/mobile/graph_picker.cljs index bf6842fc63..d053fb833b 100644 --- a/src/main/frontend/mobile/graph_picker.cljs +++ b/src/main/frontend/mobile/graph_picker.cljs @@ -6,13 +6,13 @@ [frontend.handler.file-based.nfs :as nfs-handler] [frontend.handler.notification :as notification] [frontend.handler.page :as page-handler] - [frontend.hooks :as hooks] [frontend.mobile.util :as mobile-util] [frontend.modules.shortcut.core :as shortcut] [frontend.state :as state] [frontend.ui :as ui] [frontend.util :as util] [logseq.common.path :as path] + [logseq.shui.hooks :as hooks] [logseq.shui.ui :as shui] [promesa.core :as p] [rum.core :as rum])) diff --git a/src/main/frontend/rum.cljs b/src/main/frontend/rum.cljs index 7fa12f6bdd..fcc29a78a8 100644 --- a/src/main/frontend/rum.cljs +++ b/src/main/frontend/rum.cljs @@ -5,7 +5,7 @@ [clojure.string :as string] [clojure.walk :as w] [daiquiri.interpreter :as interpreter] - [frontend.hooks :as hooks] + [logseq.shui.hooks :as hooks] [rum.core :refer [use-state] :as rum])) ;; copy from https://github.com/priornix/antizer/blob/35ba264cf48b84e6597743e28b3570d8aa473e74/src/antizer/core.cljs diff --git a/src/main/frontend/ui.cljs b/src/main/frontend/ui.cljs index 2935d50436..12498fb8e4 100644 --- a/src/main/frontend/ui.cljs +++ b/src/main/frontend/ui.cljs @@ -18,7 +18,6 @@ [frontend.db-mixins :as db-mixins] [frontend.handler.notification :as notification] [frontend.handler.plugin :as plugin-handler] - [frontend.hooks :as hooks] [frontend.mixins :as mixins] [frontend.mobile.util :as mobile-util] [frontend.modules.shortcut.config :as shortcut-config] @@ -32,6 +31,7 @@ [goog.dom :as gdom] [goog.object :as gobj] [lambdaisland.glogi :as log] + [logseq.shui.hooks :as hooks] [logseq.shui.icon.v2 :as shui.icon.v2] [logseq.shui.popup.core :as shui-popup] [logseq.shui.ui :as shui] diff --git a/yarn.lock b/yarn.lock index cba5b8e23a..8e71595af9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6943,14 +6943,13 @@ raw-body@2.5.2: iconv-lite "0.4.24" unpipe "1.0.0" -react-dom@17.0.2: - version "17.0.2" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23" - integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA== +react-dom@18.3.1: + version "18.3.1" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.3.1.tgz#c2265d79511b57d479b3dd3fdfa51536494c5cb4" + integrity sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw== dependencies: loose-envify "^1.1.0" - object-assign "^4.1.1" - scheduler "^0.20.2" + scheduler "^0.23.2" react-draggable@3.x: version "3.3.2" @@ -7038,13 +7037,12 @@ react-virtuoso@4.12.5: resolved "https://registry.yarnpkg.com/react-virtuoso/-/react-virtuoso-4.12.5.tgz#cf92efc2527e56d6df1d4d63c6e4dd3fac5a4030" integrity sha512-YeCbRRsC9CLf0buD0Rct7WsDbzf+yBU1wGbo05/XjbcN2nJuhgh040m3y3+6HVogTZxEqVm45ac9Fpae4/MxRQ== -react@17.0.2: - version "17.0.2" - resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037" - integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA== +react@18.3.1: + version "18.3.1" + resolved "https://registry.yarnpkg.com/react/-/react-18.3.1.tgz#49ab892009c53933625bd16b2533fc754cab2891" + integrity sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ== dependencies: loose-envify "^1.1.0" - object-assign "^4.1.1" read-cache@^1.0.0: version "1.0.0" @@ -7472,13 +7470,12 @@ sax@>=0.6.0: resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== -scheduler@^0.20.2: - version "0.20.2" - resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91" - integrity sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ== +scheduler@^0.23.2: + version "0.23.2" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.2.tgz#414ba64a3b282892e944cf2108ecc078d115cdc3" + integrity sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ== dependencies: loose-envify "^1.1.0" - object-assign "^4.1.1" semver-compare@^1.0.0: version "1.0.0"