Merge branch 'master' into refactor/vite-migration

This commit is contained in:
Mega Yu
2026-04-28 15:53:22 +08:00
65 changed files with 3938 additions and 1579 deletions

View File

@@ -81,6 +81,7 @@
[logseq.graph-parser.mldoc :as gp-mldoc]
[logseq.graph-parser.text :as text]
[logseq.outliner.property :as outliner-property]
[logseq.sdk.utils :as sdk-util]
[logseq.shui.dialog.core :as shui-dialog]
[logseq.shui.hooks :as hooks]
[logseq.shui.ui :as shui]
@@ -3252,6 +3253,134 @@
[block* result]
[nil result])))
(defn- build-block-renderer-children-props
[block]
(when-let [block-uuid (:block/uuid block)]
(let [repo (state/get-current-repo)
blocks (some->> (db/get-block-and-children repo block-uuid)
(map (fn [child-block]
(dissoc (db/pull (:db/id child-block)) :block.temp/load-status))))]
(or (some-> blocks
(tree/blocks->vec-tree block-uuid)
first
:block/children
sdk-util/normalize-keyword-for-json)
[]))))
(defn- build-block-renderer-match-context
([block]
(build-block-renderer-match-context block false))
([block include-children?]
(let [uuid-str (some-> (:block/uuid block) str)
page-title (or (some-> (:block/page block) :block/title)
(when (ldb/page? block) (:block/title block)))
properties-map (if-let [db-id (:db/id block)]
(->> (outliner-property/get-block-full-properties (db/get-db) db-id)
(map :db/ident)
(remove #(= % :logseq.property.class/properties))
(map (fn [property-id] [property-id (get block property-id)]))
(into {}))
(->> (:block/properties block)
(remove (fn [[property-id _]] (= property-id :logseq.property.class/properties)))
(into {})))
children (when include-children?
(build-block-renderer-children-props block))
props (cond-> {:blockId uuid-str
:properties (into {} (map (fn [[k v]]
[(subs (str k) 1)
(plugin-handler/serialize-property-value-for-plugin v)])
properties-map))}
uuid-str (assoc :uuid uuid-str)
page-title (assoc :page page-title)
(:block/title block) (assoc :content (:block/title block))
(get block :block/format :markdown) (assoc :format (name (get block :block/format :markdown)))
include-children? (assoc :children children))]
{:block-id uuid-str
:uuid uuid-str
:page page-title
:content (:block/title block)
:format (some-> (get block :block/format :markdown) name)
:properties-map properties-map
:props (clj->js props)})))
(defn- block-renderer-supported-view?
[{:keys [sidebar?]} property? table?]
(and (not sidebar?)
(not property?)
(not table?)))
(defn- block-renderer-display-mode
[{:keys [matched-block-renderer use-plugin-renderer? editing? plugin-renderer-error?]}]
(if (and matched-block-renderer use-plugin-renderer? (not editing?) (not plugin-renderer-error?))
:plugin
:outline))
(defn- show-block-renderer-plugin-toggle?
[display-mode {:keys [matched-block-renderer editing?]}]
(boolean (and matched-block-renderer (not editing?) (= :outline display-mode))))
(defn- show-block-renderer-outline-toggle?
[display-mode]
(= :plugin display-mode))
(rum/defc setup-plugin-renderer-effects!
[editing? switch-to-plugin-renderer!]
(let [*previous-editing? (hooks/use-ref editing?)]
(hooks/use-effect!
(fn []
(let [previous-editing? (.-current *previous-editing?)]
(when (and previous-editing? (not editing?))
(switch-to-plugin-renderer!))
(set! (.-current *previous-editing?) editing?))
(fn []))
[editing?])
[:<>]))
(defn- block-renderer-hides-outline-children?
[display-mode {:keys [matched-block-renderer]}]
(boolean (and (= :plugin display-mode)
(true? (:include-children matched-block-renderer)))))
(defn- block-renderer-outline-view
[config block uuid title table? property? edit-input-id editing? refs-count *hide-block-refs? *show-query? page-icon block-id collapsed?]
[:div.flex.flex-col.w-full
[:div.block-main-content.flex.flex-row.gap-2
(when page-icon
page-icon)
[:div.flex.flex-col.w-full
(let [parsed-block (merge block (block/parse-title-and-body uuid (get block :block/format :markdown) title))
hide-block-refs-count? (or (and (:embed? config)
(= (:block/uuid parsed-block) (:embed-id config)))
table?)]
(block-content-or-editor config
parsed-block
{:edit-input-id edit-input-id
:block-id block-id
:edit? editing?
:refs-count refs-count
:*hide-block-refs? *hide-block-refs?
:hide-block-refs-count? hide-block-refs-count?
:*show-query? *show-query?}))]]
(when (and (not collapsed?) (not (or table? property?)))
(block-positioned-properties config block :block-below))
(when-not (or (:table? config) (:property? config))
(block-reactions block))])
(rum/defcs block-renderer-error-boundary
< {:init (fn [state]
(assoc state ::on-error (some-> state :rum/args first :on-error)))
:did-catch (fn [state error _info]
(when-let [on-error (::on-error state)]
(on-error error))
(assoc state ::error error))}
[{error ::error} {:keys [fallback-view]} view]
(if (some? error)
fallback-view
view))
(rum/defcs ^:large-vars/cleanup-todo block-container-inner-aux < rum/reactive db-mixins/query
{:init (fn [state]
(let [*ref (atom nil)
@@ -3270,18 +3399,31 @@
::ref *ref
::hide-block-refs? (atom default-hide?)
::show-query? (atom false)
::refs-count *refs-count)))}
::refs-count *refs-count
::plugin-renderer-error? (atom false)
::use-plugin-renderer? (atom true))))}
(mixins/event-mixin
(fn [state]
(let [*ref (::ref state)]
;; React doesn't let us directly control passive via onTouchMove
;; So here we listen `touchmove` on the block node
;; React doesn't let us directly control passive via onTouchMove
;; So here we listen `touchmove` on the block node
(mixins/listen state @*ref "touchmove" block-handler/on-touch-move))))
[state container-state repo config* block {:keys [navigating-block navigated? editing? selected?] :as opts}]
(let [*ref (::ref state)
*hide-block-refs? (get state ::hide-block-refs?)
*show-query? (get state ::show-query?)
show-query? (rum/react *show-query?)
*plugin-renderer-error? (get state ::plugin-renderer-error?)
*use-plugin-renderer? (get state ::use-plugin-renderer?)
plugin-renderer-error? (rum/react *plugin-renderer-error?)
use-plugin-renderer? (rum/react *use-plugin-renderer?)
switch-to-plugin-renderer! (fn []
(reset! *plugin-renderer-error? false)
(reset! *use-plugin-renderer? true))
switch-to-outline-view! (fn []
(reset! *plugin-renderer-error? false)
(reset! *use-plugin-renderer? false))
set-plugin-renderer-error! #(reset! *plugin-renderer-error? %)
*refs-count (get state ::refs-count)
hide-block-refs? (rum/react *hide-block-refs?)
refs-count (rum/react *refs-count)
@@ -3303,10 +3445,13 @@
*control-show? (get container-state ::control-show?)
db-collapsed? (util/collapsed? block)
collapsed? (cond
(:ignore-block-collapsed? config)
false
(or ref-or-custom-query?
(:view? config)
(root-block? config block)
(and (or (ldb/class? block) (ldb/property? block)) (:page-title? config)))
(:view? config)
(root-block? config block)
(and (or (ldb/class? block) (ldb/property? block)) (:page-title? config)))
(state/sub-block-collapsed uuid container-id)
:else
@@ -3328,82 +3473,125 @@
page-icon (when (:page-title? config)
(let [icon' (get block :logseq.property/icon)]
(when-let [icon (and (ldb/page? block)
(or icon'
(some :logseq.property/icon (:block/tags block))
(when (ldb/class? block)
{:type :tabler-icon
:id "hash"})
(when (ldb/property? block)
{:type :tabler-icon
:id "letter-p"})))]
(or icon'
(some :logseq.property/icon (:block/tags block))
(when (ldb/class? block)
{:type :tabler-icon
:id "hash"})
(when (ldb/property? block)
{:type :tabler-icon
:id "letter-p"})))]
[:div.ls-page-icon.flex.self-start
(icon-component/icon-picker icon
{:on-chosen (fn [_e icon]
(if icon
(db-property-handler/set-block-property!
(:db/id block)
:logseq.property/icon
(select-keys icon [:id :type :color]))
;; del
(db-property-handler/remove-block-property!
(:db/id block)
:logseq.property/icon)))
:del-btn? (boolean icon')
:icon-props {:style {:width "1lh"
:height "1lh"
:font-size (cond
(and (util/mobile?) (:page-title? config)) 24
(:page-title? config) 38
:else 18)}}})])))]
{:on-chosen (fn [_e icon]
(if icon
(db-property-handler/set-block-property!
(:db/id block)
:logseq.property/icon
(select-keys icon [:id :type :color]))
;; del
(db-property-handler/remove-block-property!
(:db/id block)
:logseq.property/icon)))
:del-btn? (boolean icon')
:icon-props {:style {:width "1lh"
:height "1lh"
:font-size (cond
(and (util/mobile?) (:page-title? config)) 24
(:page-title? config) 38
:else 18)}}})])))
;; --- block renderer (full-block plugin replacement) ---
block-renderer-base-match-context
(when (and config/lsp-enabled?
(plugin-handler/any-block-renderers?)
(block-renderer-supported-view? config property? table?))
(build-block-renderer-match-context block false))
matched-block-renderer
(when (and (:props block-renderer-base-match-context) (not editing?))
(plugin-handler/get-matched-block-renderer block-renderer-base-match-context))
block-renderer-match-context
(if (true? (:include-children matched-block-renderer))
(build-block-renderer-match-context block true)
block-renderer-base-match-context)
block-renderer-props-js (:props block-renderer-match-context)
renderer-display-mode
(block-renderer-display-mode {:matched-block-renderer matched-block-renderer
:use-plugin-renderer? use-plugin-renderer?
:editing? editing?
:plugin-renderer-error? plugin-renderer-error?})
switch-to-plugin-renderer-title
(t (if plugin-renderer-error?
:block/retry-plugin-renderer
:block/switch-to-plugin-renderer))
switch-to-outline-view-title (t :block/switch-to-outline-view)
outline-view-cp
[:div.flex.flex-col.w-full
(block-renderer-outline-view config block uuid title table? property? edit-input-id editing? refs-count *hide-block-refs? *show-query? page-icon block-id collapsed?)
(when (show-block-renderer-plugin-toggle?
renderer-display-mode
{:matched-block-renderer matched-block-renderer
:editing? editing?})
(shui/button
{:variant :ghost
:size :icon
:class "self-start h-5 w-5 opacity-20 hover:opacity-70"
:title switch-to-plugin-renderer-title
:aria-label switch-to-plugin-renderer-title
:on-pointer-down util/stop
:on-click (fn [e]
(util/stop e)
(switch-to-plugin-renderer!))}
(shui/tabler-icon "puzzle-piece" {:size 13})))]]
[:div.ls-block.swipe-item
(cond->
{:id (str "ls-block-"
;; container-id "-"
uuid)
:blockid (str uuid)
:containerid container-id
:data-is-property (ldb/property? block)
:ref #(when (nil? @*ref) (reset! *ref %))
:data-collapsed (and collapsed? has-child?)
:class (str (when selected? "selected")
(when (ldb/recycled? block) " line-through opacity-70")
(when order-list? " is-order-list")
(when (string/blank? title) " is-blank")
(when original-block " embed-block"))
:haschild (str (boolean has-child?))
:on-touch-start (fn [event uuid]
(when-not (or @*dragging? (state/editing?))
(block-handler/on-touch-start event uuid)))
:on-touch-end (fn [event]
(when-not @*dragging?
(block-handler/on-touch-end event))
(reset! *dragging? false))
:on-touch-cancel (fn [e]
(block-handler/on-touch-cancel e))}
{:id (str "ls-block-"
;; container-id "-"
uuid)
:blockid (str uuid)
:containerid container-id
:data-is-property (ldb/property? block)
:ref #(when (nil? @*ref) (reset! *ref %))
:data-collapsed (and collapsed? has-child?)
:class (str (when selected? "selected")
(when (ldb/recycled? block) " line-through opacity-70")
(when order-list? " is-order-list")
(when (string/blank? title) " is-blank")
(when original-block " embed-block"))
:haschild (str (boolean has-child?))
:on-touch-start (fn [event uuid]
(when-not (or @*dragging? (state/editing?))
(block-handler/on-touch-start event uuid)))
:on-touch-end (fn [event]
(when-not @*dragging?
(block-handler/on-touch-end event))
(reset! *dragging? false))
:on-touch-cancel (fn [e]
(block-handler/on-touch-cancel e))}
(and (util/capacitor?) (not (ldb/page? block)))
(assoc
:draggable true
:on-drag-start
(fn [event]
(when-not (state/editing?)
(util/stop-propagation event)
(let [target ^js (.-target event)
blocks (or (seq (state/get-selection-blocks)) [target])
multiple? (> (count blocks) 1)
element (when multiple?
(let [element (dom/create-element "div")]
(-> element
(dom/set-attr! "id" "dragging-ghost-element")
(dom/set-text! (t :editor/moving-blocks-count (count blocks)))
(dom/set-class! "p-2 rounded text-sm"))
element))]
(doseq [block blocks]
(dom/add-class! block "dragging"))
(on-drag-start event block block-id)
(when element
(dom/append! js/document.body element)
(dnd/set-drag-image! event element (/ (.-offsetWidth target) 2) (/ (.-offsetHeight target) 2)))))))
:draggable true
:on-drag-start
(fn [event]
(when-not (state/editing?)
(util/stop-propagation event)
(let [target ^js (.-target event)
blocks (or (seq (state/get-selection-blocks)) [target])
multiple? (> (count blocks) 1)
element (when multiple?
(let [element (dom/create-element "div")]
(-> element
(dom/set-attr! "id" "dragging-ghost-element")
(dom/set-text! (t :editor/moving-blocks-count (count blocks)))
(dom/set-class! "p-2 rounded text-sm"))
element))]
(doseq [block blocks]
(dom/add-class! block "dragging"))
(on-drag-start event block block-id)
(when element
(dom/append! js/document.body element)
(dnd/set-drag-image! event element (/ (.-offsetWidth target) 2) (/ (.-offsetHeight target) 2)))))))
(:property-default-value? config)
(assoc :data-is-property-default-value (:property-default-value? config))
@@ -3453,48 +3641,49 @@
(when (and (not property?) (not (:table-block-title? config)))
(let [edit? (or editing?
(= uuid (:block/uuid (state/get-edit-block))))]
(= uuid (:block/uuid (state/get-edit-block))))]
(block-control (assoc config :hide-bullet? (:page-title? config))
block
(merge opts
{:uuid uuid
:block-id block-id
:collapsed? collapsed?
:*control-show? *control-show?
:edit? edit?}))))
block
(merge opts
{:uuid uuid
:block-id block-id
:collapsed? collapsed?
:*control-show? *control-show?
:edit? edit?}))))
[:div.flex.flex-col.w-full
[:div.block-main-content.flex.flex-row.gap-2
(when page-icon
page-icon)
(if (= :plugin renderer-display-mode)
;; --- Plugin renderer: full-block replacement ---
[:div.block-renderer-container.flex.flex-col.w-full
(when (show-block-renderer-outline-toggle? renderer-display-mode)
[:div.block-renderer-action-bar
(shui/button
{:variant :outline
:class "block-renderer-action-btn h-6 w-6"
:title switch-to-outline-view-title
:aria-label switch-to-outline-view-title
:on-pointer-down util/stop
:on-click (fn [e]
(util/stop e)
(switch-to-outline-view!))}
(shui/tabler-icon "list" {:size 13}))])
[:div.ls-block-plugin-renderer
(rum/with-key
(block-renderer-error-boundary
{:on-error (fn [_error]
(set-plugin-renderer-error! true))
:fallback-view outline-view-cp}
(when-some [renderer (:render matched-block-renderer)]
(js/React.createElement renderer block-renderer-props-js)))
(str "block-renderer-" (:key matched-block-renderer) "-" uuid))]]
;; Not embed self
[:div.flex.flex-col.w-full
(let [block (merge block (block/parse-title-and-body uuid (get block :block/format :markdown) title))
hide-block-refs-count? (or (and (:embed? config)
(= (:block/uuid block) (:embed-id config)))
table?)]
(block-content-or-editor config
block
{:edit-input-id edit-input-id
:block-id block-id
:edit? editing?
:refs-count refs-count
:*hide-block-refs? *hide-block-refs?
:hide-block-refs-count? hide-block-refs-count?
:*show-query? *show-query?}))]]
(when (and (not collapsed?) (not (or table? property?)))
(block-positioned-properties config block :block-below))
(when-not (or (:table? config) (:property? config))
(block-reactions block))]])
;; --- Original outline ---
outline-view-cp)])
(when (and (not (:library? config))
(or (:tag-dialog? config)
(and
(not collapsed?)
(not (or table? property?)))))
(or (:tag-dialog? config)
(and
(not collapsed?)
(not (or table? property?)))))
[:div (when-not (:page-title? config) {:style {:padding-left (if (util/mobile?) 12 45)}})
(db-properties-cp config block {:in-block-container? true})])
@@ -3510,15 +3699,15 @@
(block-container config query)])]))
(when (and (not (or (:table? config) (:property? config)))
(not hide-block-refs?)
(> refs-count 0)
(not (:page-title? config)))
(not hide-block-refs?)
(> refs-count 0)
(not (:page-title? config)))
(when-let [refs-cp (state/get-component :block/linked-references)]
[:div.px-4.py-2.border.rounded.my-2.shadow-xs {:style {:margin-left 42}}
(refs-cp block {})]))
(when (and (not collapsed?) (not (or table? property?))
(ldb/class-instance? (entity-plus/entity-memoized (db/get-db) :logseq.class/Query) block))
(ldb/class-instance? (entity-plus/entity-memoized (db/get-db) :logseq.class/Query) block))
(let [query-block (:logseq.property/query (db/entity (:db/id block)))
query-block (if query-block (db/sub-block (:db/id query-block)) query-block)
query (:block/title query-block)
@@ -3527,20 +3716,28 @@
(when query-block
[:div {:style {:padding-left 42}}
(query/custom-query (wrap-query-components (assoc config
:dsl-query? (not advanced-query?)
:cards? (ldb/class-instance? (entity-plus/entity-memoized
(db/get-db)
:logseq.class/Cards) block)))
(if advanced-query? result {:builder nil
:query (query-builder-component/sanitize-q query)}))])))
:dsl-query? (not advanced-query?)
:cards? (ldb/class-instance? (entity-plus/entity-memoized
(db/get-db)
:logseq.class/Cards) block)))
(if advanced-query? result {:builder nil
:query (query-builder-component/sanitize-q query)}))])))
(when-not (or (:hide-children? config) table? property?)
(when-not (or (:hide-children? config)
table?
property?
(block-renderer-hides-outline-children?
renderer-display-mode
{:matched-block-renderer matched-block-renderer}))
(let [config' (-> (update config :level inc)
(dissoc :original-block :data))]
(dissoc :original-block :data))]
(block-children config' block children collapsed?)))
(when-not (or table? property?)
(dnd-separator-wrapper block block-id false))]))
(dnd-separator-wrapper block block-id false))
(when config/lsp-enabled?
(setup-plugin-renderer-effects! editing? switch-to-plugin-renderer!))]))
(rum/defc block-container-inner
[container-state repo config* block opts]
@@ -3644,21 +3841,25 @@
(rum/defc block-container
[config block* & {:as opts}]
(let [[block set-block!] (hooks/use-state block*)
id (or (:db/id block*) (:block/uuid block*))]
(when-not (or (:page-title? config)
(:view? config))
(hooks/use-effect!
(fn []
id (or (:db/id block*) (:block/uuid block*))
temporary-collapsed-state (state/get-block-collapsed (:block/uuid block)
(:container-id config))
ignore-block-collapsed? (:ignore-block-collapsed? config)
load-children? (editor-handler/load-children? block
temporary-collapsed-state
ignore-block-collapsed?)]
(hooks/use-effect!
(fn []
(when-not (or (:page-title? config) (:view? config))
(p/let [block (db-async/<get-block (state/get-current-repo)
id
{:children? (not
(if-some [result (state/get-block-collapsed (:block/uuid block)
(:container-id config))]
result
(:block/collapsed? block)))
{:children? load-children?
:include-collapsed-children? (and load-children?
ignore-block-collapsed?)
:skip-refresh? false})]
(set-block! block)))
[]))
(set-block! block)))
nil)
[id load-children? ignore-block-collapsed? temporary-collapsed-state])
(when (or (:view? config) (:block/title block))
(loaded-block-container config block opts))))
@@ -4295,77 +4496,77 @@
[:div.only-page-blocks items]))]))
;; headers to hiccup
(defn ->hiccup
[blocks config option]
[:div.content
(cond-> option
(:document/mode? config) (assoc :class "doc-mode"))
(cond
(and (:custom-query? config) (:group-by-page? config))
[:div.flex.flex-col
(let [blocks (sort-by (comp :block/journal-day first) > blocks)]
(for [[page blocks] blocks]
(let [alias? (:block/alias? page)
page (db/entity (:db/id page))
blocks (tree/non-consecutive-blocks->vec-tree blocks)
parent-blocks (group-by :block/parent blocks)]
[:div.custom-query-page-result {:key (str "page-" (:db/id page))}
(ui/foldable
[:div
(page-cp config page)
(when alias? [:span.text-sm.font-medium.opacity-50 (str " " (t :property.built-in/alias))])]
(fn []
(let [{top-level-blocks true others false} (group-by
(fn [b] (= (:db/id page) (:db/id (first b))))
parent-blocks)
sorted-parent-blocks (concat top-level-blocks others)]
(for [[parent blocks] sorted-parent-blocks]
(let [top-level? (= (:db/id parent) (:db/id page))]
(rum/with-key
(breadcrumb-with-container blocks (assoc config :top-level? top-level?))
(:db/id parent))))))
{:debug-id page})])))]
(defn ->hiccup
[blocks config option]
[:div.content
(cond-> option
(:document/mode? config) (assoc :class "doc-mode"))
(cond
(and (:custom-query? config) (:group-by-page? config))
[:div.flex.flex-col
(let [blocks (sort-by (comp :block/journal-day first) > blocks)]
(for [[page blocks] blocks]
(let [alias? (:block/alias? page)
page (db/entity (:db/id page))
blocks (tree/non-consecutive-blocks->vec-tree blocks)
parent-blocks (group-by :block/parent blocks)]
[:div.custom-query-page-result {:key (str "page-" (:db/id page))}
(ui/foldable
[:div
(page-cp config page)
(when alias? [:span.text-sm.font-medium.opacity-50 (str " " (t :property.built-in/alias))])]
(fn []
(let [{top-level-blocks true others false} (group-by
(fn [b] (= (:db/id page) (:db/id (first b))))
parent-blocks)
sorted-parent-blocks (concat top-level-blocks others)]
(for [[parent blocks] sorted-parent-blocks]
(let [top-level? (= (:db/id parent) (:db/id page))]
(rum/with-key
(breadcrumb-with-container blocks (assoc config :top-level? top-level?))
(:db/id parent))))))
{:debug-id page})])))]
(and (:ref? config) (:group-by-page? config) (vector? (first blocks)))
[:div.flex.flex-col.references-blocks-wrap
(let [blocks (sort-by (comp :block/journal-day first) > blocks)
scroll-container (or (:scroll-container config)
(util/app-scroll-container-node))
scroll-container (if (fn? scroll-container)
(scroll-container) scroll-container)]
(when (seq blocks)
(if (:sidebar? config)
(for [block blocks]
(rum/with-key
(ref-block-container config block)
(str "ref-" (:container-id config) "-" (:db/id (first block)))))
(ui/virtualized-list
{:custom-scroll-parent scroll-container
:compute-item-key (fn [idx]
(let [block (nth blocks idx)]
(str "ref-" (:container-id config) "-" (:db/id (first block)))))
:total-count (count blocks)
:item-content (fn [idx]
(let [block (nth blocks idx)]
(ref-block-container config block)))}))))]
(and (:ref? config) (:group-by-page? config) (vector? (first blocks)))
[:div.flex.flex-col.references-blocks-wrap
(let [blocks (sort-by (comp :block/journal-day first) > blocks)
scroll-container (or (:scroll-container config)
(util/app-scroll-container-node))
scroll-container (if (fn? scroll-container)
(scroll-container) scroll-container)]
(when (seq blocks)
(if (:sidebar? config)
(for [block blocks]
(rum/with-key
(ref-block-container config block)
(str "ref-" (:container-id config) "-" (:db/id (first block)))))
(ui/virtualized-list
{:custom-scroll-parent scroll-container
:compute-item-key (fn [idx]
(let [block (nth blocks idx)]
(str "ref-" (:container-id config) "-" (:db/id (first block)))))
:total-count (count blocks)
:item-content (fn [idx]
(let [block (nth blocks idx)]
(ref-block-container config block)))}))))]
(and (:group-by-page? config)
(vector? (first blocks)))
[:div.flex.flex-col
(let [blocks (sort-by (comp :block/journal-day first) > blocks)]
(for [[page blocks] blocks]
(let [blocks (remove nil? blocks)]
(when (seq blocks)
(let [alias? (:block/alias? page)
page (db/entity (:db/id page))]
[:div.my-2 {:key (str "page-" (:db/id page))}
(ui/foldable
[:div
(page-cp config page)
(when alias? [:span.text-sm.font-medium.opacity-50 (str " " (t :property.built-in/alias))])]
(fn []
(blocks-container config blocks))
{})])))))]
(and (:group-by-page? config)
(vector? (first blocks)))
[:div.flex.flex-col
(let [blocks (sort-by (comp :block/journal-day first) > blocks)]
(for [[page blocks] blocks]
(let [blocks (remove nil? blocks)]
(when (seq blocks)
(let [alias? (:block/alias? page)
page (db/entity (:db/id page))]
[:div.my-2 {:key (str "page-" (:db/id page))}
(ui/foldable
[:div
(page-cp config page)
(when alias? [:span.text-sm.font-medium.opacity-50 (str " " (t :property.built-in/alias))])]
(fn []
(blocks-container config blocks))
{})])))))]
:else
(blocks-container config blocks))])
:else
(blocks-container config blocks))])

