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

Merge branch 'master' into fix/wb-fixes

Gabriel Horner 2 жил өмнө
parent
commit
51fe26ff5a
66 өөрчлөгдсөн 910 нэмэгдсэн , 664 устгасан
  1. 1 1
      .github/workflows/build-android.yml
  2. 9 0
      e2e-tests/sanitization.spec.ts
  3. 8 8
      e2e-tests/whiteboards.spec.ts
  4. 1 0
      libs/src/LSPlugin.ts
  5. 1 7
      resources/css/common.css
  6. 10 16
      src/main/frontend/components/block.cljs
  7. 2 1
      src/main/frontend/components/page.cljs
  8. 2 2
      src/main/frontend/components/search.cljs
  9. 3 30
      src/main/frontend/components/search.css
  10. 2 2
      src/main/frontend/components/sidebar.cljs
  11. 1 1
      src/main/frontend/components/theme.cljs
  12. 4 2
      src/main/frontend/dicts.cljc
  13. 48 19
      src/main/frontend/extensions/pdf/assets.cljs
  14. 81 71
      src/main/frontend/extensions/pdf/highlights.cljs
  15. 17 1
      src/main/frontend/extensions/pdf/pdf.css
  16. 12 2
      src/main/frontend/extensions/pdf/toolbar.cljs
  17. 14 0
      src/main/frontend/extensions/pdf/utils.cljs
  18. 29 16
      src/main/frontend/extensions/sci.cljs
  19. 1 1
      src/main/frontend/fs/watcher_handler.cljs
  20. 2 2
      src/main/frontend/handler/plugin.cljs
  21. 1 1
      src/main/frontend/mobile/index.css
  22. 2 1
      src/main/frontend/security.cljs
  23. 13 4
      src/main/frontend/state.cljs
  24. 5 3
      src/main/frontend/ui.cljs
  25. 10 0
      src/main/frontend/util.cljc
  26. 3 1
      src/main/logseq/api.cljs
  27. 3 3
      src/test/frontend/extensions/pdf/assets_test.cljs
  28. 1 1
      templates/config.edn
  29. 17 17
      tldraw/apps/tldraw-logseq/package.json
  30. 8 0
      tldraw/apps/tldraw-logseq/src/components/ActionBar/ActionBar.tsx
  31. 1 0
      tldraw/apps/tldraw-logseq/src/components/ContextBar/contextBarActionFactory.tsx
  32. 56 0
      tldraw/apps/tldraw-logseq/src/components/GeometryTools/GeometryTools.tsx
  33. 1 0
      tldraw/apps/tldraw-logseq/src/components/GeometryTools/index.ts
  34. 23 96
      tldraw/apps/tldraw-logseq/src/components/PrimaryTools/PrimaryTools.tsx
  35. 41 0
      tldraw/apps/tldraw-logseq/src/components/ToolButton/ToolButton.tsx
  36. 1 0
      tldraw/apps/tldraw-logseq/src/components/ToolButton/index.ts
  37. 0 1
      tldraw/apps/tldraw-logseq/src/components/icons/LogseqIcon.tsx
  38. 0 1
      tldraw/apps/tldraw-logseq/src/components/icons/index.ts
  39. 27 21
      tldraw/apps/tldraw-logseq/src/components/inputs/ColorInput.tsx
  40. 6 0
      tldraw/apps/tldraw-logseq/src/hooks/usePaste.ts
  41. 1 1
      tldraw/apps/tldraw-logseq/src/lib/shapes/BoxShape.tsx
  42. 0 2
      tldraw/apps/tldraw-logseq/src/lib/shapes/style-props.tsx
  43. 1 1
      tldraw/apps/tldraw-logseq/src/lib/tools/BoxTool.tsx
  44. 1 1
      tldraw/apps/tldraw-logseq/src/lib/tools/EraseTool.tsx
  45. 1 1
      tldraw/apps/tldraw-logseq/src/lib/tools/HighlighterTool.tsx
  46. 1 1
      tldraw/apps/tldraw-logseq/src/lib/tools/LineTool.tsx
  47. 1 1
      tldraw/apps/tldraw-logseq/src/lib/tools/LogseqPortalTool/LogseqPortalTool.tsx
  48. 2 0
      tldraw/apps/tldraw-logseq/src/lib/tools/LogseqPortalTool/states/CreatingState.tsx
  49. 1 1
      tldraw/apps/tldraw-logseq/src/lib/tools/PencilTool.tsx
  50. 1 1
      tldraw/apps/tldraw-logseq/src/lib/tools/TextTool.tsx
  51. 28 18
      tldraw/apps/tldraw-logseq/src/styles.css
  52. 14 6
      tldraw/demo/package.json
  53. 19 1
      tldraw/demo/vite.config.js
  54. 6 6
      tldraw/packages/core/package.json
  55. 13 0
      tldraw/packages/core/src/lib/TLApi/TLApi.ts
  56. 2 0
      tldraw/packages/core/src/lib/TLSettings.ts
  57. 2 0
      tldraw/packages/core/src/lib/shapes/TLShape/TLShape.tsx
  58. 2 0
      tldraw/packages/core/src/lib/tools/TLBoxTool/states/CreatingState.tsx
  59. 2 0
      tldraw/packages/core/src/lib/tools/TLDrawTool/states/CreatingState.tsx
  60. 2 0
      tldraw/packages/core/src/lib/tools/TLLineTool/states/CreatingState.tsx
  61. 1 1
      tldraw/packages/core/src/lib/tools/TLMoveTool/TLMoveTool.ts
  62. 1 1
      tldraw/packages/core/src/lib/tools/TLSelectTool/TLSelectTool.tsx
  63. 2 0
      tldraw/packages/core/src/lib/tools/TLTextTool/states/CreatingState.tsx
  64. 5 5
      tldraw/packages/react/package.json
  65. 331 281
      tldraw/yarn.lock
  66. 3 3
      yarn.lock

+ 1 - 1
.github/workflows/build-android.yml

@@ -105,7 +105,7 @@ jobs:
 
       - name: Set Build Environment Variables
         run: |
-          echo "ENABLE_FILE_SYNC_PRODUCTION=${{ inputs.enable-file-sync-production == 'true' || github.event.inputs.enable-file-sync-production == 'true' }}" >> $GITHUB_ENV
+          echo "ENABLE_FILE_SYNC_PRODUCTION=${{ inputs.enable-file-sync-production || github.event.inputs.enable-file-sync-production || inputs.build-target == '' }}" >> $GITHUB_ENV
 
       - name: Compile CLJS - android variant, use es6 instead of es-next
         run: yarn install && yarn release-android-app

+ 9 - 0
e2e-tests/sanitization.spec.ts

@@ -37,3 +37,12 @@ test('custom hiccup should not spawn any dialogs', async ({ page, block }) => {
 
   expect(true).toBeTruthy()
 })
+
+test('"is" attribute should be allowed for plugin purposes', async ({ page, block }) => {
+  await createRandomPage(page)
+
+  await page.keyboard.type('[:div {:is "custom-element" :id "custom-element-id"}]', { delay: 5 })
+  await block.enterNext()
+
+  await expect(page.locator('#custom-element-id')).toHaveAttribute('is', 'custom-element');
+})

+ 8 - 8
e2e-tests/whiteboards.spec.ts

@@ -40,11 +40,11 @@ test('can right click title to show context menu', async ({ page }) => {
     await page.click('.whiteboard-page-title', {
         button: 'right',
     })
-  
+
     await expect(page.locator('#custom-context-menu')).toBeVisible()
-  
+
     await page.keyboard.press('Escape')
-  
+
     await expect(page.locator('#custom-context-menu')).toHaveCount(0)
 })
 
@@ -69,7 +69,7 @@ test('set whiteboard title', async ({ page }) => {
 })
 
 test('select rectangle tool', async ({ page }) => {
-    await page.keyboard.press('8')
+    await page.keyboard.press('7')
     await expect(page.locator('.tl-geometry-tools-pane-anchor [title*="Rectangle"]')).toHaveAttribute('data-selected', 'true')
 })
 
