diff --git a/deps/db/bb.edn b/deps/db/bb.edn index 924c517ff..2bcda24e8 100644 --- a/deps/db/bb.edn +++ b/deps/db/bb.edn @@ -4,7 +4,7 @@ {logseq/bb-tasks #_{:local/root "../../../bb-tasks"} {:git/url "https://github.com/logseq/bb-tasks" - :git/sha "70d3edeb287f5cec7192e642549a401f7d6d4263"}} + :git/sha "1d429e223baeade426d30a4ed1c8a110173a2402"}} :pods {clj-kondo/clj-kondo {:version "2023.05.26"}} diff --git a/deps/db/src/logseq/db/frontend/validate.cljs b/deps/db/src/logseq/db/frontend/validate.cljs index c320bca9f..d84d28863 100644 --- a/deps/db/src/logseq/db/frontend/validate.cljs +++ b/deps/db/src/logseq/db/frontend/validate.cljs @@ -89,9 +89,14 @@ (let [datoms (d/datoms db :eavt) ent-maps* (db-malli-schema/datoms->entities datoms) schema (update-schema db-malli-schema/DB db {:closed-schema? true}) - ent-maps (db-malli-schema/update-properties-in-ents db ent-maps*) + ent-maps (mapv + ;; Remove some UI interactions adding this e.g. import + #(dissoc % :block.temp/fully-loaded?) + (db-malli-schema/update-properties-in-ents db ent-maps*)) errors (->> ent-maps (m/explain schema) :errors)] (cond-> {:datom-count (count datoms) :entities ent-maps} (some? errors) - (assoc :errors (group-errors-by-entity db ent-maps errors))))) + (assoc :errors (map #(-> (dissoc % :errors-by-type) + (update :errors (fn [errs] (me/humanize {:errors errs})))) + (group-errors-by-entity db ent-maps errors)))))) diff --git a/deps/graph-parser/src/logseq/graph_parser/exporter.cljs b/deps/graph-parser/src/logseq/graph_parser/exporter.cljs index 097da1078..21e1faabb 100644 --- a/deps/graph-parser/src/logseq/graph_parser/exporter.cljs +++ b/deps/graph-parser/src/logseq/graph_parser/exporter.cljs @@ -294,12 +294,19 @@ [(:name v) k])) (into {}))) -(def built-in-property-names - "Set of all built-in property names as keywords. Using in-memory property - names because these are legacy names already in a user's file graph" +;; Should this move to logseq.db.frontend.property to be better maintained? +(def db-only-built-in-property-names + "Built-in property names that are are only used by DB graphs" + #{:description :page-tags :hide-properties? :created-from-property + :built-in? :includes :excludes :status :priority :deadline :sorting + :hidden-columns :ordered-columns :view-for :remote-metadata}) + +(def file-built-in-property-names + "Built-in property names for file graphs that are imported. Expressed as set of keywords" (-> built-in-property-name-to-idents keys set ;; :filters is not in built-in-properties because it maps to 2 new properties - (conj :filters))) + (conj :filters) + (set/difference db-only-built-in-property-names))) (defn- update-built-in-property-values [props {:keys [ignored-properties all-idents]} {:block/keys [title name]} options] @@ -433,7 +440,7 @@ {:keys [import-state] :as options}] (let [{:keys [all-idents property-schemas]} import-state get-ident' #(get-ident @all-idents %) - user-properties (apply dissoc props built-in-property-names)] + user-properties (apply dissoc props file-built-in-property-names)] (when (seq user-properties) (swap! (:block-properties-text-values import-state) assoc @@ -444,7 +451,7 @@ (if (contains? props :template) {} (let [props' (-> (update-built-in-property-values - (select-keys props built-in-property-names) + (select-keys props file-built-in-property-names) (select-keys import-state [:ignored-properties :all-idents]) (select-keys block [:block/name :block/title]) (select-keys options [:property-classes])) @@ -479,7 +486,7 @@ class-related-properties)] (->> (apply dissoc properties dissoced-props) (keep (fn [[prop val]] - (if (not (contains? built-in-property-names prop)) + (if (not (contains? file-built-in-property-names prop)) ;; only update user properties (if (string? val) ;; Ignore blank values as they were usually generated by templates @@ -508,7 +515,7 @@ properties-to-infer (if (:template properties') ;; Ignore template properties as they don't consistently have representative property values {} - (apply dissoc properties' built-in-property-names)) + (apply dissoc properties' file-built-in-property-names)) property-changes (->> properties-to-infer (keep (fn [[prop val]] @@ -676,9 +683,22 @@ (not (:block/file %)))) ;; remove file path relative (map #(dissoc % :block/file))) - existing-pages (keep #(ldb/get-page @conn (:block/name %)) all-pages) + existing-pages (keep #(first + ;; 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 [(pull ?b [*]) ...] + :in $ ?name + :where [?b :block/name ?name] [(missing? $ ?b :logseq.property/built-in?)]] + @conn + (:block/name %))) + all-pages) existing-page-names-to-uuids (into {} (map (juxt :block/name :block/uuid) existing-pages)) - new-pages (remove #(contains? existing-page-names-to-uuids (:block/name %)) all-pages) + new-pages (->> all-pages + (remove #(contains? existing-page-names-to-uuids (:block/name %))) + ;; fix extract incorrectly assigning user properties built-in property uuids + (map #(if (contains? db-only-built-in-property-names (keyword (:block/name %))) + (assoc % :block/uuid (d/squuid)) + %))) page-names-to-uuids (merge existing-page-names-to-uuids (into {} (map (juxt :block/name :block/uuid) new-pages))) all-pages-m (mapv #(handle-page-properties % @conn page-names-to-uuids all-pages options) @@ -701,7 +721,11 @@ (update-page-alias page-names-to-uuids) (:block/tags m) (update-page-tags @conn tag-classes page-names-to-uuids (:all-idents import-state))))) - (build-new-page m @conn tag-classes page-names-to-uuids (:all-idents import-state)))) + (let [m' (if (contains? db-only-built-in-property-names (keyword (:block/name m))) + ;; Use fixed uuid from above + (assoc m :block/uuid (get page-names-to-uuids (:block/name m))) + m)] + (build-new-page m' @conn tag-classes page-names-to-uuids (:all-idents import-state))))) (map :block all-pages-m))] {:pages-tx pages-tx :page-properties-tx (mapcat :properties-tx all-pages-m) @@ -791,10 +815,10 @@ :tag-classes (set (map string/lower-case (:tag-classes user-options))) :property-classes (set/difference (set (map (comp keyword string/lower-case) (:property-classes user-options))) - built-in-property-names) + file-built-in-property-names) :property-parent-classes (set/difference (set (map (comp keyword string/lower-case) (:property-parent-classes user-options))) - built-in-property-names)})) + file-built-in-property-names)})) (defn- split-pages-and-properties-tx "Separates new pages from new properties tx in preparation for properties to diff --git a/src/main/frontend/components/container.cljs b/src/main/frontend/components/container.cljs index fd1b5d577..bd8d5bfc4 100644 --- a/src/main/frontend/components/container.cljs +++ b/src/main/frontend/components/container.cljs @@ -900,6 +900,7 @@ granted? (state/sub [:nfs/user-granted? (state/get-current-repo)]) theme (state/sub :ui/theme) accent-color (some-> (state/sub :ui/radix-color) (name)) + editor-font (some-> (state/sub :ui/editor-font) (name)) system-theme? (state/sub :ui/system-theme?) light? (= "light" (state/sub :ui/theme)) sidebar-open? (state/sub :ui/sidebar-open?) @@ -929,6 +930,7 @@ {:t t :theme theme :accent-color accent-color + :editor-font editor-font :route route-match :current-repo current-repo :edit? edit? diff --git a/src/main/frontend/components/imports.cljs b/src/main/frontend/components/imports.cljs index d055d5c28..5d3916d22 100644 --- a/src/main/frontend/components/imports.cljs +++ b/src/main/frontend/components/imports.cljs @@ -277,7 +277,7 @@ (do (log/error :import-errors {:msg (str "Import detected " (count errors) " invalid block(s):") :counts (assoc (counts-from-entities entities) :datoms datom-count)}) - (pprint/pprint (map :entity errors)) + (pprint/pprint errors) (notification/show! (str "Import detected " (count errors) " invalid block(s). These blocks may be buggy when you interact with them. See the javascript console for more.") :warning false)) (log/info :import-valid {:msg "Valid import!" @@ -413,7 +413,7 @@ (shui/dialog-open! #(set-graph-name-dialog e {:sqlite? true})))}]]) - (when (and db-based? (or util/electron? util/web-platform?)) + (when (or util/electron? util/web-platform?) [:label.action-input.flex.items-center.mx-2.my-2 [:span.as-flex-center [:i (svg/logo 28)]] [:span.flex.flex-col diff --git a/src/main/frontend/components/repo.cljs b/src/main/frontend/components/repo.cljs index 00c54dd19..232b561a7 100644 --- a/src/main/frontend/components/repo.cljs +++ b/src/main/frontend/components/repo.cljs @@ -94,56 +94,56 @@ (shui/tabler-icon "folder-pin") [:span.pl-1 root]]) (let [db-graph? (config/db-based-graph? url) - manager? (and db-graph? (user-handler/manager? url))] + manager? (and db-graph? (user-handler/manager? url)) + title (cond + only-cloud? + "Deletes this remote graph. Note this can't be recovered." + + db-based? + "Unsafe delete this DB-based graph. Note this can't be recovered." + + :else + "Removes Logseq's access to the local file path of your graph. It won't remove your local files.")] (when-not (and only-cloud? (not manager?)) - (ui/tippy {:html [:div.text-sm.max-w-xs - (cond - only-cloud? - "Deletes this remote graph. Note this can't be recovered." + [:a.text-gray-400.ml-4.font-medium.text-sm.whitespace-nowrap + {:title title + :on-click (fn [] + (let [has-prompt? true + prompt-str (cond only-cloud? + (str "Are you sure to permanently delete the graph \"" GraphName "\" from our server?") + db-based? + (str "Are you sure to permanently delete the graph \"" url "\" from Logseq?") + :else + (str "Are you sure to unlink the graph \"" url "\" from local folder?")) + unlink-or-remote-fn! (fn [] + (repo-handler/remove-repo! repo) + (state/pub-event! [:graph/unlinked repo (state/get-current-repo)])) + action-confirm-fn! (if only-cloud? + (fn [] + (when (or manager? (not db-graph?)) + (let [delete-graph (if db-graph? + rtc-handler/ (shui/dialog-confirm! + [:p.font-medium.-my-4 prompt-str + [:span.mt-1.flex.font-normal.opacity-70 + (if (or db-based? only-cloud?) + [:small.text-red-rx-11 "⚠️ Notice that we can't recover this graph after being deleted. Make sure you have backups before deleting it."] + [:small.opacity-70 "⚠️ It won't remove your local files!"])]]) + (p/then #(action-confirm-fn!))))] - db-based? - "Unsafe delete this DB-based graph. Note this can't be recovered." - - :else - "Removes Logseq's access to the local file path of your graph. It won't remove your local files.")] - :class "tippy-hover" - :interactive true} - [:a.text-gray-400.ml-4.font-medium.text-sm.whitespace-nowrap - {:on-click (fn [] - (let [has-prompt? (or only-cloud? db-based?) - prompt-str (cond only-cloud? - (str "Are you sure to permanently delete the graph \"" GraphName "\" from our server?") - db-based? - (str "Are you sure to permanently delete the graph \"" url "\" from Logseq?") - :else - "") - unlink-or-remote-fn! (fn [] - (repo-handler/remove-repo! repo) - (state/pub-event! [:graph/unlinked repo (state/get-current-repo)])) - action-confirm-fn! (if only-cloud? - (fn [] - (when (or manager? (not db-graph?)) - (let [delete-graph (if db-graph? - rtc-handler/ (shui/dialog-confirm! - [:p.font-medium.-my-4 prompt-str - [:span.mt-1.flex.font-normal.opacity-40 - [:small "Notice that we can't recover this graph after being deleted. Make sure you have backups before deleting it."]]]) - (p/then #(action-confirm-fn!))))] - - (if has-prompt? - (confirm-fn!) - (unlink-or-remote-fn!))))} - (if only-cloud? "Remove (server)" "Unlink (local)")])))]]])) + (if has-prompt? + (confirm-fn!) + (unlink-or-remote-fn!))))} + (if only-cloud? "Remove (server)" "Unlink (local)")]))]]])) (rum/defc repos < rum/reactive [] diff --git a/src/main/frontend/components/settings.cljs b/src/main/frontend/components/settings.cljs index 84be082b1..16f35667b 100644 --- a/src/main/frontend/components/settings.cljs +++ b/src/main/frontend/components/settings.cljs @@ -220,6 +220,38 @@ [:div {:style {:text-align "right"}} (ui/render-keyboard-shortcut (shortcut-helper/gen-shortcut-seq :ui/toggle-brackets))])]) +(defn toggle-wide-mode-row [t wide-mode?] + [:div.it.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-center + [:label.block.text-sm.font-medium.leading-5.opacity-70 + {:for "wide_mode"} + (t :settings-page/wide-mode)] + [:div + [:div.rounded-md.sm:max-w-xs + (ui/toggle wide-mode? + state/toggle-wide-mode! + true)]] + (when (not (or (util/mobile?) (mobile-util/native-platform?))) + [:div {:style {:text-align "right"}} + (ui/render-keyboard-shortcut (shortcut-helper/gen-shortcut-seq :ui/toggle-wide-mode))])]) + +(defn editor-font-family-row [t font] + [:div.it.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-center + [:label.block.text-sm.font-medium.leading-5.opacity-70 + {:for "font_family"} + (t :settings-page/editor-font)] + [:div.rounded-md.sm:max-w-xs.flex.gap-2 + (for [t [:default :serif :mono] + :let [t (name t) + tt (string/capitalize t) + active? (= font t)]] + (shui/button + {:variant :secondary + :class (when active? "border-primary border-[2px]") + :on-click #(state/set-editor-font! t)} + [:span.flex.flex-col + [:strong "Ag"] + [:small tt]]))]]) + (rum/defcs switch-spell-check-row < rum/reactive [state t] (let [enabled? (state/sub [:electron/user-cfgs :spell-check])] @@ -719,6 +751,8 @@ enable-tooltip? (state/enable-tooltip?) enable-shortcut-tooltip? (state/sub :ui/shortcut-tooltip?) show-brackets? (state/show-brackets?) + wide-mode? (state/sub :ui/wide-mode?) + editor-font (state/sub :ui/editor-font) enable-git-auto-push? (state/enable-git-auto-push? current-repo) db-graph? (config/db-based-graph? (state/get-current-repo))] @@ -728,7 +762,9 @@ (date-format-row t preferred-date-format) (when-not db-graph? (workflow-row t preferred-workflow)) + (editor-font-family-row t editor-font) (show-brackets-row t show-brackets?) + (toggle-wide-mode-row t wide-mode?) (when (util/electron?) (switch-spell-check-row t)) (outdenting-row t logical-outdenting?) diff --git a/src/main/frontend/components/shortcut.css b/src/main/frontend/components/shortcut.css index e0b75b013..afc02d954 100644 --- a/src/main/frontend/components/shortcut.css +++ b/src/main/frontend/components/shortcut.css @@ -183,14 +183,18 @@ } .sidebar-item .cp__shortcut-page-x { - padding: 12px 0 0 0; - background-color: var(--color-level-2); + padding: 12px 0 0 0; + background-color: var(--color-level-2); } .sidebar-item article { - max-height: unset; + max-height: unset; } .keyboard-shortcut { - @apply inline-flex; + @apply inline-flex; + + .ui__button { + @apply cursor-default; + } } diff --git a/src/main/frontend/components/theme.cljs b/src/main/frontend/components/theme.cljs index 5ef4e9e8b..c0290d783 100644 --- a/src/main/frontend/components/theme.cljs +++ b/src/main/frontend/components/theme.cljs @@ -34,8 +34,8 @@ (defonce *once-theme-loaded? (volatile! false)) -(rum/defc ^:large-vars/cleanup-todo container - [{:keys [route theme accent-color on-click current-repo nfs-granted? db-restoring? +(rum/defc ^:large-vars/cleanup-todo container < rum/static + [{:keys [route theme accent-color editor-font on-click current-repo nfs-granted? db-restoring? settings-open? sidebar-open? system-theme? sidebar-blocks-len onboarding-state preferred-language]} child] (let [mounted-fn (use-mounted) [restored-sidebar? set-restored-sidebar?] (rum/use-state false)] @@ -60,6 +60,11 @@ (or accent-color "logseq"))) [accent-color]) + (rum/use-effect! + #(some-> js/document.documentElement + (.setAttribute "data-font" (or editor-font "default"))) + [editor-font]) + (rum/use-effect! #(let [doc js/document.documentElement] (.setAttribute doc "lang" preferred-language))) diff --git a/src/main/frontend/components/theme.css b/src/main/frontend/components/theme.css index 990cc1fb5..41dec9f90 100644 --- a/src/main/frontend/components/theme.css +++ b/src/main/frontend/components/theme.css @@ -153,4 +153,12 @@ main.ls-fold-button-on-right { main.theme-container-inner { --left-sidebar-bg-color: var(--lx-gray-02, hsl(var(--secondary, var(--rx-gray-03-hsl)))); +} + +html[data-font='serif'] .ls-block, .ls-font-serif { + font-family: Lyon-Text, Georgia, ui-serif, serif; +} + +html[data-font='mono'] .ls-block, .ls-font-mono { + font-family: iawriter-mono, Nitti, Menlo, Courier, monospace; } \ No newline at end of file diff --git a/src/main/frontend/modules/shortcut/data_helper.cljs b/src/main/frontend/modules/shortcut/data_helper.cljs index ad2e52bc0..26b5dc3ba 100644 --- a/src/main/frontend/modules/shortcut/data_helper.cljs +++ b/src/main/frontend/modules/shortcut/data_helper.cljs @@ -139,8 +139,8 @@ (if (false? bindings) [] (-> bindings - first - (str/split #" |\+"))))) + last + (str/split #" |\+"))))) (defn binding-for-display [k binding] (let [tmp (cond diff --git a/src/main/frontend/state.cljs b/src/main/frontend/state.cljs index 45c68d49a..e62643d5f 100644 --- a/src/main/frontend/state.cljs +++ b/src/main/frontend/state.cljs @@ -95,6 +95,7 @@ :ui/custom-theme (or (storage/get :ui/custom-theme) {:light {:mode "light"} :dark {:mode "dark"}}) :ui/wide-mode? (storage/get :ui/wide-mode) :ui/radix-color (storage/get :ui/radix-color) + :ui/editor-font (storage/get :ui/editor-font) ;; ui/collapsed-blocks is to separate the collapse/expand state from db for: ;; 1. right sidebar @@ -2385,6 +2386,11 @@ Similar to re-frame subscriptions" (storage/remove :ui/radix-color) (util/set-android-theme)) +(defn set-editor-font! [font] + (let [font (if (keyword? font) (name font) (str font))] + (swap! state assoc :ui/editor-font font) + (storage/set :ui/editor-font font))) + (defn handbook-open? [] (:ui/handbooks-open? @state)) diff --git a/src/resources/dicts/en.edn b/src/resources/dicts/en.edn index 5dfa2bd9e..9106ced43 100644 --- a/src/resources/dicts/en.edn +++ b/src/resources/dicts/en.edn @@ -270,6 +270,8 @@ :settings-page/custom-theme "Custom theme" :settings-page/export-theme "Export theme" :settings-page/show-brackets "Show brackets" + :settings-page/wide-mode "Wide mode" + :settings-page/editor-font "Font family" :settings-page/spell-checker "Spell checker" :settings-page/auto-updater "Auto updater" :settings-page/disable-sentry "Send usage data and diagnostics to Logseq"