mirror of
https://github.com/logseq/logseq.git
synced 2026-05-01 17:36:33 +00:00
Merge branch 'master' into feat/ai-lab
This commit is contained in:
@@ -23,8 +23,8 @@
|
||||
[frontend.context.i18n :refer [t]]
|
||||
[frontend.date :as date]
|
||||
[frontend.db :as db]
|
||||
[frontend.db-mixins :as db-mixins]
|
||||
[frontend.db.model :as model]
|
||||
[frontend.db-mixins :as db-mixins]
|
||||
[frontend.extensions.highlight :as highlight]
|
||||
[frontend.extensions.latex :as latex]
|
||||
[frontend.extensions.lightbox :as lightbox]
|
||||
@@ -53,6 +53,7 @@
|
||||
[frontend.mobile.util :as mobile-util]
|
||||
[frontend.modules.outliner.tree :as tree]
|
||||
[frontend.security :as security]
|
||||
[frontend.shui :refer [get-shui-component-version make-shui-context]]
|
||||
[frontend.state :as state]
|
||||
[frontend.template :as template]
|
||||
[frontend.ui :as ui]
|
||||
@@ -74,6 +75,7 @@
|
||||
[logseq.graph-parser.util.block-ref :as block-ref]
|
||||
[logseq.graph-parser.util.page-ref :as page-ref]
|
||||
[logseq.graph-parser.whiteboard :as gp-whiteboard]
|
||||
[logseq.shui.core :as shui]
|
||||
[medley.core :as medley]
|
||||
[promesa.core :as p]
|
||||
[reitit.frontend.easy :as rfe]
|
||||
@@ -293,8 +295,8 @@
|
||||
(js/setTimeout #(reset! *resizing-image? false) 200)))
|
||||
:onClick (fn [e]
|
||||
(when @*resizing-image? (util/stop e)))}
|
||||
(and (:width metadata) (not (util/mobile?)))
|
||||
(assoc :style {:width (:width metadata)}))
|
||||
(and (:width metadata) (not (util/mobile?)))
|
||||
(assoc :style {:width (:width metadata)}))
|
||||
{})
|
||||
[:div.asset-container {:key "resize-asset-container"}
|
||||
[:img.rounded-sm.relative
|
||||
@@ -938,9 +940,8 @@
|
||||
inner)])
|
||||
[:span.warning.mr-1 {:title "Block ref invalid"}
|
||||
(block-ref/->block-ref id)]))
|
||||
[:span.warning.mr-1 {:title "Block ref invalid"}
|
||||
(block-ref/->block-ref id)]
|
||||
))
|
||||
[:span.warning.mr-1 {:title "Block ref invalid"}
|
||||
(block-ref/->block-ref id)]))
|
||||
|
||||
(defn inline-text
|
||||
([format v]
|
||||
@@ -1119,8 +1120,8 @@
|
||||
{:href (path/path-join "file://" path)
|
||||
:data-href path
|
||||
:target "_blank"}
|
||||
title
|
||||
(assoc :title title))
|
||||
title
|
||||
(assoc :title title))
|
||||
(map-inline config label)))
|
||||
|
||||
:else
|
||||
@@ -1213,8 +1214,8 @@
|
||||
(cond->
|
||||
{:href (ar-url->http-url href)
|
||||
:target "_blank"}
|
||||
title
|
||||
(assoc :title title))
|
||||
title
|
||||
(assoc :title title))
|
||||
(map-inline config label))
|
||||
|
||||
:else
|
||||
@@ -1223,11 +1224,11 @@
|
||||
(cond->
|
||||
{:href href
|
||||
:target "_blank"}
|
||||
title
|
||||
(assoc :title title))
|
||||
title
|
||||
(assoc :title title))
|
||||
(map-inline config label)))))))
|
||||
|
||||
(declare ->hiccup)
|
||||
(declare ->hiccup inline)
|
||||
|
||||
(defn wrap-query-components
|
||||
[config]
|
||||
@@ -1236,7 +1237,8 @@
|
||||
:->elem ->elem
|
||||
:page-cp page-cp
|
||||
:inline-text inline-text
|
||||
:map-inline map-inline}))
|
||||
:map-inline map-inline
|
||||
:inline inline}))
|
||||
|
||||
;;;; Macro component render functions
|
||||
(defn- macro-query-cp
|
||||
@@ -1751,7 +1753,7 @@
|
||||
order-list? (boolean own-number-list?)
|
||||
order-list-idx (:own-order-list-index config)
|
||||
collapsable? (editor-handler/collapsable? uuid {:semantic? true})]
|
||||
[:div.block-control-wrap.mr-1.flex.flex-row.items-center.sm:mr-2
|
||||
[:div.block-control-wrap.flex.flex-row.items-center
|
||||
{:class (util/classnames [{:is-order-list order-list?
|
||||
:bullet-closed collapsed?}])}
|
||||
(when (or (not fold-button-right?) collapsable?)
|
||||
@@ -2222,7 +2224,10 @@
|
||||
(editor-handler/clear-selection!)
|
||||
(editor-handler/unhighlight-blocks!)
|
||||
(let [f #(let [block (or (db/pull [:block/uuid (:block/uuid block)]) block)
|
||||
cursor-range (util/caret-range (gdom/getElement block-id))
|
||||
cursor-range (some-> (gdom/getElement block-id)
|
||||
(dom/by-class "block-content-wrapper")
|
||||
first
|
||||
util/caret-range)
|
||||
{:block/keys [content format]} block
|
||||
content (->> content
|
||||
(property/remove-built-in-properties format)
|
||||
@@ -2260,7 +2265,7 @@
|
||||
(and (not block-content?)
|
||||
(seq (:block/children block))
|
||||
(= move-to :nested)))
|
||||
(dnd-separator move-to block-content?))))))
|
||||
(dnd-separator move-to block-content?))))))
|
||||
|
||||
(defn clock-summary-cp
|
||||
[block body]
|
||||
@@ -2331,19 +2336,19 @@
|
||||
content (if (string? content) (string/trim content) "")
|
||||
mouse-down-key (if (util/ios?)
|
||||
:on-click
|
||||
:on-mouse-down ; TODO: it seems that Safari doesn't work well with on-mouse-down
|
||||
)
|
||||
:on-mouse-down) ; TODO: it seems that Safari doesn't work well with on-mouse-down
|
||||
|
||||
attrs (cond->
|
||||
{:blockid (str uuid)
|
||||
:data-type (name block-type)
|
||||
:style {:width "100%" :pointer-events (when stop-events? "none")}}
|
||||
|
||||
(not (string/blank? (:hl-color properties)))
|
||||
(assoc :data-hl-color (:hl-color properties))
|
||||
(not (string/blank? (:hl-color properties)))
|
||||
(assoc :data-hl-color (:hl-color properties))
|
||||
|
||||
(not block-ref?)
|
||||
(assoc mouse-down-key (fn [e]
|
||||
(block-content-on-mouse-down e block block-id content edit-input-id))))]
|
||||
(not block-ref?)
|
||||
(assoc mouse-down-key (fn [e]
|
||||
(block-content-on-mouse-down e block block-id content edit-input-id))))]
|
||||
[:div.block-content.inline
|
||||
(cond-> {:id (str "block-content-" uuid)
|
||||
:on-mouse-up (fn [e]
|
||||
@@ -3021,8 +3026,8 @@
|
||||
:li
|
||||
(cond->
|
||||
{:checked checked?}
|
||||
number
|
||||
(assoc :value number))
|
||||
number
|
||||
(assoc :value number))
|
||||
(vec-cat
|
||||
[(->elem
|
||||
:p
|
||||
@@ -3040,52 +3045,55 @@
|
||||
|
||||
(defn table
|
||||
[config {:keys [header groups col_groups]}]
|
||||
(let [tr (fn [elm cols]
|
||||
(->elem
|
||||
:tr
|
||||
(mapv (fn [col]
|
||||
(->elem
|
||||
elm
|
||||
{:scope "col"
|
||||
:class "org-left"}
|
||||
(map-inline config col)))
|
||||
cols)))
|
||||
tb-col-groups (try
|
||||
(mapv (fn [number]
|
||||
(let [col-elem [:col {:class "org-left"}]]
|
||||
(->elem
|
||||
:colgroup
|
||||
(repeat number col-elem))))
|
||||
col_groups)
|
||||
(catch :default _e
|
||||
[]))
|
||||
head (when header
|
||||
[:thead (tr :th header)])
|
||||
groups (mapv (fn [group]
|
||||
(->elem
|
||||
:tbody
|
||||
(mapv #(tr :td %) group)))
|
||||
groups)]
|
||||
[:div.table-wrapper
|
||||
(->elem
|
||||
:table
|
||||
{:class "table-auto"
|
||||
:border 2
|
||||
:cell-spacing 0
|
||||
:cell-padding 6
|
||||
:rules "groups"
|
||||
:frame "hsides"}
|
||||
(vec-cat
|
||||
tb-col-groups
|
||||
(cons head groups)))]))
|
||||
(case (get-shui-component-version :table config)
|
||||
2 (shui/table-v2 {:data (concat [[header]] groups)}
|
||||
(make-shui-context config inline))
|
||||
1 (let [tr (fn [elm cols]
|
||||
(->elem
|
||||
:tr
|
||||
(mapv (fn [col]
|
||||
(->elem
|
||||
elm
|
||||
{:scope "col"
|
||||
:class "org-left"}
|
||||
(map-inline config col)))
|
||||
cols)))
|
||||
tb-col-groups (try
|
||||
(mapv (fn [number]
|
||||
(let [col-elem [:col {:class "org-left"}]]
|
||||
(->elem
|
||||
:colgroup
|
||||
(repeat number col-elem))))
|
||||
col_groups)
|
||||
(catch :default _e
|
||||
[]))
|
||||
head (when header
|
||||
[:thead (tr :th header)])
|
||||
groups (mapv (fn [group]
|
||||
(->elem
|
||||
:tbody
|
||||
(mapv #(tr :td %) group)))
|
||||
groups)]
|
||||
[:div.table-wrapper
|
||||
(->elem
|
||||
:table
|
||||
{:class "table-auto"
|
||||
:border 2
|
||||
:cell-spacing 0
|
||||
:cell-padding 6
|
||||
:rules "groups"
|
||||
:frame "hsides"}
|
||||
(vec-cat
|
||||
tb-col-groups
|
||||
(cons head groups)))])))
|
||||
|
||||
(defn logbook-cp
|
||||
[log]
|
||||
(let [clocks (filter #(string/starts-with? % "CLOCK:") log)
|
||||
clocks (reverse (sort-by str clocks))
|
||||
clocks (reverse (sort-by str clocks))]
|
||||
;; TODO: display states change log
|
||||
; states (filter #(not (string/starts-with? % "CLOCK:")) log)
|
||||
]
|
||||
|
||||
(when (seq clocks)
|
||||
(let [tr (fn [elm cols] (->elem :tr
|
||||
(mapv (fn [col] (->elem elm col)) cols)))
|
||||
@@ -3110,6 +3118,8 @@
|
||||
[config col]
|
||||
(map #(inline config %) col))
|
||||
|
||||
(declare ->hiccup)
|
||||
|
||||
(rum/defc src-cp < rum/static
|
||||
[config options html-export?]
|
||||
(when options
|
||||
|
||||
@@ -201,11 +201,14 @@
|
||||
}
|
||||
|
||||
.block-control-wrap {
|
||||
height: 24px;
|
||||
margin-top: 0;
|
||||
@apply h-[24px] mt-0 pr-[6px];
|
||||
|
||||
&.is-order-list {
|
||||
@apply relative right-0 mr-0;
|
||||
@apply mr-0 pr-0;
|
||||
|
||||
.bullet-link-wrap {
|
||||
@apply relative left-[-3px];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -531,12 +534,14 @@
|
||||
}
|
||||
|
||||
&.as-order-list {
|
||||
@apply w-[28px] whitespace-nowrap justify-start;
|
||||
@apply w-[22px] whitespace-nowrap justify-center pl-[3px];
|
||||
}
|
||||
|
||||
.bullet {
|
||||
@apply rounded-full w-[6px] h-[6px];
|
||||
|
||||
font-size: 14px;
|
||||
|
||||
background-color: var(--ls-block-bullet-color, #394b59);
|
||||
transition: transform 0.2s;
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
(let [show? (rum/react *show-repeater?)]
|
||||
(if (or show? (and num duration kind))
|
||||
[:div.w.full.flex.flex-row.justify-left
|
||||
[:input#repeater-num.form-input.mt-1.w-8.px-1.sm:w-20.sm:px-2.text-center
|
||||
[:input#repeater-num.form-input.w-8.mr-2.px-1.sm:w-20.sm:px-2.text-center
|
||||
{:default-value num
|
||||
:on-change (fn [event]
|
||||
(let [value (util/evalue event)]
|
||||
@@ -66,7 +66,7 @@
|
||||
(swap! *timestamp assoc-in [:repeater :duration] value))
|
||||
nil)
|
||||
|
||||
[:a.ml-1.self-center {:on-click (fn []
|
||||
[:a.ml-2.self-center {:on-click (fn []
|
||||
(reset! *show-repeater? false)
|
||||
(swap! *timestamp assoc :repeater {}))}
|
||||
svg/close]]
|
||||
|
||||
@@ -588,7 +588,7 @@
|
||||
(async/go
|
||||
(set-loading? true)
|
||||
(try
|
||||
(let [files (async/<! (file-sync-handler/fetch-page-file-versions graph-uuid page-entity))]
|
||||
(let [files (async/<! (file-sync-handler/<fetch-page-file-versions graph-uuid page-entity))]
|
||||
(set-version-files files)
|
||||
(set-page-fn (first files))
|
||||
(set-list-ready? true))
|
||||
|
||||
@@ -38,7 +38,9 @@
|
||||
[logseq.graph-parser.util :as gp-util]
|
||||
[medley.core :as medley]
|
||||
[reitit.frontend.easy :as rfe]
|
||||
[rum.core :as rum]))
|
||||
[rum.core :as rum]
|
||||
[logseq.graph-parser.util.page-ref :as page-ref]
|
||||
[logseq.graph-parser.mldoc :as gp-mldoc]))
|
||||
|
||||
(defn- get-page-name
|
||||
[state]
|
||||
@@ -261,7 +263,7 @@
|
||||
:else
|
||||
(state/set-modal! (confirm-fn)))
|
||||
(util/stop e))]
|
||||
[:span.absolute.inset-0.edit-input-wrapper
|
||||
[:span.absolute.inset-0.edit-input-wrapper.z-10
|
||||
{:class (util/classnames [{:editing @*edit?}])}
|
||||
[:input.edit-input
|
||||
{:type "text"
|
||||
@@ -296,7 +298,8 @@
|
||||
(assoc state ::title-value (atom (nth (:rum/args state) 2))))}
|
||||
[state page-name icon title _format fmt-journal?]
|
||||
(when title
|
||||
(let [*title-value (get state ::title-value)
|
||||
(let [page (when page-name (db/entity [:block/name page-name]))
|
||||
*title-value (get state ::title-value)
|
||||
*edit? (get state ::edit?)
|
||||
*input-value (get state ::input-value)
|
||||
repo (state/get-current-repo)
|
||||
@@ -305,7 +308,9 @@
|
||||
untitled? (and whiteboard-page? (parse-uuid page-name)) ;; normal page cannot be untitled right?
|
||||
title (if hls-page?
|
||||
[:a.asset-ref (pdf-utils/fix-local-asset-pagename title)]
|
||||
(if fmt-journal? (date/journal-title->custom-format title) 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
|
||||
{:class (when-not whiteboard-page? "title")
|
||||
@@ -313,16 +318,17 @@
|
||||
(when (util/right-click? e)
|
||||
(state/set-state! :page-title/context {:page page-name})))
|
||||
:on-click (fn [e]
|
||||
(.preventDefault e)
|
||||
(if (gobj/get e "shiftKey")
|
||||
(when-let [page (db/pull repo '[*] [:block/name page-name])]
|
||||
(state/sidebar-add-block!
|
||||
repo
|
||||
(:db/id page)
|
||||
:page))
|
||||
(when (and (not hls-page?) (not fmt-journal?) (not config/publishing?))
|
||||
(reset! *input-value (if untitled? "" old-name))
|
||||
(reset! *edit? true))))}
|
||||
(when-not (= (.-nodeName (.-target e)) "INPUT")
|
||||
(.preventDefault e)
|
||||
(if (gobj/get e "shiftKey")
|
||||
(when-let [page (db/pull repo '[*] [:block/name page-name])]
|
||||
(state/sidebar-add-block!
|
||||
repo
|
||||
(:db/id page)
|
||||
:page))
|
||||
(when (and (not hls-page?) (not fmt-journal?) (not config/publishing?))
|
||||
(reset! *input-value (if untitled? "" old-name))
|
||||
(reset! *edit? true)))))}
|
||||
(when (not= icon "") [:span.page-icon icon])
|
||||
[:div.page-title-sizer-wrapper.relative
|
||||
(when @*edit?
|
||||
@@ -338,9 +344,13 @@
|
||||
{:data-value @*input-value
|
||||
:data-ref page-name
|
||||
:style {:opacity (when @*edit? 0)}}
|
||||
(cond @*edit? [:span {:style {:white-space "pre"}} (rum/react *input-value)]
|
||||
untitled? [:span.opacity-50 (t :untitled)]
|
||||
:else title)]]])))
|
||||
(let [nested? (and (string/includes? title page-ref/left-brackets)
|
||||
(string/includes? title page-ref/right-brackets))]
|
||||
(cond @*edit? [:span {:style {:white-space "pre"}} (rum/react *input-value)]
|
||||
untitled? [:span.opacity-50 (t :untitled)]
|
||||
nested? (component-block/map-inline {} (gp-mldoc/inline->edn title (gp-mldoc/default-config
|
||||
(:block/format page))))
|
||||
:else title))]]])))
|
||||
|
||||
(defn- page-mouse-over
|
||||
[e *control-show? *all-collapsed?]
|
||||
|
||||
@@ -249,6 +249,7 @@
|
||||
box-shadow: none;
|
||||
padding-left: 5px;
|
||||
padding-top: 5px;
|
||||
padding-bottom: 4px;
|
||||
|
||||
&-wrapper {
|
||||
@apply rounded;
|
||||
@@ -276,7 +277,7 @@ a.page-title {
|
||||
}
|
||||
|
||||
> .title {
|
||||
@apply w-full pointer-events-none overflow-hidden overflow-ellipsis;
|
||||
@apply w-full overflow-hidden overflow-ellipsis;
|
||||
}
|
||||
|
||||
.edit-input {
|
||||
|
||||
@@ -836,6 +836,16 @@
|
||||
> .injected-ui-item-pagebar {
|
||||
@apply pr-3 opacity-30 hover:opacity-100 transition-opacity;
|
||||
}
|
||||
|
||||
> .list-wrap {
|
||||
@apply flex items-center flex-nowrap overflow-x-hidden pt-[14px];
|
||||
}
|
||||
|
||||
a.button {
|
||||
@apply flex items-center;
|
||||
|
||||
color: var(--ls-primary-text-color);
|
||||
}
|
||||
}
|
||||
|
||||
.toolbar-plugins-manager {
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
view-f
|
||||
result
|
||||
group-by-page?]}]
|
||||
(let [{:keys [->hiccup ->elem inline-text page-cp map-inline]} config
|
||||
(let [{:keys [->hiccup ->elem inline-text page-cp map-inline inline]} config
|
||||
*query-error query-error-atom
|
||||
only-blocks? (:block/uuid (first result))
|
||||
blocks-grouped-by-page? (and group-by-page?
|
||||
@@ -59,6 +59,7 @@
|
||||
(:block/name (ffirst result))
|
||||
(:block/uuid (first (second (first result))))
|
||||
true)]
|
||||
(println "this should be a function" inline)
|
||||
(if @*query-error
|
||||
(do
|
||||
(log/error :exception @*query-error)
|
||||
@@ -77,10 +78,10 @@
|
||||
(util/hiccup-keywordize result))
|
||||
|
||||
page-list?
|
||||
(query-table/result-table config current-block result {:page? true} map-inline page-cp ->elem inline-text)
|
||||
(query-table/result-table config current-block result {:page? true} map-inline page-cp ->elem inline-text inline)
|
||||
|
||||
table?
|
||||
(query-table/result-table config current-block result {:page? false} map-inline page-cp ->elem inline-text)
|
||||
(query-table/result-table config current-block result {:page? false} map-inline page-cp ->elem inline-text inline)
|
||||
|
||||
(and (seq result) (or only-blocks? blocks-grouped-by-page?))
|
||||
(->hiccup result
|
||||
|
||||
@@ -3,13 +3,15 @@
|
||||
[frontend.date :as date]
|
||||
[frontend.db :as db]
|
||||
[frontend.db.query-dsl :as query-dsl]
|
||||
[frontend.format.block :as block]
|
||||
[frontend.handler.common :as common-handler]
|
||||
[frontend.handler.editor.property :as editor-property]
|
||||
[frontend.shui :refer [get-shui-component-version make-shui-context]]
|
||||
[frontend.state :as state]
|
||||
[frontend.util :as util]
|
||||
[frontend.util.clock :as clock]
|
||||
[frontend.util.property :as property]
|
||||
[frontend.format.block :as block]
|
||||
[logseq.shui.core :as shui]
|
||||
[medley.core :as medley]
|
||||
[rum.core :as rum]
|
||||
[logseq.graph-parser.text :as text]))
|
||||
@@ -42,9 +44,9 @@
|
||||
(defn- locale-compare
|
||||
"Use locale specific comparison for strings and general comparison for others."
|
||||
[x y]
|
||||
(if (and (number? x) (number? y))
|
||||
(< x y)
|
||||
(.localeCompare (str x) (str y) (state/sub :preferred-language) #js {:numeric true})))
|
||||
(if (and (number? x) (number? y))
|
||||
(< x y)
|
||||
(.localeCompare (str x) (str y) (state/sub :preferred-language) #js {:numeric true})))
|
||||
|
||||
(defn- sort-result [result {:keys [sort-by-column sort-desc? sort-nlp-date? page?]}]
|
||||
(if (some? sort-by-column)
|
||||
@@ -98,7 +100,7 @@
|
||||
keys (if page? (distinct (concat keys [:created-at :updated-at])) keys)]
|
||||
keys))
|
||||
|
||||
(defn- get-columns [current-block result {:keys [page?]}]
|
||||
(defn get-columns [current-block result {:keys [page?]}]
|
||||
(let [query-properties (some-> (get-in current-block [:block/properties :query-properties] "")
|
||||
(common-handler/safe-read-string "Parsing query properties failed"))
|
||||
query-properties (if page? (remove #{:block} query-properties) query-properties)
|
||||
@@ -152,10 +154,21 @@
|
||||
;; Fallback to original properties for page blocks
|
||||
(get-in row [:block/properties column])))]))
|
||||
|
||||
(defn build-column-text [row column]
|
||||
(case column
|
||||
:page (or (get-in row [:block/page :block/original-name])
|
||||
(get-in row [:block/original-name])
|
||||
(get-in row [:block/content]))
|
||||
:block (or (get-in row [:block/original-name])
|
||||
(get-in row [:block/content]))
|
||||
(or (get-in row [:block/properties column])
|
||||
(get-in row [:block/properties-text-values column])
|
||||
(get-in row [(keyword :block column)]))))
|
||||
|
||||
(rum/defcs result-table < rum/reactive
|
||||
(rum/local false ::select?)
|
||||
(rum/local false ::mouse-down?)
|
||||
[state config current-block result {:keys [page?]} map-inline page-cp ->elem inline-text]
|
||||
[state config current-block result {:keys [page?]} map-inline page-cp ->elem inline-text inline]
|
||||
(when current-block
|
||||
(let [select? (get state ::select?)
|
||||
*mouse-down? (::mouse-down? state)
|
||||
@@ -168,53 +181,65 @@
|
||||
;; as user needs to know if there result is sorted
|
||||
sort-state (get-sort-state current-block)
|
||||
sort-result (sort-result result (assoc sort-state :page? page?))
|
||||
property-separated-by-commas? (partial text/separated-by-commas? (state/get-config))]
|
||||
[:div.overflow-x-auto {:on-mouse-down (fn [e] (.stopPropagation e))
|
||||
:style {:width "100%"}
|
||||
:class (when-not page? "query-table")}
|
||||
[:table.table-auto
|
||||
[:thead
|
||||
[:tr.cursor
|
||||
(for [column columns]
|
||||
(let [title (if (and (= column :clock-time) (integer? clock-time-total))
|
||||
(util/format "clock-time(total: %s)" (clock/seconds->days:hours:minutes:seconds
|
||||
clock-time-total))
|
||||
(name column))]
|
||||
(sortable-title title column sort-state (:block/uuid current-block))))]]
|
||||
[:tbody
|
||||
(for [row sort-result]
|
||||
(let [format (:block/format row)]
|
||||
property-separated-by-commas? (partial text/separated-by-commas? (state/get-config))
|
||||
table-version (get-shui-component-version :table config)
|
||||
result-as-text (for [row sort-result]
|
||||
(for [column columns]
|
||||
(build-column-text row column)))
|
||||
render-column-value (fn [row-format cell-format value]
|
||||
(cond
|
||||
;; elements should be rendered as they are provided
|
||||
(= :element cell-format) value
|
||||
;; collections are treated as a comma separated list of page-cps
|
||||
(coll? value) (->> (map #(page-cp {} {:block/name %}) value)
|
||||
(interpose [:span ", "]))
|
||||
;; boolean values need to first be stringified
|
||||
(boolean? value) (str value)
|
||||
;; string values will attempt to be rendered as pages, falling back to
|
||||
;; inline-text when no page entity is found
|
||||
(string? value) (if-let [page (db/entity [:block/name (util/page-name-sanity-lc value)])]
|
||||
(page-cp {} page)
|
||||
(inline-text row-format value))
|
||||
;; anything else should just be rendered as provided
|
||||
:else value))]
|
||||
|
||||
(case table-version
|
||||
2 (shui/table-v2 {:data (conj [[columns]] result-as-text)}
|
||||
(make-shui-context config inline))
|
||||
1 [:div.overflow-x-auto {:on-mouse-down (fn [e] (.stopPropagation e))
|
||||
:style {:width "100%"}
|
||||
:class (when-not page? "query-table")}
|
||||
[:table.table-auto
|
||||
[:thead
|
||||
[:tr.cursor
|
||||
(for [column columns]
|
||||
(let [value (build-column-value row
|
||||
column
|
||||
{:page? page?
|
||||
:->elem ->elem
|
||||
:map-inline map-inline
|
||||
:config config
|
||||
:comma-separated-property? (property-separated-by-commas? column)})]
|
||||
[:td.whitespace-nowrap {:on-mouse-down (fn []
|
||||
(reset! *mouse-down? true)
|
||||
(reset! select? false))
|
||||
:on-mouse-move (fn [] (reset! select? true))
|
||||
:on-mouse-up (fn []
|
||||
(when (and @*mouse-down? (not @select?))
|
||||
(state/sidebar-add-block!
|
||||
(state/get-current-repo)
|
||||
(:db/id row)
|
||||
:block-ref)
|
||||
(reset! *mouse-down? false)))}
|
||||
(when value
|
||||
(if (= :element (first value))
|
||||
(second value)
|
||||
(let [value (second value)]
|
||||
(if (coll? value)
|
||||
(let [vals (for [row value]
|
||||
(page-cp {} {:block/name row}))]
|
||||
(interpose [:span ", "] vals))
|
||||
(cond
|
||||
(boolean? value) (str value)
|
||||
(string? value) (if-let [page (db/entity [:block/name (util/page-name-sanity-lc value)])]
|
||||
(page-cp {} page)
|
||||
(inline-text format value))
|
||||
:else value)))))]))]))]]])))
|
||||
(let [title (if (and (= column :clock-time) (integer? clock-time-total))
|
||||
(util/format "clock-time(total: %s)" (clock/seconds->days:hours:minutes:seconds
|
||||
clock-time-total))
|
||||
(name column))]
|
||||
(sortable-title title column sort-state (:block/uuid current-block))))]]
|
||||
[:tbody
|
||||
(for [row sort-result]
|
||||
(let [format (:block/format row)]
|
||||
[:tr.cursor
|
||||
(for [column columns]
|
||||
(let [value (build-column-value row
|
||||
column
|
||||
{:page? page?
|
||||
:->elem ->elem
|
||||
:map-inline map-inline
|
||||
:config config
|
||||
:comma-separated-property? (property-separated-by-commas? column)})]
|
||||
[:td.whitespace-nowrap {:on-mouse-down (fn []
|
||||
(reset! *mouse-down? true)
|
||||
(reset! select? false))
|
||||
:on-mouse-move (fn [] (reset! select? true))
|
||||
:on-mouse-up (fn []
|
||||
(when (and @*mouse-down? (not @select?))
|
||||
(state/sidebar-add-block!
|
||||
(state/get-current-repo)
|
||||
(:db/id row)
|
||||
:block-ref)
|
||||
(reset! *mouse-down? false)))}
|
||||
(when value
|
||||
(apply render-column-value format value))]))]))]]]))))
|
||||
|
||||
@@ -668,14 +668,20 @@
|
||||
[]
|
||||
[:div.panel-wrap
|
||||
[:div.text-sm.my-4
|
||||
(ui/admonition
|
||||
:tip
|
||||
[:p "If you have Logseq Sync enabled, you can view a page's edit history directly. This section is for tech-savvy only."])
|
||||
[:span.text-sm.opacity-50.my-4
|
||||
"You can view a page's edit history by clicking the three horizontal dots "
|
||||
"in the top-right corner and selecting \"View page history\". "
|
||||
"Logseq uses "]
|
||||
"To view page's edit history, click the three horizontal dots in the top-right corner and select \"View page history\"."]
|
||||
[:br][:br]
|
||||
[:span.text-sm.opacity-50.my-4
|
||||
"For professional users, Logseq also supports using "]
|
||||
[:a {:href "https://git-scm.com/" :target "_blank"}
|
||||
"Git"]
|
||||
[:span.text-sm.opacity-50.my-4
|
||||
" for version control."]]
|
||||
" for version control."]
|
||||
[:span.text-sm.opacity-50.my-4
|
||||
"Use Git at your own risk as general Git issues are not supported by the Logseq team"]]
|
||||
[:br]
|
||||
(switch-git-auto-commit-row t)
|
||||
(git-auto-commit-seconds t)
|
||||
@@ -878,9 +884,7 @@
|
||||
[[:general "general" (t :settings-page/tab-general) (ui/icon "adjustments")]
|
||||
[:editor "editor" (t :settings-page/tab-editor) (ui/icon "writing")]
|
||||
|
||||
(when (and
|
||||
(util/electron?)
|
||||
(not (file-sync-handler/synced-file-graph? current-repo)))
|
||||
(when (util/electron?)
|
||||
[:git "git" (t :settings-page/tab-version-control) (ui/icon "history")])
|
||||
|
||||
;; (when (util/electron?)
|
||||
@@ -890,7 +894,7 @@
|
||||
|
||||
[:ai "ai" "AI" (ui/icon "wand")]
|
||||
|
||||
[:features "features" (t :settings-page/tab-features) (ui/icon "square-asterisk")]
|
||||
[:features "features" (t :settings-page/tab-features) (ui/icon "app-feature")]
|
||||
|
||||
(when plugins-of-settings
|
||||
[:plugins-setting "plugins" (t :settings-of-plugins) (ui/icon "puzzle")])]]
|
||||
|
||||
@@ -770,7 +770,11 @@ independent of format as format specific heading characters are stripped"
|
||||
:include-start? true
|
||||
:scoped-block-id scoped-block-id}))
|
||||
|
||||
(contains? #{:save-block :delete-blocks} outliner-op)
|
||||
(and (= :delete-blocks outliner-op)
|
||||
(<= (count @result) initial-blocks-length)) ; load more blocks
|
||||
nil
|
||||
|
||||
(= :save-block outliner-op)
|
||||
@result
|
||||
|
||||
(contains? #{:insert-blocks :collapse-expand-blocks :move-blocks} outliner-op)
|
||||
@@ -845,6 +849,7 @@ independent of format as format specific heading characters are stripped"
|
||||
(db-utils/pull repo-url pull-keys id))) block-eids)
|
||||
(db-utils/pull-many repo-url pull-keys block-eids))
|
||||
blocks (remove (fn [b] (nil? (:block/content b))) blocks)]
|
||||
|
||||
(map (fn [b] (assoc b :block/page bare-page-map)) blocks)))}
|
||||
nil)
|
||||
react)))))
|
||||
@@ -957,15 +962,8 @@ independent of format as format specific heading characters are stripped"
|
||||
"Doesn't include nested children."
|
||||
[repo block-uuid]
|
||||
(when-let [db (conn/get-db repo)]
|
||||
(-> (d/q
|
||||
'[:find [(pull ?b [*]) ...]
|
||||
:in $ ?parent-id
|
||||
:where
|
||||
[?parent :block/uuid ?parent-id]
|
||||
[?b :block/parent ?parent]]
|
||||
db
|
||||
block-uuid)
|
||||
(sort-by-left (db-utils/entity [:block/uuid block-uuid])))))
|
||||
(when-let [parent (db-utils/entity repo [:block/uuid block-uuid])]
|
||||
(sort-by-left (:block/_parent parent) parent))))
|
||||
|
||||
(defn get-block-children
|
||||
"Including nested children."
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
;;; keywords specs for reactive query, used by `react/q` calls
|
||||
;; ::block
|
||||
;; pull-block react-query
|
||||
(s/def ::block (s/tuple #(= ::block %) uuid?))
|
||||
(s/def ::block (s/tuple #(= ::block %) int?))
|
||||
;; ::page-blocks
|
||||
;; get page-blocks react-query
|
||||
(s/def ::page-blocks (s/tuple #(= ::page-blocks %) int?))
|
||||
|
||||
@@ -372,6 +372,8 @@
|
||||
|
||||
:file-sync/other-user-graph "Current local graph is bound to other user's remote graph. So can't start syncing."
|
||||
:file-sync/graph-deleted "The current remote graph has been deleted"
|
||||
:file-sync/rsapi-cannot-upload-err "Unable to start synchronization, please check if the local time is correct."
|
||||
|
||||
|
||||
:notification/clear-all "Clear all"}
|
||||
|
||||
@@ -1684,6 +1686,7 @@
|
||||
|
||||
:file-sync/other-user-graph "当前本地图谱绑定在其他用户的远程图谱上。因此无法启动同步。"
|
||||
:file-sync/graph-deleted "当前远程图谱已经删除"
|
||||
:file-sync/rsapi-cannot-upload-err "无法同步,请检查本机时间是否准确"
|
||||
|
||||
:notification/clear-all "清除全部通知"}
|
||||
|
||||
@@ -2728,7 +2731,116 @@
|
||||
:asset/maximize "Maksimer bilde"
|
||||
:asset/open-in-browser "Åpne bilde i nettleser"
|
||||
:asset/show-in-folder "Vis bilde i mappe"
|
||||
:linked-references/filter-search "Søk i lenkede referanser"}
|
||||
:linked-references/filter-search "Søk i lenkede referanser"
|
||||
:all-whiteboards "Alle whiteboard"
|
||||
:auto-heading "Automatisk overskrift"
|
||||
:heading "Overskrift {1}"
|
||||
:new-whiteboard "Nytt whiteboard"
|
||||
:remove-heading "Fjern overskrift"
|
||||
:untitled "Uten navn"
|
||||
:accessibility/skip-to-main-content "Hopp til hovedinnhold"
|
||||
:color/blue "Blå"
|
||||
:color/gray "Grå"
|
||||
:color/green "Grønn"
|
||||
:color/pink "Rosa"
|
||||
:color/purple "Lilla"
|
||||
:color/red "Rød"
|
||||
:color/yellow "Gul"
|
||||
:content/copy-block-url "Kopier blokk URL"
|
||||
:content/copy-export-as "Kopier / Eksporter som.."
|
||||
:content/copy-ref "Kopier denne referansen"
|
||||
:content/delete-ref "Slett denne referansen"
|
||||
:content/replace-with-embed "Erstatt med innebygging"
|
||||
:content/replace-with-text "Erstatt med tekst"
|
||||
:context-menu/input-template-name "Hva heter malen?"
|
||||
:context-menu/make-a-flashcard "Lag et Flashcard"
|
||||
:context-menu/make-a-template "Lag en Mal"
|
||||
:context-menu/preview-flashcard "Forhåndsvis Flashcard"
|
||||
:context-menu/template-exists-warning "Malen eksisterer allerde!"
|
||||
:context-menu/template-include-parent-block "Inkluder overordnet blokk i malen?"
|
||||
:context-menu/toggle-number-list "Veksle nummerliste"
|
||||
:dev/show-block-ast "(Dev) Vis blokk AST"
|
||||
:dev/show-block-data "(Dev) Vis blokk data"
|
||||
:dev/show-page-ast "(Dev) Vis side AST"
|
||||
:dev/show-page-data "(Dev) Vis side data"
|
||||
:editor/collapse-block-children "Skjul alle"
|
||||
:editor/cycle-todo "Roterer TODO statusen for gjeldende element"
|
||||
:editor/delete-selection "Slett valgte blokker"
|
||||
:editor/expand-block-children "Utvid alle"
|
||||
:file/validate-existing-file-error "Siden eksisterer allerede i en annen fil: {1}, nåværen..."
|
||||
:file-rn/all-action "Utfør alle Handlinger!"
|
||||
:file-rn/apply-rename "Utfør omdøping av filen"
|
||||
:file-rn/close-panel "Lukk Panel"
|
||||
:file-rn/confirm-proceed "Oppdater format!"
|
||||
:file-rn/filename-desc-1 "Denne innstillingen konfigurerer hvordan en side blir lagret til en ..."
|
||||
:file-rn/filename-desc-2 "Noen tegn som \"/\" eller \"?\" er ikke gyldige for en..."
|
||||
:file-rn/filename-desc-3 "Logseq erstatter ugyldige tegn med deres URL ..."
|
||||
:file-rn/filename-desc-4 "Skilletegnet for navnerom \"/\" brukes også av \"_..."
|
||||
:file-rn/format-deprecated "Du bruker for øyeblikket et utdatert format. Oppdat..."
|
||||
:file-rn/instruct-1 "Det er en to-trinns prosess å oppdatere formatet for filnavn:"
|
||||
:file-rn/instruct-2 "1. Klikk "
|
||||
:file-rn/instruct-3 "2. Følg instruksjonene under for å gi filen et nytt navn..."
|
||||
:file-rn/legend "🟢 Valgfri omdøping; 🟡 Omdøping kreves..."
|
||||
:file-rn/need-action "Omdøping av fil er anbefalt for å matche de nye..."
|
||||
:file-rn/no-action "Bra jobba! Well done! Ingen ytterligere tiltak kreves."
|
||||
:file-rn/optional-rename "Forslag: "
|
||||
:file-rn/or-select-actions " eller gi filer nytt navn individuelt under, så "
|
||||
:file-rn/or-select-actions-2 ". Disse handlingene er ikke tilgjengelige når du lukker ..."
|
||||
:file-rn/otherwise-breaking "Eller tittelen vil bli"
|
||||
:file-rn/re-index "Re-indksering er sterkt anbefalt etter at filene er..."
|
||||
:file-rn/rename "Omdøp fil \"{1}\" til \"{2}\""
|
||||
:file-rn/select-confirm-proceed "Dev: skriv format"
|
||||
:file-rn/select-format "(Uviklermodus Operasjon, Farlig!) Velg filenav..."
|
||||
:file-rn/suggest-rename "Handling kreves: "
|
||||
:file-rn/unreachable-title "Advarsel! Navnet på siden vil bli {1} under nåvære.."
|
||||
:left-side-bar/create "Opprett"
|
||||
:left-side-bar/new-whiteboard "Nytt whiteboard"
|
||||
:notification/clear-all "Fjern alt"
|
||||
:on-boarding/tour-whiteboard-home "{1} Hjem for dine whiteboards"
|
||||
:on-boarding/tour-whiteboard-home-description "Whiteboards har sin egen seksjon i appen hvo..."
|
||||
:on-boarding/tour-whiteboard-new "{1} Lag nytt whiteboard"
|
||||
:on-boarding/tour-whiteboard-new-description "Det er mange måter å lage et nytt whiteboard på..."
|
||||
:on-boarding/welcome-whiteboard-modal-description "Whiteboards er et fantastisk verktøy for brainstorming og ..."
|
||||
:on-boarding/welcome-whiteboard-modal-skip "Hopp over"
|
||||
:on-boarding/welcome-whiteboard-modal-start "Start med whiteboard"
|
||||
:on-boarding/welcome-whiteboard-modal-title "Et nytt lerret for dine tanker."
|
||||
:page/logseq-is-having-a-problem "Logseq har et problem. Prøver å få den tilbake ..."
|
||||
:page/show-whiteboards "Vis whiteboards"
|
||||
:page/something-went-wrong "Noe gikk galt"
|
||||
:page/step "Steg {1}"
|
||||
:page/try "Prøv"
|
||||
:pdf/doc-metadata "Dokument metadata"
|
||||
:pdf/hl-block-colored "Farget merkelapp for å label for utheve blokk"
|
||||
:plugin/found-n-updates "Fant {1} oppdatering"
|
||||
:plugin/found-updates "Nye oppateringer"
|
||||
:plugin/update-all-selected "Oppdater alle valgte"
|
||||
:plugin/updates-downloading "Laster ned oppdateringer"
|
||||
:plugin.install-from-file/menu-title "Installer fra plugins.edn"
|
||||
:plugin.install-from-file/notice "Følgende plugins vil erstatte dine plugins:"
|
||||
:plugin.install-from-file/success "Alle plugins er installert!"
|
||||
:plugin.install-from-file/title "Installer plugins fra plugins.edn"
|
||||
:right-side-bar/history "(Dev) Angre/Gjør om logg"
|
||||
:right-side-bar/whiteboards "Whiteboards"
|
||||
:search/items "elementer"
|
||||
:search-item/block "Blokk"
|
||||
:search-item/file "Fil"
|
||||
:search-item/page "Side"
|
||||
:search-item/whiteboard "Whiteboard"
|
||||
:select/default-select-multiple "Velg en eller flere"
|
||||
:settings-page/alpha-features "Alpha funksjoner"
|
||||
:settings-page/auto-expand-block-refs "Utvid blokkreferanser automatisk når zoomet inn..."
|
||||
:settings-page/beta-features "Beta funksjoner"
|
||||
:settings-page/clear-cache-warning "Tømming av hurtigbufferen vil forkaste dine åpne grafer. Du m..."
|
||||
:settings-page/custom-date-format-warning "Re-indeksering kreves! Eksisterernde dagbokreferanse vi..."
|
||||
:settings-page/disable-sentry-desc "Logseq vil aldri samle inn dine lokale graf sin databas..."
|
||||
:settings-page/edit-setting "Rediger"
|
||||
:settings-page/enable-whiteboards "Whiteboards"
|
||||
:settings-page/filename-format "Filnavn format"
|
||||
:settings-page/login-prompt "For å få tilgang til nye funksjoner før alle andre må du..."
|
||||
:settings-page/preferred-pasting-file "Foretrekk innliming av fil"
|
||||
:settings-page/show-full-blocks "Vis alle linjer av en blokkreferanse"
|
||||
:settings-page/tab-assets "Ressurser"
|
||||
:whiteboard/link-whiteboard-or-block "Lenk whiteboard/side/blokk"}
|
||||
|
||||
:pt-BR {:on-boarding/demo-graph "Esse é um grafo de demonstração, mudanças não serão salvas enquanto uma pasta local não for aberta."
|
||||
:on-boarding/add-graph "Adicionar grafo"
|
||||
@@ -2811,6 +2923,7 @@
|
||||
:settings-page/spell-checker "Verificador ortográfico"
|
||||
:settings-page/disable-sentry "Enviar dados de utilização e diagnósticos para Logseq"
|
||||
:settings-page/preferred-outdenting "Ativar dedentação lógica"
|
||||
:settings-page/auto-expand-block-refs "Expandir as referências de bloco automaticamente ao aumentar o zoom"
|
||||
:settings-page/custom-date-format "Formato de data preferido"
|
||||
:settings-page/preferred-file-format "Formato de Arquivo preferido"
|
||||
:settings-page/preferred-workflow "Fluxo de trabalho preferido"
|
||||
@@ -3138,6 +3251,10 @@
|
||||
:left-side-bar/new-whiteboard "Novo quadro branco"
|
||||
:left-side-bar/nav-favorites "Favoritos"
|
||||
:left-side-bar/nav-recent-pages "Recente"
|
||||
:page/something-went-wrong "Algo deu errado"
|
||||
:page/logseq-is-having-a-problem "Logseq está tendo um problema. Para tentar colocá-lo de volta em um estado de funcionamento, por favor tente os seguintes passos seguros em ordem:"
|
||||
:page/step "Passo {1}"
|
||||
:page/try "Tentar"
|
||||
:page/presentation-mode "Modo de apresentação"
|
||||
:page/delete-confirmation "Tem a certeza de que quer apagar esta página e o respetivo ficheiro?"
|
||||
:page/open-in-finder "Abrir em pasta"
|
||||
@@ -3206,8 +3323,12 @@
|
||||
:color/pink "Rosa"
|
||||
:editor/copy "Copiar"
|
||||
:editor/cut "Cortar"
|
||||
:content/copy-export-as "Copiar / Exportar como.."
|
||||
:content/copy-block-url "Copiar URL do bloco"
|
||||
:content/copy-block-ref "Copiar referência do bloco"
|
||||
:content/copy-block-emebed "Copiar bloco para incorporar"
|
||||
:content/copy-ref "Copiar esta referência"
|
||||
:content/delete-ref "Apagar esta referência"
|
||||
:content/open-in-sidebar "Abrir na barra lateral"
|
||||
:content/click-to-edit "Clicar para editar"
|
||||
:settings-page/git-confirm "É necessário reiniciar a aplicação após atualizar as definições do Git."
|
||||
@@ -3391,12 +3512,14 @@
|
||||
|
||||
:command-palette/prompt "Introduza um comando"
|
||||
:select/default-prompt "Selecione um"
|
||||
:select/default-select-multiple "Selecione um ou vários"
|
||||
:select.graph/prompt "Selecione um grafo"
|
||||
:select.graph/empty-placeholder-description "Sem grafos correspondentes. Quer adicionar outro?"
|
||||
:select.graph/add-graph "Sim, adicionar outro grafo"
|
||||
|
||||
:file-sync/other-user-graph "O grafo local atual está ligado ao grafo remoto de outro utilizador. Portanto, a sincronização não pode ser iniciada."
|
||||
:file-sync/graph-deleted "O grafo remoto atual foi apagado"
|
||||
:file-sync/rsapi-cannot-upload-err "Não foi possível iniciar a sincronização, verifique se a hora local está correta."
|
||||
|
||||
:notification/clear-all "Limpar tudo"}
|
||||
|
||||
@@ -4410,6 +4533,7 @@
|
||||
:content/open-in-sidebar "Kenar çubuğunda aç"
|
||||
:content/click-to-edit "Düzenlemek için tıklayın"
|
||||
:context-menu/make-a-flashcard "Bilgi Kartı Oluştur"
|
||||
:context-menu/toggle-number-list "Numaralı liste olarak değiştir"
|
||||
:context-menu/preview-flashcard "Bilgi Kartını Önizle"
|
||||
:context-menu/make-a-template "Bir Şablon Oluştur"
|
||||
:context-menu/input-template-name "Şablonun adı nedir?"
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
[frontend.handler.notification :as notification]
|
||||
[frontend.state :as state]
|
||||
[logseq.graph-parser.block :as gp-block]
|
||||
[logseq.graph-parser.config :as gp-config]
|
||||
[logseq.graph-parser.property :as gp-property]
|
||||
[logseq.graph-parser.mldoc :as gp-mldoc]
|
||||
[lambdaisland.glogi :as log]))
|
||||
@@ -23,7 +22,6 @@ and handles unexpected failure."
|
||||
(gp-block/extract-blocks blocks content with-id? format
|
||||
{:user-config (state/get-config)
|
||||
:block-pattern (config/get-block-pattern format)
|
||||
:supported-formats (gp-config/supported-formats)
|
||||
:db (db/get-db (state/get-current-repo))
|
||||
:date-formatter (state/get-date-formatter)
|
||||
:page-name page-name})
|
||||
|
||||
@@ -238,8 +238,7 @@
|
||||
(when-not contents-matched?
|
||||
(backup-file repo-dir :backup-dir fpath disk-content))
|
||||
(db/set-file-last-modified-at! repo rpath mtime)
|
||||
(p/let [content content]
|
||||
(db/set-file-content! repo rpath content))
|
||||
(db/set-file-content! repo rpath content)
|
||||
(when ok-handler
|
||||
(ok-handler repo fpath result))
|
||||
result)
|
||||
|
||||
@@ -1680,6 +1680,10 @@
|
||||
[r]
|
||||
(some->> (ex-cause r) str (re-find #"graph-not-exist")))
|
||||
|
||||
(defn- stop-sync-by-rsapi-response?
|
||||
[r]
|
||||
(some->> (ex-cause r) str (re-find #"Request is not yet valid")))
|
||||
|
||||
|
||||
;; type = "change" | "add" | "unlink"
|
||||
(deftype FileChangeEvent [type dir path stat checksum]
|
||||
@@ -2594,10 +2598,15 @@
|
||||
(do (println :graph-has-been-deleted r*)
|
||||
{:graph-has-been-deleted true})
|
||||
|
||||
(stop-sync-by-rsapi-response? r*)
|
||||
(do (println :stop-sync-caused-by-rsapi-err-response r*)
|
||||
(notification/show! (t :file-sync/rsapi-cannot-upload-err) :warning false)
|
||||
{:stop true})
|
||||
|
||||
paused?
|
||||
{:pause true}
|
||||
|
||||
succ? ; succ
|
||||
succ? ; succ
|
||||
(do
|
||||
(println "sync-local->remote! update txid" r*)
|
||||
;; persist txid
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
[frontend.db :as db]
|
||||
[frontend.db.model :as db-model]
|
||||
[frontend.db.react :as react]
|
||||
[frontend.db.utils :as db-utils]
|
||||
[frontend.mobile.haptics :as haptics]
|
||||
[frontend.modules.outliner.core :as outliner-core]
|
||||
[frontend.modules.outliner.transaction :as outliner-tx]
|
||||
@@ -70,14 +69,9 @@
|
||||
(util/distinct-by :db/id))))))
|
||||
|
||||
(defn indentable?
|
||||
[{:block/keys [parent] :as block}]
|
||||
[{:block/keys [parent left]}]
|
||||
(when parent
|
||||
(let [parent-block (db-utils/pull (:db/id parent))
|
||||
first-child (first
|
||||
(db-model/get-block-immediate-children
|
||||
(state/get-current-repo)
|
||||
(:block/uuid parent-block)))]
|
||||
(not= (:db/id block) (:db/id first-child)))))
|
||||
(not= parent left)))
|
||||
|
||||
(defn outdentable?
|
||||
[{:block/keys [level] :as _block}]
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
[frontend.db :as db]
|
||||
[logseq.graph-parser :as graph-parser]
|
||||
[logseq.graph-parser.util :as gp-util]
|
||||
[logseq.graph-parser.config :as gp-config]
|
||||
[frontend.fs.diff-merge :as diff-merge]
|
||||
[frontend.fs :as fs]
|
||||
[frontend.context.i18n :refer [t]]
|
||||
@@ -75,13 +74,13 @@
|
||||
:fs/reset-event - the event that triggered the file update
|
||||
:fs/local-file-change - file changed on local disk
|
||||
:fs/remote-file-change - file changed on remote"
|
||||
[repo-url file content {:fs/keys [event] :as options}]
|
||||
[repo-url file-path content {:fs/keys [event] :as options}]
|
||||
(let [db-conn (db/get-db repo-url false)]
|
||||
(case event
|
||||
;; the file is already in db, so we can use the existing file's blocks
|
||||
;; to do the diff-merge
|
||||
:fs/local-file-change
|
||||
(graph-parser/parse-file db-conn file content (assoc-in options [:extract-options :resolve-uuid-fn] diff-merge-uuids-2ways))
|
||||
(graph-parser/parse-file db-conn file-path content (assoc-in options [:extract-options :resolve-uuid-fn] diff-merge-uuids-2ways))
|
||||
|
||||
;; TODO Junyi: 3 ways to handle remote file change
|
||||
;; The file is on remote, so we should have
|
||||
@@ -91,7 +90,7 @@
|
||||
;; 2. a "remote version" just fetched from remote
|
||||
|
||||
;; default to parse the file
|
||||
(graph-parser/parse-file db-conn file content options))))
|
||||
(graph-parser/parse-file db-conn file-path content options))))
|
||||
|
||||
(defn reset-file!
|
||||
"Main fn for updating a db with the results of a parsed file"
|
||||
@@ -107,7 +106,6 @@
|
||||
{:user-config (state/get-config)
|
||||
:date-formatter (state/get-date-formatter)
|
||||
:block-pattern (config/get-block-pattern (gp-util/get-format file-path))
|
||||
:supported-formats (gp-config/supported-formats)
|
||||
:filename-format (state/get-filename-format repo-url)}
|
||||
;; To avoid skipping the `:or` bounds for keyword destructuring
|
||||
(when (some? extracted-block-ids) {:extracted-block-ids extracted-block-ids})
|
||||
|
||||
@@ -426,7 +426,7 @@
|
||||
(not has-children?))]
|
||||
(outliner-tx/transact!
|
||||
{:outliner-op :insert-blocks}
|
||||
(save-current-block! {:current-block current-block})
|
||||
(save-current-block! {:current-block current-block})
|
||||
(outliner-core/insert-blocks! [new-block] current-block {:sibling? sibling?
|
||||
:keep-uuid? keep-uuid?
|
||||
:replace-empty-target? replace-empty-target?}))))
|
||||
@@ -753,30 +753,28 @@
|
||||
(outliner-core/delete-blocks! [block] {:children? children?})))))
|
||||
|
||||
(defn- move-to-prev-block
|
||||
([repo sibling-block format id value]
|
||||
(move-to-prev-block repo sibling-block format id value true))
|
||||
([repo sibling-block format id value edit?]
|
||||
(when (and repo sibling-block)
|
||||
(when-let [sibling-block-id (dom/attr sibling-block "blockid")]
|
||||
(when-let [block (db/pull repo '[*] [:block/uuid (uuid sibling-block-id)])]
|
||||
(let [original-content (util/trim-safe (:block/content block))
|
||||
value' (-> (property/remove-built-in-properties format original-content)
|
||||
(drawer/remove-logbook))
|
||||
new-value (str value' value)
|
||||
tail-len (count value)
|
||||
pos (max
|
||||
(if original-content
|
||||
(gobj/get (utf8/encode original-content) "length")
|
||||
0)
|
||||
0)]
|
||||
(when edit?
|
||||
(edit-block! block pos id
|
||||
{:custom-content new-value
|
||||
:tail-len tail-len
|
||||
:move-cursor? false}))
|
||||
{:prev-block block
|
||||
:new-content new-value
|
||||
:pos pos}))))))
|
||||
[repo sibling-block format id value move?]
|
||||
(when (and repo sibling-block)
|
||||
(when-let [sibling-block-id (dom/attr sibling-block "blockid")]
|
||||
(when-let [block (db/pull repo '[*] [:block/uuid (uuid sibling-block-id)])]
|
||||
(let [original-content (util/trim-safe (:block/content block))
|
||||
value' (-> (property/remove-built-in-properties format original-content)
|
||||
(drawer/remove-logbook))
|
||||
new-value (str value' value)
|
||||
tail-len (count value)
|
||||
pos (max
|
||||
(if original-content
|
||||
(gobj/get (utf8/encode original-content) "length")
|
||||
0)
|
||||
0)
|
||||
f (fn [] (edit-block! block pos id
|
||||
{:custom-content new-value
|
||||
:tail-len tail-len
|
||||
:move-cursor? false}))]
|
||||
(when move? (f))
|
||||
{:prev-block block
|
||||
:new-content new-value
|
||||
:move-fn f})))))
|
||||
|
||||
(declare save-block!)
|
||||
|
||||
@@ -802,7 +800,7 @@
|
||||
(when block-parent-id
|
||||
(let [block-parent (gdom/getElement block-parent-id)
|
||||
sibling-block (util/get-prev-block-non-collapsed-non-embed block-parent)
|
||||
{:keys [prev-block new-content]} (move-to-prev-block repo sibling-block format id value)
|
||||
{:keys [prev-block new-content move-fn]} (move-to-prev-block repo sibling-block format id value false)
|
||||
concat-prev-block? (boolean (and prev-block new-content))
|
||||
transact-opts (cond->
|
||||
{:outliner-op :delete-block}
|
||||
@@ -812,7 +810,8 @@
|
||||
(outliner-tx/transact! transact-opts
|
||||
(when concat-prev-block?
|
||||
(save-block! repo prev-block new-content))
|
||||
(delete-block-aux! block delete-children?))))))))))
|
||||
(delete-block-aux! block delete-children?))
|
||||
(move-fn)))))))))
|
||||
(state/set-editor-op! nil)))
|
||||
|
||||
(defn delete-blocks!
|
||||
@@ -829,7 +828,8 @@
|
||||
(move-to-prev-block repo sibling-block
|
||||
(:block/format block)
|
||||
(dom/attr sibling-block "id")
|
||||
"")))))
|
||||
""
|
||||
true)))))
|
||||
|
||||
(defn- set-block-property-aux!
|
||||
[block-or-id key value]
|
||||
@@ -1231,10 +1231,17 @@
|
||||
|
||||
(defn save-block!
|
||||
([repo block-or-uuid content]
|
||||
(save-block! repo block-or-uuid content {}))
|
||||
([repo block-or-uuid content {:keys [properties] :or {}}]
|
||||
(let [block (if (or (uuid? block-or-uuid)
|
||||
(string? block-or-uuid))
|
||||
(db-model/query-block-by-uuid block-or-uuid) block-or-uuid)]
|
||||
(save-block! {:block block :repo repo} content)))
|
||||
(save-block!
|
||||
{:block block :repo repo}
|
||||
(if (seq properties)
|
||||
(property/insert-properties (:block/format block) content properties)
|
||||
content)
|
||||
)))
|
||||
([{:keys [block repo] :as _state} value]
|
||||
(let [repo (or repo (state/get-current-repo))]
|
||||
(when (db/entity repo [:block/uuid (:block/uuid block)])
|
||||
@@ -1244,8 +1251,8 @@
|
||||
[blocks]
|
||||
(outliner-tx/transact!
|
||||
{:outliner-op :save-block}
|
||||
(doseq [[block value] blocks]
|
||||
(save-block-if-changed! block value))))
|
||||
(doseq [[block value] blocks]
|
||||
(save-block-if-changed! block value))))
|
||||
|
||||
(defn save-current-block!
|
||||
"skip-properties? if set true, when editing block is likely be properties, skip saving"
|
||||
@@ -1832,8 +1839,9 @@
|
||||
(and (= content "1. ") (= last-input-char " ") input-id edit-block
|
||||
(not (own-order-number-list? edit-block)))
|
||||
(do
|
||||
(state/pub-event! [:editor/toggle-own-number-list edit-block])
|
||||
(state/set-edit-content! input-id ""))
|
||||
(state/set-edit-content! input-id "")
|
||||
(-> (p/delay 10)
|
||||
(p/then #(state/pub-event! [:editor/toggle-own-number-list edit-block]))))
|
||||
|
||||
(and (= last-input-char (state/get-editor-command-trigger))
|
||||
(or (re-find #"(?m)^/" (str (.-value input))) (start-of-new-word? input pos)))
|
||||
@@ -1964,7 +1972,7 @@
|
||||
:or {exclude-properties []
|
||||
edit? true}}]
|
||||
(let [editing-block (when-let [editing-block (state/get-edit-block)]
|
||||
(some-> (db/pull (:db/id editing-block))
|
||||
(some-> (db/pull [:block/uuid (:block/uuid editing-block)])
|
||||
(assoc :block/content (state/get-edit-content))))
|
||||
has-unsaved-edits (and editing-block
|
||||
(not= (:block/content (db/pull (:db/id editing-block)))
|
||||
@@ -2455,8 +2463,9 @@
|
||||
:else
|
||||
(profile
|
||||
"Insert block"
|
||||
(do (save-current-block!)
|
||||
(insert-new-block! state)))))))))
|
||||
(outliner-tx/transact! {:outliner-op :insert-blocks}
|
||||
(save-current-block!)
|
||||
(insert-new-block! state)))))))))
|
||||
|
||||
(defn- inside-of-single-block
|
||||
"When we are in a single block wrapper, we should always insert a new line instead of new block"
|
||||
@@ -2610,15 +2619,18 @@
|
||||
^js input (state/get-input)
|
||||
current-pos (cursor/pos input)
|
||||
value (gobj/get input "value")
|
||||
right (outliner-core/get-right-node (outliner-core/block current-block))
|
||||
right (outliner-core/get-right-sibling (:db/id current-block))
|
||||
current-block-has-children? (db/has-children? (:block/uuid current-block))
|
||||
collapsed? (util/collapsed? current-block)
|
||||
first-child (:data (tree/-get-down (outliner-core/block current-block)))
|
||||
next-block (if (or collapsed? (not current-block-has-children?))
|
||||
(:data right)
|
||||
(when right (db/pull (:db/id right)))
|
||||
first-child)]
|
||||
(cond
|
||||
(and collapsed? right (db/has-children? (tree/-get-id right)))
|
||||
(nil? next-block)
|
||||
nil
|
||||
|
||||
(and collapsed? right (db/has-children? (:block/uuid right)))
|
||||
nil
|
||||
|
||||
(and (not collapsed?) first-child (db/has-children? (:block/uuid first-child)))
|
||||
@@ -2760,7 +2772,7 @@
|
||||
(outliner-tx/transact!
|
||||
{:outliner-op :move-blocks
|
||||
:real-outliner-op :indent-outdent}
|
||||
(outliner-core/indent-outdent-blocks! [block] indent?)))
|
||||
(outliner-core/indent-outdent-blocks! [block] indent?)))
|
||||
(state/set-editor-op! :nil)))
|
||||
|
||||
(defn keydown-tab-handler
|
||||
@@ -3148,7 +3160,7 @@
|
||||
(state/selection?)
|
||||
(shortcut-delete-selection e)
|
||||
|
||||
(whiteboard?)
|
||||
(and (whiteboard?) (not (state/editing?)))
|
||||
(.deleteShapes (.-api ^js (state/active-tldraw-app)))
|
||||
|
||||
:else
|
||||
@@ -3203,9 +3215,9 @@
|
||||
;; if the move is to cross block boundary, select the whole block
|
||||
(or (and (= direction :up) (cursor/textarea-cursor-rect-first-row? cursor-rect))
|
||||
(and (= direction :down) (cursor/textarea-cursor-rect-last-row? cursor-rect)))
|
||||
(select-block-up-down direction)
|
||||
(select-block-up-down direction)
|
||||
;; simulate text selection
|
||||
(cursor/select-up-down input direction anchor cursor-rect)))
|
||||
(cursor/select-up-down input direction anchor cursor-rect)))
|
||||
(select-block-up-down direction))))
|
||||
|
||||
(defn open-selected-block!
|
||||
|
||||
@@ -70,8 +70,7 @@
|
||||
[logseq.db.schema :as db-schema]
|
||||
[logseq.graph-parser.config :as gp-config]
|
||||
[promesa.core :as p]
|
||||
[rum.core :as rum]
|
||||
[logseq.common.path :as path]))
|
||||
[rum.core :as rum]))
|
||||
|
||||
;; TODO: should we move all events here?
|
||||
|
||||
@@ -609,10 +608,9 @@
|
||||
(plugin/open-waiting-updates-modal!))
|
||||
(plugin-handler/set-auto-checking! false))))))
|
||||
|
||||
(defmethod handle :plugin/hook-db-tx [[_ {:keys [blocks tx-data tx-meta] :as payload}]]
|
||||
(defmethod handle :plugin/hook-db-tx [[_ {:keys [blocks tx-data] :as payload}]]
|
||||
(when-let [payload (and (seq blocks)
|
||||
(merge payload {:tx-data (map #(into [] %) tx-data)
|
||||
:tx-meta (dissoc tx-meta :editor-cursor)}))]
|
||||
(merge payload {:tx-data (map #(into [] %) tx-data)}))]
|
||||
(plugin-handler/hook-plugin-db :changed payload)
|
||||
(plugin-handler/hook-plugin-block-changes payload)))
|
||||
|
||||
@@ -624,13 +622,7 @@
|
||||
|
||||
(defmethod handle :mobile-file-watcher/changed [[_ ^js event]]
|
||||
(let [type (.-event event)
|
||||
payload (js->clj event :keywordize-keys true)
|
||||
dir (:dir payload)
|
||||
payload (-> payload
|
||||
(update :path
|
||||
(fn [path]
|
||||
(when (string? path)
|
||||
(path/relative-path dir path)))))]
|
||||
payload (js->clj event :keywordize-keys true)]
|
||||
(fs-watcher/handle-changed! type payload)
|
||||
(when (file-sync-handler/enable-sync?)
|
||||
(sync/file-watch-handler type payload))))
|
||||
|
||||
@@ -164,33 +164,32 @@
|
||||
|
||||
(defn- block-table
|
||||
[{:keys [header groups]}]
|
||||
(when (seq header)
|
||||
(let [level (dec (get *state* :current-level 1))
|
||||
sep-line (raw-text "|" (string/join "|" (repeat (count header) "---")) "|")
|
||||
header-line
|
||||
(concatv (mapcatv
|
||||
(fn [h] (concatv [space (raw-text "|") space] (mapcatv inline-ast->simple-ast h)))
|
||||
header)
|
||||
[space (raw-text "|")])
|
||||
group-lines
|
||||
(mapcatv
|
||||
(fn [group]
|
||||
(mapcatv
|
||||
(fn [row]
|
||||
(concatv [(indent-with-2-spaces level)]
|
||||
(mapcatv
|
||||
(fn [col]
|
||||
(concatv [(raw-text "|") space]
|
||||
(mapcatv inline-ast->simple-ast col)
|
||||
[space]))
|
||||
row)
|
||||
[(raw-text "|") (newline* 1)]))
|
||||
group))
|
||||
groups)]
|
||||
(concatv [(newline* 1) (indent-with-2-spaces level)]
|
||||
header-line
|
||||
[(newline* 1) (indent-with-2-spaces level) sep-line (newline* 1)]
|
||||
group-lines))))
|
||||
(let [level (dec (get *state* :current-level 1))
|
||||
sep-line (raw-text "|" (string/join "|" (repeat (count header) "---")) "|")
|
||||
header-line
|
||||
(concatv (mapcatv
|
||||
(fn [h] (concatv [space (raw-text "|") space] (mapcatv inline-ast->simple-ast h)))
|
||||
header)
|
||||
[space (raw-text "|")])
|
||||
group-lines
|
||||
(mapcatv
|
||||
(fn [group]
|
||||
(mapcatv
|
||||
(fn [row]
|
||||
(concatv [(indent-with-2-spaces level)]
|
||||
(mapcatv
|
||||
(fn [col]
|
||||
(concatv [(raw-text "|") space]
|
||||
(mapcatv inline-ast->simple-ast col)
|
||||
[space]))
|
||||
row)
|
||||
[(raw-text "|") (newline* 1)]))
|
||||
group))
|
||||
groups)]
|
||||
(concatv [(newline* 1) (indent-with-2-spaces level)]
|
||||
(when (seq header) header-line)
|
||||
(when (seq header) [(newline* 1) (indent-with-2-spaces level) sep-line (newline* 1)])
|
||||
group-lines)))
|
||||
|
||||
(defn- block-comment
|
||||
[s]
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
[cljs-time.coerce :as tc]
|
||||
[cljs-time.core :as t]
|
||||
[frontend.storage :as storage]
|
||||
[logseq.graph-parser.util :as gp-util]
|
||||
[lambdaisland.glogi :as log]))
|
||||
|
||||
(def *beta-unavailable? (volatile! false))
|
||||
@@ -163,24 +162,19 @@
|
||||
version-file-paths)
|
||||
(remove nil?))))))))
|
||||
|
||||
(defn fetch-page-file-versions [graph-uuid page]
|
||||
(defn <fetch-page-file-versions [graph-uuid page]
|
||||
[]
|
||||
(let [file-id (:db/id (:block/file page))]
|
||||
(when-let [path (:file/path (db/entity file-id))]
|
||||
(let [base-path (config/get-repo-dir (state/get-current-repo))
|
||||
base-path (if (string/starts-with? base-path "file://")
|
||||
(gp-util/safe-decode-uri-component base-path)
|
||||
base-path)
|
||||
path* (string/replace-first (string/replace-first path base-path "") #"^/" "")]
|
||||
(go
|
||||
(let [version-list (:VersionList
|
||||
(<! (sync/<get-remote-file-versions sync/remoteapi graph-uuid path*)))
|
||||
local-version-list (<! (<list-file-local-versions page))
|
||||
all-version-list (->> (concat version-list local-version-list)
|
||||
(sort-by #(or (:CreateTime %)
|
||||
(:create-time %))
|
||||
>))]
|
||||
all-version-list))))))
|
||||
(go
|
||||
(when-let [path (:file/path (db/entity file-id))]
|
||||
(let [version-list (:VersionList
|
||||
(<! (sync/<get-remote-file-versions sync/remoteapi graph-uuid path)))
|
||||
local-version-list (<! (<list-file-local-versions page))
|
||||
all-version-list (->> (concat version-list local-version-list)
|
||||
(sort-by #(or (:CreateTime %)
|
||||
(:create-time %))
|
||||
>))]
|
||||
all-version-list)))))
|
||||
|
||||
|
||||
(defn init-remote-graph
|
||||
|
||||
@@ -87,7 +87,9 @@
|
||||
;; See https://developer.chrome.com/blog/web-custom-formats-for-the-async-clipboard-api/
|
||||
;; for a similar example
|
||||
(defn get-copied-blocks []
|
||||
(p/let [clipboard-items (when (and js/window (gobj/get js/window "navigator") js/navigator.clipboard)
|
||||
;; NOTE: Avoid using navigator clipboard API on Android, it will report a permission error
|
||||
(p/let [clipboard-items (when (and (not (mobile-util/native-android?))
|
||||
js/window (gobj/get js/window "navigator") js/navigator.clipboard)
|
||||
(js/navigator.clipboard.read))
|
||||
blocks-blob ^js (when clipboard-items
|
||||
(let [types (.-types ^js (first clipboard-items))]
|
||||
|
||||
@@ -89,11 +89,12 @@
|
||||
(let [assets (js->clj-keywordize (.getCleanUpAssets app))
|
||||
new-shapes (.-shapes tl-page)
|
||||
shapes-index (map #(gobj/get % "id") new-shapes)
|
||||
shape-id->index (zipmap shapes-index (range (.-length new-shapes)))
|
||||
upsert-shapes (->> (set/difference new-id-nonces db-id-nonces)
|
||||
(map (fn [{:keys [id]}]
|
||||
(-> (.-serialized ^js (.getShapeById tl-page id))
|
||||
js->clj-keywordize
|
||||
(assoc :index (.indexOf shapes-index id)))))
|
||||
(assoc :index (get shape-id->index id)))))
|
||||
(set))
|
||||
old-ids (set (map :id db-id-nonces))
|
||||
new-ids (set (map :id new-id-nonces))
|
||||
@@ -134,13 +135,15 @@
|
||||
(defn transact-tldr-delta! [page-name ^js app replace?]
|
||||
(let [tl-page ^js (second (first (.-pages app)))
|
||||
shapes (.-shapes ^js tl-page)
|
||||
shapes-index (map #(gobj/get % "id") shapes)
|
||||
new-id-nonces (set (map (fn [shape]
|
||||
page-block (model/get-page page-name)
|
||||
prev-shapes-index (get-in page-block [:block/properties :logseq.tldraw.page :shapes-index])
|
||||
shape-id->prev-index (zipmap prev-shapes-index (range (count prev-shapes-index)))
|
||||
new-id-nonces (set (map-indexed (fn [idx shape]
|
||||
(let [id (.-id shape)]
|
||||
{:id id
|
||||
:nonce (if (= shape.id (.indexOf shapes-index id))
|
||||
(.-nonce shape)
|
||||
(.getTime (js/Date.)))})) shapes))
|
||||
{:id id
|
||||
:nonce (if (= idx (get shape-id->prev-index id))
|
||||
(.-nonce shape)
|
||||
(js/Date.now))})) shapes))
|
||||
repo (state/get-current-repo)
|
||||
db-id-nonces (or
|
||||
(get-in @*last-shapes-nonce [repo page-name])
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
[txs]
|
||||
(filterv (fn [[_ a & y]]
|
||||
(= :block/content a))
|
||||
txs))
|
||||
txs))
|
||||
|
||||
(defn get-content-from-stack
|
||||
"For test."
|
||||
@@ -60,22 +60,21 @@
|
||||
(when-let [stack @undo-stack]
|
||||
(when (seq stack)
|
||||
(let [removed-e (peek stack)
|
||||
popped-stack (pop stack)
|
||||
prev-e (peek popped-stack)]
|
||||
popped-stack (pop stack)]
|
||||
(reset! undo-stack popped-stack)
|
||||
[removed-e prev-e])))))
|
||||
removed-e)))))
|
||||
|
||||
(defn push-redo
|
||||
[txs]
|
||||
(let [redo-stack (get-redo-stack)]
|
||||
(swap! redo-stack conj txs)))
|
||||
(swap! redo-stack conj txs)))
|
||||
|
||||
(defn pop-redo
|
||||
[]
|
||||
(let [redo-stack (get-redo-stack)]
|
||||
(when-let [removed-e (peek @redo-stack)]
|
||||
(swap! redo-stack pop)
|
||||
removed-e)))
|
||||
(when-let [removed-e (peek @redo-stack)]
|
||||
(swap! redo-stack pop)
|
||||
removed-e)))
|
||||
|
||||
(defn page-pop-redo
|
||||
[page-id]
|
||||
@@ -119,7 +118,7 @@
|
||||
(and redo? (not add?)) :db/retract
|
||||
(and (not redo?) (not add?)) :db/add)]
|
||||
[op id attr value tx]))
|
||||
txs)))
|
||||
txs)))
|
||||
|
||||
;;;; Invokes
|
||||
|
||||
@@ -128,7 +127,7 @@
|
||||
(let [conn (conn/get-db false)]
|
||||
(d/transact! conn txs tx-meta)))
|
||||
|
||||
(defn page-pop-undo
|
||||
(defn- page-pop-undo
|
||||
[page-id]
|
||||
(let [undo-stack (get-undo-stack)]
|
||||
(when-let [stack @undo-stack]
|
||||
@@ -144,7 +143,7 @@
|
||||
others (vec (concat before after))]
|
||||
(reset! undo-stack others)
|
||||
(prn "[debug] undo remove: " (nth stack idx'))
|
||||
[(nth stack idx') others])))))))
|
||||
(nth stack idx'))))))))
|
||||
|
||||
(defn- smart-pop-undo
|
||||
[]
|
||||
@@ -154,56 +153,77 @@
|
||||
(pop-undo))
|
||||
(pop-undo)))
|
||||
|
||||
(defn- set-editor-content!
|
||||
"Prevent block auto-save during undo/redo."
|
||||
[]
|
||||
(when-let [block (state/get-edit-block)]
|
||||
(state/set-edit-content! (state/get-edit-input-id)
|
||||
(:block/content (db/entity (:db/id block))))))
|
||||
|
||||
(defn- get-next-tx-editor-cursor
|
||||
[tx-id]
|
||||
(let [result (->> (sort (keys (:history/tx->editor-cursor @state/state)))
|
||||
(split-with #(not= % tx-id))
|
||||
second)]
|
||||
(when (> (count result) 1)
|
||||
(when-let [next-tx-id (nth result 1)]
|
||||
(get-in @state/state [:history/tx->editor-cursor next-tx-id])))))
|
||||
|
||||
(defn- get-previous-tx-id
|
||||
[tx-id]
|
||||
(let [result (->> (sort (keys (:history/tx->editor-cursor @state/state)))
|
||||
(split-with #(not= % tx-id))
|
||||
first)]
|
||||
(when (>= (count result) 1)
|
||||
(last result))))
|
||||
|
||||
(defn- get-previous-tx-editor-cursor
|
||||
[tx-id]
|
||||
(when-let [prev-tx-id (get-previous-tx-id tx-id)]
|
||||
(get-in @state/state [:history/tx->editor-cursor prev-tx-id])))
|
||||
|
||||
(defn undo
|
||||
[]
|
||||
(let [[e prev-e] (smart-pop-undo)]
|
||||
(when e
|
||||
(let [{:keys [txs tx-meta]} e
|
||||
new-txs (get-txs false txs)
|
||||
undo-delete-concat-block? (and (= :delete-block (:outliner-op tx-meta))
|
||||
(seq (:concat-data tx-meta)))
|
||||
editor-cursor (cond
|
||||
undo-delete-concat-block?
|
||||
(let [data (:concat-data tx-meta)]
|
||||
(assoc (:editor-cursor e)
|
||||
:last-edit-block {:block/uuid (:last-edit-block data)}
|
||||
:pos (if (:end? data) :max 0)))
|
||||
|
||||
;; same block
|
||||
(= (get-in e [:editor-cursor :last-edit-block :block/uuid])
|
||||
(get-in prev-e [:editor-cursor :last-edit-block :block/uuid]))
|
||||
(:editor-cursor prev-e)
|
||||
|
||||
:else
|
||||
(:editor-cursor e))]
|
||||
|
||||
(push-redo e)
|
||||
(transact! new-txs (merge {:undo? true}
|
||||
tx-meta
|
||||
(select-keys e [:pagination-blocks-range])))
|
||||
|
||||
(when undo-delete-concat-block?
|
||||
(when-let [block (state/get-edit-block)]
|
||||
(state/set-edit-content! (state/get-edit-input-id)
|
||||
(:block/content (db/entity (:db/id block))))))
|
||||
|
||||
(when (:whiteboard/transact? tx-meta)
|
||||
(state/pub-event! [:whiteboard/undo e]))
|
||||
(assoc e
|
||||
:txs-op new-txs
|
||||
:editor-cursor editor-cursor)))))
|
||||
(when-let [e (smart-pop-undo)]
|
||||
(let [{:keys [txs tx-meta tx-id]} e
|
||||
new-txs (get-txs false txs)
|
||||
current-editor-cursor (get-in @state/state [:history/tx->editor-cursor tx-id])
|
||||
save-block? (= (:outliner-op tx-meta) :save-block)
|
||||
prev-editor-cursor (get-previous-tx-editor-cursor tx-id)
|
||||
editor-cursor (if (and save-block?
|
||||
(= (:block/uuid (:last-edit-block prev-editor-cursor))
|
||||
(:block/uuid (state/get-edit-block))))
|
||||
prev-editor-cursor
|
||||
current-editor-cursor)]
|
||||
(push-redo e)
|
||||
(transact! new-txs (merge {:undo? true}
|
||||
tx-meta
|
||||
(select-keys e [:pagination-blocks-range])))
|
||||
(set-editor-content!)
|
||||
(when (:whiteboard/transact? tx-meta)
|
||||
(state/pub-event! [:whiteboard/undo e]))
|
||||
(assoc e
|
||||
:txs-op new-txs
|
||||
:editor-cursor editor-cursor))))
|
||||
|
||||
(defn redo
|
||||
[]
|
||||
(when-let [{:keys [txs tx-meta] :as e} (smart-pop-redo)]
|
||||
(let [new-txs (get-txs true txs)]
|
||||
(when-let [{:keys [txs tx-meta tx-id] :as e} (smart-pop-redo)]
|
||||
(let [new-txs (get-txs true txs)
|
||||
current-editor-cursor (get-in @state/state [:history/tx->editor-cursor tx-id])
|
||||
editor-cursor (if (= (:outliner-op tx-meta) :save-block)
|
||||
current-editor-cursor
|
||||
(get-next-tx-editor-cursor tx-id))]
|
||||
(push-undo e)
|
||||
(transact! new-txs (merge {:redo? true}
|
||||
tx-meta
|
||||
(select-keys e [:pagination-blocks-range])))
|
||||
(set-editor-content!)
|
||||
(when (:whiteboard/transact? tx-meta)
|
||||
(state/pub-event! [:whiteboard/redo e]))
|
||||
(assoc e :txs-op new-txs))))
|
||||
(assoc e
|
||||
:txs-op new-txs
|
||||
:editor-cursor editor-cursor))))
|
||||
|
||||
(defn toggle-undo-redo-mode!
|
||||
[]
|
||||
@@ -231,14 +251,14 @@
|
||||
#{:block/created-at :block/updated-at})))
|
||||
(reset-redo)
|
||||
(if (:replace? tx-meta)
|
||||
(let [[removed-e _prev-e] (pop-undo)
|
||||
(let [removed-e (pop-undo)
|
||||
entity (update removed-e :txs concat tx-data)]
|
||||
(push-undo entity))
|
||||
(let [updated-blocks (db-report/get-blocks tx-report)
|
||||
entity {:blocks updated-blocks
|
||||
entity {:tx-id (get-in tx-report [:tempids :db/current-tx])
|
||||
:blocks updated-blocks
|
||||
:txs tx-data
|
||||
:tx-meta tx-meta
|
||||
:editor-cursor (:editor-cursor tx-meta)
|
||||
:pagination-blocks-range (get-in [:ui/pagination-blocks-range (get-in tx-report [:db-after :max-tx])] @state/state)
|
||||
:app-state (select-keys @state/state
|
||||
[:route-match
|
||||
|
||||
@@ -16,8 +16,7 @@
|
||||
[logseq.graph-parser.util :as gp-util]
|
||||
[cljs.spec.alpha :as s]))
|
||||
|
||||
(s/def ::block-map (s/keys :req [:db/id]
|
||||
:opt [:block/page :block/left :block/parent]))
|
||||
(s/def ::block-map (s/keys :opt [:db/id :block/uuid :block/page :block/left :block/parent]))
|
||||
|
||||
(s/def ::block-map-or-entity (s/or :entity de/entity?
|
||||
:map ::block-map))
|
||||
@@ -26,8 +25,14 @@
|
||||
|
||||
(defn block
|
||||
[m]
|
||||
(assert (map? m) (util/format "block data must be map, got: %s %s" (type m) m))
|
||||
(->Block m))
|
||||
(assert (or (map? m) (de/entity? m)) (util/format "block data must be map or entity, got: %s %s" (type m) m))
|
||||
(if (de/entity? m)
|
||||
(->Block {:db/id (:db/id m)
|
||||
:block/uuid (:block/uuid m)
|
||||
:block/page (:block/page m)
|
||||
:block/left (:block/left m)
|
||||
:block/parent (:block/parent m)})
|
||||
(->Block m)))
|
||||
|
||||
(defn get-data
|
||||
[block]
|
||||
@@ -145,42 +150,49 @@
|
||||
m)
|
||||
other-tx (:db/other-tx m)
|
||||
id (:db/id (:data this))
|
||||
block-entity (db/entity id)
|
||||
remove-self-page #(remove (fn [b]
|
||||
(= (:db/id b) (:db/id (:block/page block-entity)))) %)
|
||||
old-refs (remove-self-page (:block/refs block-entity))
|
||||
new-refs (remove-self-page (:block/refs m))]
|
||||
block-entity (db/entity id)]
|
||||
(when (seq other-tx)
|
||||
(swap! txs-state (fn [txs]
|
||||
(vec (concat txs other-tx)))))
|
||||
|
||||
(when id
|
||||
;; Retract attributes to prepare for tx which rewrites block attributes
|
||||
(swap! txs-state (fn [txs]
|
||||
(vec
|
||||
(concat txs
|
||||
(map (fn [attribute]
|
||||
[:db/retract id attribute])
|
||||
db-schema/retract-attributes)))))
|
||||
db-schema/retract-attributes)))))
|
||||
|
||||
;; Update block's page attributes
|
||||
(when-let [e (:block/page block-entity)]
|
||||
(let [m' {:db/id (:db/id e)
|
||||
:block/updated-at (util/time-ms)}
|
||||
m' (if (:block/created-at e)
|
||||
m'
|
||||
(assoc m' :block/created-at (util/time-ms)))
|
||||
m' (if (or (:block/pre-block? block-entity)
|
||||
(:block/pre-block? m))
|
||||
(let [properties (:block/properties m)
|
||||
alias (set (:alias properties))
|
||||
tags (set (:tags properties))
|
||||
alias (map (fn [p] {:block/name (util/page-name-sanity-lc p)}) alias)
|
||||
tags (map (fn [p] {:block/name (util/page-name-sanity-lc p)}) tags)]
|
||||
(assoc m'
|
||||
:block/alias alias
|
||||
:block/tags tags
|
||||
:block/properties properties))
|
||||
m')]
|
||||
(swap! txs-state conj m'))
|
||||
(let [m' (cond-> {:db/id (:db/id e)
|
||||
:block/updated-at (util/time-ms)}
|
||||
(not (:block/created-at e))
|
||||
(assoc :block/created-at (util/time-ms)))
|
||||
txs (if (or (:block/pre-block? block-entity)
|
||||
(:block/pre-block? m))
|
||||
(let [properties (:block/properties m)
|
||||
alias (set (:alias properties))
|
||||
tags (set (:tags properties))
|
||||
alias (map (fn [p] {:block/name (util/page-name-sanity-lc p)}) alias)
|
||||
tags (map (fn [p] {:block/name (util/page-name-sanity-lc p)}) tags)
|
||||
deleteable-page-attributes {:block/alias alias
|
||||
:block/tags tags
|
||||
:block/properties properties
|
||||
:block/properties-text-values (:block/properties-text-values m)}
|
||||
;; Retract page attributes to allow for deletion of page attributes
|
||||
page-retractions
|
||||
(mapv #(vector :db/retract (:db/id e) %) (keys deleteable-page-attributes))]
|
||||
(conj page-retractions (merge m' deleteable-page-attributes)))
|
||||
[m'])]
|
||||
(swap! txs-state into txs)))
|
||||
|
||||
;; Remove orphaned refs from block
|
||||
(let [remove-self-page #(remove (fn [b]
|
||||
(= (:db/id b) (:db/id (:block/page block-entity)))) %)
|
||||
old-refs (remove-self-page (:block/refs block-entity))
|
||||
new-refs (remove-self-page (:block/refs m))]
|
||||
(remove-orphaned-page-refs! (:db/id block-entity) txs-state old-refs new-refs)))
|
||||
|
||||
(swap! txs-state conj (dissoc m :db/other-tx))
|
||||
@@ -228,11 +240,6 @@
|
||||
children (db-model/get-block-immediate-children (state/get-current-repo) parent-id)]
|
||||
(map block children))))
|
||||
|
||||
(defn get-right-node
|
||||
[node]
|
||||
{:pre [(tree/satisfied-inode? node)]}
|
||||
(tree/-get-right node))
|
||||
|
||||
(defn get-right-sibling
|
||||
[db-id]
|
||||
(when db-id
|
||||
@@ -404,7 +411,7 @@
|
||||
(let [level-blocks (blocks-with-level blocks)]
|
||||
(filter (fn [b] (= 1 (:block/level b))) level-blocks)))
|
||||
|
||||
(defn get-right-siblings
|
||||
(defn- get-right-siblings
|
||||
"Get `node`'s right siblings."
|
||||
[node]
|
||||
{:pre [(tree/satisfied-inode? node)]}
|
||||
@@ -474,7 +481,7 @@
|
||||
(:db/id target-block))
|
||||
get-new-id (fn [block lookup]
|
||||
(cond
|
||||
(or (map? lookup) (vector? lookup))
|
||||
(or (map? lookup) (vector? lookup) (de/entity? lookup))
|
||||
(when-let [uuid (if (and (vector? lookup) (= (first lookup) :block/uuid))
|
||||
(get uuids (last lookup))
|
||||
(get id->new-uuid (:db/id lookup)))]
|
||||
@@ -507,6 +514,13 @@
|
||||
(dissoc :db/id)))))
|
||||
blocks)))
|
||||
|
||||
(defn- get-target-block
|
||||
[target-block]
|
||||
(if (:db/id target-block)
|
||||
(db/pull (:db/id target-block))
|
||||
(when (:block/uuid target-block)
|
||||
(db/pull [:block/uuid (:block/uuid target-block)]))))
|
||||
|
||||
(defn insert-blocks
|
||||
"Insert blocks as children (or siblings) of target-node.
|
||||
Args:
|
||||
@@ -524,7 +538,7 @@
|
||||
[blocks target-block {:keys [sibling? keep-uuid? outliner-op replace-empty-target?] :as opts}]
|
||||
{:pre [(seq blocks)
|
||||
(s/valid? ::block-map-or-entity target-block)]}
|
||||
(let [target-block' (db/pull (:db/id target-block))
|
||||
(let [target-block' (get-target-block target-block)
|
||||
_ (assert (some? target-block') (str "Invalid target: " target-block))
|
||||
sibling? (if (page-block? target-block') false sibling?)
|
||||
move? (contains? #{:move-blocks :move-blocks-up-down :indent-outdent-blocks} outliner-op)
|
||||
@@ -710,7 +724,9 @@
|
||||
[blocks target-block {:keys [sibling? outliner-op]}]
|
||||
[:pre [(seq blocks)
|
||||
(s/valid? ::block-map-or-entity target-block)]]
|
||||
(let [non-consecutive-blocks? (seq (db-model/get-non-consecutive-blocks blocks))
|
||||
(let [target-block (get-target-block target-block)
|
||||
_ (assert (some? target-block) (str "Invalid target: " target-block))
|
||||
non-consecutive-blocks? (seq (db-model/get-non-consecutive-blocks blocks))
|
||||
original-position? (move-to-original-position? blocks target-block sibling? non-consecutive-blocks?)]
|
||||
(when (and (not (contains? (set (map :db/id blocks)) (:db/id target-block)))
|
||||
(not original-position?))
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#?(:cljs (:require-macros [frontend.modules.outliner.datascript]))
|
||||
#?(:cljs (:require [datascript.core :as d]
|
||||
[frontend.db.conn :as conn]
|
||||
[frontend.db :as db]
|
||||
[frontend.modules.outliner.pipeline :as pipelines]
|
||||
[frontend.modules.editor.undo-redo :as undo-redo]
|
||||
[frontend.state :as state]
|
||||
@@ -45,9 +46,14 @@
|
||||
v)))
|
||||
x))))))
|
||||
|
||||
#?(:cljs
|
||||
(defn get-tx-id
|
||||
[tx-report]
|
||||
(get-in tx-report [:tempids :db/current-tx])))
|
||||
|
||||
#?(:cljs
|
||||
(defn transact!
|
||||
[txs opts]
|
||||
[txs opts before-editor-cursor]
|
||||
(let [txs (remove-nil-from-transaction txs)
|
||||
txs (map (fn [m] (if (map? m)
|
||||
(dissoc m
|
||||
@@ -65,9 +71,15 @@
|
||||
(try
|
||||
(let [repo (get opts :repo (state/get-current-repo))
|
||||
conn (conn/get-db repo false)
|
||||
editor-cursor (state/get-current-edit-block-and-position)
|
||||
meta (merge opts {:editor-cursor editor-cursor})
|
||||
rs (d/transact! conn txs (assoc meta :outliner/transact? true))]
|
||||
rs (d/transact! conn txs (assoc opts :outliner/transact? true))
|
||||
tx-id (get-tx-id rs)]
|
||||
(swap! state/state assoc-in [:history/tx->editor-cursor tx-id] before-editor-cursor)
|
||||
|
||||
;; update the current edit block to include full information
|
||||
(when-let [block (state/get-edit-block)]
|
||||
(when (and (:block/uuid block) (not (:db/id block)))
|
||||
(state/set-state! :editor/block (db/pull [:block/uuid (:block/uuid block)]))))
|
||||
|
||||
(when true ; TODO: add debug flag
|
||||
(let [eids (distinct (mapv first (:tx-data rs)))
|
||||
left&parent-list (->>
|
||||
|
||||
@@ -27,7 +27,8 @@
|
||||
`(let [transact-data# frontend.modules.outliner.core/*transaction-data*
|
||||
opts# (if transact-data#
|
||||
(assoc ~opts :nested-transaction? true)
|
||||
~opts)]
|
||||
~opts)
|
||||
before-editor-cursor# (frontend.state/get-current-edit-block-and-position)]
|
||||
(if transact-data#
|
||||
(do ~@body)
|
||||
(binding [frontend.modules.outliner.core/*transaction-data* (transient [])]
|
||||
@@ -40,7 +41,7 @@
|
||||
opts## (merge (dissoc opts# :additional-tx) tx-meta#)]
|
||||
(when (seq all-tx#) ;; If it's empty, do nothing
|
||||
(when-not (:nested-transaction? opts#) ; transact only for the whole transaction
|
||||
(let [result# (frontend.modules.outliner.datascript/transact! all-tx# opts##)]
|
||||
(let [result# (frontend.modules.outliner.datascript/transact! all-tx# opts## before-editor-cursor#)]
|
||||
{:tx-report result#
|
||||
:tx-data all-tx#
|
||||
:tx-meta tx-meta#}))))))))
|
||||
|
||||
@@ -72,34 +72,34 @@
|
||||
:pdf/find {:binding "alt+f"
|
||||
:fn pdf-utils/open-finder}
|
||||
|
||||
:whiteboard/select {:binding ["1" "s"]
|
||||
:whiteboard/select {:binding ["1" "w s"]
|
||||
:fn #(.selectTool ^js (state/active-tldraw-app) "select")}
|
||||
|
||||
:whiteboard/pan {:binding ["2" "p"]
|
||||
:whiteboard/pan {:binding ["2" "w p"]
|
||||
:fn #(.selectTool ^js (state/active-tldraw-app) "move")}
|
||||
|
||||
:whiteboard/portal {:binding "3"
|
||||
:whiteboard/portal {:binding ["3" "w b"]
|
||||
:fn #(.selectTool ^js (state/active-tldraw-app) "logseq-portal")}
|
||||
|
||||
:whiteboard/pencil {:binding ["4" "d"]
|
||||
:whiteboard/pencil {:binding ["4" "w d"]
|
||||
:fn #(.selectTool ^js (state/active-tldraw-app) "pencil")}
|
||||
|
||||
:whiteboard/highlighter {:binding ["5" "h"]
|
||||
:whiteboard/highlighter {:binding ["5" "w h"]
|
||||
:fn #(.selectTool ^js (state/active-tldraw-app) "highlighter")}
|
||||
|
||||
:whiteboard/eraser {:binding ["6" "e"]
|
||||
:whiteboard/eraser {:binding ["6" "w e"]
|
||||
:fn #(.selectTool ^js (state/active-tldraw-app) "erase")}
|
||||
|
||||
:whiteboard/connector {:binding ["7" "c"]
|
||||
:whiteboard/connector {:binding ["7" "w c"]
|
||||
:fn #(.selectTool ^js (state/active-tldraw-app) "line")}
|
||||
|
||||
:whiteboard/text {:binding ["8" "t"]
|
||||
:whiteboard/text {:binding ["8" "w t"]
|
||||
:fn #(.selectTool ^js (state/active-tldraw-app) "text")}
|
||||
|
||||
:whiteboard/rectangle {:binding ["9" "r"]
|
||||
:whiteboard/rectangle {:binding ["9" "w r"]
|
||||
:fn #(.selectTool ^js (state/active-tldraw-app) "box")}
|
||||
|
||||
:whiteboard/ellipse {:binding "o"
|
||||
:whiteboard/ellipse {:binding ["o" "w o"]
|
||||
:fn #(.selectTool ^js (state/active-tldraw-app) "ellipse")}
|
||||
|
||||
:whiteboard/reset-zoom {:binding "shift+0"
|
||||
@@ -141,7 +141,7 @@
|
||||
:whiteboard/ungroup {:binding "mod+shift+g"
|
||||
:fn #(.unGroup (.-api ^js (state/active-tldraw-app)))}
|
||||
|
||||
:whiteboard/toggle-grid {:binding "shift+g"
|
||||
:whiteboard/toggle-grid {:binding "t g"
|
||||
:fn #(.toggleGrid (.-api ^js (state/active-tldraw-app)))}
|
||||
|
||||
:auto-complete/complete {:binding "enter"
|
||||
|
||||
@@ -1140,7 +1140,47 @@
|
||||
:command.ui/goto-plugins "Gå til dashbord for utvidelser"
|
||||
;; :command.ui/open-new-window "Åpne et nytt vindu"
|
||||
:command.ui/select-theme-color "Velg tilgjengelige temafarger"
|
||||
:command.ui/toggle-cards "Veksle kort"}
|
||||
:command.ui/toggle-cards "Veksle kort"
|
||||
:command.dev/show-block-ast "(Dev) Vis blokk AST"
|
||||
:command.dev/show-block-data "(Dev) Vis blokk data"
|
||||
:command.dev/show-page-ast "(Dev) Vis side AST"
|
||||
:command.dev/show-page-data "(Dev) Vis side data"
|
||||
:command.editor/copy-page-url "Kopier side url"
|
||||
:command.editor/new-whiteboard "Nytt whiteboard"
|
||||
:command.editor/select-parent "Velg overordnet blokk"
|
||||
:command.editor/toggle-number-list "Veksle nummerliste"
|
||||
:command.editor/toggle-undo-redo-mode "Veksle angremodus (global eller kun side)"
|
||||
:command.go/whiteboards "Gå til whiteboards"
|
||||
:command.graph/export-as-html "Eksporter offentlig graf som html"
|
||||
:command.pdf/find "Pdf: Søk tekst i nåværende pdf doc"
|
||||
:command.sidebar/close-top "Lukker øverste objekt i høyre sidestolpe"
|
||||
:command.ui/clear-all-notifications "Fjern alle varsler"
|
||||
:command.ui/install-plugins-from-file "Installer plugins fra plugins.edn"
|
||||
:command.whiteboard/bring-forward "Flytt fremover"
|
||||
:command.whiteboard/bring-to-front "Flytt fremst"
|
||||
:command.whiteboard/connector "Koblingsverktøy"
|
||||
:command.whiteboard/ellipse "Ellipseverktøy"
|
||||
:command.whiteboard/eraser "Sletteverktøy"
|
||||
:command.whiteboard/group "Velg gruppe"
|
||||
:command.whiteboard/highlighter "Merkepenn"
|
||||
:command.whiteboard/lock "Lås seleksjon"
|
||||
:command.whiteboard/pan "Panoreringsverktøy"
|
||||
:command.whiteboard/pencil "Blyantverktøy"
|
||||
:command.whiteboard/portal "Portalverktøy"
|
||||
:command.whiteboard/rectangle "Rektangelverktøy"
|
||||
:command.whiteboard/reset-zoom "Tilbakestill zoom"
|
||||
:command.whiteboard/select "Valg-verktøy"
|
||||
:command.whiteboard/send-backward "Flytt bakover"
|
||||
:command.whiteboard/send-to-back "Flytt bakerst"
|
||||
:command.whiteboard/text "Tekst-verktøy"
|
||||
:command.whiteboard/toggle-grid "Veksle rutenett på lerretet"
|
||||
:command.whiteboard/ungroup "Del opp gruppe"
|
||||
:command.whiteboard/unlock "Lås opp seleksjon"
|
||||
:command.whiteboard/zoom-in "Zoom inn"
|
||||
:command.whiteboard/zoom-out "Zoom ut"
|
||||
:command.whiteboard/zoom-to-fit "Zoom til tegning"
|
||||
:command.whiteboard/zoom-to-selection "Zoom for å passe seleksjonen"
|
||||
:shortcut.category/whiteboard "Whiteboard"}
|
||||
|
||||
:pt-PT {:shortcut.category/formatting "Formatação"
|
||||
:shortcut.category/basics "Básico"
|
||||
@@ -1660,6 +1700,7 @@
|
||||
:shortcut.category/block-command-editing "Blok düzenleme komutuları"
|
||||
:shortcut.category/block-selection "Blok seçimi (seçimden çıkmak için Esc tuşuna basın)"
|
||||
:shortcut.category/toggle "Aç/Kapat"
|
||||
:shortcut.category/whiteboard "Beyaz tahta"
|
||||
:shortcut.category/others "Diğer"
|
||||
:command.date-picker/complete "Tarih seçici: Seçilen günü seç"
|
||||
:command.date-picker/prev-day "Tarih seçici: Önceki günü seç"
|
||||
@@ -1732,6 +1773,31 @@
|
||||
:command.editor/zoom-in "Düzenlenen bloğu yakınlaştır / Aksi takdirde ileri git"
|
||||
:command.editor/zoom-out "Düzenlenen bloğu uzaklaştır / Aksi takdirde geri git"
|
||||
:command.editor/toggle-undo-redo-mode "Geri alma / yineleme modunu değiştir (yalnızca sayfa veya genel)"
|
||||
:command.editor/toggle-number-list "Numaralı liste olarak değiştir"
|
||||
:command.whiteboard/select "Seçim aracı"
|
||||
:command.whiteboard/pan "Kaydırma aracı"
|
||||
:command.whiteboard/portal "Portal aracı"
|
||||
:command.whiteboard/pencil "Kalem aracı"
|
||||
:command.whiteboard/highlighter "Vurgulayıcı aracı"
|
||||
:command.whiteboard/eraser "Silgi aracı"
|
||||
:command.whiteboard/connector "Bağlayıcı aracı"
|
||||
:command.whiteboard/text "Metin aracı"
|
||||
:command.whiteboard/rectangle "Dikdörtgen aracı"
|
||||
:command.whiteboard/ellipse "Elips aracı"
|
||||
:command.whiteboard/reset-zoom "Yakınlaştırmayı sıfırla"
|
||||
:command.whiteboard/zoom-to-fit "Çizimi yakınlaştır"
|
||||
:command.whiteboard/zoom-to-selection "Seçimi sığacak kadar yakınlaştır"
|
||||
:command.whiteboard/zoom-out "Uzaklaştır"
|
||||
:command.whiteboard/zoom-in "Yakınlaştır"
|
||||
:command.whiteboard/send-backward "Geriye git"
|
||||
:command.whiteboard/send-to-back "Geriye taşı"
|
||||
:command.whiteboard/bring-forward "İleriye git"
|
||||
:command.whiteboard/bring-to-front "Öne taşı"
|
||||
:command.whiteboard/lock "Seçimi kilitle"
|
||||
:command.whiteboard/unlock "Seçimin Kilidini aç"
|
||||
:command.whiteboard/group "Seçimi gruplandır"
|
||||
:command.whiteboard/ungroup "Seçimi gruptan çıkar"
|
||||
:command.whiteboard/toggle-grid "Tuval ızgarasını değiştir"
|
||||
:command.ui/toggle-brackets "Köşeli ayraçların görüntülenip görüntülenmeyeceğini değiştir"
|
||||
:command.go/search-in-page "Geçerli sayfada ara"
|
||||
:command.go/electron-find-in-page "Sayfada bul"
|
||||
|
||||
@@ -52,11 +52,11 @@
|
||||
[:div.flex.flex-col.items-start
|
||||
[:div.text-2xs.font-bold.uppercase.toned-down (t :page/step "1")]
|
||||
[:div [:span.highlighted.font-bold "Rebuild"] [:span.toned-down " search index"]]]
|
||||
[:div
|
||||
(ui/button (t :page/try)
|
||||
:small? true
|
||||
:on-click (fn []
|
||||
(search-handler/rebuild-indices! true)))]]
|
||||
[:div
|
||||
(ui/button (t :page/try)
|
||||
:small? true
|
||||
:on-click (fn []
|
||||
(search-handler/rebuild-indices! true)))]]
|
||||
[:div.flex.flex-row.justify-between.align-items.mb-2.items-center.separator-top.py-4
|
||||
[:div.flex.flex-col.items-start
|
||||
[:div.text-2xs.font-bold.uppercase.toned-down (t :page/step "2")]
|
||||
@@ -92,7 +92,7 @@
|
||||
(ui/inject-dynamic-style-node!)
|
||||
(quick-tour/init)
|
||||
(plugin-handler/host-mounted!)
|
||||
(assoc state ::teardown (setup-fns!) ))
|
||||
(assoc state ::teardown (setup-fns!)))
|
||||
:will-unmount (fn [state]
|
||||
(when-let [teardown (::teardown state)]
|
||||
(teardown)))}
|
||||
|
||||
@@ -92,7 +92,7 @@
|
||||
(fn []
|
||||
(rum/set-ref! *mounted true)
|
||||
#(rum/set-ref! *mounted false))
|
||||
[])
|
||||
[])
|
||||
#(rum/deref *mounted)))
|
||||
|
||||
(defn use-bounding-client-rect
|
||||
|
||||
@@ -62,6 +62,7 @@
|
||||
:string]]
|
||||
[:ref/default-open-blocks-level :int]
|
||||
[:ref/linked-references-collapsed-threshold :int]
|
||||
[:graph/settings [:map-of :keyword :boolean]]
|
||||
[:favorites [:vector :string]]
|
||||
;; There isn't a :float yet
|
||||
[:srs/learning-fraction float?]
|
||||
|
||||
25
src/main/frontend/shui.cljs
Normal file
25
src/main/frontend/shui.cljs
Normal file
@@ -0,0 +1,25 @@
|
||||
(ns frontend.shui
|
||||
"Glue between frontend code and deps/shui for convenience"
|
||||
(:require
|
||||
[frontend.date :refer [int->local-time-2]]
|
||||
[frontend.state :as state]
|
||||
[logseq.shui.context :refer [make-context]]))
|
||||
|
||||
(def default-versions {:logseq.table.version 1})
|
||||
|
||||
(defn get-shui-component-version
|
||||
"Returns the version of the shui component, checking first
|
||||
the block properties, then the global config, then the defaults."
|
||||
[component-name block-config]
|
||||
(let [version-key (keyword (str "logseq." (name component-name) ".version"))]
|
||||
(js/parseFloat
|
||||
(or (get-in block-config [:block :block/properties version-key])
|
||||
(get-in (state/get-config) [version-key])
|
||||
(get-in default-versions [version-key])
|
||||
1))))
|
||||
|
||||
(defn make-shui-context [block-config inline]
|
||||
(make-context {:block-config block-config
|
||||
:app-config (state/get-config)
|
||||
:inline inline
|
||||
:int->local-time-2 int->local-time-2}))
|
||||
@@ -259,9 +259,9 @@
|
||||
;; :file-sync/progress {}
|
||||
;; :file-sync/start-time {}
|
||||
;; :file-sync/last-synced-at {}}
|
||||
:file-sync/graph-state {:current-graph-uuid nil
|
||||
:file-sync/graph-state {:current-graph-uuid nil}
|
||||
;; graph-uuid -> ...
|
||||
}
|
||||
|
||||
:user/info {:UserGroups (storage/get :user-groups)}
|
||||
:encryption/graph-parsing? false
|
||||
|
||||
@@ -285,7 +285,10 @@
|
||||
:chat/current-conversation nil
|
||||
:ai/preferred-translate-target-lang (storage/get :ai/preferred-translate-target-lang)
|
||||
:ai/engines {}
|
||||
:ai/current-service "Built-in OpenAI"})))
|
||||
:ai/current-service "Built-in OpenAI"
|
||||
|
||||
;; db tx-id -> editor cursor
|
||||
:history/tx->editor-cursor {}})))
|
||||
|
||||
;; Block ast state
|
||||
;; ===============
|
||||
|
||||
@@ -730,26 +730,26 @@
|
||||
[state {:keys [on-mouse-down header title-trigger? collapsed?]}]
|
||||
(let [control? (get state ::control?)]
|
||||
[:div.content
|
||||
[:div.flex-1.flex-row.foldable-title (cond->
|
||||
{:on-mouse-over #(reset! control? true)
|
||||
:on-mouse-out #(reset! control? false)}
|
||||
title-trigger?
|
||||
(assoc :on-mouse-down on-mouse-down
|
||||
:class "cursor"))
|
||||
[:div.flex.flex-row.items-center
|
||||
(when-not (mobile-util/native-platform?)
|
||||
[:a.block-control.opacity-50.hover:opacity-100.mr-2
|
||||
(cond->
|
||||
{:style {:width 14
|
||||
:height 16
|
||||
:margin-left -30}}
|
||||
(not title-trigger?)
|
||||
(assoc :on-mouse-down on-mouse-down))
|
||||
[:span {:class (if (or @control? @collapsed?) "control-show cursor-pointer" "control-hide")}
|
||||
(rotating-arrow @collapsed?)]])
|
||||
(if (fn? header)
|
||||
(header @collapsed?)
|
||||
header)]]]))
|
||||
[:div.flex-1.flex-row.foldable-title (cond->
|
||||
{:on-mouse-over #(reset! control? true)
|
||||
:on-mouse-out #(reset! control? false)}
|
||||
title-trigger?
|
||||
(assoc :on-mouse-down on-mouse-down
|
||||
:class "cursor"))
|
||||
[:div.flex.flex-row.items-center
|
||||
(when-not (mobile-util/native-platform?)
|
||||
[:a.block-control.opacity-50.hover:opacity-100.mr-2
|
||||
(cond->
|
||||
{:style {:width 14
|
||||
:height 16
|
||||
:margin-left -30}}
|
||||
(not title-trigger?)
|
||||
(assoc :on-mouse-down on-mouse-down))
|
||||
[:span {:class (if (or @control? @collapsed?) "control-show cursor-pointer" "control-hide")}
|
||||
(rotating-arrow @collapsed?)]])
|
||||
(if (fn? header)
|
||||
(header @collapsed?)
|
||||
header)]]]))
|
||||
|
||||
(rum/defcs foldable < db-mixins/query rum/reactive
|
||||
(rum/local false ::collapsed?)
|
||||
@@ -852,10 +852,10 @@
|
||||
[:option (cond->
|
||||
{:key label
|
||||
:value (or value label)} ;; NOTE: value might be an empty string, `or` is safe here
|
||||
disabled
|
||||
(assoc :disabled disabled)
|
||||
selected
|
||||
(assoc :selected selected))
|
||||
disabled
|
||||
(assoc :disabled disabled)
|
||||
selected
|
||||
(assoc :selected selected))
|
||||
label])]))
|
||||
|
||||
(rum/defc radio-list
|
||||
|
||||
@@ -1274,7 +1274,7 @@
|
||||
#?(:cljs
|
||||
(defn scroll-editor-cursor
|
||||
[^js/HTMLElement el & {:keys [to-vw-one-quarter?]}]
|
||||
(when (and el (or (mobile-util/native-platform?) mobile?))
|
||||
(when (and el (or (mobile-util/native-platform?) (mobile?)))
|
||||
(let [box-rect (.getBoundingClientRect el)
|
||||
box-top (.-top box-rect)
|
||||
box-bottom (.-bottom box-rect)
|
||||
|
||||
@@ -226,7 +226,7 @@
|
||||
|
||||
(defn- move-cursor-up-down
|
||||
[input direction]
|
||||
(move-cursor-to input (next-cursor-pos-up-down direction (get-caret-pos input))))
|
||||
(move-cursor-to input (next-cursor-pos-up-down direction (get-caret-pos input))))
|
||||
|
||||
(defn move-cursor-up [input]
|
||||
(move-cursor-up-down input :up))
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
a good ns to be in yet"
|
||||
(:require [clojure.string :as string]
|
||||
[goog.string :as gstring]
|
||||
[frontend.util :as util]))
|
||||
[frontend.util :as util]
|
||||
[logseq.common.path :as path]))
|
||||
|
||||
(defonce between-re #"\(between ([^\)]+)\)")
|
||||
|
||||
@@ -142,10 +143,11 @@
|
||||
;; FIXME: distinguish from get-repo-name
|
||||
(defn get-graph-name-from-path
|
||||
[path]
|
||||
(when (string? path)
|
||||
(let [parts (->> (string/split path #"/")
|
||||
(take-last 2))]
|
||||
(-> (if (not= (first parts) "0")
|
||||
(util/string-join-path parts)
|
||||
(last parts))
|
||||
js/decodeURIComponent))))
|
||||
(let [path (if (path/is-file-url? path)
|
||||
(path/url-to-path path)
|
||||
path)
|
||||
parts (->> (string/split path #"/")
|
||||
(take-last 2))]
|
||||
(if (not= (first parts) "0")
|
||||
(util/string-join-path parts)
|
||||
(last parts))))
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
(ns ^:no-doc frontend.version)
|
||||
|
||||
(defonce version "0.9.4")
|
||||
(defonce version "0.9.6")
|
||||
|
||||
@@ -43,7 +43,8 @@
|
||||
[frontend.handler.shell :as shell]
|
||||
[frontend.modules.layout.core]
|
||||
[frontend.handler.code :as code-handler]
|
||||
[frontend.handler.search :as search-handler]))
|
||||
[frontend.handler.search :as search-handler]
|
||||
[logseq.api.block :as api-block]))
|
||||
|
||||
;; Alert: this namespace shouldn't invoke any reactive queries
|
||||
|
||||
@@ -651,13 +652,13 @@
|
||||
nil)))
|
||||
|
||||
(def ^:export update_block
|
||||
(fn [block-uuid content ^js _opts]
|
||||
(fn [block-uuid content ^js opts]
|
||||
(let [repo (state/get-current-repo)
|
||||
edit-input (state/get-edit-input-id)
|
||||
editing? (and edit-input (string/ends-with? edit-input (str block-uuid)))]
|
||||
(if editing?
|
||||
(state/set-edit-content! edit-input content)
|
||||
(editor-handler/save-block! repo (sdk-utils/uuid-or-throw-error block-uuid) content))
|
||||
(editor-handler/save-block! repo (sdk-utils/uuid-or-throw-error block-uuid) content (bean/->clj opts)))
|
||||
nil)))
|
||||
|
||||
(def ^:export move_block
|
||||
@@ -676,24 +677,7 @@
|
||||
target-block (db-model/query-block-by-uuid (sdk-utils/uuid-or-throw-error target-block-uuid))]
|
||||
(editor-dnd-handler/move-blocks nil [src-block] target-block move-to) nil)))
|
||||
|
||||
(def ^:export get_block
|
||||
(fn [id-or-uuid ^js opts]
|
||||
(when-let [block (cond
|
||||
(number? id-or-uuid) (db-utils/pull id-or-uuid)
|
||||
(string? id-or-uuid) (db-model/query-block-by-uuid (sdk-utils/uuid-or-throw-error id-or-uuid)))]
|
||||
(when-not (contains? block :block/name)
|
||||
(when-let [uuid (:block/uuid block)]
|
||||
(let [{:keys [includeChildren]} (bean/->clj opts)
|
||||
repo (state/get-current-repo)
|
||||
block (if includeChildren
|
||||
;; nested children results
|
||||
(first (outliner-tree/blocks->vec-tree
|
||||
(db-model/get-block-and-children repo uuid) uuid))
|
||||
;; attached shallow children
|
||||
(assoc block :block/children
|
||||
(map #(list :uuid (get-in % [:data :block/uuid]))
|
||||
(db/get-block-immediate-children repo uuid))))]
|
||||
(bean/->js (sdk-utils/normalize-keyword-for-json block))))))))
|
||||
(def ^:export get_block api-block/get_block)
|
||||
|
||||
(def ^:export get_current_block
|
||||
(fn [^js opts]
|
||||
@@ -703,7 +687,7 @@
|
||||
(gdom/getElement (state/get-editing-block-dom-id)))
|
||||
(.getAttribute "blockid")
|
||||
(db-model/get-block-by-uuid)))]
|
||||
(get_block (:db/id block) opts))))
|
||||
(get_block (:block/uuid block) opts))))
|
||||
|
||||
(def ^:export get_previous_sibling_block
|
||||
(fn [block-uuid]
|
||||
@@ -715,8 +699,9 @@
|
||||
(def ^:export get_next_sibling_block
|
||||
(fn [block-uuid]
|
||||
(when-let [block (db-model/query-block-by-uuid (sdk-utils/uuid-or-throw-error block-uuid))]
|
||||
(when-let [right-siblings (outliner/get-right-siblings (outliner/->Block block))]
|
||||
(bean/->js (sdk-utils/normalize-keyword-for-json (:data (first right-siblings))))))))
|
||||
(when-let [right-sibling (outliner/get-right-sibling (:db/id block))]
|
||||
(let [block (db/pull (:id right-sibling))]
|
||||
(bean/->js (sdk-utils/normalize-keyword-for-json block)))))))
|
||||
|
||||
(def ^:export set_block_collapsed
|
||||
(fn [block-uuid ^js opts]
|
||||
|
||||
28
src/main/logseq/api/block.cljs
Normal file
28
src/main/logseq/api/block.cljs
Normal file
@@ -0,0 +1,28 @@
|
||||
(ns logseq.api.block
|
||||
"Block related apis"
|
||||
(:require [frontend.db.model :as db-model]
|
||||
[frontend.db.utils :as db-utils]
|
||||
[cljs-bean.core :as bean]
|
||||
[frontend.state :as state]
|
||||
[frontend.modules.outliner.tree :as outliner-tree]
|
||||
[frontend.db :as db]
|
||||
[logseq.sdk.utils :as sdk-utils]))
|
||||
|
||||
(defn get_block
|
||||
[id-or-uuid ^js opts]
|
||||
(when-let [block (if (number? id-or-uuid)
|
||||
(db-utils/pull id-or-uuid)
|
||||
(db-model/query-block-by-uuid (sdk-utils/uuid-or-throw-error id-or-uuid)))]
|
||||
(when-not (contains? block :block/name)
|
||||
(when-let [uuid (:block/uuid block)]
|
||||
(let [{:keys [includeChildren]} (bean/->clj opts)
|
||||
repo (state/get-current-repo)
|
||||
block (if includeChildren
|
||||
;; nested children results
|
||||
(first (outliner-tree/blocks->vec-tree
|
||||
(db-model/get-block-and-children repo uuid) uuid))
|
||||
;; attached shallow children
|
||||
(assoc block :block/children
|
||||
(map #(list :uuid (:block/uuid %))
|
||||
(db/get-block-immediate-children repo uuid))))]
|
||||
(bean/->js (sdk-utils/normalize-keyword-for-json block)))))))
|
||||
@@ -163,6 +163,22 @@ foo:: bar"}])
|
||||
(catch :default e
|
||||
(ex-message e)))))))
|
||||
|
||||
(deftest get-block-immediate-children
|
||||
(load-test-files [{:file/path "pages/page1.md"
|
||||
:file/content "\n
|
||||
- parent
|
||||
- child 1
|
||||
- grandchild 1
|
||||
- child 2
|
||||
- grandchild 2
|
||||
- child 3"}])
|
||||
(let [parent (-> (d/q '[:find (pull ?b [*]) :where [?b :block/content "parent"]]
|
||||
(conn/get-db test-helper/test-db))
|
||||
ffirst)]
|
||||
(is (= ["child 1" "child 2" "child 3"]
|
||||
(map :block/content
|
||||
(model/get-block-immediate-children test-helper/test-db (:block/uuid parent)))))))
|
||||
|
||||
(deftest get-property-values
|
||||
(load-test-files [{:file/path "pages/Feature.md"
|
||||
:file/content "type:: [[Class]]"}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
(:require [clojure.test :refer [is use-fixtures]]
|
||||
[frontend.test.fixtures :as fixtures]
|
||||
[frontend.test.helper :as test-helper :include-macros true :refer [deftest-async]]
|
||||
[frontend.test.node-helper :as test-node-helper]
|
||||
[frontend.fs :as fs]
|
||||
[promesa.core :as p]
|
||||
["fs" :as fs-node]
|
||||
@@ -11,7 +12,7 @@
|
||||
|
||||
(deftest-async create-if-not-exists-creates-correctly
|
||||
;; dir needs to be an absolute path for fn to work correctly
|
||||
(let [dir (node-path/resolve (test-helper/create-tmp-dir))
|
||||
(let [dir (node-path/resolve (test-node-helper/create-tmp-dir))
|
||||
some-file (node-path/join dir "something.txt")]
|
||||
|
||||
(->
|
||||
@@ -29,7 +30,7 @@
|
||||
(fs-node/rmdirSync dir))))))
|
||||
|
||||
(deftest-async create-if-not-exists-does-not-create-correctly
|
||||
(let [dir (node-path/resolve (test-helper/create-tmp-dir))
|
||||
(let [dir (node-path/resolve (test-node-helper/create-tmp-dir))
|
||||
some-file (node-path/join dir "something.txt")]
|
||||
(fs-node/writeFileSync some-file "OLD")
|
||||
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
(ns frontend.handler.editor-test
|
||||
(:require [frontend.handler.editor :as editor]
|
||||
[clojure.test :refer [deftest is testing are]]
|
||||
[frontend.db :as db]
|
||||
[clojure.test :refer [deftest is testing are use-fixtures]]
|
||||
[datascript.core :as d]
|
||||
[frontend.test.helper :as test-helper :refer [load-test-files]]
|
||||
[frontend.db.model :as model]
|
||||
[frontend.state :as state]
|
||||
[frontend.util.cursor :as cursor]))
|
||||
|
||||
(use-fixtures :each test-helper/start-and-destroy-db)
|
||||
|
||||
(deftest extract-nearest-link-from-text-test
|
||||
(testing "Page, block and tag links"
|
||||
(is (= "page1"
|
||||
@@ -213,3 +219,39 @@
|
||||
"No page search within backticks"))
|
||||
;; Reset state
|
||||
(state/set-editor-action! nil))
|
||||
|
||||
(deftest save-block-aux!
|
||||
(load-test-files [{:file/path "pages/page1.md"
|
||||
:file/content "\n
|
||||
- b1 #foo"}])
|
||||
(testing "updating block's content changes content and preserves path-refs"
|
||||
(let [conn (db/get-db test-helper/test-db false)
|
||||
block (->> (d/q '[:find (pull ?b [* {:block/path-refs [:block/name]}])
|
||||
:where [?b :block/content "b1 #foo"]]
|
||||
@conn)
|
||||
ffirst)
|
||||
prev-path-refs (set (map :block/name (:block/path-refs block)))
|
||||
_ (assert (= #{"page1" "foo"} prev-path-refs)
|
||||
"block has expected :block/path-refs")
|
||||
;; Use same options as edit-box-on-change!
|
||||
_ (editor/save-block-aux! block "b12 #foo" {:skip-properties? true})
|
||||
updated-block (d/pull @conn '[* {:block/path-refs [:block/name]}] [:block/uuid (:block/uuid block)])]
|
||||
(is (= "b12 #foo" (:block/content updated-block)) "Content updated correctly")
|
||||
(is (= prev-path-refs
|
||||
(set (map :block/name (:block/path-refs updated-block))))
|
||||
"Path-refs remain the same"))))
|
||||
|
||||
(deftest save-block!
|
||||
(testing "Saving blocks with and without properties"
|
||||
(test-helper/load-test-files [{:file/path "foo.md"
|
||||
:file/content "# foo"}])
|
||||
(let [repo test-helper/test-db
|
||||
block-uuid (:block/uuid (model/get-block-by-page-name-and-block-route-name repo "foo" "foo"))]
|
||||
(editor/save-block! repo block-uuid "# bar")
|
||||
(is (= "# bar" (:block/content (model/query-block-by-uuid block-uuid))))
|
||||
|
||||
(editor/save-block! repo block-uuid "# foo" {:properties {:foo "bar"}})
|
||||
(is (= "# foo\nfoo:: bar" (:block/content (model/query-block-by-uuid block-uuid))))
|
||||
|
||||
(editor/save-block! repo block-uuid "# bar")
|
||||
(is (= "# bar" (:block/content (model/query-block-by-uuid block-uuid)))))))
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
(ns frontend.handler.plugin-config-test
|
||||
(:require [clojure.test :refer [is use-fixtures testing deftest]]
|
||||
[frontend.test.helper :as test-helper :include-macros true :refer [deftest-async]]
|
||||
[frontend.test.node-helper :as test-node-helper]
|
||||
[frontend.test.fixtures :as fixtures]
|
||||
[frontend.handler.plugin-config :as plugin-config-handler]
|
||||
[frontend.handler.global-config :as global-config-handler]
|
||||
@@ -17,7 +18,7 @@
|
||||
|
||||
(defn- create-global-config-dir
|
||||
[]
|
||||
(let [dir (test-helper/create-tmp-dir "config")
|
||||
(let [dir (test-node-helper/create-tmp-dir "config")
|
||||
root-dir (node-path/dirname dir)]
|
||||
(reset! global-config-handler/root-dir root-dir)
|
||||
dir))
|
||||
|
||||
@@ -98,7 +98,7 @@
|
||||
;; only increase over time as the docs graph rarely has deletions
|
||||
(testing "Counts"
|
||||
(is (= 211 (count files)) "Correct file count")
|
||||
(is (= 42312 (count (d/datoms db :eavt))) "Correct datoms count")
|
||||
(is (= 42304 (count (d/datoms db :eavt))) "Correct datoms count")
|
||||
|
||||
(is (= 3600
|
||||
(ffirst
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
(:require [cljs.test :refer [deftest use-fixtures testing is]]
|
||||
[frontend.handler.repo :as repo-handler]
|
||||
[frontend.test.helper :as test-helper :refer [load-test-files]]
|
||||
[frontend.state :as state]
|
||||
[logseq.graph-parser.cli :as gp-cli]
|
||||
[logseq.graph-parser.test.docs-graph-helper :as docs-graph-helper]
|
||||
[logseq.graph-parser.util.block-ref :as block-ref]
|
||||
@@ -12,13 +11,7 @@
|
||||
["path" :as node-path]
|
||||
["fs" :as fs]))
|
||||
|
||||
(use-fixtures :each {:before (fn []
|
||||
;; Set current-repo explicitly since it's not the default
|
||||
(state/set-current-repo! test-helper/test-db)
|
||||
(test-helper/start-test-db!))
|
||||
:after (fn []
|
||||
(state/set-current-repo! nil)
|
||||
(test-helper/destroy-test-db!))})
|
||||
(use-fixtures :each test-helper/start-and-destroy-db)
|
||||
|
||||
(deftest ^:integration parse-and-load-files-to-db
|
||||
(let [graph-dir "src/test/docs-0.9.2"
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
[clojure.walk :as walk]
|
||||
[logseq.graph-parser.block :as gp-block]
|
||||
[datascript.core :as d]
|
||||
[frontend.test.helper :as test-helper]
|
||||
[frontend.test.helper :as test-helper :refer [load-test-files]]
|
||||
[clojure.set :as set]))
|
||||
|
||||
(def test-db test-helper/test-db)
|
||||
@@ -440,6 +440,63 @@
|
||||
'(16 17)
|
||||
(map :block/uuid (tree/get-sorted-block-and-children test-db (:db/id (get-block 16))))))))
|
||||
|
||||
(defn- save-block!
|
||||
[block]
|
||||
(outliner-tx/transact! {:graph test-db}
|
||||
(outliner-core/save-block! block)))
|
||||
|
||||
(deftest save-test
|
||||
(load-test-files [{:file/path "pages/page1.md"
|
||||
:file/content "alias:: foo, bar
|
||||
tags:: tag1, tag2
|
||||
- block #blarg #bar"}])
|
||||
(testing "save deletes a page's tags"
|
||||
(let [conn (db/get-db test-helper/test-db false)
|
||||
pre-block (->> (d/q '[:find (pull ?b [*])
|
||||
:where [?b :block/pre-block? true]]
|
||||
@conn)
|
||||
ffirst)
|
||||
_ (save-block! (-> pre-block
|
||||
(update :block/properties dissoc :tags)
|
||||
(update :block/properties-text-values dissoc :tags)))
|
||||
updated-page (-> (d/q '[:find (pull ?bp [* {:block/alias [*]}])
|
||||
:where [?b :block/pre-block? true]
|
||||
[?b :block/page ?bp]]
|
||||
@conn)
|
||||
ffirst)]
|
||||
(is (nil? (:block/tags updated-page))
|
||||
"Page's tags are deleted")
|
||||
(is (= #{"foo" "bar"} (set (map :block/name (:block/alias updated-page))))
|
||||
"Page's aliases remain the same")
|
||||
(is (= {:block/properties {:alias #{"foo" "bar"}}
|
||||
:block/properties-text-values {:alias "foo, bar"}}
|
||||
(select-keys updated-page [:block/properties :block/properties-text-values]))
|
||||
"Page property attributes are correct")
|
||||
(is (= {:block/properties {:alias #{"foo" "bar"}}
|
||||
:block/properties-text-values {:alias "foo, bar"}}
|
||||
(-> (d/q '[:find (pull ?b [*])
|
||||
:where [?b :block/pre-block? true]]
|
||||
@conn)
|
||||
ffirst
|
||||
(select-keys [:block/properties :block/properties-text-values])))
|
||||
"Pre-block property attributes are correct")))
|
||||
|
||||
(testing "save deletes orphaned pages when a block's refs change"
|
||||
(let [conn (db/get-db test-helper/test-db false)
|
||||
pages (set (map first (d/q '[:find ?bn :where [?b :block/name ?bn]] @conn)))
|
||||
_ (assert (set/subset? #{"blarg" "bar"} pages) "Pages from block exist")
|
||||
block-with-refs (ffirst (d/q '[:find (pull ?b [* {:block/refs [*]}])
|
||||
:where [?b :block/content "block #blarg #bar"]]
|
||||
@conn))
|
||||
_ (save-block! (-> block-with-refs
|
||||
(assoc :block/content "block"
|
||||
:block/refs [])))
|
||||
updated-pages (set (map first (d/q '[:find ?bn :where [?b :block/name ?bn]] @conn)))]
|
||||
(is (not (contains? updated-pages "blarg"))
|
||||
"Deleted, orphaned page no longer exists")
|
||||
(is (contains? updated-pages "bar")
|
||||
"Deleted but not orphaned page still exists"))))
|
||||
|
||||
;;; Fuzzy tests
|
||||
|
||||
(def init-id (atom 100))
|
||||
|
||||
@@ -1,18 +1,11 @@
|
||||
(ns frontend.modules.outliner.pipeline-test
|
||||
(:require [cljs.test :refer [deftest is use-fixtures testing]]
|
||||
[datascript.core :as d]
|
||||
[frontend.state :as state]
|
||||
[frontend.db :as db]
|
||||
[frontend.modules.outliner.pipeline :as pipeline]
|
||||
[frontend.test.helper :as test-helper :refer [load-test-files]]))
|
||||
|
||||
(use-fixtures :each {:before (fn []
|
||||
;; Set current-repo explicitly since it's not the default
|
||||
(state/set-current-repo! test-helper/test-db)
|
||||
(test-helper/start-test-db!))
|
||||
:after (fn []
|
||||
(state/set-current-repo! nil)
|
||||
(test-helper/destroy-test-db!))})
|
||||
(use-fixtures :each test-helper/start-and-destroy-db)
|
||||
|
||||
(defn- get-blocks [db]
|
||||
(->> (d/q '[:find (pull ?b [* {:block/path-refs [:block/name :db/id]}])
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
(ns frontend.test.helper
|
||||
"Common helper fns for tests"
|
||||
(:require [frontend.handler.repo :as repo-handler]
|
||||
[frontend.db.conn :as conn]
|
||||
["path" :as node-path]
|
||||
["fs" :as fs-node]))
|
||||
[frontend.state :as state]
|
||||
[frontend.db.conn :as conn]))
|
||||
|
||||
(defonce test-db "test-db")
|
||||
|
||||
@@ -25,15 +24,14 @@ This can be called in synchronous contexts as no async fns should be invoked"
|
||||
;; Set :refresh? to avoid creating default files in after-parse
|
||||
{:re-render? false :verbose false :refresh? true}))
|
||||
|
||||
(defn create-tmp-dir
|
||||
"Creates a temporary directory under tmp/. If a subdir is given, creates an
|
||||
additional subdirectory under the newly created temp directory."
|
||||
([] (create-tmp-dir nil))
|
||||
([subdir]
|
||||
(when-not (fs-node/existsSync "tmp") (fs-node/mkdirSync "tmp"))
|
||||
(let [dir (fs-node/mkdtempSync (node-path/join "tmp" "unit-test-"))]
|
||||
(if subdir
|
||||
(do
|
||||
(fs-node/mkdirSync (node-path/join dir subdir))
|
||||
(node-path/join dir subdir))
|
||||
dir))))
|
||||
(defn start-and-destroy-db
|
||||
"Sets up a db connection and current repo like fixtures/reset-datascript. It
|
||||
also seeds the db with the same default data that the app does and destroys a db
|
||||
connection when done with it."
|
||||
[f]
|
||||
;; Set current-repo explicitly since it's not the default
|
||||
(state/set-current-repo! test-db)
|
||||
(start-test-db!)
|
||||
(f)
|
||||
(state/set-current-repo! nil)
|
||||
(destroy-test-db!))
|
||||
|
||||
17
src/test/frontend/test/node_helper.cljs
Normal file
17
src/test/frontend/test/node_helper.cljs
Normal file
@@ -0,0 +1,17 @@
|
||||
(ns frontend.test.node-helper
|
||||
"Common helper fns for node tests"
|
||||
(:require ["path" :as node-path]
|
||||
["fs" :as fs-node]))
|
||||
|
||||
(defn create-tmp-dir
|
||||
"Creates a temporary directory under tmp/. If a subdir is given, creates an
|
||||
additional subdirectory under the newly created temp directory."
|
||||
([] (create-tmp-dir nil))
|
||||
([subdir]
|
||||
(when-not (fs-node/existsSync "tmp") (fs-node/mkdirSync "tmp"))
|
||||
(let [dir (fs-node/mkdtempSync (node-path/join "tmp" "unit-test-"))]
|
||||
(if subdir
|
||||
(do
|
||||
(fs-node/mkdirSync (node-path/join dir subdir))
|
||||
(node-path/join dir subdir))
|
||||
dir))))
|
||||
40
src/test/logseq/api_test.cljs
Normal file
40
src/test/logseq/api_test.cljs
Normal file
@@ -0,0 +1,40 @@
|
||||
(ns logseq.api-test
|
||||
(:require [cljs.test :refer [use-fixtures deftest is]]
|
||||
[frontend.test.helper :as test-helper]
|
||||
[frontend.db :as db]
|
||||
[logseq.api.block :as api-block]
|
||||
[frontend.state :as state]
|
||||
[cljs-bean.core :as bean]))
|
||||
|
||||
(use-fixtures :each {:before test-helper/start-test-db!
|
||||
:after test-helper/destroy-test-db!})
|
||||
|
||||
(deftest get-block
|
||||
(with-redefs [state/get-current-repo (constantly test-helper/test-db)]
|
||||
(db/transact! test-helper/test-db
|
||||
[{:db/id 10000
|
||||
:block/uuid #uuid "4406f839-6410-43b5-87db-25e9b8f54cc0"
|
||||
:block/content "1"}
|
||||
{:db/id 10001
|
||||
:block/uuid #uuid "d9b7b45f-267f-4794-9569-f43d1ce77172"
|
||||
:block/content "2"}
|
||||
{:db/id 10002
|
||||
:block/uuid #uuid "adae3006-f03e-4814-a1f5-f17f15b86556"
|
||||
:block/parent 10001
|
||||
:block/left 10001
|
||||
:block/content "3"}
|
||||
{:db/id 10003
|
||||
:block/uuid #uuid "0c3053c3-2dab-4769-badd-14ce16d8ba8d"
|
||||
:block/parent 10002
|
||||
:block/left 10002
|
||||
:block/content "4"}])
|
||||
|
||||
(is (= (:content (bean/->clj (api-block/get_block 10000 #js {}))) "1"))
|
||||
(is (= (:content (bean/->clj (api-block/get_block "d9b7b45f-267f-4794-9569-f43d1ce77172" #js {}))) "2"))
|
||||
(is (= (:content (bean/->clj (api-block/get_block #uuid "d9b7b45f-267f-4794-9569-f43d1ce77172" #js {}))) "2"))
|
||||
(is (= {:id 10001, :content "2", :uuid "d9b7b45f-267f-4794-9569-f43d1ce77172", :children [["uuid" "adae3006-f03e-4814-a1f5-f17f15b86556"]]}
|
||||
(bean/->clj (api-block/get_block 10001 #js {:includeChildren false}))))
|
||||
(is (= {:content "2", :uuid "d9b7b45f-267f-4794-9569-f43d1ce77172", :id 10001, :children [{:content "3", :left {:id 10001}, :parent {:id 10001}, :uuid "adae3006-f03e-4814-a1f5-f17f15b86556", :id 10002, :level 1, :children [{:content "4", :left {:id 10002}, :parent {:id 10002}, :uuid "0c3053c3-2dab-4769-badd-14ce16d8ba8d", :id 10003, :level 2, :children []}]}]}
|
||||
(bean/->clj (api-block/get_block 10001 #js {:includeChildren true}))))))
|
||||
|
||||
#_(cljs.test/run-tests)
|
||||
Reference in New Issue
Block a user