@@ -77,7 +77,7 @@ test('draw a rectangle', async ({ page }) => {
     const canvas = await page.waitForSelector('.logseq-tldraw');
     const bounds = (await canvas.boundingBox())!;
 
-    await page.keyboard.press('8')
+    await page.keyboard.press('7')
 
     await page.mouse.move(bounds.x + 5, bounds.y + 5);
     await page.mouse.down();
@@ -112,11 +112,11 @@ test('quick add another whiteboard', async ({ page }) => {
     // create a new board first
     await page.click('.nav-header .whiteboard')
     await page.click('#tl-create-whiteboard')
-    
+
     await page.click('.whiteboard-page-title')
     await page.fill('.whiteboard-page-title input', "my-whiteboard-3")
     await page.keyboard.press('Enter')
-    
+
     const canvas = await page.waitForSelector('.logseq-tldraw');
     await canvas.dblclick({
         position: {
@@ -138,7 +138,7 @@ test('quick add another whiteboard', async ({ page }) => {
 test('go to another board and check reference', async ({ page }) => {
     await page.locator('.tl-logseq-portal-container >> text=my-whiteboard-2').click()
     await expect(page.locator('.whiteboard-page-title .title')).toContainText("my-whiteboard-2");
-  
+
     const pageRefCount$ = page.locator('.whiteboard-page-refs-count')
     await expect(pageRefCount$.locator('.open-page-ref-link')).toContainText('1')
 

+ 1 - 0
libs/src/LSPlugin.ts

@@ -592,6 +592,7 @@ export interface IEditorProxy extends Record<string, any> {
       before: boolean
       sibling: boolean
       isPageBlock: boolean
+      focus: boolean
       customUUID: string
       properties: {}
     }>

+ 1 - 7
resources/css/common.css

@@ -48,12 +48,6 @@ html[data-theme='dark'] {
   --ls-block-properties-background-color: #06323e;
   --ls-page-properties-background-color: #02171d;
   --ls-block-ref-link-text-color: #1a6376;
-  --ls-search-background-color: linear-gradient(
-    to right,
-    #021c23 0,
-    #021b21 200px,
-    #002b36 100%
-  );
   --ls-border-color: #0e5263;
   --ls-secondary-border-color: #126277;
   --ls-tertiary-border-color: rgba(0, 2, 0, 0.10);
@@ -131,7 +125,6 @@ html[data-theme='light'] {
   --ls-block-properties-background-color: #f7f7f7;
   --ls-page-properties-background-color: #f7f7f7;
   --ls-block-ref-link-text-color: #d8e1e8;
-  --ls-search-background-color: var(--ls-primary-background-color);
   --ls-border-color: #ccc;
   --ls-secondary-border-color: #e2e2e2;
   --ls-tertiary-border-color: rgba(200, 200, 200, 0.30);
@@ -151,6 +144,7 @@ html[data-theme='light'] {
   --ls-block-bullet-color: rgba(67, 63, 56, 0.25);
   --ls-block-highlight-color: #c0e6fd;
   --ls-selection-background-color: #e4f2ff;
+  --ls-selection-text-color: var(--ls-secondary-text-color);
   --ls-page-checkbox-color: #9dbbd8;
   --ls-page-checkbox-border-color: var(--ls-page-checkbox-color);
   --ls-page-blockquote-color: var(--ls-primary-text-color);

+ 10 - 16
src/main/frontend/components/block.cljs

@@ -61,6 +61,7 @@
             [frontend.util.drawer :as drawer]
             [frontend.util.property :as property]
             [frontend.util.text :as text-util]
+            [frontend.handler.notification :as notification]
             [goog.dom :as gdom]
             [goog.object :as gobj]
             [lambdaisland.glogi :as log]
@@ -264,14 +265,6 @@
     (when (seq images)
       (lightbox/preview-images! images))))
 
-(defn copy-image-to-clipboard
-  [src]
-  (-> (js/fetch src)
-      (.then (fn [data]
-               (-> (.blob data)
-                   (.then (fn [blob]
-                            (js/navigator.clipboard.write (clj->js [(js/ClipboardItem. (clj->js {(.-type blob) blob}))])))))))))
-
 (defonce *resizing-image? (atom false))
 (rum/defcs resizable-image <
   (rum/local nil ::size)
@@ -353,12 +346,13 @@
             (ui/icon "trash")]
 
            [:button.asset-action-btn
-            {:title (t :asset/copy)
-             :tabIndex "-1"
+            {:title         (t :asset/copy)
+             :tabIndex      "-1"
              :on-mouse-down util/stop
-             :on-click (fn [e]
-                         (util/stop e)
-                         (copy-image-to-clipboard image-src))}
+             :on-click      (fn [e]
+                              (util/stop e)
+                              (-> (util/copy-image-to-clipboard image-src)
+                                  (p/then #(notification/show! "Copied!" :success))))}
             (ui/icon "copy")]
 
            [:button.asset-action-btn
@@ -1045,7 +1039,7 @@
         [:a.asset-ref.is-pdf
          {:on-mouse-down (fn [_event]
                            (when-let [current (pdf-assets/inflate-asset s)]
-                             (state/set-state! :pdf/current current)))}
+                             (state/set-current-pdf! current)))}
          (or label-text
              (->elem :span (map-inline config label)))]
 
@@ -3252,9 +3246,9 @@
              :else
              [:<>
               (lazy-editor/editor config (str (d/squuid)) attr code options)
-              (let [options (:options options)]
+              (let [options (:options options) block (:block config)]
                 (when (and (= language "clojure") (contains? (set options) ":results"))
-                  (sci/eval-result code)))])])))))
+                  (sci/eval-result code block)))])])))))
 
 (defn ^:large-vars/cleanup-todo markup-element-cp
   [{:keys [html-export?] :as config} item]

+ 2 - 1
src/main/frontend/components/page.cljs

@@ -16,6 +16,7 @@
             [frontend.db.model :as model]
             [frontend.extensions.graph :as graph]
             [frontend.extensions.pdf.assets :as pdf-assets]
+            [frontend.extensions.pdf.utils :as pdf-utils]
             [frontend.format.block :as block]
             [frontend.handler.common :as common-handler]
             [frontend.handler.config :as config-handler]
@@ -282,7 +283,7 @@
           whiteboard-page? (model/whiteboard-page? page-name)
           untitled? (and whiteboard-page? (parse-uuid page-name)) ;; normal page cannot be untitled right?
           title (if hls-page?
-                  [:a.asset-ref (pdf-assets/fix-local-asset-pagename title)]
+                  [:a.asset-ref (pdf-utils/fix-local-asset-pagename title)]
                   (if fmt-journal? (date/journal-title->custom-format title) title))
           old-name (or title page-name)]
       [:h1.page-title.flex.cursor-pointer.gap-1.w-full

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

@@ -13,7 +13,7 @@
             [frontend.db.model :as model]
             [frontend.handler.search :as search-handler]
             [frontend.handler.whiteboard :as whiteboard-handler]
-            [frontend.extensions.pdf.assets :as pdf-assets]
+            [frontend.extensions.pdf.utils :as pdf-utils]
             [frontend.ui :as ui]
             [frontend.state :as state]
             [frontend.mixins :as mixins]
@@ -203,7 +203,7 @@
 (defn- search-item-render
   [search-q {:keys [type data alias]}]
   (let [search-mode (state/get-search-mode)
-        data (if (string? data) (pdf-assets/fix-local-asset-pagename data) data)]
+        data (if (string? data) (pdf-utils/fix-local-asset-pagename data) data)]
     [:div {:class "py-2"}
      (case type
        :graph-add-filter

+ 3 - 30
src/main/frontend/components/search.css

@@ -1,6 +1,9 @@
 #search {
   > .inner {
     width: 100%;
+    max-width: 100%;
+
+    border-radius: 4px;
   }
 
   .search-result {
@@ -8,10 +11,6 @@
   }
 }
 
-.search-ac {
-    background-color: var(--ls-primary-background-color);
-}
-
 #search-wrapper svg {
     color: var(--ls-search-icon-color, #9fa6b2);
     opacity: 0.6;
@@ -23,37 +22,11 @@
     opacity: 0.8;
 }
 
-#search-field {
-  background-color: var(--ls-search-background-color, #fff);
-  color: var(--ls-secondary-text-color, #161e2e);
-  transition: background .3s;
-  max-width: 545px;
-  opacity: 0;
-}
-
 #search-wrapper {
     transition: .3s;
     padding-right: 12px;
 }
 
-#search-field:hover,
-#search-field:focus-within {
-    opacity: 1;
-}
-
-#search>.inner {
-    max-width: 100%;
-    border-radius: 4px;
-}
-
-#search-field:focus {
-    background: var(--ls-search-background-color);
-}
-
-.dark-theme #search-field:focus {
-    box-shadow: 0px 0px 20px 0px rgba(18, 18, 18, .3);
-}
-
 .search-item svg {
     transform: scale(0.8);
 }

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

@@ -18,7 +18,7 @@
             [frontend.db :as db]
             [frontend.db-mixins :as db-mixins]
             [frontend.db.model :as db-model]
-            [frontend.extensions.pdf.assets :as pdf-assets]
+            [frontend.extensions.pdf.utils :as pdf-utils]
             [frontend.extensions.srs :as srs]
             [frontend.handler.common :as common-handler]
             [frontend.handler.editor :as editor-handler]
@@ -89,7 +89,7 @@
               (route-handler/redirect-to-whiteboard! name)
               (route-handler/redirect-to-page! name {:click-from-recent? recent?})))))}
      [:span.page-icon (if whiteboard-page? (ui/icon "whiteboard" {:extension? true}) icon)]
-     [:span.page-title (pdf-assets/fix-local-asset-pagename original-name)]]))
+     [:span.page-title (pdf-utils/fix-local-asset-pagename original-name)]]))
 
 (defn get-page-icon [page-entity]
   (let [default-icon (ui/icon "page" {:extension? true})

+ 1 - 1
src/main/frontend/components/theme.cljs

@@ -27,7 +27,7 @@
           (.add cls "dark")
           (.remove cls "dark"))
         (ui/apply-custom-theme-effect! theme)
-        (plugin-handler/hook-plugin-app :theme-mode-changed {:mode theme} nil))
+        (plugin-handler/hook-plugin-app :theme-mode-changed {:mode theme}))
      [theme])
 
     (rum/use-effect!

+ 4 - 2
src/main/frontend/dicts.cljc

@@ -4605,8 +4605,8 @@
         :file-rn/format-deprecated "Şu anda güncel olmayan bir biçim kullanıyorsunuz. En son biçime güncellemeniz kesinlikle önerilir. Lütfen işlemden önce verilerinizi yedekleyin ve Logseq istemcilerini diğer cihazlarda kapatın."
         :file-rn/filename-desc-1 "Bu ayar, bir sayfanın bir dosyaya nasıl saklanacağını yapılandırır. Logseq, aynı ada sahip bir dosyaya bir sayfa depolar."
         :file-rn/filename-desc-2 "\"/\" vaya \"?\" gibi bazı karakterler bir dosya adı için geçersizdir."
-        :file-rn/filename-desc-3 "Logseq, geçersiz karakterleri geçerli kılmak için URL kodlu eşdeğerleriyle değiştirir (ör. \"?\", \"%3F\" olur)."
-        :file-rn/filename-desc-4 "Ad boşluğu ayırıcısı \"/\", estetik değerlendirme için \"___\" (üçlü altçizgi) ile de değiştirilir."
+        :file-rn/filename-desc-3 "Logseq, geçersiz karakterleri URL kodlu eşdeğerleriyle değiştirir (ör. \"?\", \"%3F\" olur)."
+        :file-rn/filename-desc-4 "Ad boşluğu ayırıcısı \"/\", estetik değerlendirme için \"___\" (üçlü altçizgi) ile değiştirilir."
         :file-rn/instruct-1 "Dosya adı biçimini güncellemek 2 adımlı bir işlemdir:"
         :file-rn/instruct-2 "1. Tıklayın "
         :file-rn/instruct-3 "2. Dosyaları yeni biçimde yeniden adlandırmak için aşağıdaki talimatları izleyin:"
@@ -4671,8 +4671,10 @@
         :settings-page/disable-sentry "Kullanım verilerini ve tanılamayı Logseq'e gönderin"
         :settings-page/preferred-outdenting "Mantıksal girinti"
         :settings-page/custom-date-format "Tercih edilen tarih biçimi"
+        :settings-page/custom-date-format-warning "Yeniden dizin oluşturma gerekli! Mevcut günlük referansları bozulabilir!"
         :settings-page/preferred-file-format "Tercih edilen dosya biçimi"
         :settings-page/preferred-workflow "Tercih edilen iş akışı"
+        :settings-page/preferred-pasting-file "Dosya yapıştırmayı tercih et"
         :settings-page/enable-shortcut-tooltip "Kısayol araç ipuçlarını etkinleştir"
         :settings-page/enable-timetracking "Zaman takibi"
         :settings-page/enable-tooltip "Araç ipuçları"

+ 48 - 19
src/main/frontend/extensions/pdf/assets.cljs

@@ -8,6 +8,10 @@
             [frontend.handler.editor :as editor-handler]
             [frontend.handler.page :as page-handler]
             [frontend.handler.assets :as assets-handler]
+            [frontend.handler.notification :as notification]
+            [frontend.ui :as ui]
+            [frontend.context.i18n :refer [t]]
+            [frontend.extensions.lightbox :as lightbox]
             [frontend.util.page-property :as page-property]
             [frontend.state :as state]
             [frontend.util :as util]
@@ -59,11 +63,11 @@
         data))))
 
 (defn persist-hls-data$
-  [{:keys [hls-file]} highlights]
+  [{:keys [hls-file]} highlights extra]
   (when hls-file
     (let [repo-cur (state/get-current-repo)
           repo-dir (config/get-repo-dir repo-cur)
-          data (pr-str {:highlights highlights})]
+          data (pr-str {:highlights highlights :extra extra})]
       (fs/write-file! repo-cur repo-dir hls-file data {:skip-compare? true}))))
 
 (defn resolve-hls-data-by-key$
@@ -226,7 +230,7 @@
             (do
               (state/set-state! :pdf/ref-highlight matched)
               ;; open pdf viewer
-              (state/set-state! :pdf/current (inflate-asset file-path)))
+              (state/set-current-pdf! (inflate-asset file-path)))
             (js/console.debug "[Unmatched highlight ref]" block)))))))
 
 (defn goto-block-ref!
@@ -242,32 +246,57 @@
    (when-let [name (:key current)]
      (rfe/push-state :page {:name (str "hls__" name)} (if id {:anchor (str "block-content-" + id)} nil)))))
 
