fix: editing global config.edn and export.css in desktop app

Only enable export.css for desktop since it doesn't work
for browser and db graphs. Also remove a related and
unused db-graph? flag
This commit is contained in:
Gabriel Horner
2026-01-15 09:21:25 -05:00
parent a6c3569ea3
commit 01426fd244
10 changed files with 81 additions and 48 deletions

View File

@@ -619,7 +619,7 @@
(when (config/global-config-enabled?) (edit-global-config-edn))
(when current-repo (edit-config-edn))
(when current-repo (edit-custom-css))
(when current-repo (edit-export-css))]))
(when (and current-repo (util/electron?)) (edit-export-css))]))
(rum/defcs settings-editor < rum/reactive
[_state]

View File

@@ -3,6 +3,7 @@
platforms by delegating to implementations of the fs protocol"
(:require [cljs-bean.core :as bean]
[clojure.string :as string]
[electron.ipc :as ipc]
[frontend.fs.memory-fs :as memory-fs]
[frontend.fs.node :as node]
[frontend.fs.protocol :as protocol]
@@ -98,6 +99,20 @@
;; (js/alert "Current file can't be saved! Please copy its content to your local file system and click the refresh button.")
))))))
(defn write-file!
"A node only version of write-plain-text-file! to avoid using the fs-protocol
which has file graph assumptions"
[path content]
(when (util/electron?)
(let [file-fpath (common-util/path-normalize path)]
;; repo is nil because we don't want a backup file written
(-> (ipc/ipc "writeFile" nil file-fpath content)
(p/catch (fn [error]
(state/pub-event! [:capture-error {:error error
:payload {:type :write-file/failed
:user-agent (when js/navigator js/navigator.userAgent)
:content-length (count content)}}])))))))
;; read-file should return string on all platforms
(defn read-file
([dir path]

View File

@@ -29,6 +29,7 @@
(error-handler error)
(log/error :write-file-failed error))))
;; TODO: Remove backupDbFile which is a file graph concept
(p/let [disk-content (when (not= stat :not-found)
(-> (ipc/ipc "readFile" file-fpath)
(p/then bean/->clj)

View File

@@ -2,12 +2,30 @@
"Codemirror editor related."
(:require [clojure.string :as string]
[frontend.db :as db]
[frontend.fs :as fs]
[frontend.handler.db-based.editor :as db-editor-handler]
[frontend.handler.editor :as editor-handler]
[frontend.handler.global-config :as global-config-handler]
[frontend.state :as state]
[frontend.util :as util]
[goog.object :as gobj]
[logseq.common.path :as path]
[logseq.graph-parser.utf8 :as utf8]))
(defn- save-file! [path content]
(if (db/entity [:file/path path])
;; This fn assumes path is is already in db
(db-editor-handler/save-file! path content)
(when (util/electron?)
(if (path/absolute? path)
(do
;; Set global state first in case it's invalid edn
(when (= path (global-config-handler/global-config-path))
(global-config-handler/set-global-config-state! content)
(state/pub-event! [:shortcut/refresh]))
(fs/write-file! path content))
(js/console.error "Saving relative file ignored" path content)))))
(defn save-code-editor!
[]
(let [{:keys [config state editor]} (get @state/state :editor/code-block-context)]
@@ -42,7 +60,7 @@
(editor-handler/save-block-if-changed! block new-content))
(not-empty (:file-path config))
(db-editor-handler/save-file! (:file-path config) value)
(save-file! (:file-path config) value)
:else
nil))))))

View File

