Browse Source

Merge remote-tracking branch 'upstream/master' into whiteboards

Peng Xiao 3 years ago
parent
commit
62e962a4b4

+ 60 - 0
e2e-tests/context-menu.spec.ts

@@ -0,0 +1,60 @@
+import { expect } from '@playwright/test'
+import { test } from './fixtures'
+import { createRandomPage } from './utils'
+
+test('open context menu', async ({ page }) => {
+    await createRandomPage(page)
+
+    await page.locator('span.bullet-container >> nth=0').click({button: "right"})
+
+    await expect(page.locator('#custom-context-menu')).toBeVisible()
+})
+
+test('close context menu on esc', async ({ page }) => {
+    await createRandomPage(page)
+
+    await page.locator('span.bullet-container >> nth=0').click({button: "right"})
+
+    await page.keyboard.press('Escape')
+
+    await expect(page.locator('#custom-context-menu')).toHaveCount(0)
+})
+
+test('close context menu by left clicking on empty space', async ({ page }) => {
+    await createRandomPage(page)
+
+    await page.locator('span.bullet-container >> nth=0').click({button: "right"})
+
+    await page.mouse.click(0, 200, {button: "left"})
+
+    await expect(page.locator('#custom-context-menu')).toHaveCount(0)
+})
+
+test('close context menu by clicking on a menu item', async ({ page }) => {
+    await createRandomPage(page)
+
+    await page.locator('span.bullet-container >> nth=0').click({button: "right"})
+
+    await page.locator('#custom-context-menu .menu-link >> nth=1').click()
+
+    await expect(page.locator('#custom-context-menu')).toHaveCount(0)
+})
+
+test('close context menu by clicking on a block', async ({ page, block }) => {
+    await createRandomPage(page)
+
+    await block.mustType('fist Block')
+    await block.enterNext()
+
+    await page.locator('span.bullet-container >> nth=-1').click({button: "right"})
+
+    const elementHandle = page.locator('.block-content >> nth=0');
+
+    const box = await elementHandle.boundingBox();
+    expect(box).toBeTruthy()
+    if (box) {
+        await page.mouse.click(box.x + box.width - 5, box.y + box.height / 2);
+    }
+
+    await expect(page.locator('#custom-context-menu')).toHaveCount(0)
+})

+ 5 - 0
e2e-tests/fixtures.ts

@@ -106,6 +106,11 @@ base.beforeEach(async () => {
   if (page) {
     await page.keyboard.press('Escape')
     await page.keyboard.press('Escape')
+
+    const rightSidebar = page.locator('.cp__right-sidebar-inner')
+    if (await rightSidebar.isVisible()) {
+      await page.click('button.toggle-right-sidebar', {delay: 100})
+    }
   }
 })
 

+ 3 - 3
e2e-tests/page-search.spec.ts

@@ -38,7 +38,7 @@ import { IsMac, createRandomPage, newBlock, newInnerBlock, randomString, lastBlo
   await page.fill('[placeholder="Search or create page"]', 'Einführung in die Allgemeine Sprachwissenschaft' + rand)
 
   await page.waitForTimeout(500)
-  const results = await page.$$('#ui__ac-inner .block')
+  const results = await page.$$('#ui__ac-inner>div')
   expect(results.length).toEqual(3) // 2 blocks + 1 page
   await page.keyboard.press("Escape")
 })
