| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214 |
- (ns electron.window
- (:require ["electron" :refer [BrowserWindow app session shell dialog] :as electron]
- ["electron-window-state" :as windowStateKeeper]
- ["path" :as node-path]
- ["url" :as URL]
- [cljs-bean.core :as bean]
- [clojure.string :as string]
- [electron.configs :as cfgs]
- [electron.context-menu :as context-menu]
- [electron.logger :as logger]
- [electron.state :as state]
- [electron.utils :refer [mac? win32? linux? dev? open] :as utils]))
- (defonce *quitting? (atom false))
- (def MAIN_WINDOW_ENTRY (if dev?
- ;; Use index.html to test plugins on development mode
- "http://localhost:3001"
- (str "file://" (node-path/join js/__dirname "index.html"))))
- (defn create-main-window!
- ([]
- (create-main-window! MAIN_WINDOW_ENTRY nil))
- ([url]
- (create-main-window! url nil))
- ([url {:keys [graph] :as opts}]
- (let [win-state (windowStateKeeper (clj->js {:defaultWidth 980 :defaultHeight 700}))
- native-titlebar? (cfgs/get-item :window/native-titlebar?)
- url (if graph (str url "#/?graph=" graph) url)
- win-opts (cond->
- {:backgroundColor "#fff" ; SEE https://www.electronjs.org/docs/latest/faq#the-font-looks-blurry-what-is-this-and-what-can-i-do
- :width (.-width win-state)
- :height (.-height win-state)
- :frame (or mac? native-titlebar?)
- :titleBarStyle "hiddenInset"
- :trafficLightPosition {:x 16 :y 16}
- :autoHideMenuBar (not mac?)
- :show false
- :webPreferences
- {:plugins true ; pdf
- :nodeIntegration false
- :nodeIntegrationInWorker false
- :nativeWindowOpen true
- :sandbox false
- :webSecurity (not dev?)
- :contextIsolation true
- :spellcheck ((fnil identity true) (cfgs/get-item :spell-check))
- ;; Remove OverlayScrollbars and transition `.scrollbar-spacing`
- ;; to use `scollbar-gutter` after the feature is implemented in browsers.
- :enableBlinkFeatures 'OverlayScrollbars'
- :preload (node-path/join js/__dirname "js/preload.js")}}
- (seq opts)
- (merge opts)
- linux?
- (assoc :icon (node-path/join js/__dirname "icons/logseq.png")))
- win (BrowserWindow. (clj->js win-opts))]
- (.onBeforeSendHeaders (.. session -defaultSession -webRequest)
- (clj->js {:urls (array "*://*.youtube.com/*")})
- (fn [^js details callback]
- (let [url (.-url details)
- urlObj (js/URL. url)
- origin (.-origin urlObj)
- requestHeaders (.-requestHeaders details)]
- (if (and
- (.hasOwnProperty requestHeaders "referer")
- (not-empty (.-referer requestHeaders)))
- (callback #js {:cancel false
- :requestHeaders requestHeaders})
- (do
- (set! (.-referer requestHeaders) origin)
- (callback #js {:cancel false
- :requestHeaders requestHeaders}))))))
- (.loadURL win url)
- ;;(when dev? (.. win -webContents (openDevTools)))
- win)))
- (defn get-all-windows
- []
- (.getAllWindows BrowserWindow))
- (defn destroy-window!
- [^js win]
- (.destroy win))
- (defn close-handler
- [^js win close-watcher-f e]
- (.preventDefault e)
- (when-let [dir (state/get-window-graph-path win)]
- (close-watcher-f win dir))
- (state/close-window! win)
- (let [web-contents (. win -webContents)]
- (.send web-contents "persist-zoom-level" (.getZoomLevel web-contents)))
- (destroy-window! win))
- (defn on-close-actions!
- ;; TODO merge with the on close in core
- [^js win close-watcher-f] ;; injected watcher related func
- (.on win "close" (fn [e] (close-handler win close-watcher-f e))))
- (defn switch-to-window!
- [^js win]
- (when (.isMinimized ^object win)
- (.restore win))
- ;; Ref: https://github.com/electron/electron/issues/8734
- (.setVisibleOnAllWorkspaces win true)
- (.focus win)
- (.setVisibleOnAllWorkspaces win false))
- (defn get-graph-all-windows
- [graph-path] ;; graph-path == dir
- (->> (group-by second (:window/graph @state/state))
- (#(get % graph-path))
- (map first)))
- (defn graph-has-other-windows? [win dir]
- (let [windows (get-graph-all-windows dir)]
- ;; windows (filter #(.isVisible %) windows) ;; for mac .hide windows. such windows should also included
- (boolean (some (fn [^js window] (and (not (.isDestroyed window))
- (not= (.-id win) (.-id window))))
- windows))))
- (defn- open-default-app!
- [url default-open]
- (let [URL (.-URL URL)
- parsed-url (try (URL. url) (catch :default _ nil))]
- (when parsed-url
- (if (contains? #{"https:" "http:" "mailto:"} (.-protocol parsed-url))
- (.openExternal shell url)
- (when-let [^js res (and (fn? default-open)
- (.showMessageBoxSync dialog
- #js {:type "warning"
- :message (str "Are you sure you want to open this link? \n\n" url)
- :defaultId 1
- :cancelId 0
- :buttons #js ["Cancel" "OK"]}))]
- (when (= res 1)
- (default-open url)))))))
- (defn setup-window-listeners!
- [^js win]
- (when win
- (let [web-contents (. win -webContents)
- open-external!
- (fn [url]
- (let [url (if (string/starts-with? url "file:")
- (utils/safe-decode-uri-component url) url)
- url (if-not win32? (string/replace url "file://" "") url)]
- (logger/info "new-window" url)
- (if (string/includes?
- (.normalize node-path url)
- (.join node-path (. app getAppPath) "index.html"))
- (logger/info "pass-window" url)
- (open-default-app! url open))))
- will-navigate-handler
- (fn [e url]
- (.preventDefault e)
- (open-default-app! url open))
- context-menu-handler
- (context-menu/setup-context-menu! win)
- window-open-handler
- (fn [^js details]
- (let [url (.-url details)
- fullscreen? (.isFullScreen win)
- features (string/split (.-features details) ",")
- features (when (seq features)
- (reduce (fn [a b]
- (let [[k v] (string/split b "=")]
- (if (string? v)
- (assoc a (keyword k) (parse-long (string/trim v)))
- a))) {} features))]
- (-> (if (= url "about:blank")
- (merge {:action "allow"
- :overrideBrowserWindowOptions
- {:frame true
- :titleBarStyle "default"
- :trafficLightPosition {:x 16 :y 16}
- :autoHideMenuBar (not mac?)
- :fullscreenable (not fullscreen?)
- :webPreferences
- {:plugins true
- :nodeIntegration false
- :webSecurity (not dev?)
- :preload (node-path/join js/__dirname "js/preload.js")
- :nativeWindowOpen true}}}
- features)
- (do (open-external! url) {:action "deny"}))
- (bean/->js))))]
- (doto web-contents
- (.on "will-navigate" will-navigate-handler)
- (.on "did-start-navigation" #(.send web-contents "persist-zoom-level" (.getZoomLevel web-contents)))
- (.on "page-title-updated" #(.send web-contents "restore-zoom-level"))
- (.setWindowOpenHandler window-open-handler))
- (doto win
- (.on "enter-full-screen" #(.send web-contents "full-screen" "enter"))
- (.on "leave-full-screen" #(.send web-contents "full-screen" "leave"))
- (.on "maximize" #(.send web-contents "maximize" true))
- (.on "unmaximize" #(.send web-contents "maximize" false)))
- ;; clear
- (fn []
- (doto web-contents
- (.off "context-menu" context-menu-handler)
- (.off "will-navigate" will-navigate-handler))
- (.off win "enter-full-screen")
- (.off win "leave-full-screen")))
- #()))
|