+(defn open-lightbox
+  [e]
+  (let [images (js/document.querySelectorAll ".hl-area img")
+        images (to-array images)
+        images (if-not (= (count images) 1)
+                 (let [^js image (.closest (.-target e) ".hl-area")
+                       image (. image querySelector "img")]
+                   (->> images
+                        (sort-by (juxt #(.-y %) #(.-x %)))
+                        (split-with (complement #{image}))
+                        reverse
+                        (apply concat)))
+                 images)
+        images (for [^js it images] {:src (.-src it)
+                                     :w (.-naturalWidth it)
+                                     :h (.-naturalHeight it)})]
+
+    (when (seq images)
+      (lightbox/preview-images! images))))
+
 (rum/defc area-display
   [block]
   (when-let [asset-path' (and block (pdf-utils/get-area-block-asset-url
                                      block (db-utils/pull (:db/id (:block/page block)))))]
-    (let [asset-path     (editor-handler/make-asset-url asset-path')]
+    (let [asset-path (editor-handler/make-asset-url asset-path')]
       [:span.hl-area
+       [:span.actions
+        (when-not config/publishing?
+          [:button.asset-action-btn.px-1
+           {:title         (t :asset/copy)
+            :tabIndex      "-1"
+            :on-mouse-down util/stop
+            :on-click      (fn [e]
+                             (util/stop e)
+                             (-> (util/copy-image-to-clipboard (gp-config/remove-asset-protocol asset-path))
+                                 (p/then #(notification/show! "Copied!" :success))))}
+           (ui/icon "copy")])
+
+        [:button.asset-action-btn.px-1
+         {:title         (t :asset/maximize)
+          :tabIndex      "-1"
+          :on-mouse-down util/stop
+          :on-click      open-lightbox}
+
+         (ui/icon "maximize")]]
        [:img {:src asset-path}]])))
 
-(defn fix-local-asset-pagename
-  [filename]
-  (when-not (string/blank? filename)
-    (let [local-asset? (re-find #"[0-9]{13}_\d$" filename)
-          hls?         (re-find #"^hls__" filename)
-          len          (count filename)]
-      (if (or local-asset? hls?)
-        (-> filename
-            (subs 0 (if local-asset? (- len 15) len))
-            (string/replace #"^hls__" "")
-            (string/replace "_" " ")
-            (string/trimr))
-        filename))))
-
 (defn human-page-name
   [page-name]
   (cond
     (string/starts-with? page-name "hls__")
-    (fix-local-asset-pagename page-name)
+    (pdf-utils/fix-local-asset-pagename page-name)
 
     :else (util/trim-safe page-name)))

+ 81 - 71
src/main/frontend/extensions/pdf/highlights.cljs

@@ -12,7 +12,6 @@
             [frontend.commands :as commands]
             [frontend.rum :refer [use-atom]]
             [frontend.state :as state]
-            [frontend.storage :as storage]
             [frontend.util :as util]
             [medley.core :as medley]
             [promesa.core :as p]
@@ -44,14 +43,12 @@
   (rum/use-effect!
    (fn []
      (when viewer
-       (when-let [current (:pdf/current @state/state)]
-         (let [active-hl (:pdf/ref-highlight @state/state)
-               page-key  (:filename current)
-               last-page (and page-key
-                              (util/safe-parse-int (storage/get (str "ls-pdf-last-page-" page-key))))]
-
-           (when (and last-page (nil? active-hl))
-             (set! (.-currentPageNumber viewer) last-page))))))
+       (when-let [_ (:pdf/current @state/state)]
+         (let [active-hl (:pdf/ref-highlight @state/state)]
+           (when-not active-hl
+             (.on (.-eventBus viewer) (name :restore-last-page)
+                  (fn [last-page]
+                    (set! (.-currentPageNumber viewer) (util/safe-parse-int last-page)))))))))
    [viewer])
   nil)
 
@@ -665,7 +662,7 @@
        })]))
 
 (rum/defc pdf-viewer
-  [url initial-hls ^js pdf-document ops]
+  [_url initial-hls initial-page ^js pdf-document ops]
 
   (let [*el-ref (rum/create-ref)
         [state, set-state!] (rum/use-state {:viewer nil :bus nil :link nil :el nil})
@@ -675,33 +672,50 @@
 
     ;; instant pdfjs viewer
     (rum/use-effect!
-     (fn [] (let [^js event-bus    (js/pdfjsViewer.EventBus.)
-                  ^js link-service (js/pdfjsViewer.PDFLinkService. #js {:eventBus event-bus :externalLinkTarget 2})
-                  ^js el           (rum/deref *el-ref)
-                  ^js viewer       (js/pdfjsViewer.PDFViewer.
-                                    #js {:container         el
-                                         :eventBus          event-bus
-                                         :linkService       link-service
-                                         :findController    (js/pdfjsViewer.PDFFindController.
-                                                             #js {:linkService link-service :eventBus event-bus})
-                                         :textLayerMode     2
-                                         :annotationMode    2
-                                         :removePageBorders true})]
-              (. link-service setDocument pdf-document)
-              (. link-service setViewer viewer)
-
-              ;; TODO: debug
-              (set! (. js/window -lsPdfViewer) viewer)
-
-              (p/then (. viewer setDocument pdf-document)
-                      #(set-state! {:viewer viewer :bus event-bus :link link-service :el el}))
-
-              ;;TODO: destroy
-              (fn []
-                (when-let [last-page (.-currentPageNumber viewer)]
-                  (storage/set (str "ls-pdf-last-page-" (util/node-path.basename url)) last-page))
-
-                (when pdf-document (.destroy pdf-document)))))
+     (fn []
+       (let [^js event-bus    (js/pdfjsViewer.EventBus.)
+             ^js link-service (js/pdfjsViewer.PDFLinkService. #js {:eventBus event-bus :externalLinkTarget 2})
+             ^js el           (rum/deref *el-ref)
+             ^js viewer       (js/pdfjsViewer.PDFViewer.
+                               #js {:container         el
+                                    :eventBus          event-bus
+                                    :linkService       link-service
+                                    :findController    (js/pdfjsViewer.PDFFindController.
+                                                        #js {:linkService link-service :eventBus event-bus})
+                                    :textLayerMode     2
+                                    :annotationMode    2
+                                    :removePageBorders true})]
+
+         (. link-service setDocument pdf-document)
+         (. link-service setViewer viewer)
+
+         ;; events
+         (doto event-bus
+           ;; it must be initialized before set-up document
+           (.on "pagesinit"
+                (fn []
+                  (set! (. viewer -currentScaleValue) "auto")
+                  (set-page-ready! true)))
+
+           (.on (name :ls-update-extra-state)
+                #(when-let [extra (bean/->clj %)]
+                   (apply (:set-hls-extra! ops) [extra]))))
+
+         (p/then (. viewer setDocument pdf-document)
+                 #(set-state! {:viewer viewer :bus event-bus :link link-service :el el}))
+
+         ;; TODO: debug
+         (set! (. js/window -lsPdfViewer) viewer)
+
+         ;; set initial page
+         (js/setTimeout
+          #(set! (.-currentPageNumber viewer) initial-page) 16)
+
+         ;; destroy
+         (fn []
+           (.destroy pdf-document)
+           (set! (. js/window -lsPdfViewer) nil)
+           (.cleanup viewer))))
      [])
 
     ;; interaction events
@@ -710,20 +724,13 @@
        (when-let [^js viewer (:viewer state)]
          (let [fn-textlayer-ready
                (fn [^js p]
-                 (set-ano-state! {:loaded-pages (conj (:loaded-pages ano-state) (int (.-pageNumber p)))}))
-
-               fn-page-ready
-               (fn []
-                 (set! (. viewer -currentScaleValue) "auto")
-                 (set-page-ready! true))]
+                 (set-ano-state! {:loaded-pages (conj (:loaded-pages ano-state) (int (.-pageNumber p)))}))]
 
            (doto (.-eventBus viewer)
-             (.on "pagesinit" fn-page-ready)
              (.on "textlayerrendered" fn-textlayer-ready))
 
            #(do
               (doto (.-eventBus viewer)
-                (.off "pagesinit" fn-page-ready)
                 (.off "textlayerrendered" fn-textlayer-ready))))))
 
      [(:viewer state)
@@ -750,23 +757,27 @@
 (rum/defc ^:large-vars/data-var pdf-loader
   [{:keys [url hls-file] :as pdf-current}]
   (let [*doc-ref       (rum/use-ref nil)
-        [state, set-state!] (rum/use-state {:error nil :pdf-document nil :status nil})
-        [hls-state, set-hls-state!] (rum/use-state {:initial-hls nil :latest-hls nil})
-        set-dirty-hls! (fn [latest-hls]                     ;; TODO: incremental
-                         (set-hls-state! {:initial-hls [] :latest-hls latest-hls}))]
+        [loader-state, set-loader-state!] (rum/use-state {:error nil :pdf-document nil :status nil})
+        [hls-state, set-hls-state!] (rum/use-state {:initial-hls nil :latest-hls nil :extra nil :loaded false})
+        [initial-page, set-initial-page!] (rum/use-state 0)
+        set-dirty-hls! (fn [latest-hls]  ;; TODO: incremental
+                         (set-hls-state! #(merge % {:initial-hls [] :latest-hls latest-hls})))
+        set-hls-extra! (fn [extra]
+                         (set-hls-state! #(merge % {:extra extra})))]
 
     ;; load highlights
     (rum/use-effect!
      (fn []
        (p/catch
-        (p/let [data       (pdf-assets/load-hls-data$ pdf-current)
-                highlights (:highlights data)]
-          (set-hls-state! {:initial-hls highlights}))
+        (p/let [data (pdf-assets/load-hls-data$ pdf-current)
+                {:keys [highlights extra]} data]
+          (set-initial-page! (util/safe-parse-int (:page extra)))
+          (set-hls-state! {:initial-hls highlights :latest-hls highlights :extra extra :loaded true}))
 
         ;; error
         (fn [e]
           (js/console.error "[load hls error]" e)
-          (set-hls-state! {:initial-hls []})))
+          (set-hls-state! {:initial-hls [] :loaded true})))
 
        ;; cancel
        #())
@@ -775,15 +786,16 @@
     ;; cache highlights
     (rum/use-effect!
      (fn []
-       (when-let [hls (:latest-hls hls-state)]
+       (when (= :completed (:status loader-state))
          (p/catch
-          (pdf-assets/persist-hls-data$ pdf-current hls)
+          (pdf-assets/persist-hls-data$
+           pdf-current (:latest-hls hls-state) (:extra hls-state))
 
           ;; write hls file error
           (fn [e]
             (js/console.error "[write hls error]" e)))))
 
-     [(:latest-hls hls-state)])
+     [(:latest-hls hls-state) (:extra hls-state)])
 
     ;; load document
     (rum/use-effect!
@@ -795,20 +807,18 @@
                        ;;:cMapUrl       "https://cdn.jsdelivr.net/npm/[email protected]/cmaps/"
                        :cMapPacked    true}]
 
-         (set-state! {:status :loading})
+         (set-loader-state! {:status :loading})
 
          (-> (get-doc$ (clj->js opts))
-             (p/then #(set-state! {:pdf-document %}))
-             (p/catch #(set-state! {:error %}))
-             (p/finally #(set-state! {:status :completed})))
-
+             (p/then #(set-loader-state! {:pdf-document % :status :completed}))
+             (p/catch #(set-loader-state! {:error %})))
          #()))
      [url])
 
     (rum/use-effect!
      (fn []
-       (when-let [error (:error state)]
-         (dd "[ERROR loader]" (:error state))
+       (when-let [error (:error loader-state)]
+         (dd "[ERROR loader]" (:error loader-state))
          (case (.-name error)
            "MissingPDFException"
            (do
@@ -835,24 +845,24 @@
               :error
               false)
              (state/set-state! :pdf/current nil)))))
-     [(:error state)])
+     [(:error loader-state)])
 
     (rum/bind-context
      [*highlights-ctx* hls-state]
      [:div.extensions__pdf-loader {:ref *doc-ref}
-      (let [status-doc  (:status state)
+      (let [status-doc  (:status loader-state)
             initial-hls (:initial-hls hls-state)]
 
-        (if (or (= status-doc :loading)
-                (nil? initial-hls))
+        (if (= status-doc :loading)
 
           [:div.flex.justify-center.items-center.h-screen.text-gray-500.text-lg
            svg/loading]
 
-          [(rum/with-key (pdf-viewer
-                          url initial-hls
-                          (:pdf-document state)
-                          {:set-dirty-hls! set-dirty-hls!}) "pdf-viewer")]))])))
+          (when-let [pdf-document (and (:loaded hls-state) (:pdf-document loader-state))]
+            [(rum/with-key (pdf-viewer
+                            url initial-hls initial-page pdf-document
+                            {:set-dirty-hls! set-dirty-hls!
+                             :set-hls-extra! set-hls-extra!}) "pdf-viewer")])))])))
 
 (rum/defc pdf-container
   [{:keys [identity] :as pdf-current}]

+ 17 - 1
src/main/frontend/extensions/pdf/pdf.css

@@ -101,15 +101,21 @@ input::-webkit-inner-spin-button {
 
         > .nu {
           padding-right: 4px;
-
+          
           input {
             user-select: inherit;
             width: 35px;
             text-align: right;
             padding-right: 4px;
+            padding-left: 2px;
             height: 18px;
             border: none;
             background: transparent;
+            font-size: 15px;
+            
+            &.is-long {
+              font-size: 12px;
+            }
           }
         }
 
@@ -841,6 +847,16 @@ input::-webkit-inner-spin-button {
       overflow: hidden;
       margin-top: 4px;
 
+      .actions {
+        @apply absolute right-1 top-1 flex opacity-0 transition-opacity;
+      }
+
+      &:hover {
+        .actions {
+          @apply opacity-100;
+        }
+      }
+
       img {
         margin: 0;
         box-shadow: none;

+ 12 - 2
src/main/frontend/extensions/pdf/toolbar.cljs

@@ -433,6 +433,14 @@
          #(js-delete (. el -dataset) "theme")))
      [viewer-theme])
 
+    ;; export page state
+    (rum/use-effect!
+     (fn []
+       (when viewer
+         (.dispatch (.-eventBus viewer) (name :ls-update-extra-state)
+                    #js {:page current-page-num})))
+     [viewer current-page-num])
+
     ;; pager hooks
     (rum/use-effect!
      (fn []
@@ -511,14 +519,16 @@
         [:span.nu.flex.items-center.opacity-70
          [:input {:ref            *page-ref
                   :type           "number"
+                  :class          (util/classnames [{:is-long (> (util/safe-parse-int current-page-num) 999)}])
                   :default-value  current-page-num
                   :on-mouse-enter #(.select ^js (.-target %))
                   :on-key-up      (fn [^js e]
                                     (let [^js input (.-target e)
                                           value     (util/safe-parse-int (.-value input))]
+                                      (set-current-page-num! value)
                                       (when (and (= (.-keyCode e) 13) value (> value 0))
-                                        (set! (. viewer -currentPageNumber)
-                                              (if (> value total-page-num) total-page-num value)))))}]
+                                        (->> (if (> value total-page-num) total-page-num value)
+                                             (set! (. viewer -currentPageNumber))))))}]
          [:small "/ " total-page-num]]
 
         [:span.ct.flex.items-center

+ 14 - 0
src/main/frontend/extensions/pdf/utils.cljs

@@ -173,6 +173,20 @@
           (string/replace #"\|#\|([a-zA-Z_])" " $1")
           (string/replace sp "")))))
 
+(defn fix-local-asset-pagename
+  [filename]
+  (when-not (string/blank? filename)
+    (let [local-asset? (re-find #"[0-9]{13}_\d$" filename)
+          hls?         (re-find #"^hls__" filename)
+          len          (count filename)]
+      (if (or local-asset? hls?)
+        (-> filename
+            (subs 0 (if local-asset? (- len 15) len))
+            (string/replace #"^hls__" "")
+            (string/replace "_" " ")
+            (string/trimr))
+        filename))))
+
 ;; TODO: which viewer instance?
 (defn next-page
   []

+ 29 - 16
src/main/frontend/extensions/sci.cljs

@@ -1,6 +1,9 @@
 (ns frontend.extensions.sci
   (:require [sci.core :as sci]
-            [frontend.util :as util]))
+            [frontend.util :as util]
+            [goog.dom]
+            [goog.object]
+            [goog.string]))
 
 ;; Some helpers
 (def sum (partial apply +))
@@ -9,27 +12,37 @@
   (/ (reduce + coll) (count coll)))
 
 (defn eval-string
-  [s]
-  (try
-    (sci/eval-string s {:bindings {'sum sum
-                                   'average average
-                                   'parseFloat js/parseFloat
-                                   'isNaN js/isNaN
-                                   'log js/console.log
-                                   'pprint util/pp-str}})
-    (catch :default e
-      (println "Query: sci eval failed:")
-      (js/console.error e))))
+  "Second arg is a map of options for sci/eval-string"
+  ([s]
+   (eval-string s {}))
+  ([s options]
+   (try
+     (sci/eval-string s (merge-with merge
+                                    {:bindings {'sum sum
+                                                'average average
+                                                'parseFloat js/parseFloat
+                                                'isNaN js/isNaN
+                                                'log js/console.log
+                                                'pprint util/pp-str}}
+                                    options))
+     (catch :default e
+       (println "Query: sci eval failed:")
+       (js/console.error e)))))
 
 (defn call-fn
   [f & args]
   (apply f args))
 
 (defn eval-result
-  [code]
+  "Evaluate code with sci in a block context"
+  [code block]
   [:div
    [:code "Results:"]
    [:div.results.mt-1
-    [:pre.code
-     (let [result (eval-string code)]
-       (str result))]]])
+    (let [result (eval-string code {:bindings {'block block}
+                                    :classes {'logseq-api js/logseq.api
+                                              'logseq-gp js/logseq.graph_parser
+                                              :allow :all}})]
+      (if (and (vector? result) (:hiccup (meta result)))
+        result
+        [:pre.code (str result)]))]])

+ 1 - 1
src/main/frontend/fs/watcher_handler.cljs

@@ -88,7 +88,7 @@
                          (string/trim (or (state/get-default-journal-template) "")))
                       (= (string/trim content) "-")
                       (= (string/trim content) "*")))
-            (handle-add-and-change! repo path content db-content mtime true))
+            (handle-add-and-change! repo path content db-content mtime (not global-dir))) ;; no backup for global dir
 
           (and (= "unlink" type)
                (db/file-exists? repo path))

+ 2 - 2
src/main/frontend/handler/plugin.cljs

@@ -632,8 +632,8 @@
                                                           (when mode
                                                             (state/set-custom-theme! mode theme)
                                                             (state/set-theme-mode! mode))
-                                                          (hook-plugin-app :theme-changed theme)
-                                                          (state/set-state! :plugin/selected-theme url))))
+                                                          (state/set-state! :plugin/selected-theme url)
+                                                          (hook-plugin-app :theme-changed theme))))
 
                                 (.on "reset-custom-theme" (fn [^js themes]
                                                             (let [themes       (bean/->clj themes)

+ 1 - 1
src/main/frontend/mobile/index.css

@@ -231,7 +231,7 @@ html.is-zoomed-native-ios {
 
     .cp__graph-picker {
       padding: 58px 20px 20px 20px;
-      background: var(--ls-search-background-color);
+      background: var(--ls-primary-background-color);
 
       > h1 {
         position: absolute;

+ 2 - 1
src/main/frontend/security.cljs

@@ -3,7 +3,8 @@
   (:require ["dompurify" :as DOMPurify]))
 
 (def sanitization-options (clj->js {:ADD_TAGS ["iframe"]
-                                    :ALLOW_UNKNOWN_PROTOCOLS true}))
+                                    :ADD_ATTR ["is"]
+                                    :ALLOW_UNKNOWN_PROTOCOLS true }))
 
 (defn sanitize-html
   [html]

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

@@ -296,10 +296,6 @@
 ;;  (re-)fetches get-current-repo needlessly
 ;; TODO: Add consistent validation. Only a few config options validate at get time
 
-(defn get-current-pdf
-  []
-  (:pdf/current @state))
-
 (def default-config
   "Default config for a repo-specific, user config"
   {:feature/enable-search-remove-accents? true
@@ -1967,3 +1963,16 @@ Similar to re-frame subscriptions"
   []
   (when (mobile-util/native-ios?)
     (get-in @state [:mobile/container-urls :iCloudContainerUrl])))
+
+(defn get-current-pdf
+  []
+  (:pdf/current @state))
+
+(defn set-current-pdf!
+  [inflated-file]
+  (let [settle-file! #(set-state! :pdf/current inflated-file)]
+    (if-not (get-current-pdf)
+      (settle-file!)
+      (when (apply not= (map :identity [inflated-file (get-current-pdf)]))
+        (set-state! :pdf/current nil)
+        (js/setTimeout #(settle-file!) 16)))))

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

@@ -366,10 +366,12 @@
 (defn apply-custom-theme-effect! [theme]
   (when config/lsp-enabled?
     (when-let [custom-theme (state/sub [:ui/custom-theme (keyword theme)])]
-      (when-let [url (:url custom-theme)]
+      ;; If the name is nil, the user has not set a custom theme (initially {:mode light/dark}).
+      ;; The url is not used because the default theme does not have an url.
+      (if (some? (:name custom-theme))
         (js/LSPluginCore.selectTheme (bean/->js custom-theme)
-                                     (bean/->js {:emit true}))
-        (state/set-state! :plugin/selected-theme url)))))
+                                     (bean/->js {:emit false}))
+        (state/set-state! :plugin/selected-theme (:url custom-theme))))))
 
 (defn setup-system-theme-effect!
   []

+ 10 - 0
src/main/frontend/util.cljc

@@ -1419,3 +1419,13 @@
               (<= (+ (.-bottom r) 64)
                   (or (.-innerHeight js/window)
                       (js/document.documentElement.clientHeight))))))))
+
+#?(:cljs
+   (defn copy-image-to-clipboard
+     [src]
+     (-> (js/fetch src)
+         (.then (fn [data]
+                  (-> (.blob data)
+                      (.then (fn [blob]
+                               (js/navigator.clipboard.write (clj->js [(js/ClipboardItem. (clj->js {(.-type blob) blob}))]))))
+                      (.catch js/console.error)))))))

+ 3 - 1
src/main/logseq/api.cljs

@@ -525,9 +525,10 @@
 
 (def ^:export insert_block
   (fn [block-uuid-or-page-name content ^js opts]
-    (let [{:keys [before sibling isPageBlock customUUID properties]} (bean/->clj opts)
+    (let [{:keys [before sibling focus isPageBlock customUUID properties]} (bean/->clj opts)
           page-name (and isPageBlock block-uuid-or-page-name)
           custom-uuid (or customUUID (:id properties))
+          edit-block? (if (nil? focus) true focus)
           _ (when (not (string/blank? custom-uuid))
               (when-not (util/uuid-string? custom-uuid)
                 (throw (js/Error.
@@ -557,6 +558,7 @@
                       {:block-uuid  block-uuid'
                        :sibling?    sibling?
                        :before?     before?
+                       :edit-block? edit-block?
                        :page        page-name
                        :custom-uuid custom-uuid
                        :properties  (merge properties

+ 3 - 3
src/test/frontend/extensions/pdf/assets_test.cljs

@@ -1,15 +1,15 @@
 (ns frontend.extensions.pdf.assets-test
   (:require [clojure.test :as test :refer [are deftest testing]]
-            [frontend.extensions.pdf.assets :as assets]))
+            [frontend.extensions.pdf.utils :as pdf-utils]))
 
 (deftest fix-local-asset-pagename
   (testing "matched filenames"
-    (are [x y] (= y (assets/fix-local-asset-pagename x))
+    (are [x y] (= y (pdf-utils/fix-local-asset-pagename x))
       "2015_Book_Intertwingled_1659920114630_0" "2015 Book Intertwingled"
       "hls__2015_Book_Intertwingled_1659920114630_0" "2015 Book Intertwingled"
       "hls/2015_Book_Intertwingled_1659920114630_0" "hls/2015 Book Intertwingled"))
   (testing "non matched filenames"
-    (are [x y] (= y (assets/fix-local-asset-pagename x))
+    (are [x y] (= y (pdf-utils/fix-local-asset-pagename x))
       "foo" "foo"
       "foo_bar" "foo_bar"
       "foo__bar" "foo__bar"

+ 1 - 1
templates/config.edn

@@ -91,7 +91,7 @@
  ;; Setup custom shortcuts under `:shortcuts` key
  ;; Syntax:
  ;; 1. `+` means keys pressing simultaneously. eg: `ctrl+shift+a`
- ;; 2. ` ` empty space between keys represents key chords. eg: `t s` means press `s` follow by `t`
+ ;; 2. ` ` empty space between keys represents key chords. eg: `t s` means press `t` followed by `s`
  ;; 3. `mod` means `Ctrl` for Windows/Linux  and `Command` for Mac
  ;; 4. use `false` to disable particular shortcut
  ;; 4. you can define multiple bindings for one action, eg `["ctrl+j" "down"]`

+ 17 - 17
tldraw/apps/tldraw-logseq/package.json

@@ -10,37 +10,37 @@
     "dev:vite": "tsup --watch --sourcemap inline"
   },
   "devDependencies": {
-    "@radix-ui/react-context-menu": "^1.0.0",
-    "@radix-ui/react-dropdown-menu": "^1.0.0",
+    "@radix-ui/react-context-menu": "^2.0.1",
+    "@radix-ui/react-dropdown-menu": "^2.0.1",
     "@radix-ui/react-popover": "^1.0.0",
-    "@radix-ui/react-select": "^1.0.0",
-    "@radix-ui/react-separator": "^1.0.0",
+    "@radix-ui/react-select": "^1.1.2",
+    "@radix-ui/react-separator": "^1.0.1",
     "@radix-ui/react-slider": "^1.1.0",
-    "@radix-ui/react-switch": "^1.0.0",
-    "@radix-ui/react-toggle": "^1.0.0",
-    "@radix-ui/react-toggle-group": "^1.0.0",
+    "@radix-ui/react-switch": "^1.0.1",
+    "@radix-ui/react-toggle": "^1.0.1",
+    "@radix-ui/react-toggle-group": "^1.0.1",
     "@tldraw/core": "2.0.0-alpha.1",
     "@tldraw/react": "2.0.0-alpha.1",
     "@tldraw/vec": "2.0.0-alpha.1",
     "@types/node": "^17.0.42",
     "@types/react": "^17.0.0",
     "@types/react-dom": "^17.0.0",
-    "autoprefixer": "^10.4.7",
-    "concurrently": "^7.2.1",
-    "esbuild": "^0.15.10",
-    "mobx": "^6.6.2",
+    "autoprefixer": "^10.4.13",
+    "concurrently": "^7.5.0",
+    "esbuild": "^0.15.14",
+    "mobx": "^6.7.0",
     "mobx-react-lite": "^3.4.0",
     "perfect-freehand": "^1.2.0",
     "polished": "^4.0.0",
-    "postcss": "^8.4.17",
+    "postcss": "^8.4.19",
     "react": "^17.0.0",
     "react-dom": "^17.0.0",
-    "react-virtuoso": "^3.1.0",
+    "react-virtuoso": "^3.1.3",
     "rimraf": "3.0.2",
-    "shadow-cljs": "^2.19.5",
-    "tsup": "^6.2.3",
-    "typescript": "^4.8.2",
-    "zx": "^7.1.0"
+    "shadow-cljs": "^2.20.11",
+    "tsup": "^6.5.0",
+    "typescript": "^4.9.3",
+    "zx": "^7.1.1"
   },
   "peerDependencies": {
     "react": "^16.8.0 || ^17.0.0 || ^18.0.0",

+ 8 - 0
tldraw/apps/tldraw-logseq/src/components/ActionBar/ActionBar.tsx

@@ -6,6 +6,7 @@ import * as React from 'react'
 import type { Shape } from '../../lib'
 import { TablerIcon } from '../icons'
 import { Button } from '../Button'
+import { ToolButton } from '../ToolButton'
 import { ZoomMenu } from '../ZoomMenu'
 import * as Separator from '@radix-ui/react-separator'
 
@@ -30,6 +31,13 @@ export const ActionBar = observer(function ActionBar(): JSX.Element {
   return (
     <div className="tl-action-bar">
       <div className="tl-toolbar tl-history-bar">
+        <ToolButton title="Select" id="select" icon="select-cursor" />
+        <ToolButton
+          title="Move"
+          id="move"
+          icon={app.isIn('move.panning') ? 'hand-grab' : 'hand-stop'}
+        />
+        <Separator.Root className="tl-toolbar-separator" orientation="vertical" />
         <Button title="Undo" onClick={undo}>
           <TablerIcon name="arrow-back-up" />
         </Button>

+ 1 - 0
tldraw/apps/tldraw-logseq/src/components/ContextBar/contextBarActionFactory.tsx

@@ -356,6 +356,7 @@ const SwatchAction = observer(() => {
   return (
     <ColorInput
       title="Color Picker"
+      popoverSide="top"
       color={color}
       opacity={shapes[0].props.opacity}
       collisionRef={document.getElementById('main-content-container')}

+ 56 - 0
tldraw/apps/tldraw-logseq/src/components/GeometryTools/GeometryTools.tsx

@@ -0,0 +1,56 @@
+import { useApp } from '@tldraw/react'
+import { observer } from 'mobx-react-lite'
+import * as React from 'react'
+import { ToolButton } from '../ToolButton'
+import * as Popover from '@radix-ui/react-popover'
+import { TablerIcon } from '../icons'
+
+export const GeometryTools = observer(function GeometryTools() {
+  const geometries = [
+    {
+      id: 'box',
+      icon: 'square',
+      title: 'Rectangle',
+    },
+    {
+      id: 'ellipse',
+      icon: 'circle',
+      title: 'Circle',
+    },
+    {
+      id: 'polygon',
+      icon: 'triangle',
+      title: 'Triangle',
+    },
+  ]
+
+  const app = useApp()
+  const [activeGeomId, setActiveGeomId] = React.useState(
+    () => (geometries.find(geo => geo.id === app.selectedTool.id) ?? geometries[0]).id
+  )
+
+  React.useEffect(() => {
+    setActiveGeomId(prevId => {
+      return geometries.find(geo => geo.id === app.selectedTool.id)?.id ?? prevId
+    })
+  }, [app.selectedTool.id])
+
+  return (
+    <Popover.Root>
+      <Popover.Trigger className="tl-geometry-tools-pane-anchor">
+        <ToolButton {...geometries.find(geo => geo.id === activeGeomId)!} />
+        <TablerIcon className="tl-popover-indicator" name="chevron-down-left" />
+      </Popover.Trigger>
+
+      <Popover.Content className="tl-popover-content" side="left" sideOffset={15}>
+        <div className="tl-toolbar tl-geometry-toolbar">
+          {geometries.map(props => (
+            <ToolButton key={props.id} {...props} />
+          ))}
+        </div>
+
+        <Popover.Arrow className="tl-popover-arrow" />
+      </Popover.Content>
+    </Popover.Root>
+  )
+})

+ 1 - 0
tldraw/apps/tldraw-logseq/src/components/GeometryTools/index.ts

@@ -0,0 +1 @@
+export * from './GeometryTools'

+ 23 - 96
tldraw/apps/tldraw-logseq/src/components/PrimaryTools/PrimaryTools.tsx

@@ -1,114 +1,41 @@
-import { TLMoveTool, TLSelectTool } from '@tldraw/core'
 import { useApp } from '@tldraw/react'
 import { observer } from 'mobx-react-lite'
 import * as React from 'react'
-import { Button } from '../Button'
-import { TablerIcon, LogseqIcon } from '../icons'
-
-interface ToolButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
-  id: string
-  icon: string | React.ReactNode
-}
-
-const ToolButton = observer(({ id, icon, title, ...props }: ToolButtonProps) => {
-  const app = useApp()
-
-  const handleToolClick = React.useCallback(
-    (e: React.MouseEvent<HTMLButtonElement>) => {
-      const tool = e.currentTarget.dataset.tool
-      if (tool) app.selectTool(tool)
-    },
-    [app]
-  )
-
-  // Tool must exist
-  const Tool = [...app.Tools, TLSelectTool, TLMoveTool]?.find(T => T.id === id)
-
-  const shortcut = ((Tool as any)['shortcut'] as string[])?.[0]
-
-  const titleWithShortcut = shortcut ? `${title} (${shortcut})` : title
-  return (
-    <Button
-      {...props}
-      title={titleWithShortcut}
-      data-tool={id}
-      data-selected={id === app.selectedTool.id}
-      onClick={handleToolClick}
-    >
-      {typeof icon === 'string' ? <TablerIcon name={icon} /> : icon}
-    </Button>
-  )
-})
-
-const GeometryToolButtons = observer(() => {
-  const geometries = [
-    {
-      id: 'box',
-      icon: 'square',
-      title: 'Rectangle',
-    },
-    {
-      id: 'ellipse',
-      icon: 'circle',
-      title: 'Circle',
-    },
-    {
-      id: 'polygon',
-      icon: 'triangle',
-      title: 'Triangle',
-    },
-  ]
-
-  const app = useApp()
-  const [activeGeomId, setActiveGeomId] = React.useState(
-    () => (geometries.find(geo => geo.id === app.selectedTool.id) ?? geometries[0]).id
-  )
-
-  const [paneActive, setPaneActive] = React.useState(false)
-
-  React.useEffect(() => {
-    setActiveGeomId(prevId => {
-      return geometries.find(geo => geo.id === app.selectedTool.id)?.id ?? prevId
-    })
-  }, [app.selectedTool.id])
-
-  return (
-    <div
-      className="tl-geometry-tools-pane-anchor"
-      onMouseEnter={() => setPaneActive(true)}
-      onMouseLeave={() => setPaneActive(false)}
-    >
-      {<ToolButton {...geometries.find(geo => geo.id === activeGeomId)!} />}
-      {paneActive && (
-        <div className="tl-geometry-tools-pane">
-          {geometries.map(props => (
-            <ToolButton key={props.id} {...props} />
-          ))}
-        </div>
-      )}
-    </div>
-  )
-})
+import { ToolButton } from '../ToolButton'
+import { GeometryTools } from '../GeometryTools'
+import { ColorInput } from '../inputs/ColorInput'
+import * as Separator from '@radix-ui/react-separator'
 
 export const PrimaryTools = observer(function PrimaryTools() {
   const app = useApp()
 
+  const handleSetColor = React.useCallback((color: string) => {
+    app.api.setColor(color)
+  }, [])
+
   return (
     <div className="tl-primary-tools">
       <div className="tl-toolbar tl-tools-floating-panel">
-        <ToolButton title="Select" id="select" icon="select-cursor" />
-        <ToolButton
-          title="Move"
-          id="move"
-          icon={app.isIn('move.panning') ? 'hand-grab' : 'hand-stop'}
-        />
+        <ToolButton title="Add block or page" id="logseq-portal" icon="circle-plus" />
+        <Separator.Root className="tl-toolbar-separator" orientation="horizontal" />
         <ToolButton title="Draw" id="pencil" icon="ballpen" />
         <ToolButton title="Highlight" id="highlighter" icon="highlight" />
         <ToolButton title="Eraser" id="erase" icon="eraser" />
         <ToolButton title="Connector" id="line" icon="connector" />
         <ToolButton title="Text" id="text" icon="text" />
-        <GeometryToolButtons />
-        <ToolButton title="Logseq Portal" id="logseq-portal" icon={<LogseqIcon />} />
+        <GeometryTools />
+        <Separator.Root
+          className="tl-toolbar-separator"
+          orientation="horizontal"
+          style={{ margin: '0 -4px' }}
+        />
+        <ColorInput
+          title="Color Picker"
+          popoverSide="left"
+          color={app.settings.color}
+          collisionRef={document.getElementById('main-content-container')}
+          setColor={handleSetColor}
+        />
       </div>
     </div>
   )

+ 41 - 0
tldraw/apps/tldraw-logseq/src/components/ToolButton/ToolButton.tsx

@@ -0,0 +1,41 @@
+import { TLMoveTool, TLSelectTool } from '@tldraw/core'
+import { useApp } from '@tldraw/react'
+import { observer } from 'mobx-react-lite'
+import * as React from 'react'
+import { Button } from '../Button'
+import { TablerIcon } from '../icons'
+
+export interface ToolButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
+  id: string
+  icon: string | React.ReactNode
+}
+
+export const ToolButton = observer(({ id, icon, title, ...props }: ToolButtonProps) => {
+  const app = useApp()
+
+  const handleToolClick = React.useCallback(
+    (e: React.MouseEvent<HTMLButtonElement>) => {
+      const tool = e.currentTarget.dataset.tool
+      if (tool) app.selectTool(tool)
+    },
+    [app]
+  )
+
+  // Tool must exist
+  const Tool = [...app.Tools, TLSelectTool, TLMoveTool]?.find(T => T.id === id)
+
+  const shortcut = ((Tool as any)['shortcut'] as string[])?.[0]
+
+  const titleWithShortcut = shortcut ? `${title} (${shortcut})` : title
+  return (
+    <Button
+      {...props}
+      title={titleWithShortcut}
+      data-tool={id}
+      data-selected={id === app.selectedTool.id}
+      onClick={handleToolClick}
+    >
+      {typeof icon === 'string' ? <TablerIcon name={icon} /> : icon}
+    </Button>
+  )
+})

+ 1 - 0
tldraw/apps/tldraw-logseq/src/components/ToolButton/index.ts

@@ -0,0 +1 @@
+export * from './ToolButton'

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 1
tldraw/apps/tldraw-logseq/src/components/icons/LogseqIcon.tsx


+ 0 - 1
tldraw/apps/tldraw-logseq/src/components/icons/index.ts

@@ -1,2 +1 @@
-export * from './LogseqIcon'
 export * from './TablerIcon'

+ 27 - 21
tldraw/apps/tldraw-logseq/src/components/inputs/ColorInput.tsx

@@ -1,20 +1,24 @@
 import * as React from 'react'
 import * as Popover from '@radix-ui/react-popover'
+import type { Side } from '@radix-ui/react-popper'
 import * as Slider from '@radix-ui/react-slider'
 import { TablerIcon } from '../icons'
 import { Color } from '@tldraw/core'
+
 interface ColorInputProps extends React.InputHTMLAttributes<HTMLButtonElement> {
-  color: string
-  opacity: number
+  color?: string
+  opacity?: number
   collisionRef: HTMLElement | null
+  popoverSide: Side
   setColor: (value: string) => void
-  setOpacity: (value: number) => void
+  setOpacity?: (value: number) => void
 }
 
 export function ColorInput({
   color,
   opacity,
   collisionRef,
+  popoverSide,
   setColor,
   setOpacity,
   ...rest
@@ -35,11 +39,11 @@ export function ColorInput({
 
   return (
     <Popover.Root>
-      <Popover.Trigger className="tl-color-drip mx-1">{renderColor(color)}</Popover.Trigger>
+      <Popover.Trigger className="tl-color-drip">{renderColor(color)}</Popover.Trigger>
 
       <Popover.Content
-        className="tl-popover-content"
-        side="top"
+        className="tl-popover-content p-1"
+        side={popoverSide}
         sideOffset={15}
         collisionBoundary={collisionRef}
       >
@@ -55,21 +59,23 @@ export function ColorInput({
           ))}
         </div>
 
-        <div className="mx-1 my-2">
-          <Slider.Root
-            defaultValue={[opacity]}
-            onValueCommit={value => setOpacity(value[0])}
-            max={1}
-            step={0.1}
-            aria-label="Opacity"
-            className="tl-slider-root"
-          >
-            <Slider.Track className="tl-slider-track">
-              <Slider.Range className="tl-slider-range" />
-            </Slider.Track>
-            <Slider.Thumb className="tl-slider-thumb" />
-          </Slider.Root>
-        </div>
+        {setOpacity && (
+          <div className="mx-1 my-2">
+            <Slider.Root
+              defaultValue={[opacity]}
+              onValueCommit={value => setOpacity(value[0])}
+              max={1}
+              step={0.1}
+              aria-label="Opacity"
+              className="tl-slider-root"
+            >
+              <Slider.Track className="tl-slider-track">
+                <Slider.Range className="tl-slider-range" />
+              </Slider.Track>
+              <Slider.Thumb className="tl-slider-thumb" />
+            </Slider.Root>
+          </div>
+        )}
 
         <Popover.Arrow className="tl-popover-arrow" />
       </Popover.Content>

+ 6 - 0
tldraw/apps/tldraw-logseq/src/hooks/usePaste.ts

@@ -310,6 +310,8 @@ export function usePaste() {
                 point: [point[0], point[1]],
                 size: [400, 0], // use 0 here to enable auto-resize
                 pageId: blockRef,
+                fill: app.settings.color,
+                stroke: app.settings.color,
                 blockType: 'B' as 'B',
               },
             ]
@@ -324,6 +326,8 @@ export function usePaste() {
               point: [point[0], point[1]],
               size: [400, 0], // use 0 here to enable auto-resize
               pageId: pageName,
+              fill: app.settings.color,
+              stroke: app.settings.color,
               blockType: 'P' as 'P',
             },
           ]
@@ -339,6 +343,8 @@ export function usePaste() {
               size: [400, 0], // use 0 here to enable auto-resize
               point: [point[0], point[1]],
               pageId: uuid,
+              fill: app.settings.color,
+              stroke: app.settings.color,
               blockType: 'B' as 'B',
               compact: true,
             },

+ 1 - 1
tldraw/apps/tldraw-logseq/src/lib/shapes/BoxShape.tsx

@@ -1,5 +1,5 @@
 /* eslint-disable @typescript-eslint/no-explicit-any */
-import { SVGContainer, TLComponentProps } from '@tldraw/react'
+import { SVGContainer, TLComponentProps, useApp } from '@tldraw/react'
 import { TLBoxShape, TLBoxShapeProps, getComputedColor } from '@tldraw/core'
 import { observer } from 'mobx-react-lite'
 import { CustomStyleProps, withClampedStyles } from './style-props'

+ 0 - 2
tldraw/apps/tldraw-logseq/src/lib/shapes/style-props.tsx

@@ -1,8 +1,6 @@
 import type { Shape } from '.'
 
 export interface CustomStyleProps {
-  stroke: string
-  fill: string
   noFill: boolean
   strokeWidth: number
   strokeType: 'dashed' | 'line'

+ 1 - 1
tldraw/apps/tldraw-logseq/src/lib/tools/BoxTool.tsx

@@ -4,6 +4,6 @@ import { BoxShape, type Shape } from '../shapes'
 
 export class BoxTool extends TLBoxTool<BoxShape, Shape, TLReactEventMap> {
   static id = 'box'
-  static shortcut = ['8']
+  static shortcut = ['7']
   Shape = BoxShape
 }

+ 1 - 1
tldraw/apps/tldraw-logseq/src/lib/tools/EraseTool.tsx

@@ -4,5 +4,5 @@ import type { Shape } from '../shapes'
 
 export class NuEraseTool extends TLEraseTool<Shape, TLReactEventMap> {
   static id = 'erase'
-  static shortcut = ['5']
+  static shortcut = ['4']
 }

+ 1 - 1
tldraw/apps/tldraw-logseq/src/lib/tools/HighlighterTool.tsx

@@ -4,7 +4,7 @@ import { HighlighterShape, type Shape } from '../shapes'
 
 export class HighlighterTool extends TLDrawTool<HighlighterShape, Shape, TLReactEventMap> {
   static id = 'highlighter'
-  static shortcut = ['4']
+  static shortcut = ['3']
   Shape = HighlighterShape
   simplify = true
   simplifyTolerance = 0.618

+ 1 - 1
tldraw/apps/tldraw-logseq/src/lib/tools/LineTool.tsx

@@ -6,6 +6,6 @@ import { LineShape, type Shape } from '../shapes'
 export class LineTool extends TLLineTool<LineShape, Shape, TLReactEventMap> {
   static id = 'line'
   // not sure why "c" is not working in Logseq?
-  static shortcut = ['6']
+  static shortcut = ['5']
   Shape = LineShape
 }

+ 1 - 1
tldraw/apps/tldraw-logseq/src/lib/tools/LogseqPortalTool/LogseqPortalTool.tsx

@@ -9,7 +9,7 @@ export class LogseqPortalTool extends TLTool<
   TLApp<Shape, TLReactEventMap>
 > {
   static id = 'logseq-portal'
-  static shortcut = ['9']
+  static shortcut = ['1']
   static states = [IdleState, CreatingState]
   static initial = 'idle'
 

+ 2 - 0
tldraw/apps/tldraw-logseq/src/lib/tools/LogseqPortalTool/states/CreatingState.tsx

@@ -25,6 +25,8 @@ export class CreatingState extends TLToolState<
         parentId: this.app.currentPage.id,
         point: Vec.sub(this.app.inputs.originPoint, this.offset),
         size: LogseqPortalShape.defaultProps.size,
+        fill: this.app.settings.color,
+        stroke: this.app.settings.color,
       } as any)
       this.creatingShape = shape
       this.app.currentPage.addShapes(shape)

+ 1 - 1
tldraw/apps/tldraw-logseq/src/lib/tools/PencilTool.tsx

@@ -4,7 +4,7 @@ import { PencilShape, type Shape } from '../shapes'
 
 export class PencilTool extends TLDrawTool<PencilShape, Shape, TLReactEventMap> {
   static id = 'pencil'
-  static shortcut = ['3']
+  static shortcut = ['2']
   Shape = PencilShape
   simplify = false
 }

+ 1 - 1
tldraw/apps/tldraw-logseq/src/lib/tools/TextTool.tsx

@@ -4,6 +4,6 @@ import { TextShape, type Shape } from '../shapes'
 
 export class TextTool extends TLTextTool<TextShape, Shape, TLReactEventMap> {
   static id = 'text'
-  static shortcut = ['7']
+  static shortcut = ['6']
   Shape = TextShape
 }

+ 28 - 18
tldraw/apps/tldraw-logseq/src/styles.css

@@ -17,8 +17,8 @@
   --color-text: var(--ls-primary-text-color);
   --color-text-inverted: var(--ls-tertiary-background-color);
   --color-hover: var(--ls-secondary-background-color);
-  --color-selectedStroke: rgb(42, 123, 253);
-  --color-selectedFill: #4285f4;
+  --color-selectedStroke: var(--color-indigo-900);
+  --color-selectedFill: var(--color-indigo-500);
   --color-selectedContrast: #fff;
   --shadow-small: 0 1px 2px 0 rgb(0 0 0 / 0.05);
   --shadow-medium: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
@@ -331,20 +331,22 @@ button.tl-select-input-trigger {
 
 .tl-geometry-tools-pane-anchor {
   @apply relative;
+
+  &[aria-expanded='true'] {
+    .tl-popover-indicator {
+      transform: rotate(180deg);
+      color: #000;
+    }
+  }
 }
 
-.tl-geometry-tools-pane {
-  @apply absolute rounded-lg flex flex-col items-center justify-center;
-  right: 100%;
-  top: calc(50% - 54px);
-  background-color: var(--ls-secondary-background-color);
-  padding: 4px;
-  gap: 8px;
-  box-shadow: var(--shadow-small);
+.tl-popover-indicator {
+  @apply absolute text-2xs;
 
-  .tl-button:not([data-selected='true']):hover {
-    background-color: var(--ls-tertiary-background-color);
-  }
+  opacity: 0.8;
+  pointer-events: none;
+  left: 0;
+  bottom: -3px;
 }
 
 .floating-panel[data-tool-locked='true'] > .tl-button[data-selected='true']::after {
@@ -874,6 +876,11 @@ html[data-theme='dark'] {
   background-color: var(--ls-border-color);
   width: 1px;
   opacity: 0.5;
+
+  &[data-orientation='horizontal'] {
+    height: 1px;
+    width: auto;
+  }
 }
 
 .tl-youtube-link,
@@ -900,25 +907,28 @@ html[data-theme='dark'] {
 .tl-popover-content {
   @apply rounded-sm drop-shadow-md;
 
-  padding: 4px;
-  width: 160px;
   background-color: var(--ls-secondary-background-color);
   z-index: 100000;
 }
 
+.tl-geometry-toolbar {
+  box-shadow: none;
+  flex-flow: column;
+}
+
 .tl-popover-arrow {
   fill: var(--ls-secondary-background-color);
 }
 
 .tl-color-palette {
-  @apply flex flex-wrap;
+  @apply grid grid-cols-4;
 }
 
 .tl-color-drip {
   @apply rounded text-sm;
 
-  width: 30px;
-  height: 30px;
+  width: 32px;
+  height: 32px;
   padding: 3px;
   border: 1px solid var(--ls-secondary-border-color);
   color: var(--ls-secondary-text-color);

+ 14 - 6
tldraw/demo/package.json

@@ -3,18 +3,26 @@
   "private": true,
   "version": "0.0.0-dev",
   "devDependencies": {
-    "@babel/plugin-proposal-decorators": "^7.18.2",
+    "@babel/plugin-proposal-decorators": "^7.20.2",
+    "@swc/core": "^1.3.17",
+    "@typescript-eslint/eslint-plugin": "^5.43.0",
+    "@typescript-eslint/parser": "^5.43.0",
     "@vitejs/plugin-basic-ssl": "^0.1.2",
-    "autoprefixer": "^10.4.7",
-    "postcss": "^8.4.17",
-    "tailwindcss": "^3.0.24",
-    "vite": "^3.1.6"
+    "autoprefixer": "^10.4.13",
+    "eslint": "^8.27.0",
+    "postcss": "^8.4.19",
+    "prettier-plugin-jsdoc": "^0.4.2",
+    "tailwindcss": "^3.2.4",
+    "tslib": "^2.4.1",
+    "typescript": "^4.9.3",
+    "unplugin-swc": "^1.3.2",
+    "vite": "^3.2.4"
   },
   "scripts": {
     "dev": "vite"
   },
   "dependencies": {
-    "@vitejs/plugin-react": "^2.1.0",
+    "@vitejs/plugin-react": "^2.2.0",
     "react": "^17",
     "react-dom": "^17"
   }

+ 19 - 1
tldraw/demo/vite.config.js

@@ -1,5 +1,6 @@
 import react from '@vitejs/plugin-react'
-import basicSsl from '@vitejs/plugin-basic-ssl'
+// import swc from 'unplugin-swc'
+// import basicSsl from '@vitejs/plugin-basic-ssl'
 import path from 'path'
 import { defineConfig } from 'vite'
 
@@ -12,6 +13,23 @@ const bases = {
 // https://vitejs.dev/config/
 export default defineConfig({
   plugins: [
+    // swc.vite({
+    //   jsc: {
+    //     target: 'es2022',
+    //     parser: {
+    //       decorators: true,
+    //       tsx: true,
+    //       jsx: true,
+    //     },
+    //     transform: {
+    //       legacyDecorator: true,
+    //       react: {
+    //         refresh: true,
+    //         runtime: 'automatic',
+    //       }
+    //     }
+    //   }
+    // }),
     react({
       babel: {
         parserOpts: {

+ 6 - 6
tldraw/packages/core/package.json

@@ -37,22 +37,22 @@
   "dependencies": {
     "@tldraw/intersect": "2.0.0-alpha.1",
     "@tldraw/vec": "2.0.0-alpha.1",
-    "@use-gesture/react": "^10.2.20",
-    "fast-copy": "^2.1.3",
+    "@use-gesture/react": "^10.2.22",
+    "fast-copy": "^3.0.0",
     "fast-deep-equal": "^3.1.3",
     "hotkeys-js": "^3.10.0",
     "is-plain-object": "^5.0.0",
-    "mobx": "^6.6.2",
+    "mobx": "^6.7.0",
     "mobx-react-lite": "^3.4.0",
     "mousetrap": "^1.6.5",
-    "potpack": "^1.0.2",
+    "potpack": "^2.0.0",
     "proxy-compare": "^2.3.0",
     "rbush": "^3.0.1",
-    "uuid": "^8.0.0"
+    "uuid": "^9.0.0"
   },
   "devDependencies": {
     "@types/is-plain-object": "^2.0.4",
-    "@types/mousetrap": "^1.6.8",
+    "@types/mousetrap": "^1.6.11",
     "@types/node": "^16.11.6",
     "@types/rbush": "^3.0.0",
     "@types/react": "^17.0.0",

+ 13 - 0
tldraw/packages/core/src/lib/TLApi/TLApi.ts

@@ -166,6 +166,19 @@ export class TLApi<S extends TLShape = TLShape, K extends TLEventMap = TLEventMa
     return this
   }
 
+  setColor = (color: string): this => {
+    const { settings } = this.app
+
+    settings.update({ color: color })
+
+    this.app.selectedShapesArray.forEach(s => {
+      s.update({ fill: color, stroke: color })
+    })
+    this.app.persist()
+
+    return this
+  }
+
   save = () => {
     this.app.save()
     return this

+ 2 - 0
tldraw/packages/core/src/lib/TLSettings.ts

@@ -5,6 +5,7 @@ import { isSafari } from '../utils'
 export interface TLSettingsProps {
   mode: 'light' | 'dark'
   showGrid: boolean
+  color: string
 }
 
 export class TLSettings implements TLSettingsProps {
@@ -14,6 +15,7 @@ export class TLSettings implements TLSettingsProps {
 
   @observable mode: 'dark' | 'light' = 'light'
   @observable showGrid = true
+  @observable color = ''
 
   @action update(props: Partial<TLSettingsProps>): void {
     Object.assign(this, props)

+ 2 - 0
tldraw/packages/core/src/lib/shapes/TLShape/TLShape.tsx

@@ -28,6 +28,8 @@ export interface TLShapeProps {
   type: any
   parentId: string
   name?: string
+  fill?: string
+  stroke?: string
   point: number[]
   size?: number[]
   scale?: number[]

+ 2 - 0
tldraw/packages/core/src/lib/tools/TLBoxTool/states/CreatingState.tsx

@@ -32,6 +32,8 @@ export class CreatingState<
       type: Shape.id,
       parentId: currentPage.id,
       point: [...originPoint],
+      fill: this.app.settings.color,
+      stroke: this.app.settings.color,
       size: Vec.abs(Vec.sub(currentPoint, originPoint)),
     })
     this.initialBounds = {

+ 2 - 0
tldraw/packages/core/src/lib/tools/TLDrawTool/states/CreatingState.tsx

@@ -71,6 +71,8 @@ export class CreatingState<
         point: originPoint.slice(0, 2),
         points: this.points,
         isComplete: false,
+        fill: this.app.settings.color,
+        stroke: this.app.settings.color,
       })
       this.app.currentPage.addShapes(this.shape)
     }

+ 2 - 0
tldraw/packages/core/src/lib/tools/TLLineTool/states/CreatingState.tsx

@@ -32,6 +32,8 @@ export class CreatingState<
       type: Shape.id,
       parentId: this.app.currentPage.id,
       point: originPoint,
+      fill: this.app.settings.color,
+      stroke: this.app.settings.color,
     })
     this.initialShape = toJS(shape.props)
     this.currentShape = shape

+ 1 - 1
tldraw/packages/core/src/lib/tools/TLMoveTool/TLMoveTool.ts

@@ -10,7 +10,7 @@ export class TLMoveTool<
   R extends TLApp<S, K> = TLApp<S, K>
 > extends TLTool<S, K, R> {
   static id = 'move'
-  static shortcut = ['2']
+  static shortcut = ['9']
 
   static states = [IdleState, IdleHoldState, PanningState, PinchingState]
 

+ 1 - 1
tldraw/packages/core/src/lib/tools/TLSelectTool/TLSelectTool.tsx

@@ -33,7 +33,7 @@ export class TLSelectTool<
 
   static initial = 'idle'
 
-  static shortcut = ['1']
+  static shortcut = ['8']
 
   static states = [
     IdleState,

+ 2 - 0
tldraw/packages/core/src/lib/tools/TLTextTool/states/CreatingState.tsx

@@ -36,6 +36,8 @@ export class CreatingState<
       text: '',
       size: [16, 32],
       isSizeLocked: true,
+      fill: this.app.settings.color,
+      stroke: this.app.settings.color,
     })
     this.creatingShape = shape
     transaction(() => {

+ 5 - 5
tldraw/packages/react/package.json

@@ -35,23 +35,23 @@
     "@tldraw/core": "2.0.0-alpha.1",
     "@tldraw/intersect": "2.0.0-alpha.1",
     "@tldraw/vec": "2.0.0-alpha.1",
-    "@use-gesture/react": "^10.2.20",
+    "@use-gesture/react": "^10.2.22",
     "hotkeys-js": "^3.10.0",
     "is-plain-object": "^5.0.0",
-    "mobx": "^6.6.2",
+    "mobx": "^6.7.0",
     "mobx-react-lite": "^3.4.0",
     "mousetrap": "^1.6.5",
     "polished": "^4.2.2",
     "rbush": "^3.0.1",
-    "uuid": "^8.0.0"
+    "uuid": "^9.0.0"
   },
   "peerDependencies": {
-    "react": ">=16.8",
+    "react": ">=16.8  || ^17.0",
     "react-dom": "^16.8 || ^17.0"
   },
   "devDependencies": {
     "@types/is-plain-object": "^2.0.4",
-    "@types/mousetrap": "^1.6.8",
+    "@types/mousetrap": "^1.6.11",
     "@types/node": "^16.11.6",
     "@types/offscreencanvas": "^2019.6.4",
     "@types/rbush": "^3.0.0",

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 331 - 281
tldraw/yarn.lock


+ 3 - 3
yarn.lock

@@ -1617,9 +1617,9 @@ caniuse-api@^3.0.0:
     lodash.uniq "^4.5.0"
 
 caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001370:
-  version "1.0.30001393"
-  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001393.tgz#1aa161e24fe6af2e2ccda000fc2b94be0b0db356"
-  integrity sha512-N/od11RX+Gsk+1qY/jbPa0R6zJupEa0lxeBG598EbrtblxVCTJsQwbRBm6+V+rxpc5lHKdsXb9RY83cZIPLseA==
+  version "1.0.30001431"
+  resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001431.tgz"
+  integrity sha512-zBUoFU0ZcxpvSt9IU66dXVT/3ctO1cy4y9cscs1szkPlcWb6pasYM144GqrUygUbT+k7cmUCW61cvskjcv0enQ==
 
 caniuse-lite@^1.0.30001400, caniuse-lite@^1.0.30001426:
   version "1.0.30001431"

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