@@ -68,7 +68,7 @@ async function alias_test(page: Page, page_name: string, search_kws: string[]) {
   await page.waitForTimeout(500)
 
   // build target Page with alias
-  // the target page will contains the content in 
+  // the target page will contains the content in
   //   alias_test_content_1,
   //   alias_test_content_2, and
   //   alias_test_content_3 sequentialy, to validate the target page state
@@ -127,7 +127,7 @@ async function alias_test(page: Page, page_name: string, search_kws: string[]) {
     await page.fill('[placeholder="Search or create page"]', kw_name)
     await page.waitForTimeout(500)
 
-    const results = await page.$$('#ui__ac-inner .block')
+    const results = await page.$$('#ui__ac-inner>div')
     expect(results.length).toEqual(3) // page + block + alias property
 
     // test search results

+ 1 - 1
libs/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@logseq/libs",
-  "version": "0.0.7",
+  "version": "0.0.8",
   "description": "Logseq SDK libraries",
   "main": "dist/lsplugin.user.js",
   "typings": "index.d.ts",

+ 1 - 1
libs/src/LSPlugin.caller.ts

@@ -266,7 +266,7 @@ class LSPluginCaller extends EventEmitter {
     return new Promise((resolve, reject) => {
       timer = setTimeout(() => {
         reject(new Error(`handshake Timeout`))
-      }, 3 * 1000) // 3secs
+      }, 8 * 1000) // 8 secs
 
       handshake
         .then((refChild: ParentAPI) => {

+ 1 - 1
libs/src/LSPlugin.core.ts

@@ -907,7 +907,7 @@ class PluginLocal extends EventEmitter<'loaded'
 
       this._dispose(cleanInjectedScripts.bind(this))
     } catch (e) {
-      debug('[Load Plugin Error] ', e)
+      console.error('[Load Plugin Error] ', e)
       this.logger?.error(e)
 
       this._status = PluginLocalLoadStatus.ERROR

+ 10 - 0
libs/src/LSPlugin.ts

@@ -140,6 +140,8 @@ export interface AppUserConfigs {
 
   currentGraph: string
   showBracket: boolean
+  enabledFlashcards: boolean
+  enabledJournals: boolean
 
   [key: string]: any
 }
@@ -410,6 +412,7 @@ export interface IAppProxy {
 
   // hook events
   onCurrentGraphChanged: IUserHook
+  onGraphAfterIndexed: IUserHook<{repo: string}>
   onThemeModeChanged: IUserHook<{ mode: 'dark' | 'light' }>
   onThemeChanged: IUserHook<Partial<{name: string, mode: string, pid: string, url: string}>>
   onBlockRendererSlotted: IUserSlotHook<{ uuid: BlockUUID }>
@@ -551,6 +554,12 @@ export interface IEditorProxy extends Record<string, any> {
     namespace: BlockPageName
   ) => Promise<Array<PageEntity> | null>
 
+  /**
+   * Create a unique UUID string which can then be assigned to a block.
+   * @added 0.0.8
+   */
+  newBlockUUID: () => Promise<string>
+
   /**
    * @example https://github.com/logseq/logseq-plugin-samples/tree/master/logseq-reddit-hot-news
    *
@@ -565,6 +574,7 @@ export interface IEditorProxy extends Record<string, any> {
       before: boolean
       sibling: boolean
       isPageBlock: boolean
+      customUUID: string
       properties: {}
     }>
   ) => Promise<BlockEntity | null>

+ 5 - 1
libs/src/LSPlugin.user.ts

@@ -189,6 +189,10 @@ const app: Partial<IAppProxy> = {
 let registeredCmdUid = 0
 
 const editor: Partial<IEditorProxy> = {
+  newBlockUUID(this: LSPluginUser): Promise<string> {
+    return this._execCallableAPIAsync('new_block_uuid')
+  },
+
   registerSlashCommand(
     this: LSPluginUser,
     tag: string,
@@ -273,7 +277,7 @@ const editor: Partial<IEditorProxy> = {
     } else {
       this.App.pushState('page', { name: pageName }, { anchor })
     }
-  },
+  }
 }
 
 const db: Partial<IDBProxy> = {

+ 25 - 9
resources/css/common.css

@@ -843,8 +843,14 @@ i.ti {
 
 .heading-bg {
   border-radius: 50%;
-  width: 12px;
-  height: 12px;
+  width: 14px;
+  height: 14px;
+
+  &.remove {
+    @apply border flex items-center justify-center;
+
+    border-color: var(--border-color);
+  }
 }
 
 /** endregion **/
@@ -901,20 +907,30 @@ button.menu:focus {
   background-color: var(--ls-menu-hover-color, #f4f5f7);
 }
 
+.menu-links-wrapper {
+  @apply py-2 rounded-md shadow-lg overflow-y-auto;
+
+  max-height: calc(100vh - 100px) !important;
+  background-color: var(--ls-primary-background-color, #fff);
+  min-width: 12rem;
+}
+
+.menu-backdrop {
+  @apply w-full h-full fixed top-0 left-0;
+
+  z-index: var(--ls-z-index-level-1);
+}
+
 .menu-link {
   background-color: var(--ls-primary-background-color, #fff);
   color: var(--ls-primary-text-color);
   user-select: none;
 }
 
-.menu-link:first-of-type {
-  border-top-left-radius: var(--ls-border-radius-low);
-  border-top-right-radius: var(--ls-border-radius-low);
-}
+.menu-separator {
+  @apply my-1;
 
-.menu-link:last-of-type {
-  border-bottom-left-radius: var(--ls-border-radius-low);
-  border-bottom-right-radius: var(--ls-border-radius-low);
+  opacity: .5;
 }
 
 a.login {

File diff suppressed because it is too large
+ 0 - 0
resources/js/lsplugin.core.js


+ 207 - 192
src/main/frontend/components/content.cljs

@@ -55,35 +55,43 @@
 
 (rum/defc custom-context-menu-content
   []
-  [:div#custom-context-menu
-   [:div.py-1.rounded-md.bg-base-3.shadow-xs
-    (ui/menu-link
-     {:key "cut"
-      :on-click #(editor-handler/cut-selection-blocks true)}
-     "Cut")
-    (ui/menu-link
-     {:key "copy"
-      :on-click editor-handler/copy-selection-blocks}
-     "Copy")
-    (ui/menu-link
-     {:key "copy as"
-      :on-click (fn [_]
-                  (let [block-uuids (editor-handler/get-selected-toplevel-block-uuids)]
-                    (state/set-modal!
-                     #(export/export-blocks block-uuids))))}
-     "Copy as")
-    (ui/menu-link
-     {:key "copy block refs"
-      :on-click editor-handler/copy-block-refs}
-     "Copy block refs")
-    (ui/menu-link
-     {:key "copy block embeds"
-      :on-click editor-handler/copy-block-embeds}
-     "Copy block embeds")
-    (ui/menu-link
-     {:key "cycle todos"
-      :on-click editor-handler/cycle-todos!}
-     "Cycle todos")]])
+  [:.menu-links-wrapper
+   (ui/menu-link
+    {:key "cut"
+     :on-click #(editor-handler/cut-selection-blocks true)}
+    "Cut"
+    nil)
+   (ui/menu-link
+    {:key "copy"
+     :on-click editor-handler/copy-selection-blocks}
+    "Copy"
+    nil)
+   (ui/menu-link
+    {:key "copy as"
+     :on-click (fn [_]
+                 (let [block-uuids (editor-handler/get-selected-toplevel-block-uuids)]
+                   (state/set-modal!
+                    #(export/export-blocks block-uuids))))}
+    "Copy as..."
+    nil)
+   (ui/menu-link
+    {:key "copy block refs"
+     :on-click editor-handler/copy-block-refs}
+    "Copy block refs"
+    nil)
+   (ui/menu-link
+    {:key "copy block embeds"
+     :on-click editor-handler/copy-block-embeds}
+    "Copy block embeds"
+    nil)
+   
+   [:hr.menu-separator]
+
+   (ui/menu-link
+    {:key "cycle todos"
+     :on-click editor-handler/cycle-todos!}
+    "Cycle todos"
+    nil)])
 
 ;; FIXME: Make it configurable
 (def block-background-colors
@@ -145,192 +153,199 @@
                                           (editor-handler/set-block-property! block-id :template-including-parent false))
                                         (state/hide-custom-context-menu!)))))))])
       (ui/menu-link
-       {:key "Make template"
+       {:key "Make a Template"
         :on-click (fn [e]
                     (util/stop e)
                     (reset! edit? true))}
-       "Make template"))))
+       "Make a Template"
+       nil))))
 
 (rum/defc ^:large-vars/cleanup-todo block-context-menu-content
   [_target block-id]
-
-  (let [*el-ref (rum/use-ref nil)]
-
-    (rum/use-effect!
-     (fn []
-       (js/setTimeout
-        (fn []
-          (let [^js el (rum/deref *el-ref)
-               {:keys [x y]} (util/calc-delta-rect-offset el js/document.documentElement)]
-           (set! (.. el -style -transform)
-                 (str "translate3d(" (if (neg? x) x 0) "px," (if (neg? y) (- y 10) 0) "px" ",0)"))))
-        10)
-       #())
-     [])
-
     (when-let [block (db/entity [:block/uuid block-id])]
       (let [properties (:block/properties block)
             heading? (true? (:heading properties))]
-        [:div#custom-context-menu
-         {:ref *el-ref}
-         [:div.py-1.rounded-md.bg-base-3.shadow-xs
-          [:div.flex-row.flex.justify-between.py-4.pl-2
-           [:div.flex-row.flex.justify-between
-            (for [color block-background-colors]
-              [:a.m-2.shadow-sm
-               {:on-click (fn [_e]
-                            (editor-handler/set-block-property! block-id "background-color" color))}
-               [:div.heading-bg {:style {:background-color color}}]])]
-           [:a.text-sm
+        [:.menu-links-wrapper
+         [:div.flex-row.flex.justify-between.pb-2.pt-1.px-2
+          [:div.flex-row.flex.justify-between
+           (for [color block-background-colors]
+             [:a.m-2.shadow-sm
+              {:on-click (fn [_e]
+                           (editor-handler/set-block-property! block-id "background-color" color))}
+              [:div.heading-bg {:style {:background-color color}}]])
+           [:a.m-2.shadow-sm
             {:title    (t :remove-background)
-             :style    {:margin-right 14
-                        :margin-top   4}
              :on-click (fn [_e]
                          (editor-handler/remove-block-property! block-id "background-color"))}
-            "Clear"]]
-
-          (ui/menu-link
-           {:key      "Convert heading"
-            :on-click (fn [_e]
-                        (if heading?
-                          (editor-handler/remove-block-property! block-id :heading)
-                          (editor-handler/set-block-property! block-id :heading true)))}
-           (if heading?
-             "Convert back to a block"
-             "Convert to a heading"))
-
-          (ui/menu-link
-           {:key      "Open in sidebar"
-            :on-click (fn [_e]
-                        (editor-handler/open-block-in-sidebar! block-id))}
-           "Open in sidebar")
-
-          (ui/menu-link
-           {:key      "Copy block ref"
-            :on-click (fn [_e]
-                        (editor-handler/copy-block-ref! block-id block-ref/->block-ref))}
-           "Copy block ref")
-
-          (ui/menu-link
-           {:key      "Copy block embed"
-            :on-click (fn [_e]
-                        (editor-handler/copy-block-ref! block-id #(util/format "{{embed ((%s))}}" %)))}
-           "Copy block embed")
+            [:div.heading-bg.remove "-"]]]]
+         
+         [:hr.menu-separator]
+
+         (ui/menu-link
+          {:key      "Open in sidebar"
+           :on-click (fn [_e]
+                       (editor-handler/open-block-in-sidebar! block-id))}
+          "Open in sidebar"
+          ["shift" "click"])
+
+         [:hr.menu-separator]
+
+         (ui/menu-link
+          {:key      "Copy block ref"
+           :on-click (fn [_e]
+                       (editor-handler/copy-block-ref! block-id block-ref/->block-ref))}
+          "Copy block ref"
+          nil)
+
+         (ui/menu-link
+          {:key      "Copy block embed"
+           :on-click (fn [_e]
+                       (editor-handler/copy-block-ref! block-id #(util/format "{{embed ((%s))}}" %)))}
+          "Copy block embed"
+          nil)
 
           ;; TODO Logseq protocol mobile support
-          (when (util/electron?)
-            (ui/menu-link
-             {:key      "Copy block URL"
-              :on-click (fn [_e]
-                          (let [current-repo (state/get-current-repo)
-                                tap-f (fn [block-id]
-                                        (url-util/get-logseq-graph-uuid-url nil current-repo block-id))]
-                            (editor-handler/copy-block-ref! block-id tap-f)))}
-             "Copy block URL"))
-
-          (block-template block-id)
-
-          (ui/menu-link
-           {:key      "Copy as"
-            :on-click (fn [_]
-                        (state/set-modal! #(export/export-blocks [block-id])))}
-           "Copy as")
-
-          (if (srs/card-block? block)
-            (ui/menu-link
-             {:key      "Preview Card"
-              :on-click #(srs/preview (:db/id block))}
-             "Preview Card")
-            (ui/menu-link
-             {:key      "Make a Card"
-              :on-click #(srs/make-block-a-card! block-id)}
-             "Make a Card"))
-
-          (ui/menu-link
-           {:key      "Cut"
-            :on-click (fn [_e]
-                        (editor-handler/cut-block! block-id))}
-           "Cut")
-
-          (ui/menu-link
-           {:key      "Expand all"
-            :on-click (fn [_e]
-                        (editor-handler/expand-all! block-id))}
-           "Expand all")
-
-          (ui/menu-link
-           {:key      "Collapse all"
-            :on-click (fn [_e]
-                        (editor-handler/collapse-all! block-id {}))}
-           "Collapse all")
-
-          (when (state/sub [:plugin/simple-commands])
-            (when-let [cmds (state/get-plugins-commands-with-type :block-context-menu-item)]
-              (for [[_ {:keys [key label] :as cmd} action pid] cmds]
-                (ui/menu-link
-                 {:key      key
-                  :on-click #(commands/exec-plugin-simple-command!
-                              pid (assoc cmd :uuid block-id) action)}
-                 label))))
-
-          (when (state/sub [:ui/developer-mode?])
-            (ui/menu-link
-             {:key      "(Dev) Show block data"
-              :on-click (fn []
-                          (let [block-data (with-out-str (pprint/pprint (db/pull [:block/uuid block-id])))]
-                            (println block-data)
-                            (notification/show!
-                             [:div
-                              [:pre.code block-data]
-                              [:br]
-                              (ui/button "Copy to clipboard"
-                                :on-click #(.writeText js/navigator.clipboard block-data))]
-                             :success
-                             false)))}
-             "(Dev) Show block data"))]]))))
+         (when (util/electron?)
+           (ui/menu-link
+            {:key      "Copy block URL"
+             :on-click (fn [_e]
+                         (let [current-repo (state/get-current-repo)
+                               tap-f (fn [block-id]
+                                       (url-util/get-logseq-graph-uuid-url nil current-repo block-id))]
+                           (editor-handler/copy-block-ref! block-id tap-f)))}
+            "Copy block URL"
+            nil))
+
+         (ui/menu-link
+          {:key      "Copy as"
+           :on-click (fn [_]
+                       (state/set-modal! #(export/export-blocks [block-id])))}
+          "Copy as..."
+          nil)
+
+         (ui/menu-link
+          {:key      "Cut"
+           :on-click (fn [_e]
+                       (editor-handler/cut-block! block-id))}
+          "Cut"
+          nil)
+
+         [:hr.menu-separator]
+
+         (ui/menu-link
+          {:key      "Convert heading"
+           :on-click (fn [_e]
+                       (if heading?
+                         (editor-handler/remove-block-property! block-id :heading)
+                         (editor-handler/set-block-property! block-id :heading true)))}
+          (if heading?
+            "Convert back to a block"
+            "Convert to a heading")
+          nil)
+
+         (block-template block-id)
+
+         (if (srs/card-block? block)
+           (ui/menu-link
+            {:key      "Preview Card"
+             :on-click #(srs/preview (:db/id block))}
+            "Preview Card"
+            nil)
+           (ui/menu-link
+            {:key      "Make a Card"
+             :on-click #(srs/make-block-a-card! block-id)}
+            "Make a Flashcard"
+            nil))
+
+         [:hr.menu-separator]
+
+         (ui/menu-link
+          {:key      "Expand all"
+           :on-click (fn [_e]
+                       (editor-handler/expand-all! block-id))}
+          "Expand all"
+          nil)
+
+         (ui/menu-link
+          {:key      "Collapse all"
+           :on-click (fn [_e]
+                       (editor-handler/collapse-all! block-id {}))}
+          "Collapse all"
+          nil)
+
+         (when (state/sub [:plugin/simple-commands])
+           (when-let [cmds (state/get-plugins-commands-with-type :block-context-menu-item)]
+             (for [[_ {:keys [key label] :as cmd} action pid] cmds]
+               (ui/menu-link
+                {:key      key
+                 :on-click #(commands/exec-plugin-simple-command!
+                             pid (assoc cmd :uuid block-id) action)}
+                label
+                nil))))
+
+         (when (state/sub [:ui/developer-mode?])
+           (ui/menu-link
+            {:key      "(Dev) Show block data"
+             :on-click (fn []
+                         (let [block-data (with-out-str (pprint/pprint (db/pull [:block/uuid block-id])))]
+                           (println block-data)
+                           (notification/show!
+                            [:div
+                             [:pre.code block-data]
+                             [:br]
+                             (ui/button "Copy to clipboard"
+                                        :on-click #(.writeText js/navigator.clipboard block-data))]
+                            :success
+                            false)))}
+            "(Dev) Show block data"
+            nil))])))
 
 (rum/defc block-ref-custom-context-menu-content
   [block block-ref-id]
   (when (and block block-ref-id)
-    [:div#custom-context-menu
-     [:div.py-1.rounded-md.bg-base-3.shadow-xs
-      (ui/menu-link
-       {:key "open-in-sidebar"
-        :on-click (fn []
-                    (state/sidebar-add-block!
-                     (state/get-current-repo)
-                     block-ref-id
-                     :block-ref))}
-       "Open in sidebar")
-      (ui/menu-link
-       {:key "copy"
-        :on-click (fn [] (editor-handler/copy-current-ref block-ref-id))}
-       "Copy this reference")
-      (ui/menu-link
-       {:key "delete"
-        :on-click (fn [] (editor-handler/delete-current-ref! block block-ref-id))}
-       "Delete this reference")
-      (ui/menu-link
-       {:key "replace-with-text"
-        :on-click (fn [] (editor-handler/replace-ref-with-text! block block-ref-id))}
-       "Replace with text")
-      (ui/menu-link
-       {:key "replace-with-embed"
-        :on-click (fn [] (editor-handler/replace-ref-with-embed! block block-ref-id))}
-       "Replace with embed")]]))
+    [:.menu-links-wrapper
+     (ui/menu-link
+      {:key "open-in-sidebar"
+       :on-click (fn []
+                   (state/sidebar-add-block!
+                    (state/get-current-repo)
+                    block-ref-id
+                    :block-ref))}
+      "Open in sidebar"
+      ["shift" "click"])
+     (ui/menu-link
+      {:key "copy"
+       :on-click (fn [] (editor-handler/copy-current-ref block-ref-id))}
+      "Copy this reference"
+      nil)
+     (ui/menu-link
+      {:key "delete"
+       :on-click (fn [] (editor-handler/delete-current-ref! block block-ref-id))}
+      "Delete this reference"
+      nil)
+     (ui/menu-link
+      {:key "replace-with-text"
+       :on-click (fn [] (editor-handler/replace-ref-with-text! block block-ref-id))}
+      "Replace with text"
+      nil)
+     (ui/menu-link
+      {:key "replace-with-embed"
+       :on-click (fn [] (editor-handler/replace-ref-with-embed! block block-ref-id))}
+      "Replace with embed"
+      nil)]))
 
 (rum/defc page-title-custom-context-menu-content
   [page]
   (when-not (string/blank? page)
     (let [page-menu-options (page-menu/page-menu page)]
-      [:div#custom-context-menu
-       [:div.py-1.rounded-md.bg-base-3.shadow-xs
-        (for [{:keys [title options]} page-menu-options]
-          (ui/menu-link
-           (merge
-            {:key title}
-            options)
-           title))]])))
+      [:.menu-links-wrapper
+       (for [{:keys [title options]} page-menu-options]
+         (ui/menu-link
+          (merge
+           {:key title}
+           options)
+          title
+          nil))])))
 
 ;; TODO: content could be changed
 ;; Also, keyboard bindings should only be activated after

+ 2 - 4
src/main/frontend/components/content.css

@@ -12,10 +12,8 @@
 }
 
 #custom-context-menu {
-  @apply rounded-md shadow-lg transition ease-out duration-100 transform
-  opacity-100 scale-100 absolute overflow-y-auto;
+  @apply transition ease-out duration-100 transform
+  opacity-100 scale-100 absolute;
 
-  max-height: calc(100vh - 100px) !important;;
-  overflow-y: scroll;
   z-index: calc(var(--ls-z-index-level-1) + 1);
 }

+ 30 - 12
src/main/frontend/components/sidebar.cljs

@@ -72,7 +72,7 @@
         whiteboard-page? (db-model/whiteboard-page? name)]
     [:a {:on-click (fn [e]
                      (let [name (util/safe-page-name-sanity-lc name)
-                           source-page (db-model/get-alias-source-page (state/get-current-repo) name) 
+                           source-page (db-model/get-alias-source-page (state/get-current-repo) name)
                            name (if (empty? source-page) name (:block/name source-page))]
                        (if (and (gobj/get e "shiftKey") (not whiteboard-page?))
                          (when-let [page-entity (if (empty? source-page) (db/entity [:block/name name]) source-page)]
@@ -484,15 +484,41 @@
          :else
          [:div])])))
 
+(defn- hide-context-menu-and-clear-selection
+  [e]
+  (state/hide-custom-context-menu!)
+  (when-not (or (gobj/get e "shiftKey")
+                (util/meta-key? e)
+                (state/get-edit-input-id))
+    (editor-handler/clear-selection!)))
+
+(rum/defc render-custom-context-menu
+  [links position]
+  (let [ref (rum/use-ref nil)]
+    (rum/use-effect!
+     #(let [el (rum/deref ref)
+            {:keys [x y]} (util/calc-delta-rect-offset el js/document.documentElement)]
+        (set! (.. el -style -transform)
+              (str "translate3d(" (if (neg? x) x 0) "px," (if (neg? y) (- y 10) 0) "px" ",0)"))))
+    [:<>
+     [:div.menu-backdrop {:on-mouse-down (fn [e] (hide-context-menu-and-clear-selection e))}]
+     [:div#custom-context-menu
+      {:ref ref
+       :style {:left (str (first position) "px")
+               :top (str (second position) "px")}} links]]))
+
 (rum/defc custom-context-menu < rum/reactive
   []
-  (when (state/sub :custom-context-menu/show?)
-    (when-let [links (state/sub :custom-context-menu/links)]
+  (let [show? (state/sub :custom-context-menu/show?)
+        links (state/sub :custom-context-menu/links)
+        position (state/sub :custom-context-menu/position)]
+    (when (and show? links position)
       (ui/css-transition
        {:class-names "fade"
         :timeout {:enter 500
                   :exit 300}}
-       links))))
+       (render-custom-context-menu links position)))))
+
 
 (rum/defc new-block-mode < rum/reactive
   []
@@ -521,14 +547,6 @@
                    (state/sidebar-add-block! (state/get-current-repo) "help" :help))}
       "?"]]))
 
-(defn- hide-context-menu-and-clear-selection
-  [e]
-  (state/hide-custom-context-menu!)
-  (when-not (or (gobj/get e "shiftKey")
-                (util/meta-key? e)
-                (state/get-edit-input-id))
-    (editor-handler/clear-selection!)))
-
 (rum/defcs ^:large-vars/cleanup-todo sidebar <
   (mixins/modal :modal/show?)
   rum/reactive

+ 2 - 14
src/main/frontend/handler/common.cljs

@@ -2,7 +2,6 @@
   (:require [cljs-bean.core :as bean]
             [cljs.reader :as reader]
             [clojure.string :as string]
-            [dommy.core :as d]
             [frontend.config :as config]
             [frontend.date :as date]
             [frontend.db :as db]
@@ -116,19 +115,8 @@
 
 (defn show-custom-context-menu! [e context-menu-content]
   (util/stop e)
-  (let [client-x (gobj/get e "clientX")
-        client-y (gobj/get e "clientY")
-        scroll-y (util/cur-doc-top)]
-    (state/show-custom-context-menu! context-menu-content)
-
-    ;; FIXME: use setTimeout here because rum renders lazily.
-    (js/setTimeout
-     (fn []
-       (when-let [context-menu (d/by-id "custom-context-menu")]
-        (d/set-style! context-menu
-                      :left (str client-x "px")
-                      :top (str (+ scroll-y client-y) "px"))))
-     10)))
+  (let [position [(gobj/get e "clientX") (gobj/get e "clientY")]]
+    (state/show-custom-context-menu! context-menu-content position)))
 
 (defn parse-config
   "Parse configuration from file `content` such as from config.edn."

+ 1 - 36
src/main/frontend/handler/editor.cljs

@@ -260,40 +260,6 @@
     (and (not= current-id id)
          (db/entity [:block/uuid id]))))
 
-(defn- attach-page-properties-if-exists!
-  [block]
-  (if (and (:block/pre-block? block)
-           (seq (:block/properties block)))
-    (let [page-properties (:block/properties block)
-          str->page (fn [n] (block/page-name->map n true))
-          refs (->> page-properties
-                    (filter (fn [[_ v]] (coll? v)))
-                    (vals)
-                    (apply concat)
-                    (set)
-                    (map str->page)
-                    (concat (:block/refs block))
-                    (util/distinct-by :block/name))
-          {:keys [tags alias]} page-properties
-          page-tx (let [id (:db/id (:block/page block))
-                        retract-attributes (when id
-                                             (mapv (fn [attribute]
-                                                     [:db/retract id attribute])
-                                                   [:block/properties :block/tags :block/alias]))
-                        tags (->> (map str->page tags) (remove nil?))
-                        alias (->> (map str->page alias) (remove nil?))
-                        tx (cond-> {:db/id id
-                                    :block/properties page-properties}
-                             (seq tags)
-                             (assoc :block/tags tags)
-                             (seq alias)
-                             (assoc :block/alias alias))]
-                    (conj retract-attributes tx))]
-      (assoc block
-             :block/refs refs
-             :db/other-tx page-tx))
-    block))
-
 (defn- remove-non-existed-refs!
   [refs]
   (remove (fn [x] (or
@@ -386,7 +352,6 @@
                 block
                 (dissoc block :block/pre-block?))
         block (update block :block/refs remove-non-existed-refs!)
-        block (attach-page-properties-if-exists! block)
         new-properties (merge
                         (select-keys properties (property/hidden-properties))
                         (:block/properties block))]
@@ -2712,7 +2677,7 @@
                        (surround-by? input "#" :end)
                        (= key "#"))]
       (cond
-        (and (contains? #{"ArrowLeft" "ArrowRight" "ArrowUp" "ArrowDown"} key)
+        (and (contains? #{"ArrowLeft" "ArrowRight"} key)
              (contains? #{:property-search :property-value-search} (state/get-editor-action)))
         (state/clear-editor-action!)
 

+ 1 - 0
src/main/frontend/handler/events.cljs

@@ -59,6 +59,7 @@
   (db/set-key-value repo :ast/version db-schema/ast-version)
   (search-handler/rebuild-indices!)
   (db/persist! repo)
+  (plugin-handler/hook-plugin-app :graph-after-indexed {:repo repo :empty-graph? empty-graph?})
   (when (state/setups-picker?)
     (if empty-graph?
       (route-handler/redirect! {:to :import :query-params {:from "picker"}})

+ 27 - 7
src/main/frontend/modules/outliner/core.cljs

@@ -158,12 +158,24 @@
                                       db-schema/retract-attributes)))))
 
         (when-let [e (:block/page block-entity)]
-          (let [m {:db/id (:db/id e)
+          (let [m' {:db/id (:db/id e)
                    :block/updated-at (util/time-ms)}
-                m (if (:block/created-at e)
-                    m
-                    (assoc m :block/created-at (util/time-ms)))]
-            (swap! txs-state conj m))
+                m' (if (:block/created-at e)
+                    m'
+                    (assoc m' :block/created-at (util/time-ms)))
+                m' (if (or (:block/pre-block? block-entity)
+                           (:block/pre-block? m))
+                     (let [properties (:block/properties m)
+                           alias (set (:alias properties))
+                           tags (set (:tags properties))
+                           alias (map (fn [p] {:block/name (util/page-name-sanity-lc p)}) alias)
+                           tags (map (fn [p] {:block/name (util/page-name-sanity-lc p)}) tags)]
+                       (assoc m'
+                              :block/alias alias
+                              :block/tags tags
+                              :block/properties properties))
+                     m')]
+            (swap! txs-state conj m'))
           (remove-orphaned-page-refs! (:db/id block-entity) txs-state old-refs new-refs)))
 
       (swap! txs-state conj (dissoc m :db/other-tx))
@@ -194,8 +206,16 @@
                                                  (assoc :block/left parent))))
                                            immediate-children)))
                     txs))
-                txs)]
-      (swap! txs-state concat txs)
+                txs)
+          page-tx (let [block (db/entity [:block/uuid block-id])]
+                    (when (:block/pre-block? block)
+                      (let [id (:db/id (:block/page block))]
+                        [[:db/retract id :block/properties]
+                         [:db/retract id :block/properties-order]
+                         [:db/retract id :block/alias]
+                         [:db/retract id :block/tags]])))]
+      (swap! txs-state concat txs page-tx)
+      (util/pprint @txs-state)
       block-id))
 
   (-get-children [this]

+ 3 - 3
src/main/frontend/search.cljs

@@ -155,7 +155,7 @@
 
 (defn template-search
   ([q]
-   (template-search q 10))
+   (template-search q 100))
   ([q limit]
    (when q
      (let [q (clean-str q)
@@ -166,7 +166,7 @@
 
 (defn property-search
   ([q]
-   (property-search q 10))
+   (property-search q 100))
   ([q limit]
    (when q
      (let [q (clean-str q)
@@ -181,7 +181,7 @@
 
 (defn property-value-search
   ([property q]
-   (property-value-search property q 10))
+   (property-value-search property q 100))
   ([property q limit]
    (when q
      (let [q (clean-str q)

+ 11 - 6
src/main/frontend/state.cljs

@@ -133,6 +133,7 @@
      :selection/direction                   :down
      :custom-context-menu/show?             false
      :custom-context-menu/links             nil
+     :custom-context-menu/position          nil
 
      ;; pages or blocks in the right sidebar
      ;; It is a list of `[repo db-id block-type block-data]` 4-tuple
@@ -369,9 +370,11 @@
                  (get (sub-config) (get-current-repo))))))
 
 (defn enable-journals?
-  [repo]
-  (not (false? (:feature/enable-journals?
-                (get (sub-config) repo)))))
+  ([]
+   (enable-journals? (get-current-repo)))
+  ([repo]
+   (not (false? (:feature/enable-journals?
+                 (get (sub-config) repo))))))
 
 (defn enable-flashcards?
   ([]
@@ -771,16 +774,18 @@
   (:selection/direction @state))
 
 (defn show-custom-context-menu!
-  [links]
+  [links position]
   (swap! state assoc
          :custom-context-menu/show? true
-         :custom-context-menu/links links))
+         :custom-context-menu/links links
+         :custom-context-menu/position position))
 
 (defn hide-custom-context-menu!
   []
   (swap! state assoc
          :custom-context-menu/show? false
-         :custom-context-menu/links nil))
+         :custom-context-menu/links nil
+         :custom-context-menu/position nil))
 
 (defn toggle-navigation-item-collapsed!
   [item]

+ 28 - 26
src/main/frontend/ui.cljs

@@ -84,7 +84,7 @@
 
 (rum/defc dropdown-content-wrapper [state content class]
   (let [class (or class
-                  (util/hiccup->class "origin-top-right.absolute.right-0.mt-2.rounded-md.shadow-lg"))]
+                  (util/hiccup->class "origin-top-right.absolute.right-0.mt-2"))]
     [:div.dropdown-wrapper
      {:class (str class " "
                   (case state
@@ -109,18 +109,38 @@
         (when @open?
           (dropdown-content-wrapper dropdown-state modal-content modal-class))))]))
 
+;; `sequence` can be a list of symbols, a list of strings, or a string
+(defn render-keyboard-shortcut [sequence]
+  (let [sequence (if (string? sequence)
+                   (-> sequence ;; turn string into sequence
+                       (string/trim)
+                       (string/lower-case)
+                       (string/split  #" |\+"))
+                   sequence)]
+    [:span.keyboard-shortcut
+     (map-indexed (fn [i key]
+                    [:code {:key i}
+                   ;; Display "cmd" rather than "meta" to the user to describe the Mac
+                   ;; mod key, because that's what the Mac keyboards actually say.
+                     (if (or (= :meta key) (= "meta" key))
+                       (util/meta-key-name)
+                       (name key))])
+                  sequence)]))
+
 (rum/defc menu-link
-  [options child]
-  [:a.block.px-4.py-2.text-sm.transition.ease-in-out.duration-150.cursor.menu-link
+  [options child shortcut]
+  [:a.flex.justify-between.px-4.py-2.text-sm.transition.ease-in-out.duration-150.cursor.menu-link
    options
-   child])
+   [:span child]
+   (when shortcut
+     [:span.ml-1 (render-keyboard-shortcut shortcut)])])
 
 (rum/defc dropdown-with-links
   [content-fn links {:keys [links-header links-footer] :as opts}]
   (dropdown
    content-fn
    (fn [{:keys [close-fn]}]
-     [:div.py-1.rounded-md.shadow-xs
+     [:.menu-links-wrapper
       (when links-header links-header)
       (for [{:keys [options title icon hr hover-detail item]} (if (fn? links) (links) links)]
         (let [new-options
@@ -138,9 +158,9 @@
                            [:div {:style {:margin-right "8px"
                                           :margin-left  "4px"}} title]]))]
           (if hr
-            [:hr.my-1 {:key "dropdown-hr"}]
+            [:hr.menu-separator {:key "dropdown-hr"}]
             (rum/with-key
-              (menu-link new-options child)
+              (menu-link new-options child nil)
               title))))
       (when links-footer links-footer)])
    opts))
@@ -418,7 +438,7 @@
                                        (if (and (gobj/get e "shiftKey") on-shift-chosen)
                                          (on-shift-chosen item)
                                          (on-chosen item)))}
-                     (if item-render (item-render item chosen?) item)))]]
+                     (if item-render (item-render item chosen?) item) nil))]]
 
              (if get-group-name
                (if-let [group-name (get-group-name item)]
@@ -445,24 +465,6 @@
       {:class       (if on? (if small? "translate-x-4" "translate-x-5") "translate-x-0")
        :aria-hidden "true"}]]]))
 
-;; `sequence` can be a list of symbols, a list of strings, or a string
-(defn render-keyboard-shortcut [sequence]
-  (let [sequence (if (string? sequence)
-                   (-> sequence ;; turn string into sequence
-                       (string/trim)
-                       (string/lower-case)
-                       (string/split  #" |\+"))
-                   sequence)]
-    [:span.keyboard-shortcut
-     (map-indexed (fn [i key]
-                    [:code {:key i}
-                   ;; Display "cmd" rather than "meta" to the user to describe the Mac
-                   ;; mod key, because that's what the Mac keyboards actually say.
-                     (if (or (= :meta key) (= "meta" key))
-                       (util/meta-key-name)
-                       (name key))])
-                  sequence)]))
-
 (defn keyboard-shortcut-from-config [shortcut-name]
   (let [default-binding (:binding (get shortcut-config/all-default-keyboard-shortcuts shortcut-name))
         custom-binding  (when (state/shortcuts) (get (state/shortcuts) shortcut-name))

+ 0 - 5
src/main/frontend/ui.css

@@ -270,11 +270,6 @@ html.is-mobile {
   }
 }
 
-.dropdown-wrapper {
-  background-color: var(--ls-primary-background-color, #fff);
-  min-width: 12rem;
-}
-
 .dropdown-caret {
   display: inline-block;
   width: 0;

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

@@ -338,10 +338,6 @@
    (defn stop-propagation [e]
      (when e (.stopPropagation e))))
 
-#?(:cljs
-   (defn cur-doc-top []
-     (.. js/document -documentElement -scrollTop)))
-
 #?(:cljs
    (defn element-top [elem top]
      (when elem

+ 27 - 6
src/main/logseq/api.cljs

@@ -102,8 +102,16 @@
          :preferred-start-of-week (state/get-start-of-week)
          :current-graph         (state/get-current-repo)
          :show-brackets         (state/show-brackets?)
+         :enabled-journals      (state/enable-journals?)
+         :enabled-flashcards    (state/enable-flashcards?)
          :me                    (state/get-me)}))))
 
+(def ^:export get_current_graph_configs
+  (fn []
+    (some-> (get (:config @state/state) (state/get-current-repo))
+            (normalize-keyword-for-json)
+            (bean/->js))))
+
 (def ^:export get_current_graph
   (fn []
     (when-let [repo (state/get-current-repo)]
@@ -442,6 +450,9 @@
   [block-uuid]
   (editor-handler/open-block-in-sidebar! (uuid block-uuid)))
 
+(defn ^:export new_block_uuid []
+  (str (db/new-block-id)))
+
 (def ^:export select_block
   (fn [block-uuid]
     (when-let [block (db-model/get-block-by-uuid block-uuid)]
@@ -456,8 +467,16 @@
 
 (def ^:export insert_block
   (fn [block-uuid-or-page-name content ^js opts]
-    (let [{:keys [before sibling isPageBlock properties]} (bean/->clj opts)
+    (let [{:keys [before sibling isPageBlock customUUID properties]} (bean/->clj opts)
           page-name (and isPageBlock block-uuid-or-page-name)
+          custom-uuid (or customUUID (:id properties))
+          _ (when (not (string/blank? custom-uuid))
+              (when-not (util/uuid-string? custom-uuid)
+                (throw (js/Error.
+                        (util/format "Illegal custom block UUID pattern (%s)." custom-uuid))))
+              (when (db-model/query-block-by-uuid custom-uuid)
+                (throw (js/Error.
+                        (util/format "Custom block UUID already exists (%s)." custom-uuid)))))
           block-uuid (if isPageBlock nil (uuid block-uuid-or-page-name))
           block-uuid' (if (and (not sibling) before block-uuid)
                         (let [block (db/entity [:block/uuid block-uuid])
@@ -477,11 +496,13 @@
                     before?)
           new-block (editor-handler/api-insert-new-block!
                       content
-                      {:block-uuid block-uuid'
-                       :sibling?   sibling?
-                       :before?    before?
-                       :page       page-name
-                       :properties properties})]
+                      {:block-uuid  block-uuid'
+                       :sibling?    sibling?
+                       :before?     before?
+                       :page        page-name
+                       :custom-uuid custom-uuid
+                       :properties  (merge properties
+                                           (when custom-uuid {:id custom-uuid}))})]
       (bean/->js (normalize-keyword-for-json new-block)))))
 
 (def ^:export insert_batch_block

Some files were not shown because too many files changed in this diff