1
0
Эх сурвалжийг харах

feat: switch to use Excalidraw api

Tienson Qin 4 жил өмнө
parent
commit
92ad0296e6

+ 9 - 0
resources/css/common.css

@@ -789,3 +789,12 @@ a {
     font-weight: 600;
     font-size: 13px;
 }
+
+/* excalidraw */
+.Island > div > div > div {
+    width: 44px;
+}
+
+.excalidraw hr {
+    margin: 0;
+}

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 4
resources/css/excalidraw.min.css


+ 13 - 0
resources/css/fonts.css

@@ -0,0 +1,13 @@
+/* http://www.eaglefonts.com/fg-virgil-ttf-131249.htm */
+@font-face {
+    font-family: "Virgil";
+    src: url("../fonts/Virgil.woff2");
+    font-display: swap;
+}
+
+/* https://github.com/microsoft/cascadia-code */
+@font-face {
+    font-family: "Cascadia";
+    src: url("../fonts/Cascadia.woff2");
+    font-display: swap;
+}

+ 1 - 0
resources/css/style.css

@@ -2,6 +2,7 @@
 @import "./inter.css";
 @import "./reveal.min.css";
 @import "./reveal_black.min.css";
+@import "./fonts.css";
 @import "./excalidraw.min.css";
 @import "./katex.min.css";
 @import "./codemirror.min.css";

+ 2 - 1
resources/css/style.dev.css

@@ -2,6 +2,7 @@
 @import "./inter.css";
 @import "./reveal.min.css";
 @import "./reveal_black.min.css";
+@import "./fonts.css";
 @import "./excalidraw.min.css";
 @import "./katex.min.css";
 @import "./codemirror.min.css";
@@ -10,4 +11,4 @@
 @import "./datepicker.css";
 @import "./highlight.css";
 @import "./tailwind.core.css"; /* Build by gulp. Check `_buildTailwind` for more detail */
-@import "./common.css";
+@import "./common.css";

+ 0 - 0
resources/css/Cascadia.woff2 → resources/fonts/Cascadia.woff2


+ 0 - 0
resources/css/FG_Virgil.woff2 → resources/fonts/FG_Virgil.woff2


BIN
resources/fonts/Virgil.woff2


+ 3 - 0
shadow-cljs.edn

@@ -12,6 +12,9 @@
               :depends-on #{:main}}
              :age-encryption
              {:entries [frontend.extensions.age-encryption]
+              :depends-on #{:main}}
+             :excalidraw
+             {:entries [frontend.extensions.excalidraw]
               :depends-on #{:main}}}
 
    :output-dir "./static/js"

+ 10 - 5
src/main/frontend/commands.cljs

@@ -8,7 +8,9 @@
             [goog.dom :as gdom]
             [goog.object :as gobj]
             [frontend.format :as format]
-            [frontend.handler.common :as common-handler]))
+            [frontend.handler.common :as common-handler]
+            [frontend.handler.draw :as draw]
+            [promesa.core :as p]))
 
 ;; TODO: move to frontend.handler.editor.commands
 
@@ -102,10 +104,13 @@
      ["Scheduled" [[:editor/clear-current-slash]
                    [:editor/show-date-picker]]]
      ["Query" [[:editor/input "{{query }}" {:backward-pos 2}]]]
-     ["Draw" [[:editor/input "/draw "]
-              [:editor/show-input [{:command :draw
-                                    :id :title
-                                    :placeholder "Draw title"}]]]]
+     ["Draw" (fn []
+               (let [file (draw/file-name)
+                     path (str config/default-draw-directory "/" file)
+                     text (util/format "[[%s]]" path)]
+                 (p/let [_ (draw/create-draw-with-default-content path)]
+                   (println "draw file created, " path))
+                 text))]
      ["WAITING" (->marker "WAITING")]
      ["CANCELED" (->marker "CANCELED")]
      ["Tomorrow" #(get-page-ref-text (date/tomorrow))]

+ 31 - 22
src/main/frontend/components/block.cljs

@@ -16,7 +16,6 @@
             [goog.dom :as gdom]
             [frontend.handler.expand :as expand]
             [frontend.components.svg :as svg]
-            [frontend.components.draw :as draw]
             [frontend.components.datetime :as datetime-comp]
             [frontend.ui :as ui]
             [frontend.handler.editor :as editor-handler]
@@ -48,7 +47,8 @@
             [frontend.commands :as commands]
             [lambdaisland.glogi :as log]
             [frontend.context.i18n :as i18n]
-            [frontend.template :as template]))
+            [frontend.template :as template]
+            [shadow.loader :as loader]))
 
 ;; TODO: remove rum/with-context because it'll make reactive queries not working
 
@@ -385,32 +385,41 @@
         full-path (.. util/node-path (join repo-path (config/get-local-asset-absolute-path path)))]
     [:a.asset-ref {:target "_blank" :href full-path} (or title path)]))
 