@@ -89,17 +89,15 @@ nested keys or positional errors e.g. tuples"
(defn detect-deprecations
"Detects config keys that will or have been deprecated"
[path content {:keys [db-graph?]}]
[path content]
(let [body (try (edn/read-string content)
(catch :default _ ::failed-to-detect))
warnings (cond->
warnings (merge
{:editor/command-trigger
"is no longer supported. Please use '/' and report bugs on it."
:arweave/gateway
"is no longer supported."}
db-graph?
(merge
common-config/file-only-config))]
common-config/file-only-config)]
(cond
(= body ::failed-to-detect)
(log/info :msg "Skip deprecation check since config is not valid edn")

View File

@@ -73,7 +73,7 @@
"This fn is the db version of file-handler/alter-file"
[path content]
(let [file-valid? (if (= path "logseq/config.edn")
(do (config-edn-common-handler/detect-deprecations path content {:db-graph? true})
(do (config-edn-common-handler/detect-deprecations path content)
(config-edn-common-handler/validate-config-edn path content repo-config-schema/Config-edn))
true)]

View File

@@ -63,16 +63,14 @@
(rewrite/dissoc result (first ks))
(rewrite/assoc-in result ks v))
new-str-content (str new-result)]
(fs/write-plain-text-file! nil nil (global-config-path) new-str-content {:skip-compare? true})
(fs/write-file! (global-config-path) new-str-content)
(state/set-global-config! (rewrite/sexpr new-result) new-str-content)))
(defn start
"This component has four responsibilities on start:
"This component has three responsibilities on start:
- Fetch root-dir for later use with config paths
- Manage ui state of global config
- Create a global config dir and file if it doesn't exist
- Start a file watcher for global config dir if it's not already started.
Watcher ensures client db is seeded with correct file data."
- Create a global config dir and file if it doesn't exist"
[{:keys [repo]}]
(-> (p/do!
(p/let [root-dir' (ipc/ipc "getLogseqDotDirRoot")]

View File

@@ -1,7 +1,7 @@
(ns frontend.handler.plugin-config
"This system component encapsulates the global plugin.edn and depends on the
global-config component. This component is only enabled? if both the
global-config and plugin components are enabled. plugin.edn is automatically updated
global-config and plugin components are enabled. plugins.edn is automatically updated
when a plugin is installed, updated or removed"
(:require [borkdude.rewrite-edn :as rewrite]
[cljs-bean.core :as bean]
@@ -31,23 +31,21 @@ when a plugin is installed, updated or removed"
(->> plugin-config-schema/Plugin rest (mapv first)))
(defn add-or-update-plugin
"Adds or updates a plugin from plugin.edn"
"Adds or updates a plugin from plugins.edn"
[{:keys [id] :as plugin}]
(p/let [content (fs/read-file nil (plugin-config-path))
updated-content (-> content
rewrite/parse-string
(rewrite/assoc (keyword id) (select-keys plugin common-plugin-keys))
str)]
;; fs protocols require repo and dir when they aren't necessary. For this component,
;; neither is needed so these are blank and nil respectively
(fs/write-plain-text-file! "" nil (plugin-config-path) updated-content {:skip-compare? true})))
(fs/write-file! (plugin-config-path) updated-content)))
(defn remove-plugin
"Removes a plugin from plugin.edn"
"Removes a plugin from plugins.edn"
[plugin-id]
(p/let [content (fs/read-file "" (plugin-config-path))
(p/let [content (fs/read-file nil (plugin-config-path))
updated-content (-> content rewrite/parse-string (rewrite/dissoc (keyword plugin-id)) str)]
(fs/write-plain-text-file! "" nil (plugin-config-path) updated-content {:skip-compare? true})))
(fs/write-file! (plugin-config-path) updated-content)))
(defn- create-plugin-config-file-if-not-exists
[]

View File

@@ -21,7 +21,7 @@
(let [error-message (atom nil)]
(with-redefs [notification/show! (fn [msg _] (reset! error-message msg))
rfe/href (constantly "")]
(config-edn-common-handler/detect-deprecations "config.edn" config-body {})
(config-edn-common-handler/detect-deprecations "config.edn" config-body)
(str @error-message))))
(deftest validate-config-edn
@@ -61,8 +61,8 @@
(deftest detect-deprecations
(is (re-find
#":editor/command-trigger.*is"
(deprecation-warnings-for "{:preferred-workflow :todo :editor/command-trigger \",\"}"))
(deprecation-warnings-for "{:editor/command-trigger \",\"}"))
"Warning when there is a deprecation")
(is (= "" (deprecation-warnings-for "{:preferred-workflow :todo}"))
(is (= "" (deprecation-warnings-for "{}"))
"No warning when there is no deprecation"))

View File

@@ -1,19 +1,22 @@
(ns frontend.handler.plugin-config-test
(:require [clojure.test :refer [is use-fixtures testing deftest]]
[frontend.test.helper :as test-helper :include-macros true :refer [deftest-async]]
[frontend.test.node-helper :as test-node-helper]
[frontend.test.node-fixtures :as node-fixtures]
[frontend.handler.plugin-config :as plugin-config-handler]
[frontend.handler.global-config :as global-config-handler]
[frontend.schema.handler.plugin-config :as plugin-config-schema]
["fs" :as fs-node]
(:require ["fs" :as fs-node]
["fs/promises" :as fsp]
["path" :as node-path]
[clojure.edn :as edn]
[malli.generator :as mg]
[promesa.core :as p]
[clojure.string :as string]
[frontend.handler.notification :as notification]))
[clojure.test :refer [is use-fixtures testing deftest]]
[frontend.fs :as fs]
[frontend.handler.global-config :as global-config-handler]
[frontend.handler.notification :as notification]
[frontend.handler.plugin-config :as plugin-config-handler]
[frontend.schema.handler.plugin-config :as plugin-config-schema]
[frontend.test.helper :as test-helper :include-macros true :refer [deftest-async]]
[frontend.test.node-fixtures :as node-fixtures]
[frontend.test.node-helper :as test-node-helper]
[malli.generator :as mg]
[promesa.core :as p]))
;; For tests that call fs/readFile
(use-fixtures :once node-fixtures/redef-get-fs)
(defn- create-global-config-dir
@@ -37,13 +40,14 @@
body (pr-str (mg/generate plugin-config-schema/Plugins-edn {:size 10}))]
(fs-node/writeFileSync (plugin-config-handler/plugin-config-path) body)
(->
(p/do!
(plugin-config-handler/add-or-update-plugin plugin-to-add)
(is (= (dissoc plugin-to-add :id)
(:foo (edn/read-string (str (fs-node/readFileSync (plugin-config-handler/plugin-config-path))))))))
(p/with-redefs [fs/write-file! fsp/writeFile]
(->
(p/do!
(plugin-config-handler/add-or-update-plugin plugin-to-add)
(is (= (dissoc plugin-to-add :id)
(:foo (edn/read-string (str (fs-node/readFileSync (plugin-config-handler/plugin-config-path))))))))
(p/finally #(delete-global-config-dir dir)))))
(p/finally #(delete-global-config-dir dir))))))
(deftest-async remove-plugin
(let [dir (create-global-config-dir)
@@ -53,14 +57,15 @@
some-plugin-id (first (keys plugins))]
(fs-node/writeFileSync (plugin-config-handler/plugin-config-path) (pr-str plugins))
(->
(p/do!
(plugin-config-handler/remove-plugin some-plugin-id)
(is (= nil
(get (edn/read-string (str (fs-node/readFileSync (plugin-config-handler/plugin-config-path))))
some-plugin-id))))
(p/with-redefs [fs/write-file! fsp/writeFile]
(->
(p/do!
(plugin-config-handler/remove-plugin some-plugin-id)
(is (= nil
(get (edn/read-string (str (fs-node/readFileSync (plugin-config-handler/plugin-config-path))))
some-plugin-id))))
(p/finally #(delete-global-config-dir dir)))))
(p/finally #(delete-global-config-dir dir))))))
(deftest-async open-replace-plugins-modal-malformed-edn
(let [dir (create-global-config-dir)