Merge branch 'master' into fix/wb-fixes

This commit is contained in:
Gabriel Horner
2022-11-18 12:35:41 -05:00
committed by GitHub
66 changed files with 1355 additions and 800 deletions

View File

@@ -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

View File

@@ -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');
})

View File

@@ -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')

View File

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

View File

@@ -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);

View File

@@ -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]

View File

@@ -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

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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})

View File

@@ -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!

View File

@@ -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ı"

View File

@@ -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
[:img {:src asset-path}]])))
[: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")])
(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))))
[: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 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)))

View File

@@ -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)
(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})]
;; TODO: debug
(set! (. js/window -lsPdfViewer) viewer)
(. link-service setDocument pdf-document)
(. link-service setViewer viewer)
(p/then (. viewer setDocument pdf-document)
#(set-state! {:viewer viewer :bus event-bus :link link-service :el el}))
;; events
(doto event-bus
;; it must be initialized before set-up document
(.on "pagesinit"
(fn []
(set! (. viewer -currentScaleValue) "auto")
(set-page-ready! true)))
;;TODO: destroy
(fn []
(when-let [last-page (.-currentPageNumber viewer)]
(storage/set (str "ls-pdf-last-page-" (util/node-path.basename url)) last-page))
(.on (name :ls-update-extra-state)
#(when-let [extra (bean/->clj %)]
(apply (:set-hls-extra! ops) [extra]))))
(when pdf-document (.destroy pdf-document)))))
(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/pdfjs-dist@2.8.335/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}]

View File

@@ -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;

View File

@@ -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

View File

@@ -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
[]

View File

@@ -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)]))]])

View File

@@ -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))

View File

@@ -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)

View File

@@ -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;

View File

@@ -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]

View File

@@ -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)))))

View File

@@ -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!
[]

View File

@@ -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)))))))

View File

@@ -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

View File

@@ -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"

View File

@@ -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"]`

View File

@@ -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",

View File

@@ -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>

View File

@@ -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')}

View File

@@ -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>
)
})

View File

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

View File

@@ -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>
)

View File

@@ -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>
)
})

View File

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

File diff suppressed because one or more lines are too long

View File

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

View File

@@ -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>

View File

@@ -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,
},

View File

@@ -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'

View File

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

View File

@@ -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
}

View File

@@ -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']
}

View File

@@ -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

View File

@@ -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
}

View File

@@ -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'

View File

@@ -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)

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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);

View File

@@ -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"
}

View File

@@ -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: {

View File

@@ -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",

View File

@@ -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

View File

@@ -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)

View File

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

View File

@@ -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 = {

View File

@@ -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)
}

View File

@@ -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

View File

@@ -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]

View File

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

View File

@@ -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(() => {

View File

@@ -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",

File diff suppressed because it is too large Load Diff

View File

@@ -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"