+(defonce excalidraw-loaded? (atom false))
+(rum/defc excalidraw < rum/reactive
+  {:init (fn [state]
+           (p/let [_ (loader/load :excalidraw)]
+             (reset! excalidraw-loaded? true))
+           state)}
+  [file]
+  (let [loaded? (rum/react excalidraw-loaded?)
+        draw-component (if loaded?
+                         (resolve 'frontend.extensions.excalidraw/draw))]
+    (when draw-component
+      (draw-component {:file file}))))
+
 (rum/defc page-reference < rum/reactive
   [html-export? s config label]
   (let [show-brackets? (state/show-brackets?)
         nested-link? (:nested-link? config)
-        contents-page? (= "contents" (string/lower-case (str (:id config))))]
-    [:span.page-reference
-     (when (and (or show-brackets? nested-link?)
-                (not html-export?)
-                (not contents-page?))
-       [:span.text-gray-500.bracket "[["])
-     (if (string/ends-with? s ".excalidraw")
-       [:a.page-ref
-        {:on-click (fn [e]
-                     (util/stop e)
-                     (set! (.-href js/window.location)
-                           (rfe/href :draw nil {:file (string/replace s (str config/default-draw-directory "/") "")})))}
-        [:span
-         (svg/excalidraw-logo)
-         (string/capitalize (draw/get-file-title s))]]
+        contents-page? (= "contents" (string/lower-case (str (:id config))))
+        draw? (string/ends-with? s ".excalidraw")]
+    (if (string/ends-with? s ".excalidraw")
+      [:div.draw {:on-click (fn [e]
+                              (.stopPropagation e))}
+       (excalidraw s)]
+      [:span.page-reference
+       (when (and (or show-brackets? nested-link?)
+                  (not html-export?)
+                  (not contents-page?))
+         [:span.text-gray-500.bracket "[["])
        (page-cp (assoc config
                        :label (mldoc/plain->text label)
-                       :contents-page? contents-page?) {:page/name s}))
-     (when (and (or show-brackets? nested-link?)
-                (not html-export?)
-                (not contents-page?))
-       [:span.text-gray-500.bracket "]]"])]))
+                       :contents-page? contents-page?) {:page/name s})
+       (when (and (or show-brackets? nested-link?)
+                  (not html-export?)
+                  (not contents-page?))
+         [:span.text-gray-500.bracket "]]"])])))
 
 (defn- latex-environment-content
   [name option content]

+ 0 - 497
src/main/frontend/components/draw.cljs

@@ -1,497 +0,0 @@
-(ns frontend.components.draw
-  (:require [rum.core :as rum]
-            [goog.object :as gobj]
-            [frontend.rum :as r]
-            [frontend.util :as util :refer-macros [profile]]
-            [frontend.mixins :as mixins]
-            [frontend.storage :as storage]
-            [frontend.components.svg :as svg]
-            [cljs-bean.core :as bean]
-            [dommy.core :as d]
-            [clojure.string :as string]
-            [frontend.handler.notification :as notification]
-            [frontend.handler.draw :as draw :refer
-             [*files
-              *current-file
-              *current-title
-              *file-loading?
-              *elements
-              *unsaved?
-              *search-files
-              *saving-title
-              *excalidraw]]
-            [frontend.handler.file :as file]
-            [frontend.ui :as ui]
-            [frontend.loader :as loader]
-            [frontend.config :as config]
-            [frontend.state :as state]
-            [frontend.search :as search]
-            [frontend.components.repo :as repo]
-            [promesa.core :as p]
-            [reitit.frontend.easy :as rfe]))
-
-(defn loaded? []
-  js/window.Excalidraw)
-
-(defonce *loaded? (atom false))
-
-(defonce draw-state :draw-state)
-
-(defn get-draw-state []
-  (storage/get draw-state))
-(defn set-draw-state! [value]
-  (storage/set draw-state value))
-(defn get-k
-  ([k]
-   (get-k k (state/get-current-repo)))
-  ([repo k]
-   (when repo
-     (get-in (get-draw-state) [repo k]))))
-
-(defn set-k
-  [k v]
-  (when-let [repo (state/get-current-repo)]
-    (let [state (get-draw-state)]
-      (let [new-state (assoc-in state [repo k] v)]
-        (set-draw-state! new-state)))))
-
-(defn get-last-file
-  ([]
-   (get-k :last-file))
-  ([repo]
-   (get-k repo :last-file)))
-
-(defn get-last-title
-  ([]
-   (get-k :last-title))
-  ([repo]
-   (get-k repo :last-title)))
-
-(defn set-last-file!
-  [value]
-  (set-k :last-file value))
-(defn set-last-title!
-  [value]
-  (set-k :last-title value))
-
-(defn get-last-elements
-  []
-  (storage/get-json (str (state/get-current-repo) "-" "last-elements")))
-(defn get-last-app-state
-  []
-  (storage/get-json (str (state/get-current-repo) "-" "last-app-state")))
-
-(defn set-last-elements!
-  [value]
-  (storage/set-json (str (state/get-current-repo) "-" "last-elements") value))
-(defn set-last-app-state!
-  [value]
-  (storage/set-json (str (state/get-current-repo) "-" "last-app-state") value))
-
-(defn set-excalidraw-component!
-  []
-  (reset! *excalidraw (r/adapt-class
-                       (gobj/get js/window.Excalidraw "default"))))
-
-(defn serialize-as-json
-  [elements app-state]
-  (when (loaded?)
-    (when-let [f (gobj/get js/window.Excalidraw "serializeAsJSON")]
-      (f elements app-state))))
-
-;; api restore
-
-(defn from-json
-  [text]
-  (when-not (string/blank? text)
-    (try
-      (when-let [data (js/JSON.parse text)]
-        (if (not= "excalidraw" (gobj/get data "type"))
-          (notification/show!
-           (util/format "Could not load this invalid excalidraw file")
-           :error)
-          {:elements (gobj/get data "elements")
-           :app-state (gobj/get data "appState")}))
-      (catch js/Error e
-        (prn "from json error:")
-        (js/console.dir e)
-        (notification/show!
-         (util/format "Could not load this invalid excalidraw file")
-         :error)))))
-
-(defn get-file-title
-  [file]
-  (when file
-    (let [s (subs file 20)
-          title (string/replace s ".excalidraw" "")]
-      (string/replace title "-" " "))))
-
-(defn save-excalidraw!
-  [state _event file ok-handler]
-  (let [title @*current-title]
-    (cond
-      (string/blank? title)
-      (do
-        (reset! *saving-title nil)
-        (notification/show!
-         "Please specify a title first!"
-         :error)
-        ;; TODO: focus the title input
-)
-
-      (= title @*saving-title)
-      nil
-
-      :else
-      (when-let [elements (get-last-elements)]
-        (reset! *saving-title title)
-        (let [app-state (get-last-app-state)
-              [option] (:rum/args state)
-              file (util/trim-safe
-                    (or
-                     file
-                     @*current-file
-                     (:file option)
-                     (draw/title->file-name title)))
-              data (serialize-as-json elements app-state)]
-          (when file
-            (draw/save-excalidraw! file data
-                                   (fn [file]
-                                     (reset! *files
-                                             (distinct (conj @*files file)))
-                                     (reset! *current-file file)
-                                     (reset! *unsaved? false)
-                                     (set-last-file! file)
-                                     (when ok-handler (ok-handler file))
-                                     (reset! *saving-title nil)))))))))
-
-(defn- clear-canvas!
-  []
-  (when-let [canvas (d/by-id "canvas")]
-    (let [context (.getContext canvas "2d")]
-      (.clearRect context 0 0 (gobj/get canvas "width") (gobj/get canvas "height"))
-      (set! (.-fillStyle context) "#FFF")
-      (.fillRect context 0 0 (gobj/get canvas "width") (gobj/get canvas "height")))))
-
-(defn- new-file!
-  []
-  ;; TODO: save current firstly
-  (clear-canvas!)
-  (reset! *current-title "")
-  (reset! *current-file nil)
-  (reset! *elements nil)
-  (set-last-elements! nil)
-  (set-last-title! nil)
-  (set-last-file! nil)
-  (set-last-app-state! nil))
-
-(defn- rename-file!
-  [file new-title]
-  (when-not (string/blank? new-title)
-    (let [new-file (draw/title->file-name new-title)]
-      (when-not (= (string/trim file) (string/trim new-file))
-        (save-excalidraw!
-         {} {} new-file
-         (fn []
-           (set-last-file! new-file)
-           (util/p-handle
-            (file/remove-file!
-             (state/get-current-repo)
-             (str config/default-draw-directory "/" file))
-            (fn [_]
-              (reset! *files (->> (conj @*files new-file)
-                                  (remove #(= file %))
-                                  distinct
-                                  (vec)))
-              (reset! *current-file new-file)
-              (notification/show!
-               "File was renamed successfully!"
-               :success))
-            (fn [error]
-              (println "Rename file failed, reason: ")
-              (js/console.dir error)))))))))
-
-(rum/defc draw-title < rum/reactive
-  (mixins/event-mixin
-   (fn [state]
-     (let [old-title @*current-title]
-       (mixins/hide-when-esc-or-outside
-        state
-        :on-hide (fn [state e event]
-                   (let [title (and @*current-title (string/trim @*current-title))
-                         file @*current-file]
-                     (when (or
-                            (string/blank? old-title)
-                            (not= (string/trim old-title) title))
-                       (cond
-                         (and file (not (string/blank? title)))
-                         (rename-file! file title)
-
-                         (and (not file)
-                              (not (string/blank? title))
-                              (seq @*elements)) ; new file
-                         (save-excalidraw! {} {} nil nil)
-
-                         :else
-                         nil))))))
-     state))
-  []
-  (let [current-title (rum/react *current-title)]
-    [:input#draw-title.font-medium.w-48.px-2.py-1.ml-2
-     {:on-click (fn [e]
-                  (util/stop e))
-      :placeholder "Untitled"
-      :auto-complete "off"
-      :default-value (or (and current-title (string/capitalize current-title)) "")
-      :on-change (fn [e]
-                   (when-let [value (util/evalue e)]
-                     (set-last-title! value)
-                     (reset! *current-title value)))}]))
-
-(rum/defc files-search < rum/reactive
-  [state]
-  [:div#search-wrapper.relative.w-full.text-gray-400.focus-within:text-gray-600
-   [:div.absolute.inset-y-0.flex.items-center.pointer-events-none.left-3
-    [:svg.h-4.w-4
-     {:view-box "0 0 20 20", :fill "currentColor"}
-     [:path
-      {:d
-       "M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z",
-       :clip-rule "evenodd",
-       :fill-rule "evenodd"}]]]
-   [:input.block.w-full.pl-2.sm:text-sm.sm:leading-3.mb-2.mt-2.border-none.outline-none.focus:outline-none
-    {:style {:padding-left "2rem"
-             :border-radius 0}
-     :placeholder "Search"
-     :auto-complete "off"
-     :on-change (fn [e]
-                  (let [value (util/evalue e)
-                        files @*files]
-                    (reset! *search-files
-                            (if (string/blank? value)
-                              files
-                              (search/fuzzy-search files value :limit 10)))))}]])
-
-(rum/defcs save-button < rum/reactive
-  [state]
-  (let [unsaved? (rum/react *unsaved?)]
-    [:a.ml-2 {:title (if unsaved? "Save changes" "Save")
-              :on-click (fn [e]
-                          (save-excalidraw! state e nil nil))}
-     [:div.ToolIcon__icon {:class (if unsaved? "bg-orange-400" "bg-gray-200")
-                           :style {:width "2rem"
-                                   :height "2rem"}}
-      svg/save]]))
-
-(rum/defcs files < rum/reactive
-  [state]
-  (let [all-files (rum/react *files)
-        search-files (rum/react *search-files)
-        files (if (seq search-files) search-files all-files)
-        current-file (rum/react *current-file)
-        unsaved? (rum/react *unsaved?)]
-    [:div.flex-row.flex.items-center
-     [:a.ml-2 {:title "New file"
-               :on-click new-file!}
-      [:div.ToolIcon__icon.bg-gray-200 {:style {:width "2rem"
-                                                :height "2rem"}}
-       svg/plus]]
-
-     (ui/dropdown-with-links
-      (fn [{:keys [toggle-fn]}]
-        [:div.ToolIcon__icon.ml-2.cursor.bg-gray-200 {:title "List files"
-                                                      :on-click toggle-fn
-                                                      :style {:width "2rem"
-                                                              :height "2rem"}}
-         svg/folder])
-      (mapv
-       (fn [file]
-         {:title (get-file-title file)
-          :options {:title file
-                    :on-click
-                    (fn [e]
-                      (util/stop e)
-                      (set-last-file! file)
-                      (reset! *current-file file)
-                      (reset! *current-title (get-file-title file))
-                      (reset! *search-files []))}})
-       files)
-      {:modal-class (util/hiccup->class
-                     "origin-top-right.absolute.left-0.mt-2.rounded-md.shadow-lg.whitespace-no-wrap.bg-white.w-48.dropdown-overflow-auto")
-       :links-header (when (>= (count all-files) 5)
-                       (files-search))})
-
-     (save-button)
-
-     (let [links (->> [(when @*current-file
-                         {:title "Delete"
-                          :options {:style {:color "#db1111"}
-                                    :on-click (fn [e]
-                                                (util/stop e)
-                                                (when-let [current-file @*current-file]
-                                                  (p/let [_ (file/remove-file! (state/get-current-repo)
-                                                                               (str config/default-draw-directory "/" current-file))]
-                                                    (reset! *files (remove #(= current-file %) @*files))
-                                                    (new-file!))))}})]
-                      (remove nil?))]
-       (when (seq links)
-         (ui/dropdown-with-links
-          (fn [{:keys [toggle-fn]}]
-            [:div.ToolIcon__icon.ml-2.cursor.bg-gray-200
-             {:title "More options"
-              :on-click toggle-fn
-              :style {:width "2rem"
-                      :height "2rem"}}
-             (svg/vertical-dots nil)])
-          links
-          {:modal-class (util/hiccup->class
-                         "origin-top-right.absolute.left-0.mt-2.rounded-md.shadow-lg.whitespace-no-wrap.bg-white.w-48.dropdown-overflow-auto")})))
-
-     (draw-title)]))
-
-(defn- set-canvas-actions-style!
-  [state]
-  (when-let [section (first (d/by-tag "section"))]
-    (when (= "canvasActions-title" (d/attr section "aria-labelledby"))
-      (d/set-style! section "margin-top" "48px")))
-  state)
-
-(rum/defcs draw-inner < rum/reactive
-  (mixins/keyboard-mixin (util/->system-modifier "ctrl+s")
-                         (fn [state e]
-                           (save-excalidraw! state e nil nil)))
-  (mixins/keyboard-mixin "alt+z" set-canvas-actions-style!)
-  {:init (fn [state]
-           (reset! *elements nil)
-           (let [[option] (:rum/args state)
-                 file (or @*current-file
-                          (:file option))]
-             (do
-               (reset! *current-title (get-file-title file))
-               (set-last-file! file))
-             (cond
-               file
-               (do
-                 (reset! *file-loading? true)
-                 (draw/load-excalidraw-file
-                  file
-                  (fn [data]
-                    (let [{:keys [elements app-state]} (from-json data)]
-                      (reset! *elements elements)
-                      (reset! *file-loading? false)))))
-
-               :else
-               (when-let [elements (get-last-elements)]
-                 ;; TODO: keep this for history undo
-                 (reset! *elements (remove #(gobj/get % "isDeleted") elements))))
-             (assoc state
-                    ::layout (atom [js/window.innerWidth js/window.innerHeight]))))
-   :did-mount set-canvas-actions-style!
-   :did-update set-canvas-actions-style!}
-  [state option]
-  (let [current-repo (state/sub :git/current-repo)
-        elements (rum/react *elements)
-        loading? (rum/react *file-loading?)
-        file (rum/react *current-file)
-        layout (get state ::layout)
-        [width height] (rum/react layout)
-        options (bean/->js {:zenModeEnabled true
-                            :viewBackgroundColor "#FFF"})
-        excalidraw-component @*excalidraw]
-    [:div.draw.white-theme {:style {:background "#FFF"}}
-     (when (and (or (and file elements)
-                    (nil? file))
-                excalidraw-component)
-       (excalidraw-component
-        {:width (get option :width width)
-         :height (get option :height height)
-         :on-resize (fn []
-                      (reset! layout [js/window.innerWidth js/window.innerHeight]))
-
-         :on-change (or (:on-change option)
-                        (fn [elements state]
-                          (when (not= (bean/->clj elements)
-                                      (bean/->clj @*elements))
-                            (reset! *unsaved? true))
-                          (set-last-elements! elements)
-                          (set-last-app-state! state)
-                          (reset! *elements elements)))
-         :options options
-         :user (bean/->js {:name (or (:user-name option)
-                                     (:name (state/get-me))
-                                     (util/unique-id))})
-         :on-username-change (fn [])
-         :initial-data (or elements #js [])}))
-     [:div.absolute.top-4.left-4.hidden.md:block
-      [:div.flex.flex-row.items-center
-       [:a.mr-3.opacity-70.hover:opacity-100 {:href (rfe/href :home)
-                                              :title "Back to logseq"}
-        (svg/logo false)]
-       (files)
-       (when loading?
-         svg/loading)]]
-     (ui/notification)
-
-     (when current-repo
-       [:div.absolute.top-4.right-4.hidden.md:block
-        [:div.flex.flex-row.items-center
-         (repo/sync-status current-repo)
-         (repo/repos-dropdown true
-                              (fn [repo]
-                                (reset! *current-file (get-last-file repo))))]])]))
-
-(rum/defcs draw-2 < rum/reactive
-  {:init (fn [state]
-           (let [repo (storage/get :git/current-repo)]
-
-             (let [current-title (get-last-title repo)]
-               (reset! *current-title current-title))
-             (let [current-file (or
-                                 (get-in (first (:rum/args state))
-                                         [:query-params :file])
-                                 (get-last-file repo))]
-               (reset! *current-file current-file)
-               (reset! *current-title (get-file-title current-file))))
-
-           (if (loaded?)
-             (set-excalidraw-component!)
-             (loader/load
-              (config/asset-uri "/static/js/excalidraw.min.js")
-              (fn []
-                (reset! *loaded? true)
-                (set-excalidraw-component!))))
-
-           (draw/get-all-excalidraw-files
-            (fn [files]
-              (reset! *files (distinct files))))
-
-           (state/set-draw! true)
-           state)
-   :will-unmount (fn [state]
-                   (state/set-draw! false)
-                   state)}
-  [state option]
-  (let [loaded? (or (loaded?)
-                    (rum/react *loaded?))
-        current-repo (state/sub :git/current-repo)
-        component (rum/react *excalidraw)]
-    (if component
-      (let [current-file (rum/react *current-file)
-            current-file (or current-file
-                             (and current-repo
-                                  (get-last-file current-repo)))]
-        (let [key (if current-repo
-                    (str current-repo "-"
-                         (or (and current-file (str "draw-" current-file))
-                             "draw-with-no-file"))
-                    "draw-with-no-file")]
-          (rum/with-key (draw-inner option) key)))
-      [:div.center svg/loading])))
-
-(rum/defc draw < rum/reactive
-  [option]
-  (let [db-restoring? (state/sub :db/restoring?)]
-    (if db-restoring?
-      [:div.ls-center
-       (ui/loading "Loading")]
-      (draw-2 option))))

+ 0 - 23
src/main/frontend/components/draw.css

@@ -1,23 +0,0 @@
-#draw {
-  -webkit-app-region: no-drag;
-  overflow: hidden;
-}
-
-#draw iframe {
-  width: 100%;
-  height: 100%;
-  border: none;
-}
-
-.draw {
-  display: flex;
-  position: fixed;
-  top: 0;
-  bottom: 0;
-  left: 0;
-  right: 0;
-}
-
-.excalidraw-embed .draw {
-  position: relative;
-}

+ 2 - 2
src/main/frontend/components/sidebar.cljs

@@ -131,7 +131,7 @@
            (ui/loading (t :loading))]]
 
          :else
-         [:div {:style {:margin-bottom (if global-graph-pages? 0 120)}}
+         [:div.max-w-7xl.mx-auto {:style {:margin-bottom (if global-graph-pages? 0 120)}}
           main-content])]]
      (right-sidebar/sidebar)]))
 
@@ -176,7 +176,7 @@
         preferred-format (state/sub [:me :preferred_format])
         logged? (:name me)]
     (rum/with-context [[t] i18n/*tongue-context*]
-      [:div.max-w-7xl.mx-auto
+      [:div
        (cond
          (and default-home
               (= :home (state/get-current-route))

+ 4 - 3
src/main/frontend/encrypt.cljs

@@ -14,8 +14,9 @@
 
 (defn content-encrypted?
   [content]
-  (or (str/starts-with? content age-pem-header-line)
-      (str/starts-with? content age-version-line)))
+  (when content
+    (or (str/starts-with? content age-pem-header-line)
+        (str/starts-with? content age-version-line))))
 
 (defn encrypted-db?
   [repo-url]
@@ -100,4 +101,4 @@
           lazy-decrypt-with-user-passphrase (resolve 'frontend.extensions.age-encryption/decrypt-with-user-passphrase)
           content (utf8/encode content)
           decrypted (lazy-decrypt-with-user-passphrase passphrase content)]
-    (utf8/decode decrypted)))
+    (utf8/decode decrypted)))

+ 136 - 0
src/main/frontend/extensions/excalidraw.cljs

@@ -0,0 +1,136 @@
+(ns frontend.extensions.excalidraw
+  (:require [rum.core :as rum]
+            [goog.object :as gobj]
+            [frontend.rum :as r]
+            [frontend.util :as util :refer-macros [profile]]
+            [frontend.mixins :as mixins]
+            [frontend.storage :as storage]
+            [frontend.components.svg :as svg]
+            [cljs-bean.core :as bean]
+            [dommy.core :as d]
+            [clojure.string :as string]
+            [frontend.handler.notification :as notification]
+            [frontend.handler.draw :as draw]
+            [frontend.handler.file :as file]
+            [frontend.handler.ui :as ui-handler]
+            [frontend.ui :as ui]
+            [frontend.loader :as loader]
+            [frontend.config :as config]
+            [frontend.state :as state]
+            [frontend.search :as search]
+            [frontend.components.repo :as repo]
+            [promesa.core :as p]
+            [reitit.frontend.easy :as rfe]
+            ["@excalidraw/excalidraw" :as Excalidraw]))
+
+(def excalidraw (r/adapt-class (gobj/get Excalidraw "default")))
+
+(defn from-json
+  [text]
+  (when-not (string/blank? text)
+    (try
+      (js/JSON.parse text)
+      (catch js/Error e
+        (println "from json error:")
+        (js/console.dir e)
+        (notification/show!
+         (util/format "Could not load this invalid excalidraw file")
+         :error)))))
+
+(defonce *bounding-width (atom nil))
+(defn- get-bounding-width
+  [ref]
+  (when ref
+    (when-let [current (gobj/get ref "current")]
+      (-> current
+         (.getBoundingClientRect)
+         (gobj/get "width")))))
+
+(rum/defcs draw-inner < rum/reactive
+  (rum/local true ::zen-mode?)
+  (rum/local false ::view-mode?)
+  (rum/local nil ::elements)
+  [state data option]
+  (let [current-repo (state/sub :git/current-repo)
+        bounding-width (rum/react *bounding-width)
+        *zen-mode? (get state ::zen-mode?)
+        *view-mode? (get state ::view-mode?)
+        wide-mode? (state/sub :ui/wide-mode?)
+        *elements (get state ::elements)
+        file (:file option)]
+    (when data
+      [:div.overflow-hidden
+       [:div.my-1 {:style {:font-size 10}}
+        [:a.mr-2 {:on-click ui-handler/toggle-wide-mode!}
+         (util/format "Wide Mode (%s)" (if wide-mode? "ON" "OFF"))]
+        [:a.mr-2 {:on-click #(swap! *zen-mode? not)}
+         (util/format "Zen Mode (%s)" (if @*zen-mode? "ON" "OFF"))]
+        [:a.mr-2 {:on-click #(swap! *view-mode? not)}
+         (util/format "View Mode (%s)" (if @*view-mode? "ON" "OFF"))]]
+       [:div
+        (excalidraw
+         (merge
+          {:on-change (fn [elements state]
+                        (let [elements->clj (bean/->clj elements)]
+                          (when (and (seq elements->clj)
+                                     (not= elements @*elements))
+                            (let [state (bean/->clj state)]
+                              (draw/save-excalidraw!
+                               file
+                               (-> {:type "excalidraw"
+                                    :version 2
+                                    :source config/website
+                                    :elements elements
+                                    :appState (select-keys state [:gridSize :viewBackgroundColor])}
+                                   bean/->js
+                                   (js/JSON.stringify)))
+                              (reset! *elements elements)))))
+           :zen-mode-enabled @*zen-mode?
+           :view-mode-enabled @*view-mode?
+           :grid-mode-enabled false
+           :initial-data data}
+          (if wide-mode?
+            {:height 650}
+            {:width 800
+             :height 500})))]])))
+
+(rum/defcs draw-container < rum/reactive
+  {:init (fn [state]
+           (let [[option] (:rum/args state)
+                 file (:file option)
+                 *data (atom nil)
+                 *loading? (atom true)]
+             (when file
+               (draw/load-excalidraw-file
+                file
+                (fn [data]
+                  (let [data (from-json data)]
+                    (reset! *data data)
+                    (reset! *loading? false)))))
+             (assoc state
+                    ::data *data
+                    ::loading? *loading?)))}
+  [state option]
+  (let [*data (get state ::data)
+        *loading? (get state ::loading?)
+        loading? (rum/react *loading?)
+        data (rum/react *data)
+        db-restoring? (state/sub :db/restoring?)]
+    (when (:file option)
+      (cond
+        db-restoring?
+        [:div.ls-center
+         (ui/loading "Loading")]
+
+        (false? loading?)
+        (draw-inner data option)
+
+        :else                           ; loading
+        nil))))
+
+(rum/defc draw < rum/reactive
+  [option]
+  (let [repo (state/get-current-repo)
+        granted? (state/sub [:nfs/user-granted? repo])]
+    (when-not (and (config/local-db? repo) (not granted?))
+      (draw-container option))))

+ 4 - 2
src/main/frontend/fs/nfs.cljs

@@ -133,11 +133,13 @@
                   not-changed? (= last-modified-at local-last-modified-at)
                   format (-> (util/get-file-ext path)
                              (config/get-file-format))
-                  pending-writes (state/get-write-chan-length)]
+                  pending-writes (state/get-write-chan-length)
+                  draw? (and path (string/ends-with? path ".excalidraw"))]
             (if (and local-content (or old-content
                                        ;; temporally fix
-                                       (and path (string/ends-with? path ".excalidraw"))) new?
+                                       draw?) new?
                      (or
+                      draw?
                       ;; Writing not finished
                       (> pending-writes 0)
                       ;; not changed by other editors

+ 10 - 70
src/main/frontend/handler/draw.cljs

@@ -14,37 +14,6 @@
             [cljs-time.core :as t]
             [cljs-time.coerce :as tc]))
 
-;; state
-(defonce *files (atom nil))
-(defonce *current-file (atom nil))
-(defonce *current-title (atom ""))
-(defonce *file-loading? (atom nil))
-(defonce *elements (atom nil))
-(defonce *unsaved? (atom false))
-(defonce *search-files (atom []))
-(defonce *saving-title (atom nil))
-(defonce *excalidraw (atom nil))
-
-;; TODO: refactor
-(defonce draw-state :draw-state)
-
-(defn get-draw-state []
-  (storage/get draw-state))
-(defn set-draw-state! [value]
-  (storage/set draw-state value))
-
-(defn set-k
-  [k v]
-  (when-let [repo (state/get-current-repo)]
-    (let [state (get-draw-state)]
-      (let [new-state (assoc-in state [repo k] v)]
-        (set-draw-state! new-state)))))
-
-(defn set-last-file!
-  [value]
-  (set-k :last-file value))
-
-;; excalidraw
 (defn create-draws-directory!
   [repo]
   (when repo
@@ -55,8 +24,8 @@
        (fn [_error] nil)))))
 
 (defn save-excalidraw!
-  [file data ok-handler]
-  (let [path (str config/default-draw-directory "/" file)
+  [file data]
+  (let [path file
         repo (state/get-current-repo)]
     (when repo
       (let [repo-dir (config/get-repo-dir repo)]
@@ -65,7 +34,6 @@
           (create-draws-directory! repo)
           (fs/write-file! repo repo-dir path data nil)
           (git-handler/git-add repo path)
-          (ok-handler file)
           (db/transact! repo
                         [{:file/path path
                           :page/name file
@@ -75,58 +43,30 @@
                     (prn "Write file failed, path: " path ", data: " data)
                     (js/console.dir error))))))))
 
-(defn get-all-excalidraw-files
-  [ok-handler]
-  (when-let [repo (state/get-current-repo)]
-    (p/let [_ (create-draws-directory! repo)]
-      (let [dir (str (config/get-repo-dir repo)
-                     "/"
-                     config/default-draw-directory)]
-        (util/p-handle
-         (fs/readdir dir)
-         (fn [files]
-           (let [files (-> (filter #(string/ends-with? % ".excalidraw") files)
-                           (distinct)
-                           (sort)
-                           (reverse))]
-             (ok-handler files)))
-         (fn [error]
-           (js/console.dir error)))))))
-
 (defn load-excalidraw-file
   [file ok-handler]
   (when-let [repo (state/get-current-repo)]
     (util/p-handle
-     (file-handler/load-file repo (str config/default-draw-directory "/" file))
+     (file-handler/load-file repo file)
      (fn [content]
        (ok-handler content))
      (fn [error]
-       (prn "Error loading " file ": "
-            error)))))
+       (println "Error loading " file ": "
+                error)))))
 
 (defonce default-content
   (util/format
    "{\n  \"type\": \"excalidraw\",\n  \"version\": 2,\n  \"source\": \"%s\",\n  \"elements\": [],\n  \"appState\": {\n    \"viewBackgroundColor\": \"#FFF\",\n    \"gridSize\": null\n  }\n}"
    config/website))
 
-(defn title->file-name
-  [title]
-  (when (not (string/blank? title))
-    (let [title (string/lower-case (string/replace title " " "-"))]
-      (str (date/get-date-time-string-2) "-" title ".excalidraw"))))
+(defn file-name
+  []
+  (str (date/get-date-time-string-2) ".excalidraw"))
 
 (defn create-draw-with-default-content
-  [current-file ok-handler]
+  [current-file]
   (when-let [repo (state/get-current-repo)]
     (p/let [exists? (fs/file-exists? (config/get-repo-dir repo)
                                      (str config/default-draw-directory current-file))]
       (when-not exists?
-        (save-excalidraw! current-file default-content
-                          (fn [file]
-                            (reset! *files
-                                    (distinct (conj @*files file)))
-                            (reset! *current-file file)
-                            (reset! *unsaved? false)
-                            (set-last-file! file)
-                            (reset! *saving-title nil)
-                            (ok-handler)))))))
+        (save-excalidraw! current-file default-content)))))

+ 0 - 17
src/main/frontend/handler/editor.cljs

@@ -10,7 +10,6 @@
             [frontend.handler.repo :as repo-handler]
             [frontend.handler.file :as file-handler]
             [frontend.handler.notification :as notification]
-            [frontend.handler.draw :as draw]
             [frontend.handler.expand :as expand]
             [frontend.handler.block :as block-handler]
             [frontend.format.mldoc :as mldoc]
@@ -2112,22 +2111,6 @@
                          (get-link format link label)
                          format
                          {:last-pattern (str commands/slash "link")})))
-    :draw
-    (when-not (string/blank? (:title m))
-      (let [file (draw/title->file-name (:title m))
-            value (util/format
-                   "[[%s]]\n<iframe class=\"draw-iframe\" src=\"/#/draw?file=%s\" width=\"100%\" height=\"400\" frameborder=\"0\" allowfullscreen></iframe>"
-                   file
-                   file)]
-        (insert-command! id
-                         value
-                         format
-                         {:last-pattern (str commands/slash "draw ")})
-        (draw/create-draw-with-default-content
-         file
-         (fn []
-           (let [input (gdom/getElement "download")]
-             (.click input))))))
     nil)
 
   (state/set-editor-show-input! nil)

+ 9 - 0
src/main/frontend/handler/ui.cljs

@@ -92,3 +92,12 @@
                     ;; (state/get-custom-css-link)
 )]
     (util/add-style! style)))
+
+(defn toggle-wide-mode!
+  []
+  (let [wide? (state/get-wide-mode?)
+        elements (array-seq (js/document.getElementsByClassName "cp__sidebar-main-content"))
+        max-width (if wide? "var(--ls-main-content-max-width)" "100%")]
+    (when-let [element (first elements)]
+      (dom/set-style! element :max-width max-width))
+    (state/toggle-wide-mode!)))

+ 2 - 0
src/main/frontend/keyboards.cljs

@@ -51,6 +51,8 @@
        (enable-when-not-editing-mode! ui-handler/toggle-contents!)
        (or (shortcut :editor/toggle-settings) "t s")
        (enable-when-not-editing-mode! ui-handler/toggle-settings-modal!)
+       (or (shortcut :ui/toggle-wide-mode) "t w")
+       (enable-when-not-editing-mode! ui-handler/toggle-wide-mode!)
        (or (shortcut :ui/toggle-between-page-and-file) "s")
        (enable-when-not-editing-mode! route-handler/toggle-between-page-and-file!)
        (or (shortcut :git/commit) "c")

+ 0 - 5
src/main/frontend/routes.cljs

@@ -4,7 +4,6 @@
             [frontend.components.file :as file]
             [frontend.components.page :as page]
             [frontend.components.diff :as diff]
-            [frontend.components.draw :as draw]
             [frontend.components.journal :as journal]
             [frontend.components.settings :as settings]
             [frontend.components.external :as external]
@@ -52,10 +51,6 @@
     {:name :diff
      :view diff/diff}]
 
-   ["/draw"
-    {:name :draw
-     :view draw/draw}]
-
    ["/settings"
     {:name :settings
      :view settings/settings}]

+ 16 - 4
src/main/frontend/state.cljs

@@ -54,6 +54,7 @@
     :ui/sidebar-open? false
     :ui/left-sidebar-open? false
     :ui/theme (or (storage/get :ui/theme) "dark")
+    :ui/wide-mode? false
     ;; :show-all, :hide-block-body, :hide-block-children
     :ui/cycle-collapse :show-all
     :ui/collapsed-blocks {}
@@ -899,10 +900,13 @@
   (set-state! :indexeddb/support? value))
 
 (defn set-modal!
-  [modal-panel-content]
-  (swap! state assoc
-         :modal/show? (boolean modal-panel-content)
-         :modal/panel-content modal-panel-content))
+  ([modal-panel-content]
+   (set-modal! modal-panel-content false))
+  ([modal-panel-content fullscreen?]
+   (swap! state assoc
+          :modal/show? (boolean modal-panel-content)
+          :modal/panel-content modal-panel-content
+          :modal/fullscreen? fullscreen?)))
 
 (defn close-modal!
   []
@@ -992,6 +996,14 @@
   []
   (get-in @state [:repo/changed-files (get-current-repo)]))
 
+(defn get-wide-mode?
+  []
+  (:ui/wide-mode? @state))
+
+(defn toggle-wide-mode!
+  []
+  (update-state! :ui/wide-mode? not))
+
 (defn set-online!
   [value]
   (set-state! :network/online? value))

+ 4 - 3
src/main/frontend/ui.cljs

@@ -424,7 +424,7 @@
    [:div.absolute.inset-0.opacity-75]])
 
 (rum/defc modal-panel
-  [panel-content transition-state close-fn]
+  [panel-content transition-state close-fn fullscreen?]
   [:div.ui__modal-panel.transform.transition-all.sm:min-w-lg.sm
    {:class (case transition-state
              "entering" "ease-out duration-300 opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
@@ -444,7 +444,7 @@
         :stroke-linejoin "round"
         :stroke-linecap  "round"}]]]]
 
-   [:div.panel-content
+   [:div {:class (if fullscreen? "" "panel-content")}
     (panel-content close-fn)]])
 
 (rum/defc modal < rum/reactive
@@ -458,6 +458,7 @@
       :outside? false)))
   []
   (let [modal-panel-content (state/sub :modal/panel-content)
+        fullscreen? (state/sub :modal/fullscreen?)
         show? (boolean modal-panel-content)
         close-fn #(state/close-modal!)
         modal-panel-content (or modal-panel-content (fn [close] [:div]))]
@@ -470,7 +471,7 @@
      (css-transition
       {:in show? :timeout 0}
       (fn [state]
-        (modal-panel modal-panel-content state close-fn)))]))
+        (modal-panel modal-panel-content state close-fn fullscreen?)))]))
 
 (defn make-confirm-modal
   [{:keys [tag title sub-title sub-checkbox? on-cancel on-confirm] :as opts}]

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно