window.cljs 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. (ns electron.window
  2. (:require ["electron" :refer [BrowserWindow app session shell dialog] :as electron]
  3. ["electron-window-state" :as windowStateKeeper]
  4. ["path" :as node-path]
  5. ["url" :as URL]
  6. [cljs-bean.core :as bean]
  7. [clojure.string :as string]
  8. [electron.configs :as cfgs]
  9. [electron.context-menu :as context-menu]
  10. [electron.logger :as logger]
  11. [electron.state :as state]
  12. [electron.utils :refer [mac? win32? linux? dev? open] :as utils]))
  13. (defonce *quitting? (atom false))
  14. (def MAIN_WINDOW_ENTRY (if dev?
  15. ;; Use index.html to test plugins on development mode
  16. "http://localhost:3001"
  17. (str "file://" (node-path/join js/__dirname "index.html"))))
  18. (defn create-main-window!
  19. ([]
  20. (create-main-window! MAIN_WINDOW_ENTRY nil))
  21. ([url]
  22. (create-main-window! url nil))
  23. ([url {:keys [graph] :as opts}]
  24. (let [win-state (windowStateKeeper (clj->js {:defaultWidth 980 :defaultHeight 700}))
  25. native-titlebar? (cfgs/get-item :window/native-titlebar?)
  26. url (if graph (str url "#/?graph=" graph) url)
  27. win-opts (cond->
  28. {:backgroundColor "#fff" ; SEE https://www.electronjs.org/docs/latest/faq#the-font-looks-blurry-what-is-this-and-what-can-i-do
  29. :width (.-width win-state)
  30. :height (.-height win-state)
  31. :frame (or mac? native-titlebar?)
  32. :titleBarStyle "hiddenInset"
  33. :trafficLightPosition {:x 16 :y 16}
  34. :autoHideMenuBar (not mac?)
  35. :show false
  36. :webPreferences
  37. {:plugins true ; pdf
  38. :nodeIntegration false
  39. :nodeIntegrationInWorker false
  40. :nativeWindowOpen true
  41. :sandbox false
  42. :webSecurity (not dev?)
  43. :contextIsolation true
  44. :spellcheck ((fnil identity true) (cfgs/get-item :spell-check))
  45. ;; Remove OverlayScrollbars and transition `.scrollbar-spacing`
  46. ;; to use `scollbar-gutter` after the feature is implemented in browsers.
  47. :enableBlinkFeatures 'OverlayScrollbars'
  48. :preload (node-path/join js/__dirname "js/preload.js")}}
  49. (seq opts)
  50. (merge opts)
  51. linux?
  52. (assoc :icon (node-path/join js/__dirname "icons/logseq.png")))
  53. win (BrowserWindow. (clj->js win-opts))]
  54. (.onBeforeSendHeaders (.. session -defaultSession -webRequest)
  55. (clj->js {:urls (array "*://*.youtube.com/*")})
  56. (fn [^js details callback]
  57. (let [url (.-url details)
  58. urlObj (js/URL. url)
  59. origin (.-origin urlObj)
  60. requestHeaders (.-requestHeaders details)]
  61. (if (and
  62. (.hasOwnProperty requestHeaders "referer")
  63. (not-empty (.-referer requestHeaders)))
  64. (callback #js {:cancel false
  65. :requestHeaders requestHeaders})
  66. (do
  67. (set! (.-referer requestHeaders) origin)
  68. (callback #js {:cancel false
  69. :requestHeaders requestHeaders}))))))
  70. (.loadURL win url)
  71. ;;(when dev? (.. win -webContents (openDevTools)))
  72. win)))
  73. (defn get-all-windows
  74. []
  75. (.getAllWindows BrowserWindow))
  76. (defn destroy-window!
  77. [^js win]
  78. (.destroy win))
  79. (defn close-handler
  80. [^js win close-watcher-f e]
  81. (.preventDefault e)
  82. (when-let [dir (state/get-window-graph-path win)]
  83. (close-watcher-f win dir))
  84. (state/close-window! win)
  85. (let [web-contents (. win -webContents)]
  86. (.send web-contents "persist-zoom-level" (.getZoomLevel web-contents)))
  87. (destroy-window! win))
  88. (defn on-close-actions!
  89. ;; TODO merge with the on close in core
  90. [^js win close-watcher-f] ;; injected watcher related func
  91. (.on win "close" (fn [e] (close-handler win close-watcher-f e))))
  92. (defn switch-to-window!
  93. [^js win]
  94. (when (.isMinimized ^object win)
  95. (.restore win))
  96. ;; Ref: https://github.com/electron/electron/issues/8734
  97. (.setVisibleOnAllWorkspaces win true)
  98. (.focus win)
  99. (.setVisibleOnAllWorkspaces win false))
  100. (defn get-graph-all-windows
  101. [graph-path] ;; graph-path == dir
  102. (->> (group-by second (:window/graph @state/state))
  103. (#(get % graph-path))
  104. (map first)))
  105. (defn graph-has-other-windows? [win dir]
  106. (let [windows (get-graph-all-windows dir)]
  107. ;; windows (filter #(.isVisible %) windows) ;; for mac .hide windows. such windows should also included
  108. (boolean (some (fn [^js window] (and (not (.isDestroyed window))
  109. (not= (.-id win) (.-id window))))
  110. windows))))
  111. (defn- open-default-app!
  112. [url default-open]
  113. (let [URL (.-URL URL)
  114. parsed-url (try (URL. url) (catch :default _ nil))]
  115. (when parsed-url
  116. (if (contains? #{"https:" "http:" "mailto:"} (.-protocol parsed-url))
  117. (.openExternal shell url)
  118. (when-let [^js res (and (fn? default-open)
  119. (.showMessageBoxSync dialog
  120. #js {:type "warning"
  121. :message (str "Are you sure you want to open this link? \n\n" url)
  122. :defaultId 1
  123. :cancelId 0
  124. :buttons #js ["Cancel" "OK"]}))]
  125. (when (= res 1)
  126. (default-open url)))))))
  127. (defn setup-window-listeners!
  128. [^js win]
  129. (when win
  130. (let [web-contents (. win -webContents)
  131. open-external!
  132. (fn [url]
  133. (let [url (if (string/starts-with? url "file:")
  134. (utils/safe-decode-uri-component url) url)
  135. url (if-not win32? (string/replace url "file://" "") url)]
  136. (logger/info "new-window" url)
  137. (if (string/includes?
  138. (.normalize node-path url)
  139. (.join node-path (. app getAppPath) "index.html"))
  140. (logger/info "pass-window" url)
  141. (open-default-app! url open))))
  142. will-navigate-handler
  143. (fn [e url]
  144. (.preventDefault e)
  145. (open-default-app! url open))
  146. context-menu-handler
  147. (context-menu/setup-context-menu! win)
  148. window-open-handler
  149. (fn [^js details]
  150. (let [url (.-url details)
  151. fullscreen? (.isFullScreen win)
  152. features (string/split (.-features details) ",")
  153. features (when (seq features)
  154. (reduce (fn [a b]
  155. (let [[k v] (string/split b "=")]
  156. (if (string? v)
  157. (assoc a (keyword k) (parse-long (string/trim v)))
  158. a))) {} features))]
  159. (-> (if (= url "about:blank")
  160. (merge {:action "allow"
  161. :overrideBrowserWindowOptions
  162. {:frame true
  163. :titleBarStyle "default"
  164. :trafficLightPosition {:x 16 :y 16}
  165. :autoHideMenuBar (not mac?)
  166. :fullscreenable (not fullscreen?)
  167. :webPreferences
  168. {:plugins true
  169. :nodeIntegration false
  170. :webSecurity (not dev?)
  171. :preload (node-path/join js/__dirname "js/preload.js")
  172. :nativeWindowOpen true}}}
  173. features)
  174. (do (open-external! url) {:action "deny"}))
  175. (bean/->js))))]
  176. (doto web-contents
  177. (.on "will-navigate" will-navigate-handler)
  178. (.on "did-start-navigation" #(.send web-contents "persist-zoom-level" (.getZoomLevel web-contents)))
  179. (.on "page-title-updated" #(.send web-contents "restore-zoom-level"))
  180. (.setWindowOpenHandler window-open-handler))
  181. (doto win
  182. (.on "enter-full-screen" #(.send web-contents "full-screen" "enter"))
  183. (.on "leave-full-screen" #(.send web-contents "full-screen" "leave"))
  184. (.on "maximize" #(.send web-contents "maximize" true))
  185. (.on "unmaximize" #(.send web-contents "maximize" false)))
  186. ;; clear
  187. (fn []
  188. (doto web-contents
  189. (.off "context-menu" context-menu-handler)
  190. (.off "will-navigate" will-navigate-handler))
  191. (.off win "enter-full-screen")
  192. (.off win "leave-full-screen")))
  193. #()))