View File

@@ -1299,6 +1299,24 @@ html.is-mac {
@apply relative inline-block w-full;
}
.block-renderer-container {
@apply relative;
.block-renderer-action-bar {
@apply top-1 right-1 absolute z-10 flex items-center opacity-0 transition-opacity;
}
.block-renderer-action-btn {
@apply m-1 p-0.5 opacity-70 select-none hover:opacity-90 active:opacity-60;
}
&:hover, &:active, &:focus-within {
.block-renderer-action-bar {
@apply opacity-100;
}
}
}
.asset-transfer-placeholder {
@apply text-sm text-gray-11 mt-2;
}

View File

@@ -0,0 +1,169 @@
(ns frontend.components.plugin-logs
"Viewer panel for a single plugin's logs powered by PluginLogger."
(:require [cljs-bean.core :as bean]
[clojure.string :as string]
[frontend.context.i18n :refer [t]]
[frontend.handler.notification :as notification]
[frontend.ui :as ui]
[frontend.util :as util]
[logseq.shui.hooks :as hooks]
[logseq.shui.ui :as shui]
[rum.core :as rum]))
(def ^:private levels ["DEBUG" "INFO" "WARN" "ERROR"])
(defn- ^js get-plugin-logger
[pid]
(when-let [^js core (and pid (.-LSPluginCore js/window))]
(try (.getPluginLogger core pid) (catch :default _ nil))))
(defn- get-entries
[pid]
(when-let [^js logger (get-plugin-logger pid)]
(->> (.getEntries logger)
(bean/->clj))))
(defn- format-time
[ts]
(let [d (js/Date. ts)]
(.toLocaleTimeString d)))
(defn- entry->text
[{:keys [ts level tag message]}]
(str "[" (format-time ts) "] " level " [" tag "] " message))
(defn- copy-all!
[entries]
(-> (string/join "\n" (map entry->text entries))
(util/copy-to-clipboard!))
(notification/show! (t :plugin/logs-copied) :success))
(rum/defc ^:large-vars/cleanup-todo plugin-logs-panel
[{:keys [pid name]}]
(let [[entries set-entries!] (rum/use-state (or (get-entries pid) []))
[level-filter set-level-filter!] (rum/use-state nil)
[keyword-filter set-keyword-filter!] (rum/use-state "")
^js logger (get-plugin-logger pid)
refresh! (hooks/use-callback
(fn [] (set-entries! (or (get-entries pid) [])))
[pid])]
(hooks/use-effect!
(fn []
(when logger
(let [h (fn [] (refresh!))]
(.on logger "change" h)
#(.off logger "change" h))))
[logger refresh!])
(let [filtered (cond->> entries
(seq level-filter)
(filter #(= (:level %) level-filter))
(not (string/blank? keyword-filter))
(filter #(string/includes?
(string/lower-case (str (:message %)))
(string/lower-case keyword-filter)))
;; newest first
:always reverse)]
[:div.cp__plugins-logs.flex.flex-col.gap-3
{:style {:min-width "720px" :max-width "960px"}}
;; Row 1 - title + meta
[:div.cp__plugins-logs-head.flex.items-center.justify-between.gap-3.flex-wrap
[:h1.text-lg.font-semibold.flex.items-center.gap-2.m-0
(ui/icon "file-description")
[:span (t :plugin/logs-title)]
(when name
[:code.opacity-70.text-xs.px-1.5.py-0.5.rounded.bg-gray-03 name])]
[:div.flex.items-center.gap-3.text-xs.opacity-70
[:span (str (count filtered) " / " (count entries))]
(when logger
[:span.flex.items-center.gap-1
"level:" [:code (.getLevel logger)]])]]
;; Row 2 - controls
[:div.cp__plugins-logs-toolbar.flex.items-center.gap-2.flex-wrap
;; keyword filter (flex-grow)
[:div.relative.flex-1.min-w-48.flex.items-center
[:span.absolute.opacity-50.pointer-events-none.flex.items-center
{:style {:left "8px" :top "0" :bottom "0"}}
(ui/icon "search" {:size 14})]
[:input.form-input.text-xs.h-8.w-full
{:style {:paddingLeft "28px"}
:placeholder (t :plugin/logs-filter-placeholder)
:value keyword-filter
:on-change #(set-keyword-filter! (util/evalue %))}]]
;; level filter
(shui/select
{:value (or level-filter "*")
:on-value-change (fn [v] (set-level-filter! (if (= v "*") nil v)))}
(shui/select-trigger
{:class "w-32 h-8 text-xs shrink-0"}
(shui/select-value {:placeholder (t :plugin/logs-level-all)}))
(shui/select-content
(shui/select-item {:value "*"} (t :plugin/logs-level-all))
(for [lvl levels]
(shui/select-item {:value lvl :key lvl} lvl))))
[:div.h-5.w-px.bg-gray-05.shrink-0]
;; copy (chronological order for readability when pasted)
(shui/button
{:size :sm :variant :outline
:class "h-8 shrink-0"
:on-click #(copy-all! (reverse filtered))
:title (t :plugin/logs-copy)}
(ui/icon "copy" {:size 14})
[:span.ml-1.5 (t :plugin/logs-copy)])
;; clear
(shui/button
{:size :sm :variant :outline
:class "h-8 shrink-0"
:on-click (fn []
(when logger
(.clear logger)
(refresh!)))
:title (t :plugin/logs-clear)}
(ui/icon "trash" {:size 14})
[:span.ml-1.5 (t :plugin/logs-clear)])]
;; Body - log list
[:div.cp__plugins-logs-body.text-xs.font-mono.rounded.border
{:style {:height "60vh" :overflow "auto" :padding "8px"
:background "var(--ls-secondary-background-color)"}}
(if (empty? filtered)
[:div.opacity-60.p-4.text-center (t :plugin/logs-empty)]
(for [[idx e] (map-indexed vector filtered)]
[:div.cp__plugins-logs-row.flex.gap-1.py-0.5.items-start
{:key (str idx "-" (:ts e))
:data-level (:level e)}
[:span.opacity-60.shrink-0 {:style {:width "80px"}}
(format-time (:ts e))]
[:span.shrink-0.font-bold
{:style {:width "56px"
:color (case (:level e)
"ERROR" "var(--ls-error-text-color, #ef4444)"
"WARN" "#d97706"
"INFO" "var(--ls-active-primary-color)"
"DEBUG" "var(--ls-icon-color)"
nil)}}
(:level e)]
[:span.opacity-70.shrink-0 (str "[" (:tag e) "]")]
[:span.whitespace-pre-wrap.break-all.flex-1 (:message e)]]))]])))
(defn open-plugin-logs!
[{:keys [_pid _name] :as opts}]
(shui/dialog-open!
(fn [] (plugin-logs-panel opts))
{:label "plugin-logs-modal"
:class "lsp-plugin-logs-dialog"
:content-props {:on-open-auto-focus #(.preventDefault %)}}))

View File

@@ -3,6 +3,7 @@
[clojure.string :as string]
[electron.ipc :as ipc]
[frontend.components.plugins-settings :as plugins-settings]
[frontend.components.plugin-logs :as plugin-logs]
[frontend.components.svg :as svg]
[frontend.config :as config]
[frontend.context.i18n :refer [interpolate-rich-text interpolate-rich-text-node t]]
@@ -258,6 +259,7 @@
[:strong (ui/icon "settings")]
[:ul.menu-list
[:li {:on-click #(plugin-handler/open-plugin-settings! id false)} (t :plugin/open-settings)]
[:li {:on-click #(plugin-logs/open-plugin-logs! {:pid id :name name})} (t :plugin/open-logs)]
(when (util/electron?)
[:li {:on-click #(js/apis.openPath url)} (t :plugin/open-package)])
[:li {:on-click #(plugin-handler/open-report-modal! id name)} (t :plugin/report-security)]

View File

@@ -16,6 +16,7 @@
[frontend.db.model :as db-model]
[frontend.handler.db-based.property :as db-property-handler]
[frontend.handler.notification :as notification]
[frontend.handler.plugin :as plugin-handler]
[frontend.handler.property :as property-handler]
[frontend.handler.route :as route-handler]
[frontend.mixins :as mixins]
@@ -65,6 +66,13 @@
property)
(notification/show! (t :property.validation/invalid-name) :error)))))
(defn- enable-block-properties-renderers?
[{:keys [sidebar? sidebar-properties?]} class?]
(and config/lsp-enabled?
(not class?)
(not sidebar?)
(not sidebar-properties?)))
;; TODO: This component should be cleaned up as it's only used for new properties and used to be used for existing properties
(rum/defcs property-type-select <
shortcut/disable-all-shortcuts
@@ -827,28 +835,66 @@
:else
(let [remove-properties #{:logseq.property/icon :logseq.property/query}
properties' (->> (remove (fn [[k _v]] (contains? remove-properties k))
full-properties)
(remove (fn [[k _v]] (= k :logseq.property.class/properties))))
full-properties)
(remove (fn [[k _v]] (= k :logseq.property.class/properties))))
page? (entity-util/page? block)
class? (entity-util/class? block)]
class? (entity-util/class? block)
plugin-properties (->> (concat full-properties hidden-properties)
(remove (fn [[k _v]] (= k :logseq.property.class/properties)))
(into {}))
props-for-plugin (when (enable-block-properties-renderers? opts class?)
(clj->js {:blockId (str (:block/uuid block))
:properties (into {} (map (fn [[k v]]
[(subs (str k) 1)
(plugin-handler/serialize-property-value-for-plugin v)])
plugin-properties))}))
plugin-renderers (when props-for-plugin
(plugin-handler/get-matched-block-properties-renderers
{:block-id (str (:block/uuid block))
:properties-map plugin-properties
:props props-for-plugin}))
prepend-renderers (filter #(= "prepend" (:mode %)) plugin-renderers)
replace-renderer (first (filter #(= "replace" (:mode %)) plugin-renderers))
append-renderers (remove #(contains? #{"prepend" "replace"} (:mode %)) plugin-renderers)]
[:div.ls-properties-area
{:id id
:class (util/classnames [{:ls-page-properties page?}])
:tab-index 0}
[:<>
(properties-section block properties' opts)
(bidirectional-properties-section bidirectional-properties)
(mapv (fn [r]
(when (fn? (:render r))
(rum/with-key
(js/React.createElement (:render r) props-for-plugin)
(str "plugin-prepend-" (:key r)))))
prepend-renderers)
(if replace-renderer
(when (fn? (:render replace-renderer))
(rum/with-key
(js/React.createElement (:render replace-renderer) props-for-plugin)
(str "plugin-replace-" (:key replace-renderer))))
[:<>
(properties-section block properties' opts)
(bidirectional-properties-section bidirectional-properties)])
(when-not class?
(hidden-properties-cp block hidden-properties
(assoc opts :root-block? root-block?)))
(assoc opts :root-block? root-block?)))
(when (and page? (not class?))
(rum/with-key (new-property block opts) (str id "-add-property")))
(mapv (fn [r]
(when (fn? (:render r))
(rum/with-key
(js/React.createElement (:render r) props-for-plugin)
(str "plugin-append-" (:key r)))))
append-renderers)
(when class?
(let [properties (->> (:logseq.property.class/properties block)
(map (fn [e] [(:db/ident e)])))
(map (fn [e] [(:db/ident e)])))
opts' (assoc opts :class-schema? true)]
[:div.flex.flex-col.gap-1
[:div {:style {:font-size 15}}
@@ -860,5 +906,5 @@
[:div.ml-4
(properties-section block properties opts')
(hidden-properties-cp block hidden-properties
(assoc opts :root-block? root-block?))
(assoc opts :root-block? root-block?))
(rum/with-key (new-property block opts') (str id "-class-add-property"))]]))]])))]))

View File

@@ -659,6 +659,63 @@
{:left-label (t :settings.sync-server/url)
:action (sync-server-url-button)}))
(rum/defc publish-server-url-settings-container
[]
(let [current-url (config/get-custom-publish-server-url)
[url set-url!] (rum/use-state (or current-url ""))
reset-url! (fn []
(config/set-custom-publish-server-url! nil)
(set-url! "")
(notification/show! (t :settings-page/publish-server-url-cleared) :success))]
[:div.cp__settings-publish-server-cnt
[:h1.mb-2.text-2xl.font-bold (t :settings-page/publish-server-url)]
[:div.p-2
[:p.text-sm.opacity-70.mb-4 (t :settings-page/publish-server-url-desc)]
[:p
[:label
[:strong "URL"]
[:input.form-input.is-small
{:value url
:placeholder config/default-publish-api-base
:style {:width "100%"}
:on-change #(set-url! (util/evalue %))}]]]
[:p.pt-2.flex.gap-2
(shui/button
{:size :sm
:on-click (fn []
(let [trimmed (string/trim url)]
(if (string/blank? trimmed)
(reset-url!)
(if-not (config/valid-publish-server-url? trimmed)
(notification/show! (t :settings.sync-server/url-invalid-error) :error)
(do
(config/set-custom-publish-server-url! trimmed)
(notification/show! (t :settings-page/publish-server-url-saved) :success))))))}
(t :ui/save))
(when (seq url)
(shui/button
{:size :sm
:variant :outline
:on-click (fn [] (reset-url!))}
(t :settings-page/publish-server-url-reset)))]]]))
(rum/defc publish-server-url-button
[]
(let [current-url (config/get-custom-publish-server-url)]
(ui/button [:span.flex.items-center
[:span.pr-1
(if (seq current-url)
current-url
(t :settings-page/publish-server-url-default))]
(ui/icon "edit")]
:class "text-sm"
:on-click #(state/pub-event! [:go/publish-server-settings]))))
(defn publish-server-url-row []
(row-with-button-action
{:left-label (t :settings-page/publish-server-url)
:action (publish-server-url-button)}))
(rum/defc user-proxy-settings
[{:keys [type protocol host port] :as agent-opts}]
(ui/button [:span.flex.items-center
@@ -774,6 +831,7 @@
(usage-diagnostics-row t instrument-disabled?)
(when-not (mobile-util/native-platform?) (developer-mode-row t developer-mode?))
(sync-server-url-row)
(publish-server-url-row)
(when (util/electron?) (https-user-agent-row https-agent-opts))
(when (util/electron?) (auto-chmod-row t))
;; (clear-cache-row t)

View File

@@ -33,7 +33,7 @@
(def USER-POOL-ID "us-east-1_dtagLnju8")
(def IDENTITY-POOL-ID "us-east-1:d6d3b034-1631-402b-b838-b44513e93ee0")
(def OAUTH-DOMAIN "logseq-prod.auth.us-east-1.amazoncognito.com")
(def PUBLISH-API-BASE "https://logseq.io"))
(def default-publish-api-base "https://logseq.io"))
(do (def API-DOMAIN "api-dev.logseq.com")
(def COGNITO-IDP "https://cognito-idp.us-east-2.amazonaws.com/")
@@ -42,10 +42,10 @@
(def USER-POOL-ID "us-east-2_kAqZcxIeM")
(def IDENTITY-POOL-ID "us-east-2:cc7d2ad3-84d0-4faf-98fe-628f6b52c0a5")
(def OAUTH-DOMAIN "logseq-test2.auth.us-east-2.amazoncognito.com")
(def PUBLISH-API-BASE "https://logseq-publish-staging.logseq.workers.dev")))
(def default-publish-api-base "https://logseq-publish-staging.logseq.workers.dev")))
;; Enable for local development
;; (def PUBLISH-API-BASE "http://localhost:8787")
;; (def default-publish-api-base "http://localhost:8787")
(goog-define ENABLE-DB-SYNC-LOCAL false)
(defonce db-sync-local? ENABLE-DB-SYNC-LOCAL)
@@ -114,6 +114,43 @@
(custom-url->http-base custom)
default-db-sync-http-base))
(defn get-custom-publish-server-url
"Read the user-configured custom publish server URL from localStorage.
Returns nil when not set or empty."
[]
(when-not util/node-test?
(let [v (.getItem js/localStorage "publish-server-url")]
(when (and (string? v) (not (string/blank? v)))
v))))
(defn set-custom-publish-server-url!
"Persist the custom publish server URL to localStorage. Pass nil or empty string to clear."
[url]
(when-not util/node-test?
(if (or (nil? url) (string/blank? url))
(.removeItem js/localStorage "publish-server-url")
(.setItem js/localStorage "publish-server-url" (string/trim url)))))
(defn valid-publish-server-url?
"Return true when `url` looks like a valid HTTP(S) base URL."
[url]
(and (string? url)
(re-find #"^https?://" url)))
(defn custom-url->publish-api-base
"Normalize a custom publish base URL by stripping trailing slashes. Pure function."
[custom-url]
(string/replace custom-url #"/+$" ""))
(defn publish-api-base
"Return the base URL for the single-page publish service. Uses the user-configured
URL from localStorage when set, otherwise the default from the ENABLE-FILE-SYNC-PRODUCTION
branch above. Read on each call so URL changes take effect without a restart."
[]
(if-let [custom (get-custom-publish-server-url)]
(custom-url->publish-api-base custom)
default-publish-api-base))
;; Feature flags
;; =============

View File

@@ -22,19 +22,36 @@
[reitit.frontend :as rf]
[reitit.frontend.easy :as rfe]))
(defn set-router!
(defn- build-router
[]
(.addEventListener js/window "popstate" route-handler/restore-scroll-pos)
(rfe/start!
(rf/router (plugins/hook-custom-routes routes/routes) nil)
(fn [route]
(route-handler/set-route-match! route)
(plugin-handler/hook-plugin-app
:route-changed (select-keys route [:template :path :parameters])))
(rf/router (plugins/hook-custom-routes routes/routes) nil))
(defn- on-navigate
[route]
(route-handler/set-route-match! route)
(plugin-handler/hook-plugin-app
:route-changed (select-keys route [:template :path :parameters])))
(defn refresh-router!
"Rebuilds the reitit router so route renderers registered by plugins after
the app started take effect. Safe to call repeatedly; `rfe/start!` stops the
previous history instance internally."
[]
(rfe/start!
(build-router)
on-navigate
;; set to false to enable HistoryAPI
{:use-fragment true}))
(defonce ^:private *popstate-installed? (atom false))
(defn set-router!
[]
(when (compare-and-set! *popstate-installed? false true)
(.addEventListener js/window "popstate" route-handler/restore-scroll-pos))
(refresh-router!)
(plugin-handler/set-route-renderer-refresh-fn! refresh-router!))
(defn display-welcome-message
[]
(js/console.log

View File

@@ -237,7 +237,8 @@
:show-cloze
{:show-cloze? true
:hide-children? true}
{:show-cloze? true})]
{:show-cloze? true
:ignore-block-collapsed? true})]
(component-block/blocks-container option [block-entity]))
[:div.mt-8.pb-2
(if (contains? #{:show-cloze :show-answer} next-phase)

View File

@@ -3455,7 +3455,8 @@
[block config]
(let [block (or (db/entity (:db/id block)) block)]
(or
(util/collapsed? block)
(and (not (:ignore-block-collapsed? config))
(util/collapsed? block))
(and (util/mobile?) (ldb/class-instance? (entity-plus/entity-memoized (db/get-db) :logseq.class/Query) block))
(and (or (:list-view? config) (:ref? config))
(or (:block/_parent block) (:block.temp/has-children? block))
@@ -3466,6 +3467,13 @@
(or (ldb/page? block)
(:table-block-title? config))))))
(defn load-children?
[block temporary-collapsed-state ignore-block-collapsed?]
(or ignore-block-collapsed?
(not (if-some [result temporary-collapsed-state]
result
(:block/collapsed? block)))))
(defn batch-set-heading!
[block-ids heading]
(db-editor-handler/batch-set-heading! block-ids heading))

View File

@@ -12,15 +12,9 @@
[missionary.core :as m]
[promesa.core :as p]))
(defn rtc-collaborators-dialog?
[]
(= :rtc-collaborators (state/get-modal-id)))
(defmethod events/handle :rtc/decrypt-user-e2ee-private-key [[_ encrypted-private-key]]
(let [private-key-promise (p/deferred)
refresh-token (str (state/get-auth-refresh-token))]
(when-not (rtc-collaborators-dialog?)
(shui/dialog-close-all!))
(->
(p/let [{:keys [password]} (state/<invoke-db-worker :thread-api/get-e2ee-password refresh-token)
private-key (crypt/<decrypt-private-key password encrypted-private-key)]
@@ -39,8 +33,6 @@
(defmethod events/handle :rtc/request-e2ee-password [[_]]
(let [password-promise (p/deferred)]
(when-not (rtc-collaborators-dialog?)
(shui/dialog-close-all!))
(shui/dialog-open!
#(e2ee/e2ee-request-new-password password-promise)
{:auto-width? true

View File

@@ -87,6 +87,11 @@
(settings/sync-server-url-settings-container)
{:id :sync-server-panel :center? true :class "lg:max-w-2xl"}))
(defmethod events/handle :go/publish-server-settings [[_]]
(shui/dialog-open!
(settings/publish-server-url-settings-container)
{:id :publish-server-panel :center? true :class "lg:max-w-2xl"}))
(defmethod events/handle :redirect-to-home [_]
(page-handler/create-today-journal!)
(when (util/capacitor?)

File diff suppressed because it is too large Load Diff

View File

@@ -30,15 +30,15 @@
(defn- publish-endpoint
[]
(str config/PUBLISH-API-BASE "/pages"))
(str (config/publish-api-base) "/pages"))
(defn- publish-page-endpoint
[graph-uuid page-uuid]
(str config/PUBLISH-API-BASE "/pages/" graph-uuid "/" page-uuid))
(str (config/publish-api-base) "/pages/" graph-uuid "/" page-uuid))
(defn- asset-upload-endpoint
[]
(str config/PUBLISH-API-BASE "/assets"))
(str (config/publish-api-base) "/assets"))
(defn- asset-content-type
[ext]
@@ -382,10 +382,11 @@
data (bean/->clj json)]
(let [short-url (:short_url data)
page-id (str (:block/uuid page))
api-base (config/publish-api-base)
fallback-url (when (and graph-id page-id)
(str config/PUBLISH-API-BASE "/page/" graph-id "/" page-id))
(str api-base "/page/" graph-id "/" page-id))
url (or (when short-url
(str config/PUBLISH-API-BASE short-url))
(str api-base short-url))
fallback-url)]
(when (and url (:db/id page))
(property-handler/set-block-property! (:db/id page)

View File

@@ -50,7 +50,8 @@
[block & {:as opts}]
(op-transact!
(when-let [block' (if (de/entity? block)
(dissoc (.-kv ^js block) :db/id)
(-> (dissoc (.-kv ^js block) :db/id)
(assoc :block/uuid (:block/uuid block)))
block)]
[:save-block [block' opts]])))

View File

@@ -29,6 +29,7 @@
(s/def :copy/export-block-text-remove-options set?)
(s/def :copy/export-block-text-other-options map?)
(s/def ::sync-server-url string?)
(s/def ::publish-server-url string?)
;; Dynamic keys which aren't as easily validated:
;; :ls-pdf-last-page-*
;; :ls-js-allowed-*
@@ -68,4 +69,5 @@
:copy/export-block-text-indent-style
:copy/export-block-text-remove-options
:copy/export-block-text-other-options
::sync-server-url]))
::sync-server-url
::publish-server-url]))

View File

@@ -1,6 +1,7 @@
(ns logseq.sdk.experiments
(:require [frontend.components.page :as page]
[frontend.handler.plugin :as plugin-handler]
[lambdaisland.glogi :as log]
[frontend.state :as state]
[frontend.util :as util]
[logseq.sdk.utils :as sdk-util]))
@@ -11,43 +12,89 @@
page-name (some-> props1 :page)]
(when-let [entity (page/get-page-entity page-name)]
(page/page-blocks-cp
entity {:container-id (state/get-next-container-id)}))))
entity {:container-id (state/get-next-container-id)}))))
(defn ^:export register_fenced_code_renderer
[pid type ^js opts]
(when-let [^js _pl (plugin-handler/get-plugin-inst pid)]
(plugin-handler/register-fenced-code-renderer
(keyword pid) type (reduce #(assoc %1 %2 (aget opts (name %2))) {}
[:edit :before :subs :render]))))
(keyword pid) type (reduce #(assoc %1 %2 (aget opts (name %2))) {}
[:edit :before :subs :render]))))
(defn ^:export register_route_renderer
[pid key ^js opts]
(when-let [^js _pl (plugin-handler/get-plugin-inst pid)]
(let [key (util/safe-keyword key)]
(plugin-handler/register-route-renderer
(keyword pid) key
(reduce (fn [r k]
(assoc r k (cond-> (aget opts (name k))
(= :name k)
(#(if % (util/safe-keyword %) key)))))
{} [:v :name :path :subs :render])))))
(keyword pid) key
(reduce (fn [r k]
(assoc r k (cond-> (aget opts (name k))
(= :name k)
(#(if % (util/safe-keyword %) key)))))
{} [:v :name :path :subs :render])))))
(defn ^:export register_daemon_renderer
[pid key ^js opts]
(when-let [^js _pl (plugin-handler/get-plugin-inst pid)]
(plugin-handler/register-daemon-renderer
(keyword pid) key (reduce #(assoc %1 %2 (aget opts (name %2))) {}
[:before :subs :render]))))
(keyword pid) key (reduce #(assoc %1 %2 (aget opts (name %2))) {}
[:before :subs :render]))))
(defn- extract-js-renderer-opts
"Extract keys from a JS opts object into a clj map.
`transforms` is an optional map of {keyword transform-fn} for per-key processing.
Keys whose JS value is nil/undefined are omitted."
[^js opts ks transforms]
(reduce (fn [r k]
(let [v (aget opts (name k))]
(if (some? v)
(assoc r k (if-let [xf (get transforms k)]
(xf v)
v))
r)))
{} ks))
(defn ^:export register_hosted_renderer
[pid key ^js opts]
(when-let [^js _pl (plugin-handler/get-plugin-inst pid)]
(plugin-handler/register-hosted-renderer
(keyword pid) key (reduce #(assoc %1 %2 (aget opts (name %2))) {}
[:title :type :mode :subs :render]))))
(keyword pid) key
(extract-js-renderer-opts opts [:title :type :mode :subs :render] nil))))
(defn ^:export register_block_properties_renderer
[pid key ^js opts]
(when-let [^js _pl (plugin-handler/get-plugin-inst pid)]
(let [clj-opts (extract-js-renderer-opts
opts
[:when :mode :priority :subs :render]
{:when (fn [v]
(if (fn? v)
v
(js->clj v :keywordize-keys true)))})]
(plugin-handler/register-hosted-renderer
(keyword pid) key (assoc clj-opts :type "block-properties")))))
(defn ^:export register_block_renderer
[pid key ^js opts]
(when-let [^js _pl (plugin-handler/get-plugin-inst pid)]
(let [when-predicate (aget opts "when")]
(if (and (some? when-predicate) (not (fn? when-predicate)))
(log/error :register-block-renderer-invalid-when
{:pid pid
:key key
:message "`when` for registerBlockRenderer must be a synchronous predicate function."})
(let [include-children (aget opts "includeChildren")
clj-opts (extract-js-renderer-opts
opts
[:when :priority :subs :render]
nil)]
(plugin-handler/register-hosted-renderer
(keyword pid) key (cond-> (assoc clj-opts :type "block")
(some? include-children)
(assoc :include-children include-children))))))))
(defn ^:export register_extensions_enhancer
[pid type enhancer]
(when-let [^js _pl (and (fn? enhancer) (plugin-handler/get-plugin-inst pid))]
(plugin-handler/register-extensions-enhancer
(keyword pid) type {:enhancer enhancer})))
(keyword pid) type {:enhancer enhancer})))