mirror of
https://github.com/logseq/logseq.git
synced 2026-04-24 22:25:01 +00:00
Merge branch 'feat/db' into feat/hnswlib+transformer-js
This commit is contained in:
54
.github/workflows/build-desktop-release.yml
vendored
54
.github/workflows/build-desktop-release.yml
vendored
@@ -53,7 +53,7 @@ env:
|
||||
|
||||
jobs:
|
||||
compile-cljs:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Check build options
|
||||
if: ${{ github.event_name == 'workflow_dispatch' && (github.event.inputs.build-target == 'nightly' || github.event.inputs.build-target == 'beta') && github.event.inputs.git-ref != 'master' }}
|
||||
@@ -173,7 +173,7 @@ jobs:
|
||||
|
||||
e2e-test:
|
||||
name: E2E Test Shard ${{ matrix.shard }}
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
matrix:
|
||||
shard: [1, 2, 3]
|
||||
@@ -183,7 +183,7 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Download The Static Asset
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: static
|
||||
path: static
|
||||
@@ -223,11 +223,11 @@ jobs:
|
||||
RELEASE: true # skip dev only test
|
||||
|
||||
build-linux-x64:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
needs: [ compile-cljs ]
|
||||
steps:
|
||||
- name: Download The Static Asset
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: static
|
||||
path: static
|
||||
@@ -269,11 +269,11 @@ jobs:
|
||||
path: builds
|
||||
|
||||
build-linux-arm64:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
needs: [ compile-cljs ]
|
||||
steps:
|
||||
- name: Download The Static Asset
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: static
|
||||
path: static
|
||||
@@ -327,7 +327,7 @@ jobs:
|
||||
needs: [ compile-cljs ]
|
||||
steps:
|
||||
- name: Download The Static Asset
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: static
|
||||
path: static
|
||||
@@ -394,7 +394,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Download The Static Asset
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: static
|
||||
path: static
|
||||
@@ -470,7 +470,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Download The Static Asset
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: static
|
||||
path: static
|
||||
@@ -560,7 +560,7 @@ jobs:
|
||||
runs-on: [self-hosted, macos, token]
|
||||
steps:
|
||||
- name: Download Windows Artifact
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: logseq-win64-unsigned-builds
|
||||
path: ./builds
|
||||
@@ -581,46 +581,46 @@ jobs:
|
||||
nightly-release:
|
||||
if: ${{ github.event_name == 'schedule' || github.event.inputs.build-target == 'nightly' }}
|
||||
needs: [ build-macos-x64, build-macos-arm64, build-linux-x64, build-linux-arm64, codesign-windows, build-android, e2e-test ]
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Download MacOS x64 Artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: logseq-darwin-x64-builds
|
||||
path: ./
|
||||
|
||||
- name: Download MacOS arm64 Artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: logseq-darwin-arm64-builds
|
||||
path: ./
|
||||
|
||||
- name: Download The Linux x64 Artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: logseq-linux-x64-builds
|
||||
path: ./
|
||||
|
||||
- name: Download The Linux arm64 Artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: logseq-linux-arm64-builds
|
||||
path: ./
|
||||
|
||||
- name: Download The Windows Artifact (Signed)
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: logseq-win64-signed-builds
|
||||
path: ./
|
||||
|
||||
- name: Download The Windows Artifact
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: logseq-win64-builds
|
||||
path: ./
|
||||
|
||||
- name: Download Android Artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: logseq-android-builds
|
||||
path: ./
|
||||
@@ -661,46 +661,46 @@ jobs:
|
||||
# NOTE: For now, we only have beta channel to be released on Github
|
||||
if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.build-target == 'beta' }}
|
||||
needs: [ build-macos-x64, build-macos-arm64, build-linux-x64, build-linux-arm64, codesign-windows, build-android, e2e-test ]
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Download MacOS x64 Artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: logseq-darwin-x64-builds
|
||||
path: ./
|
||||
|
||||
- name: Download MacOS arm64 Artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: logseq-darwin-arm64-builds
|
||||
path: ./
|
||||
|
||||
- name: Download The Linux x64 Artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: logseq-linux-x64-builds
|
||||
path: ./
|
||||
|
||||
- name: Download The Linux arm64 Artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: logseq-linux-arm64-builds
|
||||
path: ./
|
||||
|
||||
- name: Download The Windows Artifact (Signed)
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: logseq-win64-signed-builds
|
||||
path: ./
|
||||
|
||||
- name: Download The Windows Artifact
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: logseq-win64-builds
|
||||
path: ./
|
||||
|
||||
- name: Download Android Artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
if: ${{ github.event_name == 'schedule' || github.event.inputs.build-android == 'true' }}
|
||||
with:
|
||||
name: logseq-android-builds
|
||||
|
||||
2
.github/workflows/build-docker.yml
vendored
2
.github/workflows/build-docker.yml
vendored
@@ -12,7 +12,7 @@ env:
|
||||
jobs:
|
||||
|
||||
build-docker:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
|
||||
8
.github/workflows/build.yml
vendored
8
.github/workflows/build.yml
vendored
@@ -20,7 +20,7 @@ env:
|
||||
jobs:
|
||||
typos:
|
||||
name: Spell Check with Typos
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout Actions Repository
|
||||
uses: actions/checkout@v4
|
||||
@@ -32,7 +32,7 @@ jobs:
|
||||
test:
|
||||
strategy:
|
||||
matrix:
|
||||
operating-system: [ubuntu-latest]
|
||||
operating-system: [ubuntu-22.04]
|
||||
|
||||
runs-on: ${{ matrix.operating-system }}
|
||||
|
||||
@@ -90,7 +90,7 @@ jobs:
|
||||
run: node static/tests.js -e fix-me
|
||||
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
@@ -180,7 +180,7 @@ jobs:
|
||||
e2e-test:
|
||||
# TODO: Re-enable when ready to enable tests for file graphs
|
||||
if: false
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
|
||||
6
.github/workflows/e2e.yml
vendored
6
.github/workflows/e2e.yml
vendored
@@ -25,7 +25,7 @@ env:
|
||||
jobs:
|
||||
e2e-test-build:
|
||||
name: Build Test Artifact
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
|
||||
@@ -98,7 +98,7 @@ jobs:
|
||||
e2e-test-run:
|
||||
needs: [ e2e-test-build ]
|
||||
name: Test Shard ${{ matrix.shard }} Repeat ${{ matrix.repeat }}
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
matrix:
|
||||
repeat: [1, 2]
|
||||
@@ -112,7 +112,7 @@ jobs:
|
||||
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
|
||||
|
||||
- name: Download test build artifact
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: logseq-e2e-artifact
|
||||
|
||||
|
||||
6
deps/db/script/create_graph.cljs
vendored
6
deps/db/script/create_graph.cljs
vendored
@@ -3,15 +3,15 @@
|
||||
(:require ["fs" :as fs]
|
||||
["os" :as os]
|
||||
["path" :as node-path]
|
||||
#_:clj-kondo/ignore
|
||||
[babashka.cli :as cli]
|
||||
[clojure.edn :as edn]
|
||||
[clojure.string :as string]
|
||||
[datascript.core :as d]
|
||||
#_:clj-kondo/ignore
|
||||
[logseq.outliner.cli :as outliner-cli]
|
||||
[validate-db]
|
||||
[nbb.classpath :as cp]
|
||||
[nbb.core :as nbb]))
|
||||
[nbb.core :as nbb]
|
||||
[validate-db]))
|
||||
|
||||
(defn- resolve-path
|
||||
"If relative path, resolve with $ORIGINAL_PWD"
|
||||
|
||||
12
deps/db/script/dump_datoms.cljs
vendored
12
deps/db/script/dump_datoms.cljs
vendored
@@ -2,13 +2,13 @@
|
||||
"A script that dumps all eavt datoms to a specified edn file
|
||||
|
||||
$ yarn -s nbb-logseq script/dump_datoms.cljs db-name datoms.edn"
|
||||
(:require [datascript.core :as d]
|
||||
[clojure.pprint :as pprint]
|
||||
[logseq.db.sqlite.cli :as sqlite-cli]
|
||||
[nbb.core :as nbb]
|
||||
["path" :as path]
|
||||
(:require ["fs" :as fs]
|
||||
["os" :as os]
|
||||
["fs" :as fs]))
|
||||
["path" :as path]
|
||||
[clojure.pprint :as pprint]
|
||||
[datascript.core :as d]
|
||||
[logseq.db.sqlite.cli :as sqlite-cli]
|
||||
[nbb.core :as nbb]))
|
||||
|
||||
(defn read-graph
|
||||
"The db graph bare version of gp-cli/parse-graph"
|
||||
|
||||
46
deps/db/script/query.cljs
vendored
46
deps/db/script/query.cljs
vendored
@@ -2,17 +2,17 @@
|
||||
"A script that queries any db graph from the commandline e.g.
|
||||
|
||||
$ yarn -s nbb-logseq script/query.cljs db-name '[:find (pull ?b [:block/name :block/title]) :where [?b :block/created-at]]'"
|
||||
(:require [datascript.core :as d]
|
||||
[clojure.edn :as edn]
|
||||
[logseq.db.sqlite.cli :as sqlite-cli]
|
||||
[logseq.db.frontend.rules :as rules]
|
||||
[nbb.core :as nbb]
|
||||
[clojure.string :as string]
|
||||
[clojure.pprint :as pprint]
|
||||
[babashka.cli :as cli]
|
||||
["child_process" :as child-process]
|
||||
(:require ["child_process" :as child-process]
|
||||
["os" :as os]
|
||||
["path" :as node-path]
|
||||
["os" :as os]))
|
||||
[babashka.cli :as cli]
|
||||
[clojure.edn :as edn]
|
||||
[clojure.pprint :as pprint]
|
||||
[clojure.string :as string]
|
||||
[datascript.core :as d]
|
||||
[logseq.db.frontend.rules :as rules]
|
||||
[logseq.db.sqlite.cli :as sqlite-cli]
|
||||
[nbb.core :as nbb]))
|
||||
|
||||
(defn- sh
|
||||
"Run shell cmd synchronously and print to inherited streams by default. Aims
|
||||
@@ -39,18 +39,15 @@
|
||||
:desc "Print more info"}
|
||||
:raw {:alias :r
|
||||
:desc "Print results plainly. Useful when piped to bb"}
|
||||
:additional-graphs {:alias :a
|
||||
:coerce []
|
||||
:desc "Additional graphs to query"}
|
||||
:entity {:alias :e
|
||||
:coerce []
|
||||
:desc "Lookup entities instead of query"}})
|
||||
|
||||
(defn -main [args]
|
||||
(let [{options :opts args' :args} (cli/parse-args args {:spec spec})
|
||||
[graph-dir & args''] args'
|
||||
_ (when (or (nil? graph-dir) (:help options))
|
||||
(println (str "Usage: $0 GRAPH-NAME [& ARGS] [OPTIONS]\nOptions:\n"
|
||||
(cli/format-opts {:spec spec})))
|
||||
(js/process.exit 1))
|
||||
[dir db-name] (get-dir-and-db-name graph-dir)
|
||||
(defn query-graph [graph-dir args'' options]
|
||||
(let [[dir db-name] (get-dir-and-db-name graph-dir)
|
||||
conn (sqlite-cli/open-db! dir db-name)
|
||||
results (if (:entity options)
|
||||
(map #(when-let [ent (d/entity @conn
|
||||
@@ -71,5 +68,18 @@
|
||||
(sh ["puget"] {:input (pr-str results) :stdio ["pipe" "inherit" "inherit"]})
|
||||
(pprint/pprint results)))))
|
||||
|
||||
(defn -main [args]
|
||||
(let [{options :opts args' :args} (cli/parse-args args {:spec spec})
|
||||
[graph-dir & args''] args'
|
||||
_ (when (or (nil? graph-dir) (:help options))
|
||||
(println (str "Usage: $0 GRAPH-NAME [& ARGS] [OPTIONS]\nOptions:\n"
|
||||
(cli/format-opts {:spec spec})))
|
||||
(js/process.exit 1))
|
||||
graph-dirs (cond-> [graph-dir]
|
||||
(:additional-graphs options)
|
||||
(into (:additional-graphs options)))]
|
||||
(doseq [graph-dir graph-dirs]
|
||||
(query-graph graph-dir args'' options))))
|
||||
|
||||
(when (= nbb/*file* (nbb/invoked-file))
|
||||
(-main *command-line-args*))
|
||||
|
||||
4
deps/db/src/logseq/db/sqlite/build.cljs
vendored
4
deps/db/src/logseq/db/sqlite/build.cljs
vendored
@@ -178,7 +178,7 @@
|
||||
(let [db-ident (get-ident all-idents prop-name)]
|
||||
(db-property-build/build-closed-values
|
||||
db-ident
|
||||
prop-name
|
||||
(:block/title prop-m)
|
||||
(assoc prop-m :db/ident db-ident :closed-values closed-values)
|
||||
{:property-attributes
|
||||
(merge {:db/id (or (property-db-ids prop-name)
|
||||
@@ -344,7 +344,7 @@
|
||||
(mapcat (fn [m]
|
||||
(if-let [pvalue-pages
|
||||
(->> (vals (:build/properties m))
|
||||
(mapcat #(if (set? %) % [%]) )
|
||||
(mapcat #(if (set? %) % [%]))
|
||||
(filter page-prop-value?)
|
||||
(map second)
|
||||
seq)]
|
||||
|
||||
4
deps/db/src/logseq/db/sqlite/export.cljs
vendored
4
deps/db/src/logseq/db/sqlite/export.cljs
vendored
@@ -10,9 +10,9 @@
|
||||
[logseq.db.frontend.content :as db-content]
|
||||
[logseq.db.frontend.entity-plus :as entity-plus]
|
||||
[logseq.db.frontend.entity-util :as entity-util]
|
||||
[logseq.db.frontend.malli-schema :as db-malli-schema]
|
||||
[logseq.db.frontend.property :as db-property]
|
||||
[logseq.db.sqlite.build :as sqlite-build]
|
||||
[logseq.db.frontend.malli-schema :as db-malli-schema]))
|
||||
[logseq.db.sqlite.build :as sqlite-build]))
|
||||
|
||||
;; Export fns
|
||||
;; ==========
|
||||
|
||||
14
deps/outliner/script/transact.cljs
vendored
14
deps/outliner/script/transact.cljs
vendored
@@ -1,14 +1,14 @@
|
||||
(ns transact
|
||||
"This script generically runs transactions against the queried blocks"
|
||||
(:require [logseq.outliner.db-pipeline :as db-pipeline]
|
||||
[logseq.db.sqlite.cli :as sqlite-cli]
|
||||
[logseq.db.frontend.rules :as rules]
|
||||
[datascript.core :as d]
|
||||
(:require ["os" :as os]
|
||||
["path" :as node-path]
|
||||
[clojure.edn :as edn]
|
||||
[clojure.string :as string]
|
||||
[nbb.core :as nbb]
|
||||
["path" :as node-path]
|
||||
["os" :as os]))
|
||||
[datascript.core :as d]
|
||||
[logseq.db.frontend.rules :as rules]
|
||||
[logseq.db.sqlite.cli :as sqlite-cli]
|
||||
[logseq.outliner.db-pipeline :as db-pipeline]
|
||||
[nbb.core :as nbb]))
|
||||
|
||||
(defn -main [args]
|
||||
(when (< (count args) 3)
|
||||
|
||||
201
deps/outliner/src/logseq/outliner/property.cljs
vendored
201
deps/outliner/src/logseq/outliner/property.cljs
vendored
@@ -148,34 +148,6 @@
|
||||
:property-id (:db/id property)}))
|
||||
property))
|
||||
|
||||
(defn upsert-property!
|
||||
"Updates property if property-id is given. Otherwise creates a property
|
||||
with the given property-id or :property-name option. When a property is created
|
||||
it is ensured to have a unique :db/ident"
|
||||
[conn property-id schema {:keys [property-name] :as opts}]
|
||||
(let [db @conn
|
||||
db-ident (or property-id
|
||||
(try (db-property/create-user-property-ident-from-name property-name)
|
||||
(catch :default e
|
||||
(throw (ex-info (str e)
|
||||
{:type :notification
|
||||
:payload {:message "Property failed to create. Please try a different property name."
|
||||
:type :error}})))))]
|
||||
(assert (qualified-keyword? db-ident))
|
||||
(if-let [property (and (qualified-keyword? property-id) (d/entity db db-ident))]
|
||||
(update-property conn db-ident property schema opts)
|
||||
(let [k-name (or (and property-name (name property-name))
|
||||
(name property-id))
|
||||
db-ident' (db-ident/ensure-unique-db-ident @conn db-ident)]
|
||||
(assert (some? k-name)
|
||||
(prn "property-id: " property-id ", property-name: " property-name))
|
||||
(outliner-validate/validate-page-title k-name {:node {:db/ident db-ident'}})
|
||||
(outliner-validate/validate-page-title-characters k-name {:node {:db/ident db-ident'}})
|
||||
(ldb/transact! conn
|
||||
[(sqlite-util/build-new-property db-ident' schema {:title k-name})]
|
||||
{:outliner-op :new-property})
|
||||
(d/entity @conn db-ident')))))
|
||||
|
||||
(defn- validate-property-value-aux
|
||||
[schema value {:keys [many?]}]
|
||||
;; normalize :many values since most components update them as a single value
|
||||
@@ -250,12 +222,12 @@
|
||||
[conn property-id v]
|
||||
(let [property (d/entity @conn property-id)
|
||||
closed-values? (seq (:property/closed-values property))
|
||||
default-type? (= :default (:logseq.property/type property))]
|
||||
default-or-url? (contains? #{:default :url} (:logseq.property/type property))]
|
||||
(cond
|
||||
closed-values?
|
||||
(get-property-value-eid @conn property-id v)
|
||||
|
||||
(and default-type?
|
||||
(and default-or-url?
|
||||
;; FIXME: remove this when :logseq.property/order-list-type updated to closed values
|
||||
(not= property-id :logseq.property/order-list-type))
|
||||
(let [v-uuid (create-property-text-block! conn nil property-id v {})]
|
||||
@@ -288,72 +260,6 @@
|
||||
:payload {:message "Can't set this block itself as own property value"
|
||||
:type :error}}))))
|
||||
|
||||
(defn set-block-property!
|
||||
"Updates a block property's value for an existing property-id and block. If
|
||||
property is a ref type, automatically handles a raw property value i.e. you
|
||||
can pass \"value\" instead of the property value entity. Also handle db
|
||||
attributes as properties"
|
||||
[conn block-eid property-id v]
|
||||
(throw-error-if-read-only-property property-id)
|
||||
(let [block-eid (->eid block-eid)
|
||||
_ (assert (qualified-keyword? property-id) "property-id should be a keyword")
|
||||
block (d/entity @conn block-eid)
|
||||
db-attribute? (some? (db-schema/schema property-id))]
|
||||
(when (= property-id :block/tags)
|
||||
(outliner-validate/validate-tags-property @conn [block-eid] v))
|
||||
(when (= property-id :logseq.property/parent)
|
||||
(outliner-validate/validate-parent-property v [block]))
|
||||
(cond
|
||||
db-attribute?
|
||||
(when-not (and (= property-id :block/alias) (= v (:db/id block))) ; alias can't be itself
|
||||
(ldb/transact! conn [{:db/id (:db/id block) property-id v}]
|
||||
{:outliner-op :save-block}))
|
||||
:else
|
||||
(let [property (d/entity @conn property-id)
|
||||
_ (assert (some? property) (str "Property " property-id " doesn't exist yet"))
|
||||
property-type (get property :logseq.property/type :default)
|
||||
ref? (db-property-type/all-ref-property-types property-type)
|
||||
new-value (if ref?
|
||||
(convert-ref-property-value conn property-id v property-type)
|
||||
v)
|
||||
existing-value (get block property-id)]
|
||||
(throw-error-if-self-value block new-value ref?)
|
||||
(when-not (= existing-value new-value)
|
||||
(raw-set-block-property! conn block property property-type new-value))))))
|
||||
|
||||
(defn batch-set-property!
|
||||
"Sets properties for multiple blocks. Automatically handles property value refs.
|
||||
Does no validation of property values."
|
||||
[conn block-ids property-id v]
|
||||
(assert property-id "property-id is nil")
|
||||
(throw-error-if-read-only-property property-id)
|
||||
(let [block-eids (map ->eid block-ids)
|
||||
_ (when (= property-id :block/tags)
|
||||
(outliner-validate/validate-tags-property @conn block-eids v))
|
||||
property (d/entity @conn property-id)
|
||||
_ (when (= (:db/ident property) :logseq.property/parent)
|
||||
(outliner-validate/validate-parent-property
|
||||
(if (number? v) (d/entity @conn v) v)
|
||||
(map #(d/entity @conn %) block-eids)))
|
||||
_ (assert (some? property) (str "Property " property-id " doesn't exist yet"))
|
||||
property-type (get property :logseq.property/type :default)
|
||||
_ (assert (some? v) "Can't set a nil property value must be not nil")
|
||||
ref? (db-property-type/value-ref-property-types property-type)
|
||||
v' (if ref?
|
||||
(convert-ref-property-value conn property-id v property-type)
|
||||
v)]
|
||||
(doseq [eid block-eids]
|
||||
(let [block (d/entity @conn eid)]
|
||||
(throw-error-if-self-value block v' ref?)))
|
||||
(let [txs (mapcat
|
||||
(fn [eid]
|
||||
(if-let [block (d/entity @conn eid)]
|
||||
(build-property-value-tx-data conn block property-id v')
|
||||
(js/console.error "Skipping setting a block's property because the block id could not be found:" eid)))
|
||||
block-eids)]
|
||||
(when (seq txs)
|
||||
(ldb/transact! conn txs {:outliner-op :save-block})))))
|
||||
|
||||
(defn batch-remove-property!
|
||||
[conn block-ids property-id]
|
||||
(throw-error-if-read-only-property property-id)
|
||||
@@ -386,6 +292,46 @@
|
||||
(when (seq txs)
|
||||
(ldb/transact! conn txs {:outliner-op :save-block})))))))
|
||||
|
||||
(defn batch-set-property!
|
||||
"Sets properties for multiple blocks. Automatically handles property value refs.
|
||||
Does no validation of property values."
|
||||
[conn block-ids property-id v]
|
||||
(assert property-id "property-id is nil")
|
||||
(throw-error-if-read-only-property property-id)
|
||||
(if (nil? v)
|
||||
(batch-remove-property! conn block-ids property-id)
|
||||
(let [block-eids (map ->eid block-ids)
|
||||
_ (when (= property-id :block/tags)
|
||||
(outliner-validate/validate-tags-property @conn block-eids v))
|
||||
property (d/entity @conn property-id)
|
||||
_ (when (= (:db/ident property) :logseq.property/parent)
|
||||
(outliner-validate/validate-parent-property
|
||||
(if (number? v) (d/entity @conn v) v)
|
||||
(map #(d/entity @conn %) block-eids)))
|
||||
_ (assert (some? property) (str "Property " property-id " doesn't exist yet"))
|
||||
property-type (get property :logseq.property/type :default)
|
||||
_ (assert (some? v) "Can't set a nil property value must be not nil")
|
||||
ref? (db-property-type/value-ref-property-types property-type)
|
||||
default-url-not-closed? (and (contains? #{:default :url} property-type)
|
||||
(not (seq (:property/closed-values property))))
|
||||
v' (if ref?
|
||||
(convert-ref-property-value conn property-id v property-type)
|
||||
v)
|
||||
txs (doall
|
||||
(mapcat
|
||||
(fn [eid]
|
||||
(if-let [block (d/entity @conn eid)]
|
||||
(let [v' (if default-url-not-closed?
|
||||
(let [v (if (number? v) (:block/title (d/entity @conn v)) v)]
|
||||
(convert-ref-property-value conn property-id v property-type))
|
||||
v')]
|
||||
(throw-error-if-self-value block v' ref?)
|
||||
(build-property-value-tx-data conn block property-id v'))
|
||||
(js/console.error "Skipping setting a block's property because the block id could not be found:" eid)))
|
||||
block-eids))]
|
||||
(when (seq txs)
|
||||
(ldb/transact! conn txs {:outliner-op :save-block})))))
|
||||
|
||||
(defn remove-block-property!
|
||||
[conn eid property-id]
|
||||
(throw-error-if-read-only-property property-id)
|
||||
@@ -415,6 +361,69 @@
|
||||
:else
|
||||
(batch-remove-property! conn [eid] property-id))))
|
||||
|
||||
(defn set-block-property!
|
||||
"Updates a block property's value for an existing property-id and block. If
|
||||
property is a ref type, automatically handles a raw property value i.e. you
|
||||
can pass \"value\" instead of the property value entity. Also handle db
|
||||
attributes as properties"
|
||||
[conn block-eid property-id v]
|
||||
(throw-error-if-read-only-property property-id)
|
||||
(if (nil? v)
|
||||
(remove-block-property! conn block-eid property-id)
|
||||
(let [block-eid (->eid block-eid)
|
||||
_ (assert (qualified-keyword? property-id) "property-id should be a keyword")
|
||||
block (d/entity @conn block-eid)
|
||||
db-attribute? (some? (db-schema/schema property-id))]
|
||||
(when (= property-id :block/tags)
|
||||
(outliner-validate/validate-tags-property @conn [block-eid] v))
|
||||
(when (= property-id :logseq.property/parent)
|
||||
(outliner-validate/validate-parent-property v [block]))
|
||||
(cond
|
||||
db-attribute?
|
||||
(when-not (and (= property-id :block/alias) (= v (:db/id block))) ; alias can't be itself
|
||||
(ldb/transact! conn [{:db/id (:db/id block) property-id v}]
|
||||
{:outliner-op :save-block}))
|
||||
:else
|
||||
(let [property (d/entity @conn property-id)
|
||||
_ (assert (some? property) (str "Property " property-id " doesn't exist yet"))
|
||||
property-type (get property :logseq.property/type :default)
|
||||
ref? (db-property-type/all-ref-property-types property-type)
|
||||
new-value (if ref?
|
||||
(convert-ref-property-value conn property-id v property-type)
|
||||
v)
|
||||
existing-value (get block property-id)]
|
||||
(throw-error-if-self-value block new-value ref?)
|
||||
(when-not (= existing-value new-value)
|
||||
(raw-set-block-property! conn block property property-type new-value)))))))
|
||||
|
||||
(defn upsert-property!
|
||||
"Updates property if property-id is given. Otherwise creates a property
|
||||
with the given property-id or :property-name option. When a property is created
|
||||
it is ensured to have a unique :db/ident"
|
||||
[conn property-id schema {:keys [property-name] :as opts}]
|
||||
(let [db @conn
|
||||
db-ident (or property-id
|
||||
(try (db-property/create-user-property-ident-from-name property-name)
|
||||
(catch :default e
|
||||
(throw (ex-info (str e)
|
||||
{:type :notification
|
||||
:payload {:message "Property failed to create. Please try a different property name."
|
||||
:type :error}})))))]
|
||||
(assert (qualified-keyword? db-ident))
|
||||
(if-let [property (and (qualified-keyword? property-id) (d/entity db db-ident))]
|
||||
(update-property conn db-ident property schema opts)
|
||||
(let [k-name (or (and property-name (name property-name))
|
||||
(name property-id))
|
||||
db-ident' (db-ident/ensure-unique-db-ident @conn db-ident)]
|
||||
(assert (some? k-name)
|
||||
(prn "property-id: " property-id ", property-name: " property-name))
|
||||
(outliner-validate/validate-page-title k-name {:node {:db/ident db-ident'}})
|
||||
(outliner-validate/validate-page-title-characters k-name {:node {:db/ident db-ident'}})
|
||||
(ldb/transact! conn
|
||||
[(sqlite-util/build-new-property db-ident' schema {:title k-name})]
|
||||
{:outliner-op :new-property})
|
||||
(d/entity @conn db-ident')))))
|
||||
|
||||
(defn delete-property-value!
|
||||
"Delete value if a property has multiple values"
|
||||
[conn block-eid property-id property-value]
|
||||
|
||||
8
deps/publishing/script/publishing.cljs
vendored
8
deps/publishing/script/publishing.cljs
vendored
@@ -1,13 +1,13 @@
|
||||
(ns publishing
|
||||
"Basic script for publishing from CLI"
|
||||
(:require [logseq.graph-parser.cli :as gp-cli]
|
||||
[logseq.publishing :as publishing]
|
||||
[logseq.db.sqlite.cli :as sqlite-cli]
|
||||
["fs" :as fs]
|
||||
(:require ["fs" :as fs]
|
||||
["path" :as node-path]
|
||||
[clojure.edn :as edn]
|
||||
[datascript.core :as d]
|
||||
[logseq.db.sqlite.cli :as sqlite-cli]
|
||||
[logseq.db.sqlite.util :as sqlite-util]
|
||||
[logseq.graph-parser.cli :as gp-cli]
|
||||
[logseq.publishing :as publishing]
|
||||
[nbb.core :as nbb]))
|
||||
|
||||
(defn- get-db [graph-dir]
|
||||
|
||||
67
deps/shui/src/logseq/shui/popup/core.cljs
vendored
67
deps/shui/src/logseq/shui/popup/core.cljs
vendored
@@ -1,9 +1,9 @@
|
||||
(ns logseq.shui.popup.core
|
||||
(:require [rum.core :as rum]
|
||||
(:require [dommy.core :as d]
|
||||
[logseq.shui.util :as util]
|
||||
[medley.core :as medley]
|
||||
[logseq.shui.util :refer [use-atom]]
|
||||
[dommy.core :as d]))
|
||||
[medley.core :as medley]
|
||||
[rum.core :as rum]))
|
||||
|
||||
;; ui
|
||||
(def button (util/lsui-wrap "Button"))
|
||||
@@ -39,7 +39,7 @@
|
||||
[id]
|
||||
(when id
|
||||
(some->> (medley/indexed @*popups)
|
||||
(filter #(= id (:id (second %)))) (first))))
|
||||
(filter #(= id (:id (second %)))) (first))))
|
||||
|
||||
(defn get-popups [] @*popups)
|
||||
(defn get-last-popup [] (last @*popups))
|
||||
@@ -97,13 +97,14 @@
|
||||
:start 0
|
||||
:end width
|
||||
(/ width 2)))
|
||||
(- bottom height) width height])
|
||||
(- bottom height)
|
||||
width height])
|
||||
:else [0 0])]
|
||||
(reset! *last-show-target @*target)
|
||||
(js/setTimeout #(reset! *last-show-target nil) 64)
|
||||
(some-> @*target
|
||||
(d/set-attr! "data-popup-active"
|
||||
(if (keyword? id) (name id) (str id))))
|
||||
(d/set-attr! "data-popup-active"
|
||||
(if (keyword? id) (name id) (str id))))
|
||||
(upsert-popup!
|
||||
(merge opts
|
||||
{:id id :target (deref *target)
|
||||
@@ -166,32 +167,32 @@
|
||||
(when-not (false? (some-> content-props (:onPointerDownOutside) (apply [e])))
|
||||
(hide! id 1)))]
|
||||
(popup-root
|
||||
(merge root-props {:open open?})
|
||||
(popup-trigger
|
||||
{:as-child true}
|
||||
(button {:class "overflow-hidden fixed p-0 opacity-0"
|
||||
:style {:height (if (and (number? height)
|
||||
(> height 0))
|
||||
height 1)
|
||||
:width 1
|
||||
:top y
|
||||
:left x}} ""))
|
||||
(let [content-props (cond-> (merge content-props {:onEscapeKeyDown handle-key-escape!
|
||||
:disableOutsideScroll false
|
||||
:onPointerDownOutside handle-pointer-outside!})
|
||||
(and (not force-popover?)
|
||||
(not as-dropdown?))
|
||||
(assoc :on-key-down (fn [^js e]
|
||||
(some-> content-props :on-key-down (apply [e]))
|
||||
(set! (. e -defaultPrevented) true))
|
||||
:on-pointer-move #(set! (. % -defaultPrevented) true)))
|
||||
content (if (fn? content)
|
||||
(content (cond-> {:id id}
|
||||
as-content?
|
||||
(assoc :content-props content-props))) content)]
|
||||
(if as-content?
|
||||
content
|
||||
(popup-content content-props content)))))))
|
||||
(merge root-props {:open open?})
|
||||
(popup-trigger
|
||||
{:as-child true}
|
||||
(button {:class "overflow-hidden fixed p-0 opacity-0"
|
||||
:style {:height (if (and (number? height)
|
||||
(> height 0))
|
||||
height 1)
|
||||
:width 1
|
||||
:top y
|
||||
:left x}} ""))
|
||||
(let [content-props (cond-> (merge content-props {:onEscapeKeyDown handle-key-escape!
|
||||
:disableOutsideScroll false
|
||||
:onPointerDownOutside handle-pointer-outside!})
|
||||
(and (not force-popover?)
|
||||
(not as-dropdown?))
|
||||
(assoc :on-key-down (fn [^js e]
|
||||
(some-> content-props :on-key-down (apply [e]))
|
||||
(set! (. e -defaultPrevented) true))
|
||||
:on-pointer-move #(set! (. % -defaultPrevented) true)))
|
||||
content (if (fn? content)
|
||||
(content (cond-> {:id id}
|
||||
as-content?
|
||||
(assoc :content-props content-props))) content)]
|
||||
(if as-content?
|
||||
content
|
||||
(popup-content content-props content)))))))
|
||||
|
||||
(rum/defc install-popups
|
||||
< rum/static
|
||||
|
||||
17
deps/shui/src/logseq/shui/ui.cljs
vendored
17
deps/shui/src/logseq/shui/ui.cljs
vendored
@@ -1,21 +1,22 @@
|
||||
(ns logseq.shui.ui
|
||||
(:require [logseq.shui.util :as util]
|
||||
(:require [logseq.shui.base.core :as base-core]
|
||||
[logseq.shui.dialog.core :as dialog-core]
|
||||
[logseq.shui.form.core :as form-core]
|
||||
[logseq.shui.icon.v2 :as icon-v2]
|
||||
[logseq.shui.shortcut.v1 :as shui.shortcut.v1]
|
||||
[logseq.shui.toaster.core :as toaster-core]
|
||||
[logseq.shui.popup.core :as popup-core]
|
||||
[logseq.shui.select.core :as select-core]
|
||||
[logseq.shui.select.multi :as select-multi]
|
||||
[logseq.shui.dialog.core :as dialog-core]
|
||||
[logseq.shui.popup.core :as popup-core]
|
||||
[logseq.shui.base.core :as base-core]
|
||||
[logseq.shui.form.core :as form-core]
|
||||
[logseq.shui.table.core :as table-core]))
|
||||
[logseq.shui.shortcut.v1 :as shui.shortcut.v1]
|
||||
[logseq.shui.table.core :as table-core]
|
||||
[logseq.shui.toaster.core :as toaster-core]
|
||||
[logseq.shui.util :as util]))
|
||||
|
||||
(def button base-core/button)
|
||||
(def button-icon base-core/button-icon)
|
||||
(def button-ghost-icon base-core/button-ghost-icon)
|
||||
(def button-outline-icon base-core/button-outline-icon)
|
||||
(def button-secondary-icon base-core/button-secondary-icon)
|
||||
(def button-group (util/lsui-wrap "ButtonGroup"))
|
||||
(def link base-core/link)
|
||||
(def trigger-as base-core/trigger-as)
|
||||
(def trigger-child-wrap base-core/trigger-child-wrap)
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"devDependencies": {
|
||||
"@axe-core/playwright": "=4.4.4",
|
||||
"@capacitor/cli": "^5.0.0",
|
||||
"@playwright/test": "=1.44.0",
|
||||
"@playwright/test": "=1.51.0",
|
||||
"@tailwindcss/aspect-ratio": "0.4.2",
|
||||
"@tailwindcss/forms": "0.5.3",
|
||||
"@tailwindcss/typography": "0.5.7",
|
||||
@@ -24,7 +24,7 @@
|
||||
"karma-chrome-launcher": "^3.2.0",
|
||||
"karma-cljs-test": "^0.1.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"playwright": "=1.44.0",
|
||||
"playwright": "=1.51.0",
|
||||
"postcss": "^8.4.47",
|
||||
"postcss-cli": "10.0.0",
|
||||
"postcss-functions": "^4.0.2",
|
||||
@@ -136,8 +136,8 @@
|
||||
"d3-force": "3.0.0",
|
||||
"diff": "5.0.0",
|
||||
"dompurify": "2.4.0",
|
||||
"electron": "31.7.5",
|
||||
"electron-dl": "3.3.0",
|
||||
"electron": "35.0.1",
|
||||
"electron-dl": "^4.0.0",
|
||||
"emoji-mart": "^5.5.2",
|
||||
"fs": "0.0.1-security",
|
||||
"fs-extra": "9.1.0",
|
||||
|
||||
53
packages/ui/@/components/ui/button-group.tsx
Normal file
53
packages/ui/@/components/ui/button-group.tsx
Normal file
@@ -0,0 +1,53 @@
|
||||
import { Children, ReactElement, cloneElement } from 'react';
|
||||
|
||||
import { ButtonProps } from '@/components/ui/button';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
interface ButtonGroupProps {
|
||||
className?: string;
|
||||
orientation?: 'horizontal' | 'vertical';
|
||||
children: ReactElement<ButtonProps>[];
|
||||
}
|
||||
|
||||
export const ButtonGroup = ({
|
||||
className,
|
||||
orientation = 'horizontal',
|
||||
children,
|
||||
}: ButtonGroupProps) => {
|
||||
const totalButtons = Children.count(children);
|
||||
const isHorizontal = orientation === 'horizontal';
|
||||
const isVertical = orientation === 'vertical';
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'flex',
|
||||
{
|
||||
'flex-col': isVertical,
|
||||
'w-fit': isVertical,
|
||||
},
|
||||
className
|
||||
)}
|
||||
>
|
||||
{Children.map(children, (child, index) => {
|
||||
const isFirst = index === 0;
|
||||
const isLast = index === totalButtons - 1;
|
||||
|
||||
return cloneElement(child, {
|
||||
className: cn(
|
||||
{
|
||||
'rounded-l-none': isHorizontal && !isFirst,
|
||||
'rounded-r-none': isHorizontal && !isLast,
|
||||
'border-l-0': isHorizontal && !isFirst,
|
||||
|
||||
'rounded-t-none': isVertical && !isFirst,
|
||||
'rounded-b-none': isVertical && !isLast,
|
||||
'border-t-0': isVertical && !isFirst,
|
||||
},
|
||||
child.props.className
|
||||
),
|
||||
});
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { ButtonGroup } from '@/components/ui/button-group'
|
||||
import { Slider, SliderTrack, SliderRange, SliderThumb } from '@/components/ui/slider'
|
||||
import {
|
||||
DropdownMenu,
|
||||
@@ -99,7 +100,7 @@ declare global {
|
||||
}
|
||||
|
||||
const shadui = {
|
||||
Link, Button,
|
||||
Link, Button, ButtonGroup,
|
||||
Slider, SliderTrack, SliderRange, SliderThumb,
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
"electron:make-linux-arm64": "electron-forge make --platform=linux --arch=arm64",
|
||||
"electron:make-macos-arm64": "electron-forge make --platform=darwin --arch=arm64",
|
||||
"electron:publish:github": "electron-forge publish",
|
||||
"rebuild:all": "electron-rebuild -v 28.3.1 -f",
|
||||
"rebuild:all": "electron-rebuild -v 35.0.1 -f",
|
||||
"postinstall": "install-app-deps"
|
||||
},
|
||||
"config": {
|
||||
@@ -54,16 +54,16 @@
|
||||
"@electron-forge/maker-squirrel": "^7.3.1",
|
||||
"@electron-forge/maker-zip": "^7.3.1",
|
||||
"@electron/rebuild": "3.2.10",
|
||||
"electron": "31.7.5",
|
||||
"electron": "35.0.1",
|
||||
"electron-builder": "25.1.8",
|
||||
"electron-forge-maker-appimage": "https://github.com/logseq/electron-forge-maker-appimage.git",
|
||||
"electron-devtools-installer": "^3.2.0"
|
||||
},
|
||||
"resolutions": {
|
||||
"**/electron": "31.7.5",
|
||||
"**/node-abi": "3.68.0",
|
||||
"**/node-gyp": "10.0.1",
|
||||
"string-width": "^4.2.0",
|
||||
"**/electron": "35.0.1",
|
||||
"**/node-abi": "3.74.0",
|
||||
"**/node-gyp": "11.1.0",
|
||||
"string-width": "4.2.0",
|
||||
"wrap-ansi": "^7.0.0",
|
||||
"strip-ansi": "^6.0.1"
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
(ns logseq.tasks.db-graph.create-graph-with-large-sizes
|
||||
"Script that generates graphs at large sizes"
|
||||
(:require [logseq.outliner.cli :as outliner-cli]
|
||||
(:require ["os" :as os]
|
||||
["path" :as node-path]
|
||||
[babashka.cli :as cli]
|
||||
[clojure.string :as string]
|
||||
[datascript.core :as d]
|
||||
[babashka.cli :as cli]
|
||||
["path" :as node-path]
|
||||
["os" :as os]
|
||||
[logseq.outliner.cli :as outliner-cli]
|
||||
[nbb.classpath :as cp]
|
||||
[nbb.core :as nbb]))
|
||||
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
"This ns is a wrapper around the chokidar file watcher,
|
||||
https://www.npmjs.com/package/chokidar. File watcher events are sent to the
|
||||
`file-watcher` ipc channel"
|
||||
(:require [cljs-bean.core :as bean]
|
||||
["fs" :as fs]
|
||||
["chokidar" :as watcher]
|
||||
[electron.utils :as utils]
|
||||
[electron.logger :as logger]
|
||||
(:require ["chokidar" :as watcher]
|
||||
["electron" :refer [app]]
|
||||
["fs" :as fs]
|
||||
[cljs-bean.core :as bean]
|
||||
[electron.logger :as logger]
|
||||
[electron.utils :as utils]
|
||||
[electron.window :as window]
|
||||
[logseq.common.graph :as common-graph]))
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
(utils/read-file path))
|
||||
stat (when (and (not= event "unlink")
|
||||
(not dir-path?))
|
||||
(fs/statSync path))]
|
||||
(utils/fs-stat->clj path))]
|
||||
(send-file-watcher! dir event (merge {:dir (utils/fix-win-path! dir)
|
||||
:path (utils/fix-win-path! path)
|
||||
:content content
|
||||
|
||||
@@ -123,7 +123,7 @@
|
||||
(when (and (chmod-enabled?) (fs/existsSync path) (not (writable? path)))
|
||||
(fs/chmodSync path "644"))
|
||||
(fs/writeFileSync path content)
|
||||
(fs/statSync path)
|
||||
(utils/fs-stat->clj path)
|
||||
(catch :default e
|
||||
(logger/warn ::write-file path e)
|
||||
(let [backup-path (try
|
||||
@@ -144,7 +144,7 @@
|
||||
(fs/renameSync old-path new-path))
|
||||
|
||||
(defmethod handle :stat [_window [_ path]]
|
||||
(fs/statSync path))
|
||||
(utils/fs-stat->clj path))
|
||||
|
||||
(defn- get-files
|
||||
"Returns vec of file-objs"
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
["electron" :refer [app BrowserWindow]]
|
||||
["fs-extra" :as fs]
|
||||
["path" :as node-path]
|
||||
[cljs-bean.core :as bean]
|
||||
[clojure.string :as string]
|
||||
[electron.configs :as cfgs]
|
||||
[electron.db :as db]
|
||||
[electron.logger :as logger]
|
||||
[logseq.db.sqlite.util :as sqlite-util]
|
||||
[cljs-bean.core :as bean]
|
||||
[electron.db :as db]
|
||||
[promesa.core :as p]))
|
||||
|
||||
(defonce *win (atom nil)) ;; The main window
|
||||
@@ -151,7 +151,6 @@
|
||||
(logger/warn "Unknown PAC rule:" line)
|
||||
nil))))
|
||||
|
||||
|
||||
(defn <get-system-proxy
|
||||
"Get system proxy for url, requires proxy to be set to system"
|
||||
([] (<get-system-proxy "https://www.google.com"))
|
||||
@@ -295,3 +294,10 @@
|
||||
(catch :default _
|
||||
(println "decodeURIComponent failed: " uri)
|
||||
uri)))
|
||||
|
||||
(defn fs-stat->clj
|
||||
[path]
|
||||
(let [stat (fs/statSync path)]
|
||||
{:size (.-size stat)
|
||||
:mtime (.-mtime stat)
|
||||
:ctime (.-ctime stat)}))
|
||||
|
||||
@@ -1084,9 +1084,13 @@
|
||||
(not contents-page?))
|
||||
[:span.text-gray-500.bracket page-ref/left-brackets])
|
||||
(when (and (config/db-based-graph?) (ldb/class-instance? (db/entity :logseq.class/Task) block))
|
||||
[:div.inline-block {:style {:margin-right 1}
|
||||
:on-pointer-down (fn [e]
|
||||
(util/stop e))} (block-positioned-properties config block :block-left)])
|
||||
[:div.inline-block
|
||||
{:style {:margin-right 1
|
||||
:margin-top -2
|
||||
:vertical-align "middle"}
|
||||
:on-pointer-down (fn [e]
|
||||
(util/stop e))}
|
||||
(block-positioned-properties config block :block-left)])
|
||||
(page-cp config' (if (uuid? uuid-or-title)
|
||||
{:block/uuid uuid-or-title}
|
||||
{:block/name uuid-or-title}))
|
||||
@@ -2162,7 +2166,8 @@
|
||||
(not edit?)
|
||||
(not (:block.temp/top? block))
|
||||
(not (:block.temp/bottom? block))
|
||||
(not (util/react *control-show?)))
|
||||
(not (util/react *control-show?))
|
||||
(not (:logseq.property/created-from-property block)))
|
||||
(and doc-mode?
|
||||
(not collapsed?)
|
||||
(not (util/react *control-show?))))
|
||||
|
||||
@@ -383,7 +383,7 @@
|
||||
}
|
||||
|
||||
.page-reference {
|
||||
@apply inline-flex flex-row items-center rounded transition-[background];
|
||||
@apply rounded transition-[background];
|
||||
|
||||
.bracket {
|
||||
@apply opacity-30;
|
||||
|
||||
@@ -878,16 +878,18 @@
|
||||
{:keys [page page-entity]} (state/sub :page-title/context)]
|
||||
|
||||
(let [show!
|
||||
(fn [content]
|
||||
(fn [content & {:as option}]
|
||||
(shui/popup-show! e
|
||||
(fn [{:keys [id]}]
|
||||
[:div {:on-click #(shui/popup-hide! id)
|
||||
:data-keep-selection true}
|
||||
content])
|
||||
{:on-before-hide state/dom-clear-selection!
|
||||
:on-after-hide state/state-clear-selection!
|
||||
:content-props {:class "w-[280px] ls-context-menu-content"}
|
||||
:as-dropdown? true}))
|
||||
(merge
|
||||
{:on-before-hide state/dom-clear-selection!
|
||||
:on-after-hide state/state-clear-selection!
|
||||
:content-props {:class "w-[280px] ls-context-menu-content"}
|
||||
:as-dropdown? true}
|
||||
option)))
|
||||
|
||||
handled
|
||||
(cond
|
||||
@@ -901,11 +903,12 @@
|
||||
(show! (cp-content/block-ref-custom-context-menu-content block block-ref))
|
||||
(state/set-state! :block-ref/context nil))
|
||||
|
||||
;; block selection
|
||||
;; block selection
|
||||
(and (state/selection?) (not (d/has-class? target "bullet")))
|
||||
(show! (cp-content/custom-context-menu-content))
|
||||
(show! (cp-content/custom-context-menu-content)
|
||||
{:id :blocks-selection-context-menu})
|
||||
|
||||
;; block bullet
|
||||
;; block bullet
|
||||
(and block-id (parse-uuid block-id))
|
||||
(let [block (.closest target ".ls-block")
|
||||
property-default-value? (when block
|
||||
@@ -922,6 +925,10 @@
|
||||
[]
|
||||
nil)
|
||||
|
||||
(defn- on-mouse-up
|
||||
[_e]
|
||||
(editor-handler/show-action-bar!))
|
||||
|
||||
(rum/defcs ^:large-vars/cleanup-todo root-container < rum/reactive
|
||||
(mixins/event-mixin
|
||||
(fn [state]
|
||||
@@ -1014,6 +1021,7 @@
|
||||
(ui/focus-element (ui/main-node))))}
|
||||
(t :accessibility/skip-to-main-content)]
|
||||
[:div.#app-container
|
||||
{:on-mouse-up on-mouse-up}
|
||||
[:div#left-container
|
||||
{:class (if (state/sub :ui/sidebar-open?) "overflow-hidden" "w-full")}
|
||||
(header/header {:light? light?
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
|
||||
(shui/dropdown-menu-item
|
||||
{:key "copy"
|
||||
:on-click editor-handler/copy-selection-blocks}
|
||||
:on-click #(editor-handler/copy-selection-blocks true)}
|
||||
(t :editor/copy)
|
||||
(shui/dropdown-menu-shortcut (ui/keyboard-shortcut-from-config :editor/copy)))
|
||||
|
||||
|
||||
@@ -661,63 +661,63 @@
|
||||
(rum/defc shui-editor-popups
|
||||
[id format action _data]
|
||||
(hooks/use-effect!
|
||||
(fn []
|
||||
(let [pid (case action
|
||||
:commands
|
||||
(open-editor-popup! :commands
|
||||
(commands id format)
|
||||
{:content-props {:withoutAnimation false}})
|
||||
(fn []
|
||||
(let [pid (case action
|
||||
:commands
|
||||
(open-editor-popup! :commands
|
||||
(commands id format)
|
||||
{:content-props {:withoutAnimation false}})
|
||||
|
||||
(:block-search :page-search :page-search-hashtag)
|
||||
(open-editor-popup! action
|
||||
(if (= :block-search action)
|
||||
(block-search id format)
|
||||
(page-search id format))
|
||||
{:root-props {:onOpenChange
|
||||
#(when-not %
|
||||
(when (contains?
|
||||
#{:block-search :page-search :page-search-hashtag}
|
||||
(state/get-editor-action))
|
||||
(state/clear-editor-action!)))}})
|
||||
(:block-search :page-search :page-search-hashtag)
|
||||
(open-editor-popup! action
|
||||
(if (= :block-search action)
|
||||
(block-search id format)
|
||||
(page-search id format))
|
||||
{:root-props {:onOpenChange
|
||||
#(when-not %
|
||||
(when (contains?
|
||||
#{:block-search :page-search :page-search-hashtag}
|
||||
(state/get-editor-action))
|
||||
(state/clear-editor-action!)))}})
|
||||
|
||||
:datepicker
|
||||
(open-editor-popup! :datepicker
|
||||
(datetime-comp/date-picker id format nil) {})
|
||||
:datepicker
|
||||
(open-editor-popup! :datepicker
|
||||
(datetime-comp/date-picker id format nil) {})
|
||||
|
||||
:input
|
||||
(open-editor-popup! :input
|
||||
(editor-input id
|
||||
:input
|
||||
(open-editor-popup! :input
|
||||
(editor-input id
|
||||
;; on-submit
|
||||
(fn [command m]
|
||||
(editor-handler/handle-command-input command id format m))
|
||||
(fn [command m]
|
||||
(editor-handler/handle-command-input command id format m))
|
||||
;; on-cancel
|
||||
(fn []
|
||||
(editor-handler/handle-command-input-close id)))
|
||||
{:content-props {:onOpenAutoFocus #()}})
|
||||
(fn []
|
||||
(editor-handler/handle-command-input-close id)))
|
||||
{:content-props {:onOpenAutoFocus #()}})
|
||||
|
||||
:select-code-block-mode
|
||||
(open-editor-popup! :code-block-mode-picker
|
||||
(code-block-mode-picker id format) {})
|
||||
:select-code-block-mode
|
||||
(open-editor-popup! :code-block-mode-picker
|
||||
(code-block-mode-picker id format) {})
|
||||
|
||||
:template-search
|
||||
(open-editor-popup! :template-search
|
||||
(template-search id format) {})
|
||||
:template-search
|
||||
(open-editor-popup! :template-search
|
||||
(template-search id format) {})
|
||||
|
||||
(:property-search :property-value-search)
|
||||
(open-editor-popup! action
|
||||
(if (= :property-search action)
|
||||
(property-search id) (property-value-search id))
|
||||
{})
|
||||
(:property-search :property-value-search)
|
||||
(open-editor-popup! action
|
||||
(if (= :property-search action)
|
||||
(property-search id) (property-value-search id))
|
||||
{})
|
||||
|
||||
:zotero
|
||||
(open-editor-popup! :zotero
|
||||
(zotero/zotero-search id) {})
|
||||
:zotero
|
||||
(open-editor-popup! :zotero
|
||||
(zotero/zotero-search id) {})
|
||||
|
||||
;; TODO: try remove local model state
|
||||
false)]
|
||||
#(when pid
|
||||
(shui/popup-hide! pid))))
|
||||
[action])
|
||||
false)]
|
||||
#(when pid
|
||||
(shui/popup-hide! pid))))
|
||||
[action])
|
||||
[:<>])
|
||||
|
||||
(rum/defc command-popups <
|
||||
|
||||
@@ -357,6 +357,7 @@
|
||||
(.focus input))
|
||||
(util/scroll-to (rum/deref *result-ref) 0 false))))]
|
||||
[:div.cp__emoji-icon-picker
|
||||
{:data-keep-selection true}
|
||||
;; header
|
||||
[:div.hd.bg-popover
|
||||
(tab-observer @*tab {:reset-q! reset-q!})
|
||||
|
||||
@@ -139,10 +139,13 @@
|
||||
|
||||
(defn- add-new-property-object!
|
||||
[property set-data! properties]
|
||||
(p/let [block (editor-handler/api-insert-new-block! ""
|
||||
(p/let [default-value (if (= :checkbox (:logseq.property/type property))
|
||||
false
|
||||
(:db/id (db/entity :logseq.property/empty-placeholder)))
|
||||
block (editor-handler/api-insert-new-block! ""
|
||||
{:page (:block/uuid property)
|
||||
:properties (merge
|
||||
{(:db/ident property) (:db/id (db/entity :logseq.property/empty-placeholder))}
|
||||
{(:db/ident property) default-value}
|
||||
properties)
|
||||
:edit-block? false})
|
||||
_ (set-data! (get-property-related-objects (state/get-current-repo) property))]
|
||||
|
||||
@@ -244,7 +244,7 @@
|
||||
block' (if last-child-id (db/entity last-child-id) (last blocks))
|
||||
link (:block/link block')]
|
||||
(string/blank? (:block/title (or link block'))))))]
|
||||
[:div
|
||||
[:div.relative
|
||||
{:class (when add-button? "show-add-button")}
|
||||
(page-blocks-inner page-e blocks config sidebar? whiteboard? block-id)
|
||||
(let [args (if block-id
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
[frontend.db.async :as db-async]
|
||||
[frontend.db.model :as db-model]
|
||||
[frontend.handler.db-based.property :as db-property-handler]
|
||||
[frontend.handler.editor :as editor-handler]
|
||||
[frontend.handler.notification :as notification]
|
||||
[frontend.handler.property :as property-handler]
|
||||
[frontend.handler.property.util :as pu]
|
||||
[frontend.handler.route :as route-handler]
|
||||
[frontend.hooks :as hooks]
|
||||
@@ -116,6 +116,8 @@
|
||||
(do
|
||||
(shui/popup-hide!)
|
||||
(shui/dialog-close!))
|
||||
(pv/batch-operation?)
|
||||
nil
|
||||
(and block (= type :checkbox))
|
||||
(p/do!
|
||||
(ui/hide-popups-until-preview-popup!)
|
||||
@@ -167,15 +169,18 @@
|
||||
(map :block/title)
|
||||
set))))
|
||||
[])
|
||||
(let [items (concat
|
||||
(map (fn [x]
|
||||
{:label (:block/title x)
|
||||
:value (:block/uuid x)}) properties)
|
||||
(map (fn [x]
|
||||
{:label (:block/title x)
|
||||
:value (:block/uuid x)
|
||||
:group "Tags"}) classes))]
|
||||
(let [items (->>
|
||||
(concat
|
||||
(map (fn [x]
|
||||
{:label (:block/title x)
|
||||
:value (:block/uuid x)}) properties)
|
||||
(map (fn [x]
|
||||
{:label (:block/title x)
|
||||
:value (:block/uuid x)
|
||||
:group "Tags"}) classes))
|
||||
(util/distinct-by-last-wins :value))]
|
||||
[:div.ls-property-add.flex.flex-row.items-center.property-key
|
||||
{:data-keep-selection true}
|
||||
[:div.ls-property-key
|
||||
(select/select (merge
|
||||
{:items items
|
||||
@@ -212,79 +217,87 @@
|
||||
:size 15})))
|
||||
|
||||
(defn- property-input-on-chosen
|
||||
[block *property *property-key *show-new-property-config? {:keys [class-schema?]}]
|
||||
[block *property *property-key *show-new-property-config? {:keys [class-schema? remove-property?]}]
|
||||
(fn [{:keys [value label]}]
|
||||
(reset! *property-key (if (uuid? value) label value))
|
||||
(let [property (when (uuid? value) (db/entity [:block/uuid value]))]
|
||||
(when (and *show-new-property-config? (not (ldb/property? property)))
|
||||
(reset! *show-new-property-config? true))
|
||||
(reset! *property property)
|
||||
(when property
|
||||
(let [add-class-property? (and (ldb/class? block) class-schema?)
|
||||
type (:logseq.property/type property)]
|
||||
(cond
|
||||
add-class-property?
|
||||
(p/do!
|
||||
(pv/<add-property! block (:db/ident property) "" {:class-schema? class-schema?})
|
||||
(shui/popup-hide!)
|
||||
(shui/dialog-close!))
|
||||
(let [property (when (uuid? value) (db/entity [:block/uuid value]))
|
||||
batch? (pv/batch-operation?)
|
||||
repo (state/get-current-repo)]
|
||||
(if (and property remove-property?)
|
||||
(let [block-ids (map :block/uuid (pv/get-operating-blocks block))]
|
||||
(property-handler/batch-remove-block-property! repo block-ids (:db/ident property))
|
||||
(shui/popup-hide!))
|
||||
(do
|
||||
(when (and *show-new-property-config? (not (ldb/property? property)))
|
||||
(reset! *show-new-property-config? true))
|
||||
(reset! *property property)
|
||||
(when property
|
||||
(let [add-class-property? (and (ldb/class? block) class-schema?)
|
||||
type (:logseq.property/type property)
|
||||
default-or-url? (and (contains? #{:default :url} type)
|
||||
(not (seq (:property/closed-values property))))]
|
||||
(cond
|
||||
add-class-property?
|
||||
(p/do!
|
||||
(pv/<add-property! block (:db/ident property) "" {:class-schema? class-schema?})
|
||||
(shui/popup-hide!)
|
||||
(shui/dialog-close!))
|
||||
|
||||
(= :checkbox type)
|
||||
(p/do!
|
||||
(ui/hide-popups-until-preview-popup!)
|
||||
(shui/popup-hide!)
|
||||
(shui/dialog-close!)
|
||||
(let [value (if-some [value (:logseq.property/scalar-default-value property)]
|
||||
value
|
||||
false)]
|
||||
(pv/<add-property! block (:db/ident property) value {:exit-edit? true})))
|
||||
;; using class as property
|
||||
(and property (ldb/class? property))
|
||||
(p/do!
|
||||
(pv/<set-class-as-property! (state/get-current-repo) property)
|
||||
(reset! *show-new-property-config? false))
|
||||
|
||||
(and (contains? #{:default :url} type)
|
||||
(not (seq (:property/closed-values property))))
|
||||
(pv/<create-new-block! block property "")
|
||||
(and batch? (or (= :checkbox type) (and batch? default-or-url?)))
|
||||
nil
|
||||
|
||||
;; using class as property
|
||||
(and property (ldb/class? property))
|
||||
(pv/<set-class-as-property! (state/get-current-repo) property)
|
||||
(= :checkbox type)
|
||||
(p/do!
|
||||
(ui/hide-popups-until-preview-popup!)
|
||||
(shui/popup-hide!)
|
||||
(shui/dialog-close!)
|
||||
(let [value (if-some [value (:logseq.property/scalar-default-value property)]
|
||||
value
|
||||
false)]
|
||||
(pv/<add-property! block (:db/ident property) value {:exit-edit? true})))
|
||||
|
||||
(or (not= :default type)
|
||||
(and (= :default type) (seq (:property/closed-values property))))
|
||||
(reset! *show-new-property-config? false)))))))
|
||||
default-or-url?
|
||||
(pv/<create-new-block! block property "" {:batch-op? true})
|
||||
|
||||
(or (not= :default type)
|
||||
(and (= :default type) (seq (:property/closed-values property))))
|
||||
(reset! *show-new-property-config? false)))))))))
|
||||
|
||||
(rum/defc property-key-title
|
||||
[block property class-schema? property-position]
|
||||
(let [block-container (state/get-component :block/container)]
|
||||
(shui/trigger-as
|
||||
:a
|
||||
{:tabIndex 0
|
||||
:title (:block/title property)
|
||||
:class "property-k flex select-none jtrigger w-full"
|
||||
:on-pointer-down (fn [^js e]
|
||||
(when (util/meta-key? e)
|
||||
(route-handler/redirect-to-page! (:block/uuid property))
|
||||
(.preventDefault e)))
|
||||
:on-click (fn [^js/MouseEvent e]
|
||||
(if (state/editing?)
|
||||
(editor-handler/escape-editing {:select? true})
|
||||
(shui/popup-show! (.-target e)
|
||||
(fn []
|
||||
(property-config/dropdown-editor property block {:debug? (.-altKey e)
|
||||
:class-schema? class-schema?}))
|
||||
{:content-props
|
||||
{:class "ls-property-dropdown-editor as-root"
|
||||
:onEscapeKeyDown (fn [e]
|
||||
(util/stop e)
|
||||
(shui/popup-hide!)
|
||||
(when-let [input (state/get-input)]
|
||||
(.focus input)))}
|
||||
:align "start"
|
||||
:as-dropdown? true})))}
|
||||
(if (= :block-below property-position)
|
||||
(:block/title property)
|
||||
(block-container {:property? true} property)))))
|
||||
[block property class-schema?]
|
||||
(shui/trigger-as
|
||||
:a
|
||||
{:tabIndex 0
|
||||
:title (:block/title property)
|
||||
:class "property-k flex select-none jtrigger w-full"
|
||||
:on-pointer-down (fn [^js e]
|
||||
(when (util/meta-key? e)
|
||||
(route-handler/redirect-to-page! (:block/uuid property))
|
||||
(.preventDefault e)))
|
||||
:on-click (fn [^js/MouseEvent e]
|
||||
(shui/popup-show! (.-target e)
|
||||
(fn []
|
||||
(property-config/dropdown-editor property block {:debug? (.-altKey e)
|
||||
:class-schema? class-schema?}))
|
||||
{:content-props
|
||||
{:class "ls-property-dropdown-editor as-root"
|
||||
:onEscapeKeyDown (fn [e]
|
||||
(util/stop e)
|
||||
(shui/popup-hide!)
|
||||
(when-let [input (state/get-input)]
|
||||
(.focus input)))}
|
||||
:align "start"
|
||||
:as-dropdown? true}))}
|
||||
(:block/title property)))
|
||||
|
||||
(rum/defc property-key-cp < rum/static
|
||||
[block property {:keys [other-position? class-schema? property-position]}]
|
||||
[block property {:keys [other-position? class-schema?]}]
|
||||
(let [icon (:logseq.property/icon property)]
|
||||
[:div.property-key-inner.jtrigger-view
|
||||
;; icon picker
|
||||
@@ -319,10 +332,9 @@
|
||||
[:a.property-k.flex.select-none.jtrigger
|
||||
{:on-click #(route-handler/redirect-to-page! (:block/uuid property))}
|
||||
(:block/title property)]
|
||||
(property-key-title block property class-schema? property-position))]))
|
||||
(property-key-title block property class-schema?))]))
|
||||
|
||||
(rum/defcs property-input < rum/reactive
|
||||
(rum/local nil ::ref)
|
||||
(rum/defcs ^:large-vars/cleanup-todo property-input < rum/reactive
|
||||
(rum/local false ::show-new-property-config?)
|
||||
(rum/local false ::show-class-select?)
|
||||
(rum/local {} ::property-schema)
|
||||
@@ -354,8 +366,7 @@
|
||||
state)}
|
||||
[state block *property-key {:keys [class-schema?]
|
||||
:as opts}]
|
||||
(let [*ref (::ref state)
|
||||
*property (::property state)
|
||||
(let [*property (::property state)
|
||||
*show-new-property-config? (::show-new-property-config? state)
|
||||
*show-class-select? (::show-class-select? state)
|
||||
*property-schema (::property-schema state)
|
||||
@@ -374,16 +385,24 @@
|
||||
(and (not= view-context :all) (not (contains? block-types view-context)))
|
||||
(and (ldb/built-in? block) (contains? #{:logseq.property/parent} (:db/ident m))))))
|
||||
property (rum/react *property)
|
||||
property-key (rum/react *property-key)]
|
||||
property-key (rum/react *property-key)
|
||||
batch? (pv/batch-operation?)
|
||||
hide-property-key? (or (contains? #{:date :datetime} (:logseq.property/type property))
|
||||
(pv/select-type? block property)
|
||||
(and
|
||||
batch?
|
||||
(contains? #{:default :url} (:logseq.property/type property))
|
||||
(not (seq (:property/closed-values property))))
|
||||
(and property (ldb/class? property)))]
|
||||
[:div.ls-property-input.flex.flex-1.flex-row.items-center.flex-wrap.gap-1
|
||||
{:ref #(reset! *ref %)}
|
||||
(if property-key
|
||||
[:div.ls-property-add.gap-1.flex.flex-1.flex-row.items-center
|
||||
[:div.flex.flex-row.items-center.property-key.gap-1
|
||||
(when-not (:db/id property) (property-icon property (:logseq.property/type @*property-schema)))
|
||||
(if (:db/id property) ; property exists already
|
||||
(property-key-cp block property opts)
|
||||
[:div property-key])]
|
||||
(when-not hide-property-key?
|
||||
[:div.flex.flex-row.items-center.property-key.gap-1
|
||||
(when-not (:db/id property) (property-icon property (:logseq.property/type @*property-schema)))
|
||||
(if (:db/id property) ; property exists already
|
||||
(property-key-cp block property opts)
|
||||
[:div property-key])])
|
||||
[:div.flex.flex-row {:on-pointer-down (fn [e] (util/stop-propagation e))}
|
||||
(when (not= @*show-new-property-config? :adding-property)
|
||||
(cond
|
||||
@@ -416,8 +435,9 @@
|
||||
(= "" (.-value (.-target e))))
|
||||
(util/stop e)
|
||||
(shui/popup-hide!)))}]
|
||||
(property-select exclude-properties {:on-chosen on-chosen
|
||||
:input-opts input-opts})))]))
|
||||
(property-select exclude-properties
|
||||
(merge (:select-opts opts) {:on-chosen on-chosen
|
||||
:input-opts input-opts}))))]))
|
||||
|
||||
(rum/defcs new-property < rum/reactive
|
||||
[state block opts]
|
||||
|
||||
@@ -68,13 +68,13 @@
|
||||
}
|
||||
|
||||
.property-k {
|
||||
@apply inline-block overflow-hidden text-ellipsis whitespace-nowrap leading-[26px];
|
||||
|
||||
.jtrigger {
|
||||
&-id {
|
||||
@apply left-[16px];
|
||||
}
|
||||
}
|
||||
|
||||
@apply inline-block overflow-hidden text-ellipsis whitespace-nowrap;
|
||||
}
|
||||
|
||||
.property-value {
|
||||
@@ -188,6 +188,10 @@
|
||||
@apply flex items-center shrink;
|
||||
}
|
||||
|
||||
&[data-type] {
|
||||
@apply flex items-center;
|
||||
}
|
||||
|
||||
&[data-type="datetime"] {
|
||||
@apply whitespace-nowrap;
|
||||
}
|
||||
@@ -270,7 +274,7 @@ a.control-link {
|
||||
.property-value-container {
|
||||
@apply flex-1 shrink-0;
|
||||
min-width: 100px;
|
||||
min-height: 29px;
|
||||
min-height: 28px;
|
||||
}
|
||||
|
||||
.ls-page-properties .property-key, .ls-properties-area .property-key {
|
||||
@@ -280,7 +284,7 @@ a.control-link {
|
||||
.property-key {
|
||||
/* Same height with one-line block container */
|
||||
min-width: 160px;
|
||||
min-height: 29px;
|
||||
min-height: 28px;
|
||||
|
||||
h1.title, h2.title {
|
||||
border-bottom: 0;
|
||||
@@ -303,16 +307,14 @@ a.control-link {
|
||||
}
|
||||
|
||||
.property-key-inner {
|
||||
@apply flex flex-row items-start gap-1 relative;
|
||||
@apply flex flex-row items-center gap-1 relative;
|
||||
|
||||
.property-icon {
|
||||
@apply flex items-center;
|
||||
height: 28px;
|
||||
|
||||
button {
|
||||
margin-top: -1px;
|
||||
}
|
||||
height: 26px;
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
||||
.editor-inner {
|
||||
textarea {
|
||||
field-sizing: content;
|
||||
|
||||
@@ -83,58 +83,54 @@
|
||||
(rum/defc class-select
|
||||
[property {:keys [multiple-choices? disabled? default-open? no-class? on-hide]
|
||||
:or {multiple-choices? true}}]
|
||||
(let [*ref (rum/use-ref nil)]
|
||||
(hooks/use-effect!
|
||||
(fn []
|
||||
(when default-open?
|
||||
(some-> (rum/deref *ref)
|
||||
(.click))))
|
||||
[default-open?])
|
||||
(let [schema-classes (:logseq.property/classes property)]
|
||||
[:div.flex.flex-1.col-span-3
|
||||
(let [content-fn
|
||||
(fn [{:keys [id]}]
|
||||
(let [toggle-fn #(do
|
||||
(when (fn? on-hide) (on-hide))
|
||||
(shui/popup-hide! id))
|
||||
classes (model/get-all-readable-classes (state/get-current-repo) {:except-root-class? true})
|
||||
options (map (fn [class]
|
||||
{:label (:block/title class)
|
||||
:value (:block/uuid class)})
|
||||
classes)
|
||||
options (if no-class?
|
||||
(cons {:label "Skip choosing tag"
|
||||
:value :no-tag}
|
||||
options)
|
||||
options)
|
||||
opts {:items options
|
||||
:input-default-placeholder (if multiple-choices? "Choose tags" "Choose tag")
|
||||
:dropdown? false
|
||||
:close-modal? false
|
||||
:multiple-choices? multiple-choices?
|
||||
:selected-choices (map :block/uuid schema-classes)
|
||||
:extract-fn :label
|
||||
:extract-chosen-fn :value
|
||||
:show-new-when-not-exact-match? true
|
||||
:input-opts {:on-key-down
|
||||
(fn [e]
|
||||
(case (util/ekey e)
|
||||
"Escape"
|
||||
(do
|
||||
(util/stop e)
|
||||
(toggle-fn))
|
||||
nil))}
|
||||
:on-chosen (fn [value select?]
|
||||
(if (= value :no-tag)
|
||||
(toggle-fn)
|
||||
(p/let [result (<create-class-if-not-exists! value)
|
||||
value' (or result value)
|
||||
tx-data [[(if select? :db/add :db/retract) (:db/id property) :logseq.property/classes [:block/uuid value']]]
|
||||
_ (db/transact! (state/get-current-repo) tx-data {:outliner-op :update-property})]
|
||||
(when-not multiple-choices? (toggle-fn)))))}]
|
||||
(let [*ref (rum/use-ref nil)
|
||||
schema-classes (:logseq.property/classes property)]
|
||||
[:div.flex.flex-1.col-span-3
|
||||
(let [content-fn
|
||||
(fn [{:keys [id]}]
|
||||
(let [toggle-fn #(do
|
||||
(when (fn? on-hide) (on-hide))
|
||||
(shui/popup-hide! id))
|
||||
classes (model/get-all-readable-classes (state/get-current-repo) {:except-root-class? true})
|
||||
options (map (fn [class]
|
||||
{:label (:block/title class)
|
||||
:value (:block/uuid class)})
|
||||
classes)
|
||||
options (if no-class?
|
||||
(cons {:label "Skip choosing tag"
|
||||
:value :no-tag}
|
||||
options)
|
||||
options)
|
||||
opts {:items options
|
||||
:input-default-placeholder (if multiple-choices? "Choose tags" "Choose tag")
|
||||
:dropdown? false
|
||||
:close-modal? false
|
||||
:multiple-choices? multiple-choices?
|
||||
:selected-choices (map :block/uuid schema-classes)
|
||||
:extract-fn :label
|
||||
:extract-chosen-fn :value
|
||||
:show-new-when-not-exact-match? true
|
||||
:input-opts {:on-key-down
|
||||
(fn [e]
|
||||
(case (util/ekey e)
|
||||
"Escape"
|
||||
(do
|
||||
(util/stop e)
|
||||
(toggle-fn))
|
||||
nil))}
|
||||
:on-chosen (fn [value select?]
|
||||
(if (= value :no-tag)
|
||||
(toggle-fn)
|
||||
(p/let [result (<create-class-if-not-exists! value)
|
||||
value' (or result value)
|
||||
tx-data [[(if select? :db/add :db/retract) (:db/id property) :logseq.property/classes [:block/uuid value']]]
|
||||
_ (db/transact! (state/get-current-repo) tx-data {:outliner-op :update-property})]
|
||||
(when-not multiple-choices? (toggle-fn)))))}]
|
||||
|
||||
(select/select opts)))]
|
||||
(select/select opts)))]
|
||||
|
||||
(if default-open?
|
||||
(content-fn nil)
|
||||
[:div.flex.flex-1.cursor-pointer
|
||||
{:ref *ref
|
||||
:on-click (if disabled?
|
||||
@@ -147,7 +143,7 @@
|
||||
[:a.text-sm (str "#" (:block/title class))])
|
||||
[:span.opacity-60.pl-1.top-1.relative.hover:opacity-80.active:opacity-60
|
||||
(shui/tabler-icon "edit")]]
|
||||
(pv/property-empty-btn-value property))])])))
|
||||
(pv/property-empty-btn-value property))]))]))
|
||||
|
||||
(rum/defc name-edit-pane
|
||||
[property {:keys [set-sub-open! disabled?]}]
|
||||
@@ -326,12 +322,12 @@
|
||||
:button-opts {:title "Set Icon"}})
|
||||
[:strong {:on-click (fn [^js e]
|
||||
(shui/popup-show! (.-target e)
|
||||
(fn [] (choice-base-edit-form property block))
|
||||
{:id :ls-base-edit-form
|
||||
:align "start"}))}
|
||||
(fn [] (choice-base-edit-form property block))
|
||||
{:id :ls-base-edit-form
|
||||
:align "start"}))}
|
||||
value]
|
||||
(shui/dropdown-menu
|
||||
(shui/dropdown-menu-trigger
|
||||
(shui/dropdown-menu-trigger
|
||||
{:as-child true
|
||||
:disabled disabled?}
|
||||
(shui/button
|
||||
@@ -409,24 +405,24 @@
|
||||
[:<>
|
||||
[:ul.choices-list
|
||||
(dnd/items choice-items
|
||||
{:sort-by-inner-element? false
|
||||
:on-drag-end (fn [_ {:keys [active-id over-id direction]}]
|
||||
(let [move-down? (= direction :down)
|
||||
over (db/entity [:block/uuid (uuid over-id)])
|
||||
active (db/entity [:block/uuid (uuid active-id)])
|
||||
over-order (:block/order over)
|
||||
new-order (if move-down?
|
||||
(let [next-order (db-order/get-next-order (db/get-db) property (:db/id over))]
|
||||
(db-order/gen-key over-order next-order))
|
||||
(let [prev-order (db-order/get-prev-order (db/get-db) property (:db/id over))]
|
||||
(db-order/gen-key prev-order over-order)))]
|
||||
{:sort-by-inner-element? false
|
||||
:on-drag-end (fn [_ {:keys [active-id over-id direction]}]
|
||||
(let [move-down? (= direction :down)
|
||||
over (db/entity [:block/uuid (uuid over-id)])
|
||||
active (db/entity [:block/uuid (uuid active-id)])
|
||||
over-order (:block/order over)
|
||||
new-order (if move-down?
|
||||
(let [next-order (db-order/get-next-order (db/get-db) property (:db/id over))]
|
||||
(db-order/gen-key over-order next-order))
|
||||
(let [prev-order (db-order/get-prev-order (db/get-db) property (:db/id over))]
|
||||
(db-order/gen-key prev-order over-order)))]
|
||||
|
||||
(db/transact! (state/get-current-repo)
|
||||
[{:db/id (:db/id active)
|
||||
:block/order new-order}
|
||||
(outliner-core/block-with-updated-at
|
||||
{:db/id (:db/id property)})]
|
||||
{:outliner-op :save-block})))})]
|
||||
(db/transact! (state/get-current-repo)
|
||||
[{:db/id (:db/id active)
|
||||
:block/order new-order}
|
||||
(outliner-core/block-with-updated-at
|
||||
{:db/id (:db/id property)})]
|
||||
{:outliner-op :save-block})))})]
|
||||
(shui/dropdown-menu-separator)])
|
||||
|
||||
;; add choice
|
||||
@@ -463,14 +459,14 @@
|
||||
[choices]
|
||||
(let [select-cp (fn [opts]
|
||||
(shui/select
|
||||
opts
|
||||
(shui/select-trigger
|
||||
{:class "h-8"}
|
||||
(shui/select-value {:placeholder "Select a choice"}))
|
||||
(shui/select-content
|
||||
(map (fn [choice]
|
||||
(shui/select-item {:key (str (:db/id choice))
|
||||
:value (:db/id choice)} (:block/title choice))) choices))))
|
||||
opts
|
||||
(shui/select-trigger
|
||||
{:class "h-8"}
|
||||
(shui/select-value {:placeholder "Select a choice"}))
|
||||
(shui/select-content
|
||||
(map (fn [choice]
|
||||
(shui/select-item {:key (str (:db/id choice))
|
||||
:value (:db/id choice)} (:block/title choice))) choices))))
|
||||
checked-choice (some (fn [choice] (when (true? (:logseq.property/choice-checkbox-state choice)) choice)) choices)
|
||||
unchecked-choice (some (fn [choice] (when (false? (:logseq.property/choice-checkbox-state choice)) choice)) choices)]
|
||||
[:div.flex.flex-col.gap-4.text-sm.p-2
|
||||
|
||||
@@ -1,18 +1,27 @@
|
||||
(ns frontend.components.property.dialog
|
||||
"Property && value choose"
|
||||
(:require [frontend.components.property :as property-component]
|
||||
[rum.core :as rum]
|
||||
[frontend.db :as db]
|
||||
[frontend.modules.shortcut.core :as shortcut]
|
||||
[frontend.db :as db]))
|
||||
[frontend.state :as state]
|
||||
[rum.core :as rum]))
|
||||
|
||||
(rum/defcs dialog <
|
||||
shortcut/disable-all-shortcuts
|
||||
(rum/local nil ::property-value)
|
||||
{:init (fn [state]
|
||||
(let [k (:property-key (last (:rum/args state)))]
|
||||
(let [opts (last (:rum/args state))
|
||||
k (:property-key opts)]
|
||||
(when-let [view-selected-blocks (:selected-blocks opts)]
|
||||
(state/set-state! :view/selected-blocks view-selected-blocks))
|
||||
(assoc state
|
||||
::property-key (atom k)
|
||||
::property (atom (when k (db/get-case-page k))))))}
|
||||
::property (atom (when k (db/get-case-page k))))))
|
||||
:will-unmount (fn [state]
|
||||
(when-let [close-fn (:on-dialog-close (last (:rum/args state)))]
|
||||
(close-fn))
|
||||
(state/set-state! :view/selected-blocks nil)
|
||||
state)}
|
||||
[state blocks opts]
|
||||
(when (seq blocks)
|
||||
(let [*property-key (::property-key state)
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
[frontend.handler.db-based.page :as db-page-handler]
|
||||
[frontend.handler.db-based.property :as db-property-handler]
|
||||
[frontend.handler.editor :as editor-handler]
|
||||
[frontend.handler.notification :as notification]
|
||||
[frontend.handler.page :as page-handler]
|
||||
[frontend.handler.property :as property-handler]
|
||||
[frontend.handler.property.util :as pu]
|
||||
@@ -64,6 +65,29 @@
|
||||
(ui/icon "line-dashed"))
|
||||
"Empty")])
|
||||
|
||||
(defn- get-selected-blocks
|
||||
[]
|
||||
(some->> (state/get-selection-block-ids)
|
||||
(map (fn [id] (db/entity [:block/uuid id])))
|
||||
(seq)
|
||||
block-handler/get-top-level-blocks
|
||||
(remove ldb/property?)))
|
||||
|
||||
(defn get-operating-blocks
|
||||
[block]
|
||||
(let [selected-blocks (get-selected-blocks)
|
||||
view-selected-blocks (:view/selected-blocks @state/state)]
|
||||
(or (seq selected-blocks)
|
||||
(seq view-selected-blocks)
|
||||
[block])))
|
||||
|
||||
(defn batch-operation?
|
||||
[]
|
||||
(let [selected-blocks (get-selected-blocks)
|
||||
view-selected-blocks (:view/selected-blocks @state/state)]
|
||||
(or (> (count selected-blocks) 1)
|
||||
(seq view-selected-blocks))))
|
||||
|
||||
(rum/defc icon-row
|
||||
[block editing?]
|
||||
(let [icon-value (:logseq.property/icon block)
|
||||
@@ -71,14 +95,13 @@
|
||||
(shui/dialog-close!)
|
||||
(shui/popup-hide-all!))
|
||||
on-chosen! (fn [_e icon]
|
||||
(if icon
|
||||
(db-property-handler/set-block-property!
|
||||
(:db/id block)
|
||||
(let [repo (state/get-current-repo)
|
||||
blocks (get-operating-blocks block)]
|
||||
(property-handler/batch-set-block-property!
|
||||
repo
|
||||
(map :db/id blocks)
|
||||
:logseq.property/icon
|
||||
(select-keys icon [:type :id :color]))
|
||||
(db-property-handler/remove-block-property!
|
||||
(:db/id block)
|
||||
:logseq.property/icon))
|
||||
(when icon (select-keys icon [:type :id :color]))))
|
||||
(clear-overlay!)
|
||||
(when editing?
|
||||
(editor-handler/restore-last-saved-cursor!)))]
|
||||
@@ -96,14 +119,14 @@
|
||||
(.querySelector ".block-main-container"))]
|
||||
(state/set-editor-action! :property-icon-picker)
|
||||
(shui/popup-show! target
|
||||
#(icon-component/icon-search
|
||||
{:on-chosen on-chosen!
|
||||
:icon-value icon
|
||||
:del-btn? (some? icon)})
|
||||
{:id :ls-icon-picker
|
||||
:on-after-hide #(state/set-editor-action! nil)
|
||||
:content-props {:onEscapeKeyDown #(when editing? (editor-handler/restore-last-saved-cursor!))}
|
||||
:align :start})))))))
|
||||
#(icon-component/icon-search
|
||||
{:on-chosen on-chosen!
|
||||
:icon-value icon
|
||||
:del-btn? (some? icon)})
|
||||
{:id :ls-icon-picker
|
||||
:on-after-hide #(state/set-editor-action! nil)
|
||||
:content-props {:onEscapeKeyDown #(when editing? (editor-handler/restore-last-saved-cursor!))}
|
||||
:align :start})))))))
|
||||
[editing?])
|
||||
|
||||
[:div.col-span-3.flex.flex-row.items-center.gap-2
|
||||
@@ -112,22 +135,14 @@
|
||||
:del-btn? (some? icon-value)
|
||||
:on-chosen on-chosen!})]))
|
||||
|
||||
(defn- select-type?
|
||||
[block property type]
|
||||
(or (contains? #{:node :number :date :page :class :property} type)
|
||||
;; closed values
|
||||
(seq (:property/closed-values property))
|
||||
(and (= (:db/ident property) :logseq.property/default-value)
|
||||
(= (:logseq.property/type block) :number))))
|
||||
|
||||
(defn- get-operating-blocks
|
||||
[block]
|
||||
(let [selected-blocks (some->> (state/get-selection-block-ids)
|
||||
(map (fn [id] (db/entity [:block/uuid id])))
|
||||
(seq)
|
||||
block-handler/get-top-level-blocks
|
||||
(remove ldb/property?))]
|
||||
(or (seq selected-blocks) [block])))
|
||||
(defn select-type?
|
||||
[block property]
|
||||
(let [type (:logseq.property/type property)]
|
||||
(or (contains? #{:node :number :date :page :class :property} type)
|
||||
;; closed values
|
||||
(seq (:property/closed-values property))
|
||||
(and (= (:db/ident property) :logseq.property/default-value)
|
||||
(= (:logseq.property/type block) :number)))))
|
||||
|
||||
(defn <create-new-block!
|
||||
[block property value & {:keys [edit-block? batch-op?]
|
||||
@@ -139,22 +154,17 @@
|
||||
(let [<create-block (fn [block]
|
||||
(if (and (contains? #{:default :url} (:logseq.property/type property))
|
||||
(not (db-property/many? property)))
|
||||
(p/let [existing-value (get block (:db/ident property))
|
||||
default-value (:logseq.property/default-value property)
|
||||
existing-value? (and (some? existing-value)
|
||||
(not= (:db/ident existing-value) :logseq.property/empty-placeholder)
|
||||
(not= (:db/id existing-value) (:db/id default-value)))
|
||||
new-block-id (when-not existing-value? (db/new-block-id))
|
||||
_ (when-not existing-value?
|
||||
(let [value' (if (and default-value (string? value) (string/blank? value))
|
||||
(db-property/property-value-content default-value)
|
||||
value)]
|
||||
(db-property-handler/create-property-text-block!
|
||||
(:db/id block)
|
||||
(:db/id property)
|
||||
value'
|
||||
{:new-block-id new-block-id})))]
|
||||
(if existing-value? existing-value (db/entity [:block/uuid new-block-id])))
|
||||
(p/let [default-value (:logseq.property/default-value property)
|
||||
new-block-id (db/new-block-id)
|
||||
_ (let [value' (if (and default-value (string? value) (string/blank? value))
|
||||
(db-property/property-value-content default-value)
|
||||
value)]
|
||||
(db-property-handler/create-property-text-block!
|
||||
(:db/id block)
|
||||
(:db/id property)
|
||||
value'
|
||||
{:new-block-id new-block-id}))]
|
||||
(db/entity [:block/uuid new-block-id]))
|
||||
(p/let [new-block-id (db/new-block-id)
|
||||
_ (db-property-handler/create-property-text-block!
|
||||
(:db/id block)
|
||||
@@ -187,7 +197,7 @@
|
||||
(defn <add-property!
|
||||
"If a class and in a class schema context, add the property to its schema.
|
||||
Otherwise, add a block's property and its value"
|
||||
([block property-key property-value] (<add-property! block property-key property-value {}))
|
||||
([block property-id property-value] (<add-property! block property-id property-value {}))
|
||||
([block property-id property-value {:keys [selected? exit-edit? class-schema?]
|
||||
:or {exit-edit? true}}]
|
||||
(let [repo (state/get-current-repo)
|
||||
@@ -204,20 +214,17 @@
|
||||
(<set-class-as-property! repo property))
|
||||
(db-property-handler/class-add-property! (:db/id block) property-id))
|
||||
(let [block-ids (map :block/uuid blocks)]
|
||||
(if (and (db-property-type/all-ref-property-types (:logseq.property/type property))
|
||||
(string? property-value))
|
||||
(p/let [new-block (<create-new-block! block (db/entity property-id) property-value {:edit-block? false})]
|
||||
(when (seq (remove #{(:db/id block)} (map :db/id block)))
|
||||
(property-handler/batch-set-block-property! repo block-ids property-id (:db/id new-block)))
|
||||
new-block)
|
||||
(property-handler/batch-set-block-property! repo block-ids property-id property-value))))
|
||||
(cond
|
||||
exit-edit?
|
||||
(do
|
||||
(ui/hide-popups-until-preview-popup!)
|
||||
(shui/dialog-close!))
|
||||
selected?
|
||||
(shui/popup-hide!))
|
||||
(property-handler/batch-set-block-property! repo block-ids property-id property-value)))
|
||||
(when (seq (:view/selected-blocks @state/state))
|
||||
(notification/show! "Property updated!" :success))
|
||||
(when-not many?
|
||||
(cond
|
||||
exit-edit?
|
||||
(do
|
||||
(ui/hide-popups-until-preview-popup!)
|
||||
(shui/dialog-close!))
|
||||
selected?
|
||||
(shui/popup-hide!)))
|
||||
(when-not (or many? checkbox?)
|
||||
(when-let [input (state/get-input)]
|
||||
(.focus input)))
|
||||
@@ -439,8 +446,7 @@
|
||||
|
||||
(rum/defc date-picker
|
||||
[value {:keys [block property datetime? on-change on-delete del-btn? editing? multiple-values? other-position?]}]
|
||||
(let [*trigger-ref (rum/use-ref nil)
|
||||
content-fn (fn [{:keys [id]}] (calendar-inner id
|
||||
(let [content-fn (fn [{:keys [id]}] (calendar-inner id
|
||||
{:block block
|
||||
:property property
|
||||
:on-change on-change
|
||||
@@ -456,52 +462,44 @@
|
||||
(shui/popup-show! (.-target e) content-fn
|
||||
{:align "start" :auto-focus? true}))))
|
||||
repeated-task? (:logseq.task/repeated? block)]
|
||||
(hooks/use-effect!
|
||||
(fn []
|
||||
(when editing?
|
||||
(js/setTimeout
|
||||
#(some-> (rum/deref *trigger-ref)
|
||||
(.click)) 32)))
|
||||
[editing?])
|
||||
(if editing?
|
||||
(content-fn {:id :date-picker})
|
||||
(if multiple-values?
|
||||
(shui/button
|
||||
{:class "jtrigger h-6 empty-btn"
|
||||
:variant :text
|
||||
:size :sm
|
||||
:on-click open-popup!}
|
||||
(ui/icon "calendar-plus" {:size 16}))
|
||||
(shui/trigger-as
|
||||
:div.flex.flex-1.flex-row.gap-1.items-center.flex-wrap
|
||||
{:tabIndex 0
|
||||
:class "jtrigger min-h-[24px]" ; FIXME: min-h-6 not works
|
||||
:on-click open-popup!}
|
||||
[:div.flex.flex-row.gap-1.items-center
|
||||
(when repeated-task?
|
||||
(ui/icon "repeat" {:size 14 :class "opacity-40"}))
|
||||
(cond
|
||||
(map? value)
|
||||
(let [date (tc/to-date-time (date/journal-day->utc-ms (:block/journal-day value)))
|
||||
compare-value (some-> date
|
||||
(t/plus (t/days 1))
|
||||
(t/minus (t/seconds 1)))
|
||||
content (when-let [page-cp (state/get-component :block/page-cp)]
|
||||
(rum/with-key
|
||||
(page-cp {:disable-preview? true
|
||||
:meta-click? other-position?
|
||||
:label (human-date-label (t/to-default-time-zone date))} value)
|
||||
(:db/id value)))]
|
||||
(if (or repeated-task? (contains? #{:logseq.task/deadline :logseq.task/scheduled} (:db/id property)))
|
||||
(overdue compare-value content)
|
||||
content))
|
||||
|
||||
(if multiple-values?
|
||||
(shui/button
|
||||
{:class "jtrigger h-6 empty-btn"
|
||||
:ref *trigger-ref
|
||||
:variant :text
|
||||
:size :sm
|
||||
:on-click open-popup!}
|
||||
(ui/icon "calendar-plus" {:size 16}))
|
||||
(shui/trigger-as
|
||||
:div.flex.flex-1.flex-row.gap-1.items-center.flex-wrap
|
||||
{:tabIndex 0
|
||||
:class "jtrigger min-h-[24px]" ; FIXME: min-h-6 not works
|
||||
:ref *trigger-ref
|
||||
:on-click open-popup!}
|
||||
[:div.flex.flex-row.gap-1.items-center
|
||||
(when repeated-task?
|
||||
(ui/icon "repeat" {:size 14 :class "opacity-40"}))
|
||||
(cond
|
||||
(map? value)
|
||||
(let [date (tc/to-date-time (date/journal-day->utc-ms (:block/journal-day value)))
|
||||
compare-value (some-> date
|
||||
(t/plus (t/days 1))
|
||||
(t/minus (t/seconds 1)))
|
||||
content (when-let [page-cp (state/get-component :block/page-cp)]
|
||||
(rum/with-key
|
||||
(page-cp {:disable-preview? true
|
||||
:meta-click? other-position?
|
||||
:label (human-date-label (t/to-default-time-zone date))} value)
|
||||
(:db/id value)))]
|
||||
(if (or repeated-task? (contains? #{:logseq.task/deadline :logseq.task/scheduled} (:db/id property)))
|
||||
(overdue compare-value content)
|
||||
content))
|
||||
(number? value)
|
||||
(datetime-value value (:db/ident property) repeated-task?)
|
||||
|
||||
(number? value)
|
||||
(datetime-value value (:db/ident property) repeated-task?)
|
||||
|
||||
:else
|
||||
(property-empty-btn-value nil))]))))
|
||||
:else
|
||||
(property-empty-btn-value nil))])))))
|
||||
|
||||
(rum/defc property-value-date-picker
|
||||
[block property value opts]
|
||||
@@ -578,8 +576,8 @@
|
||||
(remove nil?)
|
||||
(remove #(= :logseq.property/empty-placeholder %)))
|
||||
clear-value (str "No " (:block/title property))
|
||||
clear-value-label [:div.flex.flex-row.items-center.gap-2
|
||||
(ui/icon "x")
|
||||
clear-value-label [:div.flex.flex-row.items-center.gap-1.text-sm
|
||||
(ui/icon "x" {:size 14})
|
||||
[:div clear-value]]
|
||||
items' (->>
|
||||
(if (and (seq selected-choices)
|
||||
@@ -685,10 +683,17 @@
|
||||
(and alias? (= (or (:db/id (:block/page block))
|
||||
(:db/id block))
|
||||
(:db/id node)))
|
||||
(when (and property-type (not= property-type :node))
|
||||
(cond
|
||||
(= property-type :class)
|
||||
(ldb/private-tags (:db/ident node))
|
||||
|
||||
(and property-type (not= property-type :node))
|
||||
(if (= property-type :page)
|
||||
(not (db/page? node))
|
||||
(not (contains? (ldb/get-entity-types node) property-type))))))
|
||||
(not (contains? (ldb/get-entity-types node) property-type)))
|
||||
|
||||
:else
|
||||
false)))
|
||||
result))))
|
||||
|
||||
options (map (fn [node]
|
||||
@@ -731,13 +736,11 @@
|
||||
"Set tags"
|
||||
alias?
|
||||
"Set alias"
|
||||
multiple-choices?
|
||||
"Choose nodes"
|
||||
:else
|
||||
"Choose node")
|
||||
(str "Set " (:block/title property)))
|
||||
:show-new-when-not-exact-match? (if (or (and parent-property? (contains? (set children-pages) (:db/id block)))
|
||||
;; Don't allow creating private tags
|
||||
(seq (set/intersection (set (map :db/ident classes))
|
||||
(seq (set/intersection (set (map :db/ident classes'))
|
||||
ldb/private-tags)))
|
||||
false
|
||||
true)
|
||||
@@ -801,7 +804,10 @@
|
||||
(set-result! result)))))
|
||||
repo (state/get-current-repo)
|
||||
classes (:logseq.property/classes property)
|
||||
non-root-classes (remove (fn [c] (= (:db/ident c) :logseq.class/Root)) classes)
|
||||
class? (= :class (:logseq.property/type property))
|
||||
non-root-classes (cond-> (remove (fn [c] (= (:db/ident c) :logseq.class/Root)) classes)
|
||||
class?
|
||||
(conj (frontend.db/entity :logseq.class/Tag)))
|
||||
parent-property? (= (:db/ident property) :logseq.property/parent)]
|
||||
(when (and (not parent-property?) (seq non-root-classes))
|
||||
;; effect runs once
|
||||
@@ -891,7 +897,7 @@
|
||||
:selected-choices selected-choices
|
||||
:dropdown? dropdown?
|
||||
:show-new-when-not-exact-match? (not (or closed-values? (= :date type)))
|
||||
:input-default-placeholder "Select"
|
||||
:input-default-placeholder (str "Set " (:block/title property))
|
||||
:extract-chosen-fn :value
|
||||
:extract-fn (fn [x] (or (:label-value x) (:label x)))
|
||||
:content-props content-props
|
||||
@@ -1044,49 +1050,46 @@
|
||||
(inline-text-cp (str value)))]))
|
||||
|
||||
(rum/defc single-value-select
|
||||
[block property value value-f select-opts opts]
|
||||
(let [*el (rum/use-ref nil)]
|
||||
;; Open popover initially when editing a property
|
||||
(hooks/use-effect!
|
||||
(fn []
|
||||
(when (:editing? opts)
|
||||
(.click (rum/deref *el))))
|
||||
[(:editing? opts)])
|
||||
(let [type (:logseq.property/type property)
|
||||
select-opts' (assoc select-opts :multiple-choices? false)
|
||||
popup-content (fn content-fn [_]
|
||||
[:div.property-select
|
||||
(case type
|
||||
(:entity :number :default :url)
|
||||
(select block property select-opts' opts)
|
||||
[block property value select-opts {:keys [value-render] :as opts}]
|
||||
(let [*el (rum/use-ref nil)
|
||||
editing? (:editing? opts)
|
||||
type (:logseq.property/type property)
|
||||
select-opts' (assoc select-opts :multiple-choices? false)
|
||||
popup-content (fn content-fn [_]
|
||||
[:div.property-select
|
||||
(case type
|
||||
(:entity :number :default :url)
|
||||
(select block property select-opts' opts)
|
||||
|
||||
(:node :class :property :page :date)
|
||||
(property-value-select-node block property select-opts' opts))])
|
||||
trigger-id (str "trigger-" (:container-id opts) "-" (:db/id block) "-" (:db/id property))
|
||||
show! (fn [e]
|
||||
(let [target (.-target e)]
|
||||
(when-not (or config/publishing?
|
||||
(util/shift-key? e)
|
||||
(util/meta-key? e)
|
||||
(util/link? target)
|
||||
(when-let [node (.closest target "a")]
|
||||
(not (or (d/has-class? node "page-ref")
|
||||
(d/has-class? node "tag")))))
|
||||
(:node :class :property :page :date)
|
||||
(property-value-select-node block property select-opts' opts))])]
|
||||
(if editing?
|
||||
(popup-content nil)
|
||||
(let [trigger-id (str "trigger-" (:container-id opts) "-" (:db/id block) "-" (:db/id property))
|
||||
show! (fn [e]
|
||||
(let [target (.-target e)]
|
||||
(when-not (or config/publishing?
|
||||
(util/shift-key? e)
|
||||
(util/meta-key? e)
|
||||
(util/link? target)
|
||||
(when-let [node (.closest target "a")]
|
||||
(not (or (d/has-class? node "page-ref")
|
||||
(d/has-class? node "tag")))))
|
||||
|
||||
(shui/popup-show! target popup-content
|
||||
{:align "start"
|
||||
:as-dropdown? true
|
||||
:auto-focus? true
|
||||
:trigger-id trigger-id}))))]
|
||||
(shui/trigger-as
|
||||
(if (:other-position? opts) :div.jtrigger :div.jtrigger.flex.flex-1.w-full)
|
||||
{:ref *el
|
||||
:id trigger-id
|
||||
:tabIndex 0
|
||||
:on-click show!}
|
||||
(if (string/blank? value)
|
||||
(property-empty-text-value property opts)
|
||||
(value-f))))))
|
||||
(shui/popup-show! target popup-content
|
||||
{:align "start"
|
||||
:as-dropdown? true
|
||||
:auto-focus? true
|
||||
:trigger-id trigger-id}))))]
|
||||
(shui/trigger-as
|
||||
(if (:other-position? opts) :div.jtrigger :div.jtrigger.flex.flex-1.w-full)
|
||||
{:ref *el
|
||||
:id trigger-id
|
||||
:tabIndex 0
|
||||
:on-click show!}
|
||||
(if (string/blank? value)
|
||||
(property-empty-text-value property opts)
|
||||
(value-render)))))))
|
||||
|
||||
(defn- property-value-inner
|
||||
[block property value {:keys [inline-text page-cp
|
||||
@@ -1125,8 +1128,10 @@
|
||||
editing? (or editing?
|
||||
(and (state/sub-editing? [container-id (:block/uuid block)])
|
||||
(= (:db/id property) (:db/id (:property (state/get-editor-action-data))))))
|
||||
select-type?' (select-type? block property type)
|
||||
batch? (batch-operation?)
|
||||
closed-values? (seq (:property/closed-values property))
|
||||
select-type?' (or (select-type? block property)
|
||||
(and editing? batch? (contains? #{:default :url} type) (not closed-values?)))
|
||||
select-opts {:on-chosen on-chosen}
|
||||
value (if (and (de/entity? value*) (= (:db/ident value*) :logseq.property/empty-placeholder))
|
||||
nil
|
||||
@@ -1154,9 +1159,10 @@
|
||||
(when choice
|
||||
(db-property-handler/set-block-property! (:db/id block) (:db/ident property) (:db/id choice)))))}))
|
||||
(single-value-select block property value
|
||||
(fn [] (select-item property type value opts))
|
||||
select-opts
|
||||
(assoc opts :editing? editing?))))
|
||||
(assoc opts
|
||||
:editing? editing?
|
||||
:value-render (fn [] (select-item property type value opts))))))
|
||||
(case type
|
||||
(:date :datetime)
|
||||
(property-value-date-picker block property value (merge opts {:editing? editing?}))
|
||||
@@ -1187,24 +1193,22 @@
|
||||
*el (rum/use-ref nil)
|
||||
items (cond->> (if (de/entity? v) #{v} v)
|
||||
(= (:db/ident property) :block/tags)
|
||||
(remove (fn [v] (contains? ldb/hidden-tags (:db/ident v)))))]
|
||||
(hooks/use-effect!
|
||||
(fn []
|
||||
(when editing?
|
||||
(.click (rum/deref *el))))
|
||||
[editing?])
|
||||
(let [select-cp (fn [select-opts]
|
||||
(let [select-opts (merge {:multiple-choices? true
|
||||
:on-chosen (fn []
|
||||
(when on-chosen (on-chosen)))}
|
||||
select-opts
|
||||
{:dropdown? false})]
|
||||
[:div.property-select
|
||||
(if (contains? #{:node :page :class :property} type)
|
||||
(property-value-select-node block property
|
||||
select-opts
|
||||
opts)
|
||||
(select block property select-opts opts))]))]
|
||||
(remove (fn [v] (contains? ldb/hidden-tags (:db/ident v)))))
|
||||
select-cp (fn [select-opts]
|
||||
(let [select-opts (merge {:multiple-choices? true
|
||||
:on-chosen (fn []
|
||||
(when on-chosen (on-chosen)))}
|
||||
select-opts
|
||||
(when-not editing?
|
||||
{:dropdown? false}))]
|
||||
[:div.property-select
|
||||
(if (contains? #{:node :page :class :property} type)
|
||||
(property-value-select-node block property
|
||||
select-opts
|
||||
opts)
|
||||
(select block property select-opts opts))]))]
|
||||
(if editing?
|
||||
(select-cp {})
|
||||
(let [toggle-fn shui/popup-hide!
|
||||
content-fn (fn [{:keys [_id content-props]}]
|
||||
(select-cp {:content-props content-props}))]
|
||||
@@ -1223,7 +1227,7 @@
|
||||
(do (some-> (rum/deref *el) (.click))
|
||||
(util/stop e))
|
||||
:dune))
|
||||
:class "flex flex-1 flex-row items-center flex-wrap gap-x-2 gap-y-2"}
|
||||
:class "flex flex-1 flex-row items-center flex-wrap gap-1"}
|
||||
(let [not-empty-value? (not= (map :db/ident items) [:logseq.property/empty-placeholder])]
|
||||
(if (and (seq items) not-empty-value?)
|
||||
(concat
|
||||
@@ -1244,8 +1248,8 @@
|
||||
(when (some? value) #{value}))]
|
||||
(multiple-values-inner block property value' opts)))
|
||||
|
||||
(rum/defcs property-value < rum/reactive db-mixins/query
|
||||
[state block property {:keys [show-tooltip? p-block p-property]
|
||||
(rum/defcs ^:large-vars/cleanup-todo property-value < rum/reactive db-mixins/query
|
||||
[state block property {:keys [show-tooltip? p-block p-property editing?]
|
||||
:as opts}]
|
||||
(ui/catch-error
|
||||
(ui/block-error "Something wrong" {})
|
||||
@@ -1303,7 +1307,7 @@
|
||||
(properties-cp {} block {:selected? false
|
||||
:class-schema? true})
|
||||
|
||||
(and multiple-values? (contains? #{:default :url} type) (not closed-values?))
|
||||
(and multiple-values? (contains? #{:default :url} type) (not closed-values?) (not editing?))
|
||||
(property-normal-block-value block property v)
|
||||
|
||||
multiple-values?
|
||||
@@ -1339,7 +1343,9 @@
|
||||
(shui/tooltip
|
||||
{:delayDuration 1200}
|
||||
(shui/tooltip-trigger
|
||||
{:onFocusCapture #(util/stop-propagation %)} value-cp)
|
||||
{:onFocusCapture #(util/stop-propagation %)
|
||||
:as-child true}
|
||||
value-cp)
|
||||
(shui/tooltip-content
|
||||
(str "Change " (:block/title property)))))
|
||||
value-cp))))))
|
||||
|
||||
@@ -27,3 +27,7 @@
|
||||
@apply text-yellow-rx-08 dark:text-yellow-rx-10;
|
||||
}
|
||||
}
|
||||
|
||||
.ls-property-key .cp__select-input, .property-select .cp__select-input {
|
||||
@apply text-sm;
|
||||
}
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
"RTC state indicator"
|
||||
(:require [cljs-time.core :as t]
|
||||
[clojure.pprint :as pprint]
|
||||
[frontend.common.missionary :as c.m]
|
||||
[frontend.db :as db]
|
||||
[frontend.handler.db-based.rtc-flows :as rtc-flows]
|
||||
[frontend.state :as state]
|
||||
[frontend.ui :as ui]
|
||||
[frontend.util :as util]
|
||||
[frontend.common.missionary :as c.m]
|
||||
[logseq.shui.ui :as shui]
|
||||
[missionary.core :as m]
|
||||
[rum.core :as rum]))
|
||||
@@ -51,7 +51,7 @@
|
||||
:local-tx (:local-tx state)
|
||||
:remote-tx (:remote-tx state)
|
||||
:rtc-state (if (:rtc-lock state) :open :close)))
|
||||
rtc-flows/rtc-state-flow))
|
||||
rtc-flows/rtc-state-stream-flow))
|
||||
::update-detail-info)]
|
||||
(reset! *update-detail-info-canceler canceler))))
|
||||
(run-task--update-detail-info)
|
||||
|
||||
@@ -3,20 +3,20 @@
|
||||
select-config to add a new use or select-type for this component. To use the
|
||||
new select-type, create an event that calls `select/dialog-select!` with the
|
||||
select-type. See the :graph/open command for a full example."
|
||||
(:require [frontend.modules.shortcut.core :as shortcut]
|
||||
(:require [clojure.string :as string]
|
||||
[frontend.config :as config]
|
||||
[frontend.context.i18n :refer [t]]
|
||||
[frontend.handler.common.developer :as dev-common-handler]
|
||||
[frontend.handler.repo :as repo-handler]
|
||||
[frontend.modules.shortcut.core :as shortcut]
|
||||
[frontend.search :as search]
|
||||
[frontend.state :as state]
|
||||
[frontend.ui :as ui]
|
||||
[logseq.shui.ui :as shui]
|
||||
[frontend.util :as util]
|
||||
[frontend.util.text :as text-util]
|
||||
[rum.core :as rum]
|
||||
[frontend.config :as config]
|
||||
[frontend.handler.repo :as repo-handler]
|
||||
[frontend.handler.common.developer :as dev-common-handler]
|
||||
[logseq.shui.ui :as shui]
|
||||
[reitit.frontend.easy :as rfe]
|
||||
[clojure.string :as string]))
|
||||
[rum.core :as rum]))
|
||||
|
||||
(rum/defc render-item < rum/reactive
|
||||
[result chosen? multiple-choices? *selected-choices]
|
||||
@@ -24,7 +24,9 @@
|
||||
(:value result)) result)
|
||||
header (:header result)
|
||||
selected-choices (rum/react *selected-choices)
|
||||
row [:div.flex.flex-row.justify-between.w-full {:class (when chosen? "chosen")}
|
||||
row [:div.flex.flex-row.justify-between.w-full
|
||||
{:class (when chosen? "chosen")
|
||||
:on-pointer-down util/stop-propagation}
|
||||
[:div.flex.flex-row.items-center.gap-1
|
||||
(when multiple-choices?
|
||||
(ui/checkbox {:checked (boolean (selected-choices (:value result)))
|
||||
|
||||
87
src/main/frontend/components/selection.cljs
Normal file
87
src/main/frontend/components/selection.cljs
Normal file
@@ -0,0 +1,87 @@
|
||||
(ns frontend.components.selection
|
||||
"Block selection"
|
||||
(:require [frontend.config :as config]
|
||||
[frontend.handler.editor :as editor-handler]
|
||||
[frontend.state :as state]
|
||||
[frontend.ui :as ui]
|
||||
[frontend.util :as util]
|
||||
[logseq.shui.ui :as shui]
|
||||
[rum.core :as rum]))
|
||||
|
||||
(rum/defc action-bar
|
||||
[& {:keys [on-cut on-copy selected-blocks hide-dots? button-border?]
|
||||
:or {on-cut #(editor-handler/cut-selection-blocks true)}}]
|
||||
(let [on-copy (if (and selected-blocks (nil? on-copy))
|
||||
#(editor-handler/copy-selection-blocks true {:selected-blocks selected-blocks
|
||||
:page-title-only? true})
|
||||
(or on-copy #(editor-handler/copy-selection-blocks true)))
|
||||
button-opts {:variant :outline
|
||||
:size :sm
|
||||
:class (str "p-2 text-xs h-8"
|
||||
(when-not button-border?
|
||||
" !border-b-0"))}
|
||||
db-graph? (config/db-based-graph?)]
|
||||
[:div.selection-action-bar
|
||||
(shui/button-group
|
||||
;; set tag
|
||||
(when db-graph?
|
||||
(shui/button
|
||||
(assoc button-opts
|
||||
:on-pointer-down (fn [e]
|
||||
(util/stop e)
|
||||
(state/pub-event! [:editor/new-property {:target (.-target e)
|
||||
:selected-blocks selected-blocks
|
||||
:property-key "Tags"
|
||||
:on-dialog-close #(state/pub-event! [:editor/hide-action-bar])}])))
|
||||
(ui/tooltip (ui/icon "hash" {:size 13}) "Set tag"
|
||||
{:trigger-props {:class "flex"}})))
|
||||
(shui/button
|
||||
(assoc button-opts
|
||||
:on-pointer-down (fn [e]
|
||||
(util/stop e)
|
||||
(on-copy)
|
||||
(state/pub-event! [:editor/hide-action-bar])))
|
||||
"Copy")
|
||||
(when db-graph?
|
||||
(shui/button
|
||||
(assoc button-opts
|
||||
:on-pointer-down (fn [e]
|
||||
(util/stop e)
|
||||
(state/pub-event! [:editor/new-property {:target (.-target e)
|
||||
:selected-blocks selected-blocks
|
||||
:on-dialog-close #(state/pub-event! [:editor/hide-action-bar])}])))
|
||||
"Set property"))
|
||||
(when db-graph?
|
||||
(shui/button
|
||||
(assoc button-opts
|
||||
:on-pointer-down (fn [e]
|
||||
(util/stop e)
|
||||
(state/pub-event! [:editor/new-property {:target (.-target e)
|
||||
:selected-blocks selected-blocks
|
||||
:remove-property? true
|
||||
:select-opts {:show-new-when-not-exact-match? false}
|
||||
:on-dialog-close #(state/pub-event! [:editor/hide-action-bar])}])))
|
||||
"Unset property"))
|
||||
(shui/button
|
||||
(assoc button-opts
|
||||
:on-pointer-down (fn [e]
|
||||
(util/stop e)
|
||||
(on-cut)
|
||||
(state/pub-event! [:editor/hide-action-bar])))
|
||||
(ui/icon "trash" {:size 13}))
|
||||
(when-not hide-dots?
|
||||
(shui/button
|
||||
(assoc button-opts
|
||||
:on-pointer-down (fn [e]
|
||||
(util/stop e)
|
||||
(shui/popup-hide!)
|
||||
(shui/popup-show! e
|
||||
(fn [{:keys [id]}]
|
||||
[:div {:on-click #(shui/popup-hide! id)
|
||||
:data-keep-selection true}
|
||||
((state/get-component :selection/context-menu))])
|
||||
{:on-before-hide state/dom-clear-selection!
|
||||
:on-after-hide state/state-clear-selection!
|
||||
:content-props {:class "w-[280px] ls-context-menu-content"}
|
||||
:as-dropdown? true})))
|
||||
(ui/icon "dots" {:size 13}))))]))
|
||||
@@ -13,6 +13,7 @@
|
||||
[frontend.components.property.config :as property-config]
|
||||
[frontend.components.property.value :as pv]
|
||||
[frontend.components.select :as select]
|
||||
[frontend.components.selection :as selection]
|
||||
[frontend.config :as config]
|
||||
[frontend.context.i18n :refer [t]]
|
||||
[frontend.date :as date]
|
||||
@@ -408,18 +409,12 @@
|
||||
[table selected-rows {:keys [on-delete-rows]}]
|
||||
(shui/table-actions
|
||||
{}
|
||||
(shui/button
|
||||
{:variant "ghost"
|
||||
:class "h-8 !pl-4 !px-2 !py-0 hover:text-foreground w-full justify-start"
|
||||
:disabled true}
|
||||
(str (count selected-rows) " selected"))
|
||||
(when (fn? on-delete-rows)
|
||||
(shui/button
|
||||
{:variant "ghost"
|
||||
:class "h-8 !pl-0 !px-2 !py-0 text-muted-foreground hover:text-foreground w-full justify-start"
|
||||
:on-click (fn []
|
||||
(on-delete-rows table selected-rows))}
|
||||
(ui/icon "trash")))))
|
||||
[:div (str (count selected-rows) " selected")]
|
||||
(selection/action-bar
|
||||
{:on-cut #(on-delete-rows table selected-rows)
|
||||
:selected-blocks selected-rows
|
||||
:hide-dots? true
|
||||
:button-border? true})))
|
||||
|
||||
(rum/defc column-resizer
|
||||
[_column on-sized!]
|
||||
@@ -1580,8 +1575,9 @@
|
||||
(search input {:on-change set-input!
|
||||
:set-input! set-input!})
|
||||
|
||||
[:div.text-muted-foreground.text-sm
|
||||
(pv/property-value view-entity (db/entity :logseq.property.view/type) {})]
|
||||
(when db-based?
|
||||
[:div.text-muted-foreground.text-sm
|
||||
(pv/property-value view-entity (db/entity :logseq.property.view/type) {})])
|
||||
|
||||
(when db-based? (more-actions view-entity columns table))
|
||||
|
||||
|
||||
@@ -32,3 +32,13 @@
|
||||
(m/eduction
|
||||
(dedupe)
|
||||
(m/watch *current-login-user)))
|
||||
|
||||
(def document-visibility-state-flow
|
||||
(->> (m/observe
|
||||
(fn ctor [emit!]
|
||||
(let [callback-fn #(emit! js/document.visibilityState)]
|
||||
(.addEventListener ^js js/document "visibilitychange" callback-fn)
|
||||
(callback-fn)
|
||||
(fn dtor [] (.removeEventListener ^js js/document "visibilitychange" callback-fn)))))
|
||||
(m/eduction (dedupe))
|
||||
(m/relieve)))
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
[electron.ipc :as ipc]
|
||||
[electron.listener :as el]
|
||||
[frontend.components.block :as block]
|
||||
[frontend.components.content :as cp-content]
|
||||
[frontend.components.editor :as editor]
|
||||
[frontend.components.page :as page]
|
||||
[frontend.components.reference :as reference]
|
||||
@@ -16,8 +17,8 @@
|
||||
[frontend.error :as error]
|
||||
[frontend.handler.command-palette :as command-palette]
|
||||
[frontend.handler.events :as events]
|
||||
[frontend.handler.file-based.file :as file-handler]
|
||||
[frontend.handler.file-based.events]
|
||||
[frontend.handler.file-based.file :as file-handler]
|
||||
[frontend.handler.global-config :as global-config-handler]
|
||||
[frontend.handler.notification :as notification]
|
||||
[frontend.handler.page :as page-handler]
|
||||
@@ -127,6 +128,7 @@
|
||||
(state/set-component! :block/inline-text block/inline-text)
|
||||
(state/set-component! :block/asset-cp block/asset-cp)
|
||||
(state/set-component! :editor/box editor/box)
|
||||
(state/set-component! :selection/context-menu cp-content/custom-context-menu-content)
|
||||
(command-palette/register-global-shortcut-commands))
|
||||
|
||||
(defn- get-system-info
|
||||
|
||||
@@ -71,13 +71,13 @@
|
||||
result (ui-outliner-tx/transact!
|
||||
{:outliner-op :create-page}
|
||||
(outliner-op/create-page! title' options'))
|
||||
[_page-name page-uuid] (ldb/read-transit-str result)]
|
||||
[_page-name page-uuid] (ldb/read-transit-str result)
|
||||
page (db/get-page (or page-uuid title'))]
|
||||
(when redirect?
|
||||
(route-handler/redirect-to-page! page-uuid)
|
||||
(let [page (db/get-page (or page-uuid title'))]
|
||||
(when-let [first-block (ldb/get-first-child @conn (:db/id page))]
|
||||
(block-handler/edit-block! first-block :max {:container-id :unknown-container}))
|
||||
page)))))))))
|
||||
(when-let [first-block (ldb/get-first-child @conn (:db/id page))]
|
||||
(block-handler/edit-block! first-block :max {:container-id :unknown-container})))
|
||||
page)))))))
|
||||
|
||||
;; favorite fns
|
||||
;; ============
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
[frontend.flows :as flows]
|
||||
[frontend.state :as state]
|
||||
[logseq.common.util :as common-util]
|
||||
[missionary.core :as m]))
|
||||
[missionary.core :as m])
|
||||
(:import [missionary Cancelled]))
|
||||
|
||||
(def rtc-log-flow
|
||||
(m/watch (:rtc/log @state/state)))
|
||||
@@ -25,7 +26,10 @@
|
||||
rtc-log-flow))
|
||||
|
||||
(def rtc-state-flow
|
||||
(m/stream (m/watch (:rtc/state @state/state))))
|
||||
(m/watch (:rtc/state @state/state)))
|
||||
|
||||
(def rtc-state-stream-flow
|
||||
(m/stream rtc-state-flow))
|
||||
|
||||
(def rtc-online-users-flow
|
||||
(c.m/throttle
|
||||
@@ -36,7 +40,7 @@
|
||||
(:rtc-lock m))
|
||||
(:online-users m))))
|
||||
(dedupe)
|
||||
rtc-state-flow)))
|
||||
rtc-state-stream-flow)))
|
||||
|
||||
(def ^:private network-online-change-flow
|
||||
(m/stream
|
||||
@@ -60,7 +64,7 @@ conditions:
|
||||
(->> (m/latest
|
||||
(fn [rtc-state _ login-user]
|
||||
(assoc rtc-state :login-user login-user))
|
||||
(c.m/continue-flow rtc-state-flow)
|
||||
(c.m/continue-flow rtc-state-stream-flow)
|
||||
(c.m/continue-flow network-online-change-flow)
|
||||
flows/current-login-user-flow)
|
||||
(m/eduction
|
||||
@@ -89,6 +93,19 @@ conditions:
|
||||
(assert (some? repo))
|
||||
(reset! *rtc-start-trigger repo))
|
||||
|
||||
(def ^:private document-visible&rtc-not-running-flow
|
||||
(m/ap
|
||||
(let [visibility (m/?< flows/document-visibility-state-flow)]
|
||||
(try
|
||||
(if (= "visible" visibility)
|
||||
(let [rtc-lock (:rtc-lock (m/? (m/reduce {} nil (m/eduction (take 1) rtc-state-flow))))]
|
||||
(if (not rtc-lock)
|
||||
:document-visible&rtc-not-running
|
||||
(m/amb)))
|
||||
(m/amb))
|
||||
(catch Cancelled _
|
||||
(m/amb))))))
|
||||
|
||||
(def trigger-start-rtc-flow
|
||||
(->>
|
||||
[(m/eduction
|
||||
@@ -99,7 +116,10 @@ conditions:
|
||||
flows/current-repo-flow)
|
||||
(m/eduction
|
||||
(keep (fn [repo] (when repo [:trigger-rtc repo])))
|
||||
(m/watch *rtc-start-trigger))]
|
||||
(m/watch *rtc-start-trigger))
|
||||
(m/eduction
|
||||
(map vector)
|
||||
document-visible&rtc-not-running-flow)]
|
||||
(apply c.m/mix)
|
||||
(m/eduction (filter (fn [_] (some? (state/get-auth-id-token)))))
|
||||
(c.m/debounce 200)))
|
||||
|
||||
@@ -924,14 +924,16 @@
|
||||
(block-handler/select-block! block-uuid))
|
||||
|
||||
(defn- compose-copied-blocks-contents
|
||||
[repo block-ids]
|
||||
[repo block-ids & {:as opts}]
|
||||
(let [blocks (db-utils/pull-many repo '[*] (mapv (fn [id] [:block/uuid id]) block-ids))
|
||||
top-level-block-uuids (->> (block-handler/get-top-level-blocks blocks)
|
||||
(map :block/uuid))
|
||||
content (export-text/export-blocks-as-markdown
|
||||
repo top-level-block-uuids
|
||||
{:indent-style (state/get-export-block-text-indent-style)
|
||||
:remove-options (set (state/get-export-block-text-remove-options))})]
|
||||
(merge
|
||||
opts
|
||||
{:indent-style (state/get-export-block-text-indent-style)
|
||||
:remove-options (set (state/get-export-block-text-remove-options))}))]
|
||||
[top-level-block-uuids content]))
|
||||
|
||||
(defn- get-all-blocks-by-ids
|
||||
@@ -947,35 +949,37 @@
|
||||
result)))
|
||||
|
||||
(defn copy-selection-blocks
|
||||
[html?]
|
||||
(when-let [blocks (seq (state/get-selection-blocks))]
|
||||
(let [repo (state/get-current-repo)
|
||||
ids (distinct (keep #(when-let [id (dom/attr % "blockid")]
|
||||
[html? & {:keys [selected-blocks] :as opts}]
|
||||
(let [repo (state/get-current-repo)
|
||||
blocks (seq (state/get-selection-blocks))
|
||||
ids (if blocks
|
||||
(distinct (keep #(when-let [id (dom/attr % "blockid")]
|
||||
(uuid id)) blocks))
|
||||
[top-level-block-uuids content] (compose-copied-blocks-contents repo ids)
|
||||
block (db/entity [:block/uuid (first ids)])
|
||||
db-based? (config/db-based-graph? repo)]
|
||||
(when block
|
||||
(let [html (export-html/export-blocks-as-html repo top-level-block-uuids nil)
|
||||
copied-blocks (cond->> (get-all-blocks-by-ids repo top-level-block-uuids)
|
||||
db-based?
|
||||
(map (fn [block]
|
||||
(let [b (db/entity (:db/id block))]
|
||||
(->
|
||||
(->> (map (fn [[k v]]
|
||||
(let [v' (cond
|
||||
(and (map? v) (:db/id v))
|
||||
[:block/uuid (:block/uuid (db/entity (:db/id v)))]
|
||||
(and (coll? v) (every? #(and (map? %) (:db/id %)) v))
|
||||
(set (map (fn [i] [:block/uuid (:block/uuid (db/entity (:db/id i)))]) v))
|
||||
:else
|
||||
v)]
|
||||
[k v'])) b)
|
||||
(into {}))
|
||||
(assoc :db/id (:db/id b)))))))]
|
||||
(common-handler/copy-to-clipboard-without-id-property! repo (get block :block/format :markdown) content (when html? html) copied-blocks))
|
||||
(state/set-block-op-type! :copy)
|
||||
(notification/show! "Copied!" :success)))))
|
||||
(map :block/uuid selected-blocks))
|
||||
[top-level-block-uuids content] (compose-copied-blocks-contents repo ids opts)
|
||||
block (db/entity [:block/uuid (first ids)])
|
||||
db-based? (config/db-based-graph? repo)]
|
||||
(when block
|
||||
(let [html (export-html/export-blocks-as-html repo top-level-block-uuids nil)
|
||||
copied-blocks (cond->> (get-all-blocks-by-ids repo top-level-block-uuids)
|
||||
db-based?
|
||||
(map (fn [block]
|
||||
(let [b (db/entity (:db/id block))]
|
||||
(->
|
||||
(->> (map (fn [[k v]]
|
||||
(let [v' (cond
|
||||
(and (map? v) (:db/id v))
|
||||
[:block/uuid (:block/uuid (db/entity (:db/id v)))]
|
||||
(and (coll? v) (every? #(and (map? %) (:db/id %)) v))
|
||||
(set (map (fn [i] [:block/uuid (:block/uuid (db/entity (:db/id i)))]) v))
|
||||
:else
|
||||
v)]
|
||||
[k v'])) b)
|
||||
(into {}))
|
||||
(assoc :db/id (:db/id b)))))))]
|
||||
(common-handler/copy-to-clipboard-without-id-property! repo (get block :block/format :markdown) content (when html? html) copied-blocks))
|
||||
(state/set-block-op-type! :copy)
|
||||
(notification/show! "Copied!" :success))))
|
||||
|
||||
(defn copy-block-refs
|
||||
[]
|
||||
@@ -1236,6 +1240,18 @@
|
||||
(state/conj-selection-block! blocks direction)))
|
||||
(state/exit-editing-and-set-selected-blocks! blocks direction))))))))
|
||||
|
||||
(defonce *action-bar-timeout (atom nil))
|
||||
|
||||
(defn show-action-bar!
|
||||
[& {:keys [delay]
|
||||
:or {delay 200}}]
|
||||
(when (config/db-based-graph?)
|
||||
(when-let [timeout @*action-bar-timeout]
|
||||
(js/clearTimeout timeout))
|
||||
(state/pub-event! [:editor/hide-action-bar])
|
||||
(let [timeout (js/setTimeout #(state/pub-event! [:editor/show-action-bar]) delay)]
|
||||
(reset! *action-bar-timeout timeout))))
|
||||
|
||||
(defn- select-block-up-down
|
||||
[direction]
|
||||
(cond
|
||||
@@ -1273,6 +1289,7 @@
|
||||
(when element
|
||||
(util/scroll-to-block element)
|
||||
(state/drop-last-selection-block!))))
|
||||
(show-action-bar! {:delay 500})
|
||||
nil)
|
||||
|
||||
(defn on-select-block
|
||||
@@ -2638,7 +2655,7 @@
|
||||
(keydown-new-line))))))
|
||||
|
||||
(defn- select-first-last
|
||||
"Select first or last block in viewpoint"
|
||||
"Select first or last block in viewport"
|
||||
[direction]
|
||||
(let [f (case direction :up last :down first)
|
||||
container (if (some-> js/document.activeElement
|
||||
@@ -3375,6 +3392,7 @@
|
||||
|
||||
(defn shortcut-up-down [direction]
|
||||
(fn [e]
|
||||
(state/pub-event! [:editor/hide-action-bar])
|
||||
(when (and (not (auto-complete?))
|
||||
(or (in-page-preview?)
|
||||
(not (in-shui-popup?)))
|
||||
@@ -3414,10 +3432,14 @@
|
||||
(cursor/select-up-down input direction anchor cursor-rect)))
|
||||
(select-block-up-down direction))))
|
||||
|
||||
(defn popup-exists?
|
||||
[id]
|
||||
(some->> (shui-popup/get-popups)
|
||||
(some #(some-> % (:id) (str) (string/includes? (str id))))))
|
||||
|
||||
(defn editor-commands-popup-exists?
|
||||
[]
|
||||
(some->> (shui-popup/get-popups)
|
||||
(some #(some-> % (:id) (str) (string/starts-with? ":editor.commands")))))
|
||||
(popup-exists? "editor.commands"))
|
||||
|
||||
(defn open-selected-block!
|
||||
[direction e]
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
[frontend.components.property.dialog :as property-dialog]
|
||||
[frontend.components.repo :as repo]
|
||||
[frontend.components.select :as select]
|
||||
[frontend.components.selection :as selection]
|
||||
[frontend.components.settings :as settings]
|
||||
[frontend.components.shell :as shell]
|
||||
[frontend.components.user.login :as login]
|
||||
@@ -927,11 +928,15 @@
|
||||
(when-let [blocks (and block (db-model/get-block-immediate-children (state/get-current-repo) (:block/uuid block)))]
|
||||
(editor-handler/toggle-blocks-as-own-order-list! blocks)))
|
||||
|
||||
(defn- editor-new-property [block target opts]
|
||||
(defn- editor-new-property [block target {:keys [selected-blocks] :as opts}]
|
||||
(let [editing-block (state/get-edit-block)
|
||||
pos (state/get-edit-pos)
|
||||
edit-block-or-selected (if editing-block
|
||||
edit-block-or-selected (cond
|
||||
editing-block
|
||||
[editing-block]
|
||||
(seq selected-blocks)
|
||||
selected-blocks
|
||||
:else
|
||||
(seq (keep #(db/entity [:block/uuid %]) (state/get-selection-block-ids))))
|
||||
current-block (when-let [s (state/get-current-page)]
|
||||
(when (util/uuid-string? s)
|
||||
@@ -1069,6 +1074,24 @@
|
||||
(defmethod handle :editor/run-query-command [_]
|
||||
(editor-handler/run-query-command!))
|
||||
|
||||
(defmethod handle :editor/show-action-bar []
|
||||
(let [selection (state/get-selection-blocks)
|
||||
first-visible-block (some #(when (util/el-visible-in-viewport? % true) %) selection)]
|
||||
(when first-visible-block
|
||||
(shui/popup-hide! :selection-action-bar)
|
||||
(shui/popup-show!
|
||||
first-visible-block
|
||||
(fn []
|
||||
(selection/action-bar))
|
||||
{:id :selection-action-bar
|
||||
:content-props {:side "top"
|
||||
:class "!py-0 !px-0 !border-none"}
|
||||
:auto-side? false
|
||||
:align :start}))))
|
||||
|
||||
(defmethod handle :editor/hide-action-bar []
|
||||
(shui/popup-hide! :selection-action-bar))
|
||||
|
||||
(defn run!
|
||||
[]
|
||||
(let [chan (state/get-events-chan)]
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
(:refer-clojure :exclude [map filter mapcat concat remove])
|
||||
(:require [cljs.core.match :refer [match]]
|
||||
[clojure.string :as string]
|
||||
[frontend.common.file.core :as common-file]
|
||||
[frontend.db :as db]
|
||||
[frontend.format.mldoc :as mldoc]
|
||||
[frontend.modules.file.core :as outliner-file]
|
||||
@@ -12,11 +13,10 @@
|
||||
[frontend.persist-db.browser :as db-browser]
|
||||
[frontend.state :as state]
|
||||
[frontend.util :as util :refer [concatv mapcatv removev]]
|
||||
[frontend.common.file.core :as common-file]
|
||||
[logseq.db :as ldb]
|
||||
[malli.core :as m]
|
||||
[malli.util :as mu]
|
||||
[promesa.core :as p]
|
||||
[logseq.db :as ldb]))
|
||||
[promesa.core :as p]))
|
||||
|
||||
;;; TODO: split frontend.handler.export.text related states
|
||||
(def ^:dynamic *state*
|
||||
@@ -64,8 +64,14 @@
|
||||
(outliner-file/tree->file-content {:init-level init-level})))
|
||||
|
||||
(defn root-block-uuids->content
|
||||
[repo root-block-uuids]
|
||||
(let [contents (mapv #(get-blocks-contents repo %) root-block-uuids)]
|
||||
[repo root-block-uuids & {:keys [page-title-only?]}]
|
||||
(let [contents (mapv (fn [id]
|
||||
(if-let [page (and page-title-only?
|
||||
(let [e (db/entity [:block/uuid id])]
|
||||
(when (:block/name e)
|
||||
e)))]
|
||||
(:block/title page)
|
||||
(get-blocks-contents repo id))) root-block-uuids)]
|
||||
(string/join "\n" (mapv string/trim-newline contents))))
|
||||
|
||||
(declare remove-block-ast-pos Properties-block-ast?)
|
||||
|
||||
@@ -506,24 +506,25 @@
|
||||
"options:
|
||||
:indent-style \"dashes\" | \"spaces\" | \"no-indent\"
|
||||
:remove-options [:emphasis :page-ref :tag :property]
|
||||
:other-options {:keep-only-level<=N int :newline-after-block bool}"
|
||||
:other-options {:keep-only-level<=N int :newline-after-block bool}
|
||||
:page-title-only? boolean"
|
||||
[repo root-block-uuids-or-page-uuid options]
|
||||
{:pre [(or (coll? root-block-uuids-or-page-uuid)
|
||||
(uuid? root-block-uuids-or-page-uuid))]}
|
||||
(util/profile
|
||||
:export-blocks-as-markdown
|
||||
(try
|
||||
(let [content
|
||||
(if (uuid? root-block-uuids-or-page-uuid)
|
||||
:export-blocks-as-markdown
|
||||
(try
|
||||
(let [content
|
||||
(if (uuid? root-block-uuids-or-page-uuid)
|
||||
;; page
|
||||
(common/get-page-content root-block-uuids-or-page-uuid)
|
||||
(common/root-block-uuids->content repo root-block-uuids-or-page-uuid))
|
||||
first-block (and (coll? root-block-uuids-or-page-uuid)
|
||||
(db/entity [:block/uuid (first root-block-uuids-or-page-uuid)]))
|
||||
format (get first-block :block/format :markdown)]
|
||||
(export-helper content format options))
|
||||
(catch :default e
|
||||
(js/console.error e)))))
|
||||
(common/get-page-content root-block-uuids-or-page-uuid)
|
||||
(common/root-block-uuids->content repo root-block-uuids-or-page-uuid options))
|
||||
first-block (and (coll? root-block-uuids-or-page-uuid)
|
||||
(db/entity [:block/uuid (first root-block-uuids-or-page-uuid)]))
|
||||
format (get first-block :block/format :markdown)]
|
||||
(export-helper content format options))
|
||||
(catch :default e
|
||||
(js/console.error e)))))
|
||||
|
||||
(defn export-files-as-markdown
|
||||
"options see also `export-blocks-as-markdown`"
|
||||
@@ -531,7 +532,7 @@
|
||||
(mapv
|
||||
(fn [{:keys [path title content]}]
|
||||
(util/profile (print-str :export-files-as-markdown title)
|
||||
[(or path title) (export-helper content :markdown options)]))
|
||||
[(or path title) (export-helper content :markdown options)]))
|
||||
files))
|
||||
|
||||
(defn export-repo-as-markdown!
|
||||
|
||||
@@ -177,10 +177,23 @@
|
||||
(state/toggle-wide-mode!))
|
||||
|
||||
;; auto-complete
|
||||
(defn- reorder-matched
|
||||
"Reorder matched if grouped"
|
||||
[state]
|
||||
(let [[matched {:keys [grouped?]}] (:rum/args state)]
|
||||
(if grouped?
|
||||
(let [*idx (atom -1)
|
||||
inc-idx #(swap! *idx inc)]
|
||||
(->>
|
||||
(for [[_group matched] (group-by :group matched)]
|
||||
(doall (map (fn [item] (inc-idx) item) matched)))
|
||||
(apply concat)))
|
||||
matched)))
|
||||
|
||||
(defn auto-complete-prev
|
||||
[state e]
|
||||
(let [current-idx (get state :frontend.ui/current-idx)
|
||||
matched (first (:rum/args state))]
|
||||
matched (reorder-matched state)]
|
||||
(util/stop e)
|
||||
(cond
|
||||
(>= @current-idx 1)
|
||||
@@ -197,7 +210,7 @@
|
||||
(defn auto-complete-next
|
||||
[state e]
|
||||
(let [current-idx (get state :frontend.ui/current-idx)
|
||||
matched (first (:rum/args state))]
|
||||
matched (reorder-matched state)]
|
||||
(util/stop e)
|
||||
(let [total (count matched)]
|
||||
(if (>= @current-idx (dec total))
|
||||
@@ -211,7 +224,8 @@
|
||||
|
||||
(defn auto-complete-complete
|
||||
[state e]
|
||||
(let [[matched {:keys [on-chosen on-enter]}] (:rum/args state)
|
||||
(let [[_matched {:keys [on-chosen on-enter]}] (:rum/args state)
|
||||
matched (reorder-matched state)
|
||||
current-idx (get state :frontend.ui/current-idx)]
|
||||
(util/stop e)
|
||||
(if (and (seq matched)
|
||||
@@ -222,7 +236,8 @@
|
||||
|
||||
(defn auto-complete-shift-complete
|
||||
[state e]
|
||||
(let [[matched {:keys [on-chosen on-shift-chosen on-enter]}] (:rum/args state)
|
||||
(let [[_matched {:keys [on-chosen on-shift-chosen on-enter]}] (:rum/args state)
|
||||
matched (reorder-matched state)
|
||||
current-idx (get state :frontend.ui/current-idx)]
|
||||
(util/stop e)
|
||||
(if (and (seq matched)
|
||||
|
||||
@@ -260,6 +260,7 @@
|
||||
:command-palette/commands (atom [])
|
||||
|
||||
:view/components {}
|
||||
:view/selected-blocks nil
|
||||
|
||||
:srs/mode? false
|
||||
|
||||
@@ -1187,8 +1188,21 @@ Similar to re-frame subscriptions"
|
||||
(doseq [node nodes]
|
||||
(dom/add-class! node "selected")))
|
||||
|
||||
(defn get-events-chan
|
||||
[]
|
||||
(:system/events @state))
|
||||
|
||||
(defn pub-event!
|
||||
{:malli/schema [:=> [:cat vector?] :any]}
|
||||
[payload]
|
||||
(let [d (p/deferred)
|
||||
chan (get-events-chan)]
|
||||
(async/put! chan [payload d])
|
||||
d))
|
||||
|
||||
(defn- set-selection-blocks-aux!
|
||||
[blocks]
|
||||
(set-state! :view/selected-blocks nil)
|
||||
(let [selected-ids (set (get-selected-block-ids @(:selection/blocks @state)))
|
||||
_ (set-state! :selection/blocks blocks)
|
||||
new-ids (set (get-selection-block-ids))
|
||||
@@ -1212,7 +1226,8 @@ Similar to re-frame subscriptions"
|
||||
(set-state! :selection/blocks nil)
|
||||
(set-state! :selection/direction nil)
|
||||
(set-state! :selection/start-block nil)
|
||||
(set-state! :selection/selected-all? false))
|
||||
(set-state! :selection/selected-all? false)
|
||||
(pub-event! [:editor/hide-action-bar]))
|
||||
|
||||
(defn clear-selection!
|
||||
[]
|
||||
@@ -1368,7 +1383,8 @@ Similar to re-frame subscriptions"
|
||||
(set-state! :editor/content {})
|
||||
(set-state! :ui/select-query-cache {})
|
||||
(set-state! :editor/block-refs #{})
|
||||
(set-state! :editor/action-data nil))
|
||||
(set-state! :editor/action-data nil)
|
||||
(set-state! :view/selected-blocks nil))
|
||||
|
||||
(defn into-code-editor-mode!
|
||||
[]
|
||||
@@ -1831,18 +1847,6 @@ Similar to re-frame subscriptions"
|
||||
([] (open-settings! true))
|
||||
([active-tab] (set-state! :ui/settings-open? active-tab)))
|
||||
|
||||
(defn get-events-chan
|
||||
[]
|
||||
(:system/events @state))
|
||||
|
||||
(defn pub-event!
|
||||
{:malli/schema [:=> [:cat vector?] :any]}
|
||||
[payload]
|
||||
(let [d (p/deferred)
|
||||
chan (get-events-chan)]
|
||||
(async/put! chan [payload d])
|
||||
d))
|
||||
|
||||
(defn sidebar-add-block!
|
||||
[repo db-id block-type]
|
||||
(when (not (util/sm-breakpoint?))
|
||||
|
||||
@@ -517,20 +517,20 @@
|
||||
(let [*current-idx (get state ::current-idx)
|
||||
*groups (atom #{})
|
||||
render-f (fn [matched]
|
||||
(for [[idx item] (medley/indexed matched)]
|
||||
(for [[idx item] matched]
|
||||
(let [react-key (str idx)
|
||||
item-cp
|
||||
[:div.menu-link-wrap
|
||||
{:key react-key
|
||||
;; mouse-move event to indicate that cursor moved by user
|
||||
;; mouse-move event to indicate that cursor moved by user
|
||||
:on-mouse-move #(reset! *current-idx idx)}
|
||||
(let [chosen? (= @*current-idx idx)]
|
||||
(menu-link
|
||||
{:id (str "ac-" react-key)
|
||||
:tab-index "0"
|
||||
:class (when chosen? "chosen")
|
||||
;; TODO: should have more tests on touch devices
|
||||
;:on-pointer-down #(util/stop %)
|
||||
;; TODO: should have more tests on touch devices
|
||||
;:on-pointer-down #(util/stop %)
|
||||
:on-click (fn [e]
|
||||
(util/stop e)
|
||||
(when-not (:disabled? item)
|
||||
@@ -552,13 +552,16 @@
|
||||
[:div#ui__ac-inner.hide-scrollbar
|
||||
(when header header)
|
||||
(if grouped?
|
||||
(for [[group matched] (group-by :group matched)]
|
||||
(if group
|
||||
[:div
|
||||
[:div.ui__ac-group-name group]
|
||||
(render-f matched)]
|
||||
(render-f matched)))
|
||||
(render-f matched))]
|
||||
(let [*idx (atom -1)
|
||||
inc-idx #(swap! *idx inc)]
|
||||
(for [[group matched] (group-by :group matched)]
|
||||
(let [matched' (doall (map (fn [item] [(inc-idx) item]) matched))]
|
||||
(if group
|
||||
[:div
|
||||
[:div.ui__ac-group-name group]
|
||||
(render-f matched')]
|
||||
(render-f matched')))))
|
||||
(render-f (medley/indexed matched)))]
|
||||
(when empty-placeholder
|
||||
empty-placeholder))]))
|
||||
|
||||
@@ -1016,13 +1019,11 @@
|
||||
:small? true)]]))
|
||||
|
||||
(rum/defc tooltip
|
||||
[trigger tooltip-content]
|
||||
[trigger tooltip-content & {:keys [trigger-props]}]
|
||||
(shui/tooltip-provider
|
||||
(shui/tooltip
|
||||
(shui/tooltip-trigger
|
||||
trigger)
|
||||
(shui/tooltip-content
|
||||
tooltip-content))))
|
||||
(shui/tooltip-trigger trigger-props trigger)
|
||||
(shui/tooltip-content tooltip-content))))
|
||||
|
||||
(rum/defc DelDateButton
|
||||
[on-delete]
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
[frontend.worker.db.migrate :as db-migrate]
|
||||
[frontend.worker.db.validate :as worker-db-validate]
|
||||
[frontend.worker.device :as worker-device]
|
||||
[frontend.worker.embedding]
|
||||
[frontend.worker.export :as worker-export]
|
||||
[frontend.worker.file :as file]
|
||||
[frontend.worker.handler.page :as worker-page]
|
||||
@@ -42,8 +43,7 @@
|
||||
[logseq.outliner.op :as outliner-op]
|
||||
[me.tonsky.persistent-sorted-set :as set :refer [BTSet]]
|
||||
[promesa.core :as p]
|
||||
[shadow.cljs.modern :refer [defclass]]
|
||||
[frontend.worker.embedding]))
|
||||
[shadow.cljs.modern :refer [defclass]]))
|
||||
|
||||
(defonce *sqlite worker-state/*sqlite)
|
||||
(defonce *sqlite-conns worker-state/*sqlite-conns)
|
||||
@@ -336,6 +336,8 @@
|
||||
(= "db" (:kv/value (d/entity @conn :logseq.kv/db-type)))))]
|
||||
(swap! *datascript-conns assoc repo conn)
|
||||
(swap! *client-ops-conns assoc repo client-ops-conn)
|
||||
(when (not= client-op/schema-in-db (d/schema @client-ops-conn))
|
||||
(d/reset-schema! client-ops-conn client-op/schema-in-db))
|
||||
(when (and db-based? (not initial-data-exists?) (not datoms))
|
||||
(let [config (or config "")
|
||||
initial-data (sqlite-create-graph/build-db-initial-data config
|
||||
@@ -438,7 +440,6 @@
|
||||
(let [result (when-not (= result @worker-state/*state) result)]
|
||||
(ldb/write-transit-str result)))))
|
||||
|
||||
|
||||
#_{:clj-kondo/ignore [:unresolved-symbol]}
|
||||
(defclass DBWorker
|
||||
(extends js/Object)
|
||||
@@ -594,7 +595,7 @@
|
||||
;; (prn :debug :transact :tx-data tx-data' :tx-meta tx-meta')
|
||||
|
||||
(worker-util/profile "Worker db transact"
|
||||
(ldb/transact! conn tx-data' tx-meta')))
|
||||
(ldb/transact! conn tx-data' tx-meta')))
|
||||
nil)
|
||||
(catch :default e
|
||||
(prn :debug :error)
|
||||
@@ -705,11 +706,11 @@
|
||||
(when-let [conn (worker-state/get-datascript-conn repo)]
|
||||
(try
|
||||
(worker-util/profile
|
||||
"apply outliner ops"
|
||||
(let [ops (ldb/read-transit-str ops-str)
|
||||
opts (ldb/read-transit-str opts-str)
|
||||
result (outliner-op/apply-ops! repo conn ops (worker-state/get-date-formatter repo) opts)]
|
||||
(ldb/write-transit-str result)))
|
||||
"apply outliner ops"
|
||||
(let [ops (ldb/read-transit-str ops-str)
|
||||
opts (ldb/read-transit-str opts-str)
|
||||
result (outliner-op/apply-ops! repo conn ops (worker-state/get-date-formatter repo) opts)]
|
||||
(ldb/write-transit-str result)))
|
||||
(catch :default e
|
||||
(let [data (ex-data e)
|
||||
{:keys [type payload]} (when (map? data) data)]
|
||||
@@ -973,7 +974,7 @@
|
||||
(glogi-console/install!)
|
||||
(check-worker-scope!)
|
||||
(let [^js obj #_{:clj-kondo/ignore [:unresolved-symbol]}
|
||||
(DBWorker.)]
|
||||
(DBWorker.)]
|
||||
(outliner-register-op-handlers!)
|
||||
(worker-state/set-worker-object! obj)
|
||||
(<ratelimit-file-writes!)
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
[frontend.worker.rtc.skeleton :as r.skeleton]
|
||||
[frontend.worker.rtc.ws :as ws]
|
||||
[frontend.worker.rtc.ws-util :as ws-util]
|
||||
[logseq.db :as ldb]
|
||||
[logseq.db.frontend.schema :as db-schema]
|
||||
[missionary.core :as m]))
|
||||
|
||||
@@ -36,7 +37,8 @@
|
||||
"Return a task: get or create a mws(missionary wrapped websocket).
|
||||
see also `ws/get-mws-create`.
|
||||
But ensure `register-graph-updates` and `calibrate-graph-skeleton` has been sent"
|
||||
[get-ws-create-task graph-uuid major-schema-version repo conn *last-calibrate-t *online-users add-log-fn]
|
||||
[get-ws-create-task graph-uuid major-schema-version repo conn
|
||||
*last-calibrate-t *online-users *server-schema-version add-log-fn]
|
||||
(assert (some? graph-uuid))
|
||||
(let [*sent (atom {}) ;; ws->bool
|
||||
]
|
||||
@@ -75,8 +77,10 @@
|
||||
(let [t (client-op/get-local-tx repo)]
|
||||
(when (or (nil? @*last-calibrate-t)
|
||||
(< 500 (- t @*last-calibrate-t)))
|
||||
;; (m/? (r.skeleton/new-task--calibrate-graph-skeleton get-ws-create-task graph-uuid conn t))
|
||||
(m/? (r.skeleton/new-task--calibrate-graph-skeleton get-ws-create-task graph-uuid major-schema-version @conn))
|
||||
(let [{:keys [server-schema-version _server-builtin-db-idents]}
|
||||
(m/? (r.skeleton/new-task--calibrate-graph-skeleton
|
||||
get-ws-create-task graph-uuid major-schema-version @conn))]
|
||||
(reset! *server-schema-version server-schema-version))
|
||||
(reset! *last-calibrate-t t)))
|
||||
(swap! *sent assoc ws true))
|
||||
ws))))
|
||||
@@ -142,16 +146,14 @@
|
||||
(defn- schema-av-coll->update-schema-op
|
||||
[db block-uuid db-ident schema-av-coll]
|
||||
(when (and (seq schema-av-coll) db-ident)
|
||||
(let [db-ident-ns (namespace db-ident)]
|
||||
(when (and (string/ends-with? db-ident-ns ".property")
|
||||
(not= db-ident-ns "logseq.property"))
|
||||
(when-let [ent (d/entity db db-ident)]
|
||||
[:update-schema
|
||||
(cond-> {:block-uuid block-uuid
|
||||
:db/ident db-ident
|
||||
:db/valueType (or (:db/valueType ent) :db.type/string)}
|
||||
(:db/cardinality ent) (assoc :db/cardinality (:db/cardinality ent))
|
||||
(:db/index ent) (assoc :db/index (:db/index ent)))])))))
|
||||
(when-let [ent (d/entity db db-ident)]
|
||||
(when (ldb/property? ent)
|
||||
[:update-schema
|
||||
(cond-> {:block-uuid block-uuid
|
||||
:db/ident db-ident
|
||||
:db/valueType (or (:db/valueType ent) :db.type/string)}
|
||||
(:db/cardinality ent) (assoc :db/cardinality (:db/cardinality ent))
|
||||
(:db/index ent) (assoc :db/index (:db/index ent)))]))))
|
||||
|
||||
(defn- av-coll->card-one-attrs
|
||||
[db-schema av-coll]
|
||||
@@ -268,6 +270,30 @@
|
||||
(:remote-ops (local-block-ops->remote-ops db block-ops-map))]))
|
||||
block-ops-map-coll))
|
||||
|
||||
(defmulti ^:private local-db-ident-kv-ops->remote-ops-aux (fn [op-type & _] op-type))
|
||||
(defmethod local-db-ident-kv-ops->remote-ops-aux :update-kv-value
|
||||
[_ op]
|
||||
(let [op-value (last op)
|
||||
db-ident (:db-ident op-value)
|
||||
value (:value op-value)]
|
||||
[:update-kv-value {:db-ident db-ident :value (ldb/write-transit-str value)}]))
|
||||
|
||||
(defmethod local-db-ident-kv-ops->remote-ops-aux :db-ident
|
||||
[_ _op]
|
||||
;; ignore
|
||||
)
|
||||
|
||||
(defn- local-db-ident-kv-ops->remote-ops
|
||||
[db-ident-kv-ops-map]
|
||||
(keep
|
||||
(fn [[op-type op]]
|
||||
(local-db-ident-kv-ops->remote-ops-aux op-type op))
|
||||
db-ident-kv-ops-map))
|
||||
|
||||
(defn- gen-db-ident-kv-remote-ops
|
||||
[db-ident-kv-ops-map-coll]
|
||||
(mapcat local-db-ident-kv-ops->remote-ops db-ident-kv-ops-map-coll))
|
||||
|
||||
(defn- merge-remove-remove-ops
|
||||
[remote-remove-ops]
|
||||
(when-let [block-uuids (->> remote-remove-ops
|
||||
@@ -326,61 +352,73 @@
|
||||
(concat update-schema-ops update-page-ops remove-ops sorted-move-ops update-ops remove-page-ops)))
|
||||
|
||||
(defn- rollback
|
||||
[repo block-ops-map-coll]
|
||||
(let [ops (mapcat
|
||||
(fn [m]
|
||||
(keep (fn [[k op]]
|
||||
(when (not= :block/uuid k)
|
||||
op))
|
||||
m))
|
||||
block-ops-map-coll)]
|
||||
(client-op/add-ops repo ops)
|
||||
[repo block-ops-map-coll db-ident-kv-ops-map-coll]
|
||||
(let [block-ops
|
||||
(mapcat
|
||||
(fn [m]
|
||||
(keep (fn [[k op]]
|
||||
(when-not (keyword-identical? :block/uuid k)
|
||||
op))
|
||||
m))
|
||||
block-ops-map-coll)
|
||||
db-ident-kv-ops
|
||||
(mapcat
|
||||
(fn [m]
|
||||
(keep (fn [[k op]]
|
||||
(when-not (keyword-identical? :db-ident k)
|
||||
op))
|
||||
m))
|
||||
db-ident-kv-ops-map-coll)]
|
||||
(client-op/add-ops! repo block-ops)
|
||||
(client-op/add-ops! repo db-ident-kv-ops)
|
||||
nil))
|
||||
|
||||
(defn new-task--push-local-ops
|
||||
"Return a task: push local updates"
|
||||
[repo conn graph-uuid major-schema-version date-formatter get-ws-create-task *remote-profile? add-log-fn]
|
||||
(m/sp
|
||||
(let [block-ops-map-coll (client-op/get&remove-all-block-ops repo)]
|
||||
(when-let [block-uuid->remote-ops (not-empty (gen-block-uuid->remote-ops @conn block-ops-map-coll))]
|
||||
(when-let [ops-for-remote (rtc-schema/to-ws-ops-decoder
|
||||
(sort-remote-ops
|
||||
block-uuid->remote-ops))]
|
||||
(let [local-tx (client-op/get-local-tx repo)
|
||||
r (try
|
||||
(m/? (ws-util/send&recv get-ws-create-task
|
||||
(cond-> {:action "apply-ops"
|
||||
:graph-uuid graph-uuid :schema-version (str major-schema-version)
|
||||
:ops ops-for-remote :t-before (or local-tx 1)}
|
||||
(true? @*remote-profile?) (assoc :profile true))))
|
||||
(catch :default e
|
||||
(rollback repo block-ops-map-coll)
|
||||
(throw e)))]
|
||||
(if-let [remote-ex (:ex-data r)]
|
||||
(do (add-log-fn :rtc.log/push-local-update remote-ex)
|
||||
(case (:type remote-ex)
|
||||
;; - :graph-lock-failed
|
||||
;; conflict-update remote-graph, keep these local-pending-ops
|
||||
;; and try to send ops later
|
||||
:graph-lock-failed
|
||||
(rollback repo block-ops-map-coll)
|
||||
;; - :graph-lock-missing
|
||||
;; this case means something wrong in remote-graph data,
|
||||
;; nothing to do at client-side
|
||||
:graph-lock-missing
|
||||
(do (rollback repo block-ops-map-coll)
|
||||
(throw r.ex/ex-remote-graph-lock-missing))
|
||||
(let [block-ops-map-coll (client-op/get&remove-all-block-ops repo)
|
||||
db-ident-kv-ops-map-coll (client-op/get&remove-all-db-ident-kv-ops repo)
|
||||
block-uuid->remote-ops (not-empty (gen-block-uuid->remote-ops @conn block-ops-map-coll))
|
||||
other-remote-ops (gen-db-ident-kv-remote-ops db-ident-kv-ops-map-coll)
|
||||
remote-ops (concat (when block-uuid->remote-ops (sort-remote-ops block-uuid->remote-ops))
|
||||
other-remote-ops)]
|
||||
(when-let [ops-for-remote (rtc-schema/to-ws-ops-decoder remote-ops)]
|
||||
(let [local-tx (client-op/get-local-tx repo)
|
||||
r (try
|
||||
(m/? (ws-util/send&recv get-ws-create-task
|
||||
(cond-> {:action "apply-ops"
|
||||
:graph-uuid graph-uuid :schema-version (str major-schema-version)
|
||||
:ops ops-for-remote :t-before (or local-tx 1)}
|
||||
(true? @*remote-profile?) (assoc :profile true))))
|
||||
(catch :default e
|
||||
(rollback repo block-ops-map-coll db-ident-kv-ops-map-coll)
|
||||
(throw e)))]
|
||||
(if-let [remote-ex (:ex-data r)]
|
||||
(do (add-log-fn :rtc.log/push-local-update remote-ex)
|
||||
(case (:type remote-ex)
|
||||
;; - :graph-lock-failed
|
||||
;; conflict-update remote-graph, keep these local-pending-ops
|
||||
;; and try to send ops later
|
||||
:graph-lock-failed
|
||||
(rollback repo block-ops-map-coll db-ident-kv-ops-map-coll)
|
||||
;; - :graph-lock-missing
|
||||
;; this case means something wrong in remote-graph data,
|
||||
;; nothing to do at client-side
|
||||
:graph-lock-missing
|
||||
(do (rollback repo block-ops-map-coll db-ident-kv-ops-map-coll)
|
||||
(throw r.ex/ex-remote-graph-lock-missing))
|
||||
|
||||
:rtc.exception/get-s3-object-failed
|
||||
(rollback repo block-ops-map-coll)
|
||||
;; else
|
||||
(do (rollback repo block-ops-map-coll)
|
||||
(throw (ex-info "Unavailable1" {:remote-ex remote-ex})))))
|
||||
:rtc.exception/get-s3-object-failed
|
||||
(rollback repo block-ops-map-coll db-ident-kv-ops-map-coll)
|
||||
;; else
|
||||
(do (rollback repo block-ops-map-coll db-ident-kv-ops-map-coll)
|
||||
(throw (ex-info "Unavailable1" {:remote-ex remote-ex})))))
|
||||
|
||||
(do (assert (pos? (:t r)) r)
|
||||
(r.remote-update/apply-remote-update
|
||||
graph-uuid repo conn date-formatter {:type :remote-update :value r} add-log-fn)
|
||||
(add-log-fn :rtc.log/push-local-update {:remote-t (:t r)})))))))))
|
||||
(do (assert (pos? (:t r)) r)
|
||||
(r.remote-update/apply-remote-update
|
||||
graph-uuid repo conn date-formatter {:type :remote-update :value r} add-log-fn)
|
||||
(add-log-fn :rtc.log/push-local-update {:remote-t (:t r)}))))))))
|
||||
|
||||
(defn new-task--pull-remote-data
|
||||
[repo conn graph-uuid major-schema-version date-formatter get-ws-create-task add-log-fn]
|
||||
|
||||
@@ -12,6 +12,14 @@
|
||||
|
||||
(def op-schema
|
||||
[:multi {:dispatch first}
|
||||
[:update-kv-value
|
||||
;; update :logseq.kv/xxx entities
|
||||
[:catn
|
||||
[:op :keyword]
|
||||
[:t :int]
|
||||
[:value [:map
|
||||
[:db-ident :keyword]
|
||||
[:value :any]]]]]
|
||||
[:move
|
||||
[:catn
|
||||
[:op :keyword]
|
||||
@@ -60,15 +68,17 @@
|
||||
(def ops-schema [:sequential op-schema])
|
||||
(def ops-coercer (ma/coercer ops-schema mt/json-transformer nil
|
||||
#(do (log/error ::bad-ops (:value %))
|
||||
(ma/-fail! ::ops-schema %))))
|
||||
(ma/-fail! ::ops-schema (select-keys % [:value])))))
|
||||
|
||||
(def ^:private block-op-types #{:move :remove :update-page :remove-page :update})
|
||||
(def ^:private asset-op-types #{:update-asset :remove-asset})
|
||||
(def ^:private db-ident-kv-op-types #{:update-kv-value})
|
||||
|
||||
(def schema-in-db
|
||||
"TODO: rename this db-name from client-op to client-metadata+op.
|
||||
and move it to its own namespace."
|
||||
{:block/uuid {:db/unique :db.unique/identity}
|
||||
:db-ident {:db/unique :db.unique/identity}
|
||||
:local-tx {:db/index true}
|
||||
:graph-uuid {:db/index true}
|
||||
:aes-key-jwk {:db/index true}
|
||||
@@ -190,13 +200,66 @@
|
||||
(cons [:db/add tmpid :block/uuid block-uuid] tx-data))))
|
||||
block-uuid->op-type->op)))
|
||||
|
||||
(defn add-ops
|
||||
(defn- generate-ident-kv-ops-tx-data
|
||||
[client-ops-db ops]
|
||||
(let [sorted-ops (sort-by second ops)
|
||||
db-idents (map (fn [[_op-type _t value]] (:db-ident value)) sorted-ops)
|
||||
ents (d/pull-many client-ops-db '[*] (map (fn [db-ident] [:db-ident db-ident]) db-idents))
|
||||
op-types [:update-kv-value]
|
||||
init-db-ident->op-type->op
|
||||
(into {}
|
||||
(map (fn [ent]
|
||||
[(:db-ident ent)
|
||||
(into {}
|
||||
(keep
|
||||
(fn [op-type]
|
||||
(when-let [op (get ent op-type)]
|
||||
[op-type op])))
|
||||
op-types)]))
|
||||
ents)
|
||||
db-ident->op-type->op
|
||||
(reduce
|
||||
(fn [r op]
|
||||
(let [[op-type _t value] op
|
||||
db-ident (:db-ident value)]
|
||||
(case op-type
|
||||
:update-kv-value
|
||||
(assoc-in r [db-ident :update-kv-value] op))))
|
||||
init-db-ident->op-type->op sorted-ops)]
|
||||
(mapcat
|
||||
(fn [[db-ident op-type->op]]
|
||||
(let [tmpid (str db-ident)]
|
||||
(when-let [tx-data (not-empty
|
||||
(keep
|
||||
(fn [[op-type op]]
|
||||
(when op [:db/add tmpid op-type op]))
|
||||
op-type->op))]
|
||||
(cons [:db/add tmpid :db-ident db-ident] tx-data))))
|
||||
db-ident->op-type->op)))
|
||||
|
||||
(defn- partition-ops
|
||||
"Return [db-ident-kv-ops block-ops]"
|
||||
[ops]
|
||||
((juxt :db-ident :block-uuid)
|
||||
(group-by
|
||||
(fn [[_op-type _t value :as op]]
|
||||
(cond
|
||||
(:block-uuid value) :block-uuid
|
||||
(:db-ident value) :db-ident
|
||||
:else (throw (ex-info "invalid op" {:op op}))))
|
||||
ops)))
|
||||
|
||||
(defn add-ops!
|
||||
[repo ops]
|
||||
(let [conn (worker-state/get-client-ops-conn repo)
|
||||
ops (ops-coercer ops)]
|
||||
(assert (some? conn) repo)
|
||||
(when-let [tx-data (not-empty (generate-block-ops-tx-data @conn ops))]
|
||||
(d/transact! conn tx-data))))
|
||||
(when (seq ops)
|
||||
(let [conn (worker-state/get-client-ops-conn repo)
|
||||
ops (ops-coercer ops)
|
||||
_ (assert (some? conn) repo)
|
||||
[db-ident-kv-ops block-ops] (partition-ops ops)
|
||||
tx-data1 (when (seq block-ops) (generate-block-ops-tx-data @conn block-ops))
|
||||
tx-data2 (when (seq db-ident-kv-ops) (generate-ident-kv-ops-tx-data @conn db-ident-kv-ops))]
|
||||
(when-let [tx-data (not-empty (concat tx-data1 tx-data2))]
|
||||
(d/transact! conn tx-data)))))
|
||||
|
||||
(defn- get-all-block-ops*
|
||||
"Return e->op-map"
|
||||
@@ -207,7 +270,8 @@
|
||||
(let [op-map (into {}
|
||||
(keep (fn [datom]
|
||||
(let [a (:a datom)]
|
||||
(when (or (= :block/uuid a) (contains? block-op-types a))
|
||||
(when (or (keyword-identical? :block/uuid a)
|
||||
(contains? block-op-types a))
|
||||
[a (:v datom)]))))
|
||||
datoms)]
|
||||
(when (and (:block/uuid op-map)
|
||||
@@ -216,7 +280,26 @@
|
||||
[e op-map]))))
|
||||
(into {})))
|
||||
|
||||
(defn get&remove-all-block-ops*
|
||||
(defn- get-all-db-ident-kv-ops*
|
||||
"Return e->op-map"
|
||||
[db]
|
||||
(let [db-ident-datoms (d/datoms db :avet :db-ident)
|
||||
es (map :e db-ident-datoms)]
|
||||
(->> (map (fn [e] [e (d/datoms db :eavt e)]) es)
|
||||
(keep (fn [[e datoms]]
|
||||
(let [op-map (into {}
|
||||
(keep (fn [datom]
|
||||
(let [a (:a datom)]
|
||||
(when (or (keyword-identical? :db-ident a)
|
||||
(contains? db-ident-kv-op-types a))
|
||||
[a (:v datom)]))))
|
||||
datoms)]
|
||||
(when (and (:db-ident op-map)
|
||||
(> (count op-map) 1))
|
||||
[e op-map]))))
|
||||
(into {}))))
|
||||
|
||||
(defn- get&remove-all-block-ops*
|
||||
[conn]
|
||||
(let [e->op-map (get-all-block-ops* @conn)
|
||||
retract-all-tx-data (mapcat (fn [e] (map (fn [a] [:db.fn/retractAttribute e a]) block-op-types))
|
||||
@@ -224,6 +307,14 @@
|
||||
(d/transact! conn retract-all-tx-data)
|
||||
(vals e->op-map)))
|
||||
|
||||
(defn- get&remove-all-db-ident-kv-ops*
|
||||
[conn]
|
||||
(let [e->op-map (get-all-db-ident-kv-ops* @conn)
|
||||
retract-all-tx-data (mapcat (fn [e] (map (fn [a] [:db.fn/retractAttribute e a]) db-ident-kv-op-types))
|
||||
(keys e->op-map))]
|
||||
(d/transact! conn retract-all-tx-data)
|
||||
(vals e->op-map)))
|
||||
|
||||
(defn get-all-block-ops
|
||||
[repo]
|
||||
(when-let [conn (worker-state/get-client-ops-conn repo)]
|
||||
@@ -234,6 +325,12 @@
|
||||
m))
|
||||
(vals (get-all-block-ops* @conn)))))
|
||||
|
||||
(comment
|
||||
(defn get-all-db-ident-kv-ops
|
||||
[repo]
|
||||
(when-let [conn (worker-state/get-client-ops-conn repo)]
|
||||
(get-all-db-ident-kv-ops* @conn))))
|
||||
|
||||
(defn get&remove-all-block-ops
|
||||
"Return coll of
|
||||
{:block/uuid ...
|
||||
@@ -244,6 +341,11 @@
|
||||
(when-let [conn (worker-state/get-client-ops-conn repo)]
|
||||
(get&remove-all-block-ops* conn)))
|
||||
|
||||
(defn get&remove-all-db-ident-kv-ops
|
||||
[repo]
|
||||
(when-let [conn (worker-state/get-client-ops-conn repo)]
|
||||
(get&remove-all-db-ident-kv-ops* conn)))
|
||||
|
||||
(defn get-unpushed-block-ops-count
|
||||
[repo]
|
||||
(when-let [conn (worker-state/get-client-ops-conn repo)]
|
||||
@@ -327,7 +429,7 @@
|
||||
(let [op-map (into {}
|
||||
(keep (fn [datom]
|
||||
(let [a (:a datom)]
|
||||
(when (or (= :block/uuid a) (contains? asset-op-types a))
|
||||
(when (or (keyword-identical? :block/uuid a) (contains? asset-op-types a))
|
||||
[a (:v datom)]))))
|
||||
datoms)]
|
||||
(when (and (:block/uuid op-map)
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
[frontend.worker.rtc.exception :as r.ex]
|
||||
[frontend.worker.rtc.full-upload-download-graph :as r.upload-download]
|
||||
[frontend.worker.rtc.log-and-state :as rtc-log-and-state]
|
||||
[frontend.worker.rtc.migrate :as r.migrate]
|
||||
[frontend.worker.rtc.remote-update :as r.remote-update]
|
||||
[frontend.worker.rtc.skeleton]
|
||||
[frontend.worker.rtc.ws :as ws]
|
||||
@@ -157,6 +158,24 @@
|
||||
ws-state (assoc :ws-state ws-state)))
|
||||
(m/reductions {} nil ws-state-flow)))
|
||||
|
||||
(defn- add-migration-client-ops!
|
||||
[repo db server-schema-version]
|
||||
(when server-schema-version
|
||||
(let [client-schema-version (ldb/get-graph-schema-version db)
|
||||
added-ops (r.migrate/add-migration-client-ops! repo db server-schema-version client-schema-version)]
|
||||
(when (seq added-ops)
|
||||
(log/info :add-migration-client-ops
|
||||
{:repo repo
|
||||
:server-schema-version server-schema-version
|
||||
:client-schema-version client-schema-version})))))
|
||||
|
||||
(defn- update-remote-schema-version!
|
||||
[conn server-schema-version]
|
||||
(when server-schema-version
|
||||
(d/transact! conn [(ldb/kv :logseq.kv/remote-schema-version server-schema-version)]
|
||||
{:gen-undo-ops? false
|
||||
:persist-op? false})))
|
||||
|
||||
(defonce ^:private *rtc-lock (atom nil))
|
||||
(defn- holding-rtc-lock
|
||||
"Use this fn to prevent multiple rtc-loops at same time.
|
||||
@@ -188,15 +207,16 @@
|
||||
*last-calibrate-t (atom nil)
|
||||
*online-users (atom nil)
|
||||
*assets-sync-loop-canceler (atom nil)
|
||||
*server-schema-version (atom nil)
|
||||
started-dfv (m/dfv)
|
||||
add-log-fn (fn [type message]
|
||||
(assert (map? message) message)
|
||||
(rtc-log-and-state/rtc-log type (assoc message :graph-uuid graph-uuid)))
|
||||
{:keys [*current-ws get-ws-create-task]}
|
||||
(gen-get-ws-create-map--memoized ws-url)
|
||||
get-ws-create-task (r.client/ensure-register-graph-updates
|
||||
get-ws-create-task graph-uuid major-schema-version
|
||||
repo conn *last-calibrate-t *online-users add-log-fn)
|
||||
get-ws-create-task (r.client/ensure-register-graph-updates
|
||||
get-ws-create-task graph-uuid major-schema-version
|
||||
repo conn *last-calibrate-t *online-users *server-schema-version add-log-fn)
|
||||
{:keys [assets-sync-loop-task]}
|
||||
(r.asset/create-assets-sync-loop repo get-ws-create-task graph-uuid major-schema-version conn *auto-push?)
|
||||
mixed-flow (create-mixed-flow repo get-ws-create-task *auto-push? *online-users)]
|
||||
@@ -214,6 +234,8 @@
|
||||
;; init run to open a ws
|
||||
(m/? get-ws-create-task)
|
||||
(started-dfv true)
|
||||
(update-remote-schema-version! conn @*server-schema-version)
|
||||
(add-migration-client-ops! repo @conn @*server-schema-version)
|
||||
(reset! *assets-sync-loop-canceler
|
||||
(c.m/run-task assets-sync-loop-task :assets-sync-loop-task))
|
||||
(->>
|
||||
|
||||
@@ -1,148 +1,14 @@
|
||||
(ns frontend.worker.rtc.db-listener
|
||||
"listen datascript changes, infer operations from the db tx-report"
|
||||
(:require [clojure.string :as string]
|
||||
[datascript.core :as d]
|
||||
[frontend.worker.db-listener :as db-listener]
|
||||
(:require [frontend.worker.db-listener :as db-listener]
|
||||
[frontend.worker.rtc.client-op :as client-op]
|
||||
[frontend.worker.rtc.const :as rtc-const]
|
||||
[logseq.db :as ldb]
|
||||
[logseq.db.frontend.property :as db-property]))
|
||||
|
||||
(defn- latest-add?->v->t
|
||||
[add?->v->t]
|
||||
(let [latest-add (first (sort-by second > (seq (add?->v->t true))))
|
||||
latest-retract (first (sort-by second > (seq (add?->v->t false))))]
|
||||
(cond
|
||||
(nil? latest-add) {false (conj {} latest-retract)}
|
||||
(nil? latest-retract) {true (conj {} latest-add)}
|
||||
(= (second latest-add) (second latest-retract)) {true (conj {} latest-add)
|
||||
false (conj {} latest-retract)}
|
||||
(> (second latest-add) (second latest-retract)) {true (conj {} latest-add)}
|
||||
:else {false (conj {} latest-retract)})))
|
||||
|
||||
(def ^:private watched-attrs
|
||||
#{:block/title :block/created-at :block/updated-at :block/alias
|
||||
:block/tags :block/link :block/journal-day
|
||||
:logseq.property/classes :logseq.property/value
|
||||
:db/index :db/valueType :db/cardinality})
|
||||
|
||||
(def ^:private watched-attr-ns
|
||||
(conj db-property/logseq-property-namespaces "logseq.class"))
|
||||
|
||||
(defn- watched-attr?
|
||||
[attr]
|
||||
(or (contains? watched-attrs attr)
|
||||
(let [ns (namespace attr)]
|
||||
(or (contains? watched-attr-ns ns)
|
||||
(string/ends-with? ns ".property")
|
||||
(string/ends-with? ns ".class")))))
|
||||
|
||||
(defn- ref-attr?
|
||||
[db attr]
|
||||
(= :db.type/ref (get-in (d/schema db) [attr :db/valueType])))
|
||||
|
||||
(defn- update-op-av-coll
|
||||
[db-before db-after a->add?->v->t]
|
||||
(mapcat
|
||||
(fn [[a add?->v->t]]
|
||||
(mapcat
|
||||
(fn [[add? v->t]]
|
||||
(keep
|
||||
(fn [[v t]]
|
||||
(let [ref? (ref-attr? db-after a)]
|
||||
(case [add? ref?]
|
||||
[true true]
|
||||
(when-let [v-uuid (:block/uuid (d/entity db-after v))]
|
||||
[a v-uuid t add?])
|
||||
[false true]
|
||||
(when-let [v-uuid (:block/uuid
|
||||
(or (d/entity db-after v)
|
||||
(d/entity db-before v)))]
|
||||
[a v-uuid t add?])
|
||||
([true false] [false false]) [a (ldb/write-transit-str v) t add?])))
|
||||
v->t))
|
||||
add?->v->t))
|
||||
a->add?->v->t))
|
||||
|
||||
(defn- redundant-update-op-av-coll?
|
||||
[av-coll]
|
||||
(every? (fn [av] (keyword-identical? :block/updated-at (first av))) av-coll))
|
||||
|
||||
(defn- max-t
|
||||
[a->add?->v->t]
|
||||
(apply max (mapcat vals (mapcat vals (vals a->add?->v->t)))))
|
||||
|
||||
(defn- get-first-vt
|
||||
[add?->v->t k]
|
||||
(some-> add?->v->t (get k) first))
|
||||
|
||||
(defn- entity-datoms=>ops
|
||||
[db-before db-after e->a->add?->v->t ignore-attr-set entity-datoms]
|
||||
(let [e (ffirst entity-datoms)
|
||||
entity (d/entity db-after e)
|
||||
{block-uuid :block/uuid} entity
|
||||
a->add?->v->t (e->a->add?->v->t e)
|
||||
{add?->block-name->t :block/name
|
||||
add?->block-title->t :block/title
|
||||
add?->block-uuid->t :block/uuid
|
||||
add?->block-parent->t :block/parent
|
||||
add?->block-order->t :block/order}
|
||||
a->add?->v->t
|
||||
[retract-block-uuid t1] (some-> add?->block-uuid->t (get false) first)
|
||||
[retract-block-name _] (some-> add?->block-name->t (get false) first)
|
||||
[add-block-name t2] (some-> add?->block-name->t latest-add?->v->t (get-first-vt true))
|
||||
[add-block-title t3] (some-> add?->block-title->t latest-add?->v->t (get-first-vt true))
|
||||
[add-block-parent t4] (some-> add?->block-parent->t latest-add?->v->t (get-first-vt true))
|
||||
[add-block-order t5] (some-> add?->block-order->t latest-add?->v->t (get-first-vt true))
|
||||
a->add?->v->t* (into {}
|
||||
(filter
|
||||
(fn [[a _]]
|
||||
(and (watched-attr? a)
|
||||
(not (contains? ignore-attr-set a)))))
|
||||
a->add?->v->t)]
|
||||
(cond
|
||||
(and retract-block-uuid retract-block-name)
|
||||
[[:remove-page t1 {:block-uuid retract-block-uuid}]]
|
||||
|
||||
retract-block-uuid
|
||||
[[:remove t1 {:block-uuid retract-block-uuid}]]
|
||||
|
||||
:else
|
||||
(let [ops (cond-> []
|
||||
(or add-block-parent add-block-order)
|
||||
(conj [:move (or t4 t5) {:block-uuid block-uuid}])
|
||||
|
||||
(or add-block-name
|
||||
(and (ldb/page? entity) add-block-title))
|
||||
(conj [:update-page (or t2 t3) {:block-uuid block-uuid}]))
|
||||
update-op (when-let [av-coll (not-empty (update-op-av-coll db-before db-after a->add?->v->t*))]
|
||||
(when-not (redundant-update-op-av-coll? av-coll)
|
||||
(let [t (max-t a->add?->v->t*)]
|
||||
[:update t {:block-uuid block-uuid :av-coll av-coll}])))]
|
||||
(cond-> ops update-op (conj update-op))))))
|
||||
|
||||
(defn- generate-rtc-ops
|
||||
[repo db-before db-after same-entity-datoms-coll e->a->v->add?->t]
|
||||
(let [ops (mapcat
|
||||
(partial entity-datoms=>ops
|
||||
db-before db-after e->a->v->add?->t rtc-const/ignore-attrs-when-syncing)
|
||||
same-entity-datoms-coll)]
|
||||
(when (seq ops)
|
||||
(client-op/add-ops repo ops))))
|
||||
[frontend.worker.rtc.gen-client-op :as gen-client-op]))
|
||||
|
||||
(comment
|
||||
;; TODO: make it a qualified-keyword
|
||||
(defkeywords
|
||||
:persist-op? {:doc "tx-meta option, generate rtc ops when not nil (default true)"}))
|
||||
|
||||
(defn- entity-datoms=>a->add?->v->t
|
||||
[entity-datoms]
|
||||
(reduce
|
||||
(fn [m datom]
|
||||
(let [[_e a v t add?] datom]
|
||||
(assoc-in m [a add? v] t)))
|
||||
{} entity-datoms))
|
||||
|
||||
(defmethod db-listener/listen-db-changes :gen-rtc-ops
|
||||
[_
|
||||
{:keys [repo same-entity-datoms-coll id->same-entity-datoms]}
|
||||
@@ -151,5 +17,7 @@
|
||||
(:persist-op? tx-meta true))
|
||||
(let [e->a->add?->v->t (update-vals
|
||||
id->same-entity-datoms
|
||||
entity-datoms=>a->add?->v->t)]
|
||||
(generate-rtc-ops repo db-before db-after same-entity-datoms-coll e->a->add?->v->t))))
|
||||
gen-client-op/entity-datoms=>a->add?->v->t)
|
||||
ops (gen-client-op/generate-rtc-ops db-before db-after same-entity-datoms-coll e->a->add?->v->t)]
|
||||
(when (seq ops)
|
||||
(client-op/add-ops! repo ops)))))
|
||||
|
||||
160
src/main/frontend/worker/rtc/gen_client_op.cljs
Normal file
160
src/main/frontend/worker/rtc/gen_client_op.cljs
Normal file
@@ -0,0 +1,160 @@
|
||||
(ns frontend.worker.rtc.gen-client-op
|
||||
"Generate client-ops from entities/datoms"
|
||||
(:require [clojure.string :as string]
|
||||
[datascript.core :as d]
|
||||
[frontend.worker.rtc.const :as rtc-const]
|
||||
[logseq.db :as ldb]
|
||||
[logseq.db.frontend.property :as db-property]))
|
||||
|
||||
(defn- latest-add?->v->t
|
||||
[add?->v->t]
|
||||
(let [latest-add (first (sort-by second > (seq (add?->v->t true))))
|
||||
latest-retract (first (sort-by second > (seq (add?->v->t false))))]
|
||||
(cond
|
||||
(nil? latest-add) {false (conj {} latest-retract)}
|
||||
(nil? latest-retract) {true (conj {} latest-add)}
|
||||
(= (second latest-add) (second latest-retract)) {true (conj {} latest-add)
|
||||
false (conj {} latest-retract)}
|
||||
(> (second latest-add) (second latest-retract)) {true (conj {} latest-add)}
|
||||
:else {false (conj {} latest-retract)})))
|
||||
|
||||
(def ^:private watched-attrs
|
||||
#{:block/title :block/created-at :block/updated-at :block/alias
|
||||
:block/tags :block/link :block/journal-day
|
||||
:logseq.property/classes :logseq.property/value
|
||||
:db/index :db/valueType :db/cardinality})
|
||||
|
||||
(def ^:private watched-attr-ns
|
||||
(conj db-property/logseq-property-namespaces "logseq.class"))
|
||||
|
||||
(defn- watched-attr?
|
||||
[attr]
|
||||
(or (contains? watched-attrs attr)
|
||||
(let [ns (namespace attr)]
|
||||
(or (contains? watched-attr-ns ns)
|
||||
(string/ends-with? ns ".property")
|
||||
(string/ends-with? ns ".class")))))
|
||||
|
||||
(defn- ref-attr?
|
||||
[db attr]
|
||||
(= :db.type/ref (get-in (d/schema db) [attr :db/valueType])))
|
||||
|
||||
(defn- update-op-av-coll
|
||||
[db-before db-after a->add?->v->t]
|
||||
(mapcat
|
||||
(fn [[a add?->v->t]]
|
||||
(mapcat
|
||||
(fn [[add? v->t]]
|
||||
(keep
|
||||
(fn [[v t]]
|
||||
(let [ref? (ref-attr? db-after a)]
|
||||
(case [add? ref?]
|
||||
[true true]
|
||||
(when-let [v-uuid (:block/uuid (d/entity db-after v))]
|
||||
[a v-uuid t add?])
|
||||
[false true]
|
||||
(when-let [v-uuid (:block/uuid
|
||||
(or (d/entity db-after v)
|
||||
(d/entity db-before v)))]
|
||||
[a v-uuid t add?])
|
||||
([true false] [false false]) [a (ldb/write-transit-str v) t add?])))
|
||||
v->t))
|
||||
add?->v->t))
|
||||
a->add?->v->t))
|
||||
|
||||
(defn- redundant-update-op-av-coll?
|
||||
[av-coll]
|
||||
(every? (fn [av] (keyword-identical? :block/updated-at (first av))) av-coll))
|
||||
|
||||
(defn- max-t
|
||||
[a->add?->v->t]
|
||||
(apply max (mapcat vals (mapcat vals (vals a->add?->v->t)))))
|
||||
|
||||
(defn- get-first-vt
|
||||
[add?->v->t k]
|
||||
(some-> add?->v->t (get k) first))
|
||||
|
||||
(defn- entity-datoms=>ops
|
||||
[db-before db-after e->a->add?->v->t ignore-attr-set entity-datoms]
|
||||
(let [e (ffirst entity-datoms)
|
||||
entity (d/entity db-after e)
|
||||
{block-uuid :block/uuid} entity
|
||||
a->add?->v->t (e->a->add?->v->t e)
|
||||
{add?->block-name->t :block/name
|
||||
add?->block-title->t :block/title
|
||||
add?->block-uuid->t :block/uuid
|
||||
add?->block-parent->t :block/parent
|
||||
add?->block-order->t :block/order}
|
||||
a->add?->v->t
|
||||
[retract-block-uuid t1] (some-> add?->block-uuid->t (get false) first)
|
||||
[retract-block-name _] (some-> add?->block-name->t (get false) first)
|
||||
[add-block-name t2] (some-> add?->block-name->t latest-add?->v->t (get-first-vt true))
|
||||
[add-block-title t3] (some-> add?->block-title->t latest-add?->v->t (get-first-vt true))
|
||||
[add-block-parent t4] (some-> add?->block-parent->t latest-add?->v->t (get-first-vt true))
|
||||
[add-block-order t5] (some-> add?->block-order->t latest-add?->v->t (get-first-vt true))
|
||||
a->add?->v->t* (into {}
|
||||
(filter
|
||||
(fn [[a _]]
|
||||
(and (watched-attr? a)
|
||||
(not (contains? ignore-attr-set a)))))
|
||||
a->add?->v->t)]
|
||||
(cond
|
||||
(and retract-block-uuid retract-block-name)
|
||||
[[:remove-page t1 {:block-uuid retract-block-uuid}]]
|
||||
|
||||
retract-block-uuid
|
||||
[[:remove t1 {:block-uuid retract-block-uuid}]]
|
||||
|
||||
:else
|
||||
(let [ops (cond-> []
|
||||
(or add-block-parent add-block-order)
|
||||
(conj [:move (or t4 t5) {:block-uuid block-uuid}])
|
||||
|
||||
(or add-block-name
|
||||
(and (ldb/page? entity) add-block-title))
|
||||
(conj [:update-page (or t2 t3) {:block-uuid block-uuid}]))
|
||||
update-op (when-let [av-coll (not-empty (update-op-av-coll db-before db-after a->add?->v->t*))]
|
||||
(when-not (redundant-update-op-av-coll? av-coll)
|
||||
(let [t (max-t a->add?->v->t*)]
|
||||
[:update t {:block-uuid block-uuid :av-coll av-coll}])))]
|
||||
(cond-> ops update-op (conj update-op))))))
|
||||
|
||||
(defn entity-datoms=>a->add?->v->t
|
||||
[entity-datoms]
|
||||
(reduce
|
||||
(fn [m datom]
|
||||
(let [[_e a v t add?] datom]
|
||||
(assoc-in m [a add? v] t)))
|
||||
{} entity-datoms))
|
||||
|
||||
(defn generate-rtc-ops
|
||||
[db-before db-after same-entity-datoms-coll e->a->v->add?->t]
|
||||
(mapcat
|
||||
(partial entity-datoms=>ops
|
||||
db-before db-after e->a->v->add?->t rtc-const/ignore-attrs-when-syncing)
|
||||
same-entity-datoms-coll))
|
||||
|
||||
(defn- generate-rtc-ops-from-entities
|
||||
[ents]
|
||||
(let [db (d/entity-db (first ents))
|
||||
id->same-entity-datoms
|
||||
(into {}
|
||||
(map (fn [ent]
|
||||
(let [e (:db/id ent)
|
||||
datoms (d/datoms db :eavt e)]
|
||||
[e datoms])))
|
||||
ents)
|
||||
e->a->v->add?->t (update-vals id->same-entity-datoms entity-datoms=>a->add?->v->t)]
|
||||
(generate-rtc-ops db db (vals id->same-entity-datoms) e->a->v->add?->t)))
|
||||
|
||||
(defn generate-rtc-ops-from-property-entities
|
||||
[property-ents]
|
||||
(when (seq property-ents)
|
||||
(assert (every? ldb/property? property-ents))
|
||||
(generate-rtc-ops-from-entities property-ents)))
|
||||
|
||||
(defn generate-rtc-ops-from-class-entities
|
||||
[class-ents]
|
||||
(when (seq class-ents)
|
||||
(assert (every? ldb/class? class-ents))
|
||||
(generate-rtc-ops-from-entities class-ents)))
|
||||
@@ -25,6 +25,11 @@
|
||||
|
||||
(def to-ws-op-schema
|
||||
[:multi {:dispatch first :decode/string #(update % 0 keyword)}
|
||||
[:update-kv-value
|
||||
[:cat :keyword
|
||||
[:map
|
||||
[:db-ident :keyword]
|
||||
[:value :string]]]]
|
||||
[:move
|
||||
[:cat :keyword
|
||||
[:map
|
||||
@@ -356,4 +361,4 @@
|
||||
(def data-to-ws-coercer (m/coercer data-to-ws-schema mt/string-transformer nil
|
||||
#(do
|
||||
(log/error ::data-to-ws-schema %)
|
||||
(m/-fail! ::data-to-ws-schema %))))
|
||||
(m/-fail! ::data-to-ws-schema (select-keys % [:value])))))
|
||||
|
||||
49
src/main/frontend/worker/rtc/migrate.cljs
Normal file
49
src/main/frontend/worker/rtc/migrate.cljs
Normal file
@@ -0,0 +1,49 @@
|
||||
(ns frontend.worker.rtc.migrate
|
||||
"migrate server data according to schema-version and client's migration-updates"
|
||||
(:require [datascript.core :as d]
|
||||
[frontend.worker.db.migrate :as db-migrate]
|
||||
[frontend.worker.rtc.client-op :as client-op]
|
||||
[frontend.worker.rtc.gen-client-op :as gen-client-op]
|
||||
[logseq.db.frontend.schema :as db-schema]))
|
||||
|
||||
(defn- server-client-schema-version->migrations
|
||||
[server-schema-version client-schema-version]
|
||||
(when (neg? (db-schema/compare-schema-version server-schema-version client-schema-version))
|
||||
(let [sorted-schema-version->updates
|
||||
(->> (map (fn [[schema-version updates]]
|
||||
[((juxt :major :minor) (db-schema/parse-schema-version schema-version))
|
||||
updates])
|
||||
db-migrate/schema-version->updates)
|
||||
(sort-by first))]
|
||||
(->> sorted-schema-version->updates
|
||||
(drop-while (fn [[schema-version _updates]]
|
||||
(not (neg? (db-schema/compare-schema-version server-schema-version schema-version)))))
|
||||
(take-while (fn [[schema-version _updates]]
|
||||
(not (neg? (db-schema/compare-schema-version client-schema-version schema-version)))))
|
||||
(map second)))))
|
||||
|
||||
(defn- migration-updates->client-ops
|
||||
"convert :classes, :properties from frontend.worker.db.migrate/schema-version->updates into client-ops"
|
||||
[db client-schema-version migrate-updates]
|
||||
(let [property-ks (mapcat :properties migrate-updates)
|
||||
class-ks (mapcat :classes migrate-updates)
|
||||
d-entity-fn (partial d/entity db)
|
||||
new-property-entities (keep d-entity-fn property-ks)
|
||||
new-class-entities (keep d-entity-fn class-ks)
|
||||
client-ops (vec (concat (gen-client-op/generate-rtc-ops-from-property-entities new-property-entities)
|
||||
(gen-client-op/generate-rtc-ops-from-class-entities new-class-entities)))
|
||||
max-t (apply max 0 (map second client-ops))]
|
||||
(conj client-ops
|
||||
[:update-kv-value
|
||||
max-t
|
||||
{:db-ident :logseq.kv/schema-version
|
||||
:value client-schema-version}])))
|
||||
|
||||
(defn add-migration-client-ops!
|
||||
[repo db server-schema-version client-schema-version]
|
||||
(assert (and server-schema-version client-schema-version))
|
||||
(when-let [ops (not-empty
|
||||
(some->> (server-client-schema-version->migrations server-schema-version client-schema-version)
|
||||
(migration-updates->client-ops db client-schema-version)))]
|
||||
(client-op/add-ops! repo ops)
|
||||
ops))
|
||||
@@ -317,10 +317,10 @@ so need to pull earlier remote-data from websocket."})
|
||||
|
||||
(defn- affected-blocks->diff-type-ops
|
||||
[repo affected-blocks]
|
||||
(let [unpushed-ops (client-op/get-all-block-ops repo)
|
||||
affected-blocks-map* (if unpushed-ops
|
||||
(let [unpushed-block-ops (client-op/get-all-block-ops repo)
|
||||
affected-blocks-map* (if unpushed-block-ops
|
||||
(update-remote-data-by-local-unpushed-ops
|
||||
affected-blocks unpushed-ops)
|
||||
affected-blocks unpushed-block-ops)
|
||||
affected-blocks)
|
||||
{remove-ops-map :remove move-ops-map :move update-ops-map :update-attrs
|
||||
move+update-ops-map :move+update-attrs
|
||||
|
||||
@@ -51,4 +51,5 @@
|
||||
(conj [:p (str :client-only-db-idents client-only)])
|
||||
(seq server-only)
|
||||
(conj [:p (str :server-only-db-idents server-only)]))
|
||||
:error]))))))))
|
||||
:error])))
|
||||
r)))))
|
||||
|
||||
@@ -534,8 +534,8 @@
|
||||
:command.editor/delete "向右删除"
|
||||
:command.editor/cycle-todo "切换TODO状态"
|
||||
:command.editor/clear-block "清除块内容"
|
||||
:command.editor/kill-line-before "删除光标右侧行"
|
||||
:command.editor/kill-line-after "删除光标左侧行"
|
||||
:command.editor/kill-line-before "删除光标左侧行"
|
||||
:command.editor/kill-line-after "删除光标右侧行"
|
||||
:command.editor/beginning-of-block "移动光标到块开始位置"
|
||||
:command.editor/end-of-block "移动光标到块末尾"
|
||||
:command.editor/forward-word "光标向后移动一个单词"
|
||||
|
||||
@@ -40,7 +40,25 @@
|
||||
(testing "user.property/xxx creation"
|
||||
(let [block-uuid (random-uuid)
|
||||
block-order "b0P"
|
||||
db (d/db-with empty-db [{:db/index true
|
||||
db (d/db-with empty-db [{:block/uuid #uuid "00000002-5389-0208-3000-000000000000",
|
||||
:block/updated-at 1741424828774,
|
||||
:block/created-at 1741424828774,
|
||||
:logseq.property/built-in? true,
|
||||
:block/tags [2],
|
||||
:block/title "Tag",
|
||||
:db/id 2,
|
||||
:db/ident :logseq.class/Tag,
|
||||
:block/name "tag"}
|
||||
{:block/uuid #uuid "00000002-1038-7670-4800-000000000000",
|
||||
:block/updated-at 1741424828774,
|
||||
:block/created-at 1741424828774,
|
||||
:logseq.property/built-in? true,
|
||||
:block/tags [2]
|
||||
:block/title "Property",
|
||||
:db/id 3,
|
||||
:db/ident :logseq.class/Property,
|
||||
:block/name "property"}
|
||||
{:db/index true
|
||||
:block/uuid block-uuid
|
||||
:db/valueType :db.type/ref
|
||||
:block/updated-at 1716880036491
|
||||
@@ -48,6 +66,7 @@
|
||||
:logseq.property/type :number
|
||||
:db/cardinality :db.cardinality/one
|
||||
:db/ident :user.property/xxx,
|
||||
:block/tags [3]
|
||||
:block/type "property",
|
||||
:block/order block-order,
|
||||
:block/name "xxx",
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
[frontend.test.helper :as test-helper]
|
||||
[frontend.worker.db-listener :as worker-db-listener]
|
||||
[frontend.worker.rtc.client-op :as client-op]
|
||||
[frontend.worker.rtc.db-listener]
|
||||
[frontend.worker.state :as worker-state]))
|
||||
|
||||
(def listen-test-db-to-gen-rtc-ops-fixture
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
(ns frontend.worker.rtc.db-listener-test
|
||||
(ns frontend.worker.rtc.gen-client-op-test
|
||||
(:require [cljs.test :as t :refer [deftest is testing]]
|
||||
[clojure.set :as set]
|
||||
[datascript.core :as d]
|
||||
[frontend.db.conn :as conn]
|
||||
[frontend.state :as state]
|
||||
[frontend.test.helper :as test-helper]
|
||||
[frontend.worker.handler.page :as worker-page]
|
||||
[frontend.worker.rtc.client-op :as client-op]
|
||||
[frontend.worker.rtc.db-listener :as subject]
|
||||
[frontend.worker.rtc.fixture :as r.fixture]
|
||||
[frontend.worker.rtc.gen-client-op :as subject]
|
||||
[frontend.worker.state :as worker-state]
|
||||
[logseq.db.test.helper :as db-test]
|
||||
[logseq.outliner.batch-tx :as batch-tx]
|
||||
[logseq.outliner.core :as outliner-core]))
|
||||
[logseq.outliner.core :as outliner-core]
|
||||
[meander.epsilon :as me]))
|
||||
|
||||
(t/use-fixtures :each
|
||||
test-helper/db-based-start-and-destroy-db-map-fixture
|
||||
@@ -156,3 +158,35 @@
|
||||
(is (=
|
||||
{block-uuid1 #{:remove}}
|
||||
(ops-coll=>block-uuid->op-types (client-op/get&remove-all-block-ops repo))))))))
|
||||
|
||||
(deftest generate-rtc-ops-from-property-entity-test
|
||||
(let [repo (state/get-current-repo)
|
||||
db (conn/get-db repo true)
|
||||
ent (d/entity db :logseq.property.view/feature-type)
|
||||
av-coll-attrs #{:logseq.property/type :logseq.property/built-in?
|
||||
:logseq.property/public? :logseq.property/hide?
|
||||
:block/tags :block/title :db/cardinality}]
|
||||
#_{:clj-kondo/ignore [:unresolved-symbol :invalid-arity]}
|
||||
(is (->> (me/find (subject/generate-rtc-ops-from-property-entities [ent])
|
||||
([:move _ {:block-uuid ?block-uuid}]
|
||||
[:update-page _ {:block-uuid ?block-uuid}]
|
||||
[:update _ {:block-uuid ?block-uuid :av-coll ([!av-coll-attrs . _ ...] ...)}])
|
||||
!av-coll-attrs)
|
||||
set
|
||||
(set/difference av-coll-attrs)
|
||||
empty?))))
|
||||
|
||||
(deftest generate-rtc-ops-from-class-entity-test
|
||||
(let [repo (state/get-current-repo)
|
||||
db (conn/get-db repo true)
|
||||
ent (d/entity db :logseq.class/Template)
|
||||
av-coll-attrs #{:logseq.property.class/properties :logseq.property/built-in? :logseq.property/parent
|
||||
:block/tags :block/title}]
|
||||
#_{:clj-kondo/ignore [:unresolved-symbol :invalid-arity]}
|
||||
(is (->> (me/find (subject/generate-rtc-ops-from-class-entities [ent])
|
||||
([:update-page _ {:block-uuid ?block-uuid}]
|
||||
[:update _ {:block-uuid ?block-uuid :av-coll ([!av-coll-attrs . _ ...] ...)}])
|
||||
!av-coll-attrs)
|
||||
set
|
||||
(set/difference av-coll-attrs)
|
||||
empty?))))
|
||||
263
static/yarn.lock
263
static/yarn.lock
@@ -561,6 +561,13 @@
|
||||
wrap-ansi "^8.1.0"
|
||||
wrap-ansi-cjs "npm:wrap-ansi@^7.0.0"
|
||||
|
||||
"@isaacs/fs-minipass@^4.0.0":
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz#2d59ae3ab4b38fb4270bfa23d30f8e2e86c7fe32"
|
||||
integrity sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==
|
||||
dependencies:
|
||||
minipass "^7.0.4"
|
||||
|
||||
"@logseq/rsapi-darwin-arm64@0.0.91":
|
||||
version "0.0.91"
|
||||
resolved "https://registry.yarnpkg.com/@logseq/rsapi-darwin-arm64/-/rsapi-darwin-arm64-0.0.91.tgz#e29cd37a372609b04d7dcfc7ae4c1405c7a54353"
|
||||
@@ -648,10 +655,10 @@
|
||||
"@nodelib/fs.scandir" "2.1.5"
|
||||
fastq "^1.6.0"
|
||||
|
||||
"@npmcli/agent@^2.0.0":
|
||||
version "2.2.2"
|
||||
resolved "https://registry.yarnpkg.com/@npmcli/agent/-/agent-2.2.2.tgz#967604918e62f620a648c7975461c9c9e74fc5d5"
|
||||
integrity sha512-OrcNPXdpSl9UX7qPVRWbmWMCSXrcDa2M9DvrbOTj7ao1S4PlqVFYv9/yLKMkrJKZ/V5A/kDBC690or307i26Og==
|
||||
"@npmcli/agent@^3.0.0":
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@npmcli/agent/-/agent-3.0.0.tgz#1685b1fbd4a1b7bb4f930cbb68ce801edfe7aa44"
|
||||
integrity sha512-S79NdEgDQd/NGCay6TCoVzXSj74skRZIKJcpJjC5lOq34SZzyI6MqtiiWoiVWoVrTcGjNeC4ipbh1VIHlpfF5Q==
|
||||
dependencies:
|
||||
agent-base "^7.1.0"
|
||||
http-proxy-agent "^7.0.0"
|
||||
@@ -659,10 +666,10 @@
|
||||
lru-cache "^10.0.1"
|
||||
socks-proxy-agent "^8.0.3"
|
||||
|
||||
"@npmcli/fs@^3.1.0":
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-3.1.1.tgz#59cdaa5adca95d135fc00f2bb53f5771575ce726"
|
||||
integrity sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg==
|
||||
"@npmcli/fs@^4.0.0":
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-4.0.0.tgz#a1eb1aeddefd2a4a347eca0fab30bc62c0e1c0f2"
|
||||
integrity sha512-/xGlezI6xfGO9NwuJlnwz/K14qD1kCSAGtacBHnGzeAIuJGazcp45KP5NuyARXoKb7cwulAGWVsbeSxdG/cb0Q==
|
||||
dependencies:
|
||||
semver "^7.3.5"
|
||||
|
||||
@@ -845,12 +852,12 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.7.23.tgz#75c580983846181ebe5f4abc40fe9dfb2d65665f"
|
||||
integrity sha512-DWNcCHolDq0ZKGizjx2DZjR/PqsYwAcYUJmfMWqtVU2MBMG5Mo+xFZrhGId5r/O5HOuMPyQEcM6KUBp5lBZZBg==
|
||||
|
||||
"@types/node@^20.9.0":
|
||||
version "20.17.8"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.17.8.tgz#42748cdb169adf5be7c9760604c72820c7b7d560"
|
||||
integrity sha512-ahz2g6/oqbKalW9sPv6L2iRbhLnojxjYWspAqhjvqSWBgGebEJT5GvRmk0QXPj3sbC6rU0GTQjPLQkmR8CObvA==
|
||||
"@types/node@^22.7.7":
|
||||
version "22.13.10"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-22.13.10.tgz#df9ea358c5ed991266becc3109dc2dc9125d77e4"
|
||||
integrity sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw==
|
||||
dependencies:
|
||||
undici-types "~6.19.2"
|
||||
undici-types "~6.20.0"
|
||||
|
||||
"@types/plist@^3.0.1":
|
||||
version "3.0.2"
|
||||
@@ -884,10 +891,10 @@
|
||||
resolved "https://registry.yarnpkg.com/@xmldom/xmldom/-/xmldom-0.8.10.tgz#a1337ca426aa61cef9fe15b5b28e340a72f6fa99"
|
||||
integrity sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==
|
||||
|
||||
abbrev@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-2.0.0.tgz#cf59829b8b4f03f89dda2771cb7f3653828c89bf"
|
||||
integrity sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==
|
||||
abbrev@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-3.0.0.tgz#c29a6337e167ac61a84b41b80461b29c5c271a27"
|
||||
integrity sha512-+/kfrslGQ7TNV2ecmQwMJj/B65g5KVq1/L3SGVZ3tCYGqlzFuFCGBZJtMP99wH3NpEUyAjn0zPdPUg0D+DwrOA==
|
||||
|
||||
abort-controller@3.0.0:
|
||||
version "3.0.0"
|
||||
@@ -1351,12 +1358,12 @@ builder-util@25.1.7:
|
||||
stat-mode "^1.0.0"
|
||||
temp-file "^3.4.0"
|
||||
|
||||
cacache@^18.0.0:
|
||||
version "18.0.4"
|
||||
resolved "https://registry.yarnpkg.com/cacache/-/cacache-18.0.4.tgz#4601d7578dadb59c66044e157d02a3314682d6a5"
|
||||
integrity sha512-B+L5iIa9mgcjLbliir2th36yEwPftrzteHYujzsx3dFP/31GCHcIeS8f5MGd80odLOjaOvSpU3EEAmRQptkxLQ==
|
||||
cacache@^19.0.1:
|
||||
version "19.0.1"
|
||||
resolved "https://registry.yarnpkg.com/cacache/-/cacache-19.0.1.tgz#3370cc28a758434c85c2585008bd5bdcff17d6cd"
|
||||
integrity sha512-hdsUxulXCi5STId78vRVYEtDAjq99ICAUktLTeTYsLoTE6Z8dS0c8pWNCxwdrk9YfJeobDZc2Y186hD/5ZQgFQ==
|
||||
dependencies:
|
||||
"@npmcli/fs" "^3.1.0"
|
||||
"@npmcli/fs" "^4.0.0"
|
||||
fs-minipass "^3.0.0"
|
||||
glob "^10.2.2"
|
||||
lru-cache "^10.0.1"
|
||||
@@ -1364,10 +1371,10 @@ cacache@^18.0.0:
|
||||
minipass-collect "^2.0.1"
|
||||
minipass-flush "^1.0.5"
|
||||
minipass-pipeline "^1.2.4"
|
||||
p-map "^4.0.0"
|
||||
ssri "^10.0.0"
|
||||
tar "^6.1.11"
|
||||
unique-filename "^3.0.0"
|
||||
p-map "^7.0.2"
|
||||
ssri "^12.0.0"
|
||||
tar "^7.4.3"
|
||||
unique-filename "^4.0.0"
|
||||
|
||||
cacheable-lookup@^5.0.3:
|
||||
version "5.0.4"
|
||||
@@ -1415,6 +1422,11 @@ chownr@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece"
|
||||
integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==
|
||||
|
||||
chownr@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/chownr/-/chownr-3.0.0.tgz#9855e64ecd240a9cc4267ce8a4aa5d24a1da15e4"
|
||||
integrity sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==
|
||||
|
||||
chrome-trace-event@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac"
|
||||
@@ -1997,13 +2009,13 @@ electron-wix-msi@^5.1.3:
|
||||
optionalDependencies:
|
||||
"@bitdisaster/exe-icon-extractor" "^1.0.10"
|
||||
|
||||
electron@*, electron@31.7.5:
|
||||
version "31.7.5"
|
||||
resolved "https://registry.yarnpkg.com/electron/-/electron-31.7.5.tgz#98396c75041808b26c7b6a8844cbedacee93d8a6"
|
||||
integrity sha512-8zFzVJdhxTRmoPcRiKkEmPW0bJHAUsTQJwEX2YJ8X0BVFIJLwSvHkSlpCjEExVbNCAk+gHnkIYX+2OyCXrRwHQ==
|
||||
electron@*, electron@35.0.1:
|
||||
version "35.0.1"
|
||||
resolved "https://registry.yarnpkg.com/electron/-/electron-35.0.1.tgz#8596e3936c4b5787e0cf52739bf6842925cfcdc1"
|
||||
integrity sha512-iQonj6lnPhqfqha2KXx6LzV1dnu6UPTCWK+b7f9Zvg828umGemi22DKbcJ3/q+Opn7iUVTWyqp9z1JQqkIi6OA==
|
||||
dependencies:
|
||||
"@electron/get" "^2.0.0"
|
||||
"@types/node" "^20.9.0"
|
||||
"@types/node" "^22.7.7"
|
||||
extract-zip "^2.0.1"
|
||||
|
||||
emoji-regex@^8.0.0:
|
||||
@@ -2540,7 +2552,7 @@ glob-parent@^5.1.2, glob-parent@~5.1.2:
|
||||
dependencies:
|
||||
is-glob "^4.0.1"
|
||||
|
||||
glob@^10.2.2, glob@^10.3.12:
|
||||
glob@^10.2.2, glob@^10.3.12, glob@^10.3.7:
|
||||
version "10.4.5"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956"
|
||||
integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==
|
||||
@@ -2900,11 +2912,6 @@ is-interactive@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e"
|
||||
integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==
|
||||
|
||||
is-lambda@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/is-lambda/-/is-lambda-1.0.1.tgz#3d9877899e6a53efc0160504cde15f82e6f061d5"
|
||||
integrity sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==
|
||||
|
||||
is-my-ip-valid@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/is-my-ip-valid/-/is-my-ip-valid-1.0.1.tgz#f7220d1146257c98672e6fba097a9f3f2d348442"
|
||||
@@ -3294,23 +3301,22 @@ macos-alias@~0.2.5:
|
||||
dependencies:
|
||||
nan "^2.4.0"
|
||||
|
||||
make-fetch-happen@^13.0.0:
|
||||
version "13.0.1"
|
||||
resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-13.0.1.tgz#273ba2f78f45e1f3a6dca91cede87d9fa4821e36"
|
||||
integrity sha512-cKTUFc/rbKUd/9meOvgrpJ2WrNzymt6jfRDdwg5UCnVzv9dTpEj9JS5m3wtziXVCjluIXyL8pcaukYqezIzZQA==
|
||||
make-fetch-happen@^14.0.3:
|
||||
version "14.0.3"
|
||||
resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-14.0.3.tgz#d74c3ecb0028f08ab604011e0bc6baed483fcdcd"
|
||||
integrity sha512-QMjGbFTP0blj97EeidG5hk/QhKQ3T4ICckQGLgz38QF7Vgbk6e6FTARN8KhKxyBbWn8R0HU+bnw8aSoFPD4qtQ==
|
||||
dependencies:
|
||||
"@npmcli/agent" "^2.0.0"
|
||||
cacache "^18.0.0"
|
||||
"@npmcli/agent" "^3.0.0"
|
||||
cacache "^19.0.1"
|
||||
http-cache-semantics "^4.1.1"
|
||||
is-lambda "^1.0.1"
|
||||
minipass "^7.0.2"
|
||||
minipass-fetch "^3.0.0"
|
||||
minipass-fetch "^4.0.0"
|
||||
minipass-flush "^1.0.5"
|
||||
minipass-pipeline "^1.2.4"
|
||||
negotiator "^0.6.3"
|
||||
proc-log "^4.2.0"
|
||||
negotiator "^1.0.0"
|
||||
proc-log "^5.0.0"
|
||||
promise-retry "^2.0.1"
|
||||
ssri "^10.0.0"
|
||||
ssri "^12.0.0"
|
||||
|
||||
map-age-cleaner@^0.1.1:
|
||||
version "0.1.3"
|
||||
@@ -3439,14 +3445,14 @@ minipass-collect@^2.0.1:
|
||||
dependencies:
|
||||
minipass "^7.0.3"
|
||||
|
||||
minipass-fetch@^3.0.0:
|
||||
version "3.0.5"
|
||||
resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-3.0.5.tgz#f0f97e40580affc4a35cc4a1349f05ae36cb1e4c"
|
||||
integrity sha512-2N8elDQAtSnFV0Dk7gt15KHsS0Fyz6CbYZ360h0WTYV1Ty46li3rAXVOQj1THMNLdmrD9Vt5pBPtWtVkpwGBqg==
|
||||
minipass-fetch@^4.0.0:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-4.0.1.tgz#f2d717d5a418ad0b1a7274f9b913515d3e78f9e5"
|
||||
integrity sha512-j7U11C5HXigVuutxebFadoYBbd7VSdZWggSe64NVdvWNBqGAiXPL2QVCehjmw7lY1oF9gOllYbORh+hiNgfPgQ==
|
||||
dependencies:
|
||||
minipass "^7.0.3"
|
||||
minipass-sized "^1.0.3"
|
||||
minizlib "^2.1.2"
|
||||
minizlib "^3.0.1"
|
||||
optionalDependencies:
|
||||
encoding "^0.1.13"
|
||||
|
||||
@@ -3488,12 +3494,12 @@ minipass@^5.0.0:
|
||||
resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.0.4.tgz#dbce03740f50a4786ba994c1fb908844d27b038c"
|
||||
integrity sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==
|
||||
|
||||
minipass@^7.0.2, minipass@^7.0.3, minipass@^7.1.2:
|
||||
minipass@^7.0.2, minipass@^7.0.3, minipass@^7.0.4, minipass@^7.1.2:
|
||||
version "7.1.2"
|
||||
resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707"
|
||||
integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==
|
||||
|
||||
minizlib@^2.1.1, minizlib@^2.1.2:
|
||||
minizlib@^2.1.1:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931"
|
||||
integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==
|
||||
@@ -3501,6 +3507,14 @@ minizlib@^2.1.1, minizlib@^2.1.2:
|
||||
minipass "^3.0.0"
|
||||
yallist "^4.0.0"
|
||||
|
||||
minizlib@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-3.0.1.tgz#46d5329d1eb3c83924eff1d3b858ca0a31581012"
|
||||
integrity sha512-umcy022ILvb5/3Djuu8LWeqUa8D68JaBzlttKeMWen48SjabqS3iY5w/vzeMzMUNhLDifyhbOwKDSznB1vvrwg==
|
||||
dependencies:
|
||||
minipass "^7.0.4"
|
||||
rimraf "^5.0.5"
|
||||
|
||||
mkdirp@^0.5.1:
|
||||
version "0.5.6"
|
||||
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6"
|
||||
@@ -3513,6 +3527,11 @@ mkdirp@^1.0.3:
|
||||
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
|
||||
integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
|
||||
|
||||
mkdirp@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-3.0.1.tgz#e44e4c5607fb279c168241713cc6e0fea9adcb50"
|
||||
integrity sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==
|
||||
|
||||
mnemonist@0.39.8:
|
||||
version "0.39.8"
|
||||
resolved "https://registry.yarnpkg.com/mnemonist/-/mnemonist-0.39.8.tgz#9078cd8386081afd986cca34b52b5d84ea7a4d38"
|
||||
@@ -3554,20 +3573,20 @@ nan@^2.4.0:
|
||||
resolved "https://registry.yarnpkg.com/nan/-/nan-2.17.0.tgz#c0150a2368a182f033e9aa5195ec76ea41a199cb"
|
||||
integrity sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==
|
||||
|
||||
negotiator@^0.6.3:
|
||||
version "0.6.3"
|
||||
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd"
|
||||
integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==
|
||||
negotiator@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-1.0.0.tgz#b6c91bb47172d69f93cfd7c357bbb529019b5f6a"
|
||||
integrity sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==
|
||||
|
||||
nice-try@^1.0.4:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
|
||||
integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
|
||||
|
||||
node-abi@3.68.0, node-abi@^3.0.0, node-abi@^3.45.0:
|
||||
version "3.68.0"
|
||||
resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.68.0.tgz#8f37fb02ecf4f43ebe694090dcb52e0c4cc4ba25"
|
||||
integrity sha512-7vbj10trelExNjFSBm5kTvZXXa7pZyKWx9RCKIyqe6I9Ev3IzGpQoqBP3a+cOdxY+pWj6VkP28n/2wWysBHD/A==
|
||||
node-abi@3.74.0, node-abi@^3.0.0, node-abi@^3.45.0:
|
||||
version "3.74.0"
|
||||
resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.74.0.tgz#5bfb4424264eaeb91432d2adb9da23c63a301ed0"
|
||||
integrity sha512-c5XK0MjkGBrQPGYG24GBADZud0NCbznxNx0ZkS+ebUTrmV1qTDxPxSL8zEAPURXSbLRWVexxmP4986BziahL5w==
|
||||
dependencies:
|
||||
semver "^7.3.5"
|
||||
|
||||
@@ -3612,28 +3631,28 @@ node-gyp-build@^4.2.1:
|
||||
resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.5.0.tgz#7a64eefa0b21112f89f58379da128ac177f20e40"
|
||||
integrity sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg==
|
||||
|
||||
node-gyp@10.0.1, node-gyp@^9.0.0:
|
||||
version "10.0.1"
|
||||
resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-10.0.1.tgz#205514fc19e5830fa991e4a689f9e81af377a966"
|
||||
integrity sha512-gg3/bHehQfZivQVfqIyy8wTdSymF9yTyP4CJifK73imyNMU8AIGQE2pUa7dNWfmMeG9cDVF2eehiRMv0LC1iAg==
|
||||
node-gyp@11.1.0, node-gyp@^9.0.0:
|
||||
version "11.1.0"
|
||||
resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-11.1.0.tgz#212a1d9c167c50d727d42659410780b40e07bbd3"
|
||||
integrity sha512-/+7TuHKnBpnMvUQnsYEb0JOozDZqarQbfNuSGLXIjhStMT0fbw7IdSqWgopOP5xhRZE+lsbIvAHcekddruPZgQ==
|
||||
dependencies:
|
||||
env-paths "^2.2.0"
|
||||
exponential-backoff "^3.1.1"
|
||||
glob "^10.3.10"
|
||||
graceful-fs "^4.2.6"
|
||||
make-fetch-happen "^13.0.0"
|
||||
nopt "^7.0.0"
|
||||
proc-log "^3.0.0"
|
||||
make-fetch-happen "^14.0.3"
|
||||
nopt "^8.0.0"
|
||||
proc-log "^5.0.0"
|
||||
semver "^7.3.5"
|
||||
tar "^6.1.2"
|
||||
which "^4.0.0"
|
||||
tar "^7.4.3"
|
||||
which "^5.0.0"
|
||||
|
||||
nopt@^7.0.0:
|
||||
version "7.2.1"
|
||||
resolved "https://registry.yarnpkg.com/nopt/-/nopt-7.2.1.tgz#1cac0eab9b8e97c9093338446eddd40b2c8ca1e7"
|
||||
integrity sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==
|
||||
nopt@^8.0.0:
|
||||
version "8.1.0"
|
||||
resolved "https://registry.yarnpkg.com/nopt/-/nopt-8.1.0.tgz#b11d38caf0f8643ce885818518064127f602eae3"
|
||||
integrity sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A==
|
||||
dependencies:
|
||||
abbrev "^2.0.0"
|
||||
abbrev "^3.0.0"
|
||||
|
||||
normalize-package-data@^2.3.2:
|
||||
version "2.5.0"
|
||||
@@ -3791,6 +3810,11 @@ p-map@^4.0.0:
|
||||
dependencies:
|
||||
aggregate-error "^3.0.0"
|
||||
|
||||
p-map@^7.0.2:
|
||||
version "7.0.3"
|
||||
resolved "https://registry.yarnpkg.com/p-map/-/p-map-7.0.3.tgz#7ac210a2d36f81ec28b736134810f7ba4418cdb6"
|
||||
integrity sha512-VkndIv2fIB99swvQoA65bm+fsmt6UNdGeIB0oxBs+WhAhdh08QA04JXpI7rbB9r08/nkbysKoya9rtDERYOYMA==
|
||||
|
||||
p-try@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3"
|
||||
@@ -3987,15 +4011,10 @@ postject@^1.0.0-alpha.6:
|
||||
dependencies:
|
||||
commander "^9.4.0"
|
||||
|
||||
proc-log@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/proc-log/-/proc-log-3.0.0.tgz#fb05ef83ccd64fd7b20bbe9c8c1070fc08338dd8"
|
||||
integrity sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==
|
||||
|
||||
proc-log@^4.2.0:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/proc-log/-/proc-log-4.2.0.tgz#b6f461e4026e75fdfe228b265e9f7a00779d7034"
|
||||
integrity sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==
|
||||
proc-log@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/proc-log/-/proc-log-5.0.0.tgz#e6c93cf37aef33f835c53485f314f50ea906a9d8"
|
||||
integrity sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ==
|
||||
|
||||
process-nextick-args@~2.0.0:
|
||||
version "2.0.1"
|
||||
@@ -4278,6 +4297,13 @@ rimraf@^3.0.0, rimraf@^3.0.2:
|
||||
dependencies:
|
||||
glob "^7.1.3"
|
||||
|
||||
rimraf@^5.0.5:
|
||||
version "5.0.10"
|
||||
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-5.0.10.tgz#23b9843d3dc92db71f96e1a2ce92e39fd2a8221c"
|
||||
integrity sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==
|
||||
dependencies:
|
||||
glob "^10.3.7"
|
||||
|
||||
rimraf@~2.6.2:
|
||||
version "2.6.3"
|
||||
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab"
|
||||
@@ -4599,10 +4625,10 @@ sprintf-js@^1.1.3:
|
||||
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.3.tgz#4914b903a2f8b685d17fdf78a70e917e872e444a"
|
||||
integrity sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==
|
||||
|
||||
ssri@^10.0.0:
|
||||
version "10.0.6"
|
||||
resolved "https://registry.yarnpkg.com/ssri/-/ssri-10.0.6.tgz#a8aade2de60ba2bce8688e3fa349bad05c7dc1e5"
|
||||
integrity sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ==
|
||||
ssri@^12.0.0:
|
||||
version "12.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ssri/-/ssri-12.0.0.tgz#bcb4258417c702472f8191981d3c8a771fee6832"
|
||||
integrity sha512-S7iGNosepx9RadX82oimUkvr0Ct7IjJbEbs4mJcTxst8um95J3sDYU1RBEOvdu6oL1Wek2ODI5i4MAw+dZ6cAQ==
|
||||
dependencies:
|
||||
minipass "^7.0.3"
|
||||
|
||||
@@ -4625,7 +4651,7 @@ stream-buffers@~2.2.0:
|
||||
is-fullwidth-code-point "^3.0.0"
|
||||
strip-ansi "^6.0.1"
|
||||
|
||||
string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3, string-width@^5.0.0, string-width@^5.1.2:
|
||||
string-width@4.2.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3, string-width@^5.0.0, string-width@^5.1.2:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.0.tgz#952182c46cc7b2c313d1596e623992bd163b72b5"
|
||||
integrity sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==
|
||||
@@ -4703,7 +4729,7 @@ supports-preserve-symlinks-flag@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
|
||||
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
|
||||
|
||||
tar@^6.0.5, tar@^6.1.11, tar@^6.1.2:
|
||||
tar@^6.0.5, tar@^6.1.11:
|
||||
version "6.1.11"
|
||||
resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621"
|
||||
integrity sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==
|
||||
@@ -4727,6 +4753,18 @@ tar@^6.1.12:
|
||||
mkdirp "^1.0.3"
|
||||
yallist "^4.0.0"
|
||||
|
||||
tar@^7.4.3:
|
||||
version "7.4.3"
|
||||
resolved "https://registry.yarnpkg.com/tar/-/tar-7.4.3.tgz#88bbe9286a3fcd900e94592cda7a22b192e80571"
|
||||
integrity sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==
|
||||
dependencies:
|
||||
"@isaacs/fs-minipass" "^4.0.0"
|
||||
chownr "^3.0.0"
|
||||
minipass "^7.1.2"
|
||||
minizlib "^3.0.1"
|
||||
mkdirp "^3.0.1"
|
||||
yallist "^5.0.0"
|
||||
|
||||
temp-file@^3.4.0:
|
||||
version "3.4.0"
|
||||
resolved "https://registry.yarnpkg.com/temp-file/-/temp-file-3.4.0.tgz#766ea28911c683996c248ef1a20eea04d51652c7"
|
||||
@@ -4857,22 +4895,22 @@ typescript@^5.4.3:
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.7.2.tgz#3169cf8c4c8a828cde53ba9ecb3d2b1d5dd67be6"
|
||||
integrity sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==
|
||||
|
||||
undici-types@~6.19.2:
|
||||
version "6.19.8"
|
||||
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02"
|
||||
integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==
|
||||
undici-types@~6.20.0:
|
||||
version "6.20.0"
|
||||
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.20.0.tgz#8171bf22c1f588d1554d55bf204bc624af388433"
|
||||
integrity sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==
|
||||
|
||||
unique-filename@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-3.0.0.tgz#48ba7a5a16849f5080d26c760c86cf5cf05770ea"
|
||||
integrity sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==
|
||||
dependencies:
|
||||
unique-slug "^4.0.0"
|
||||
|
||||
unique-slug@^4.0.0:
|
||||
unique-filename@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-4.0.0.tgz#6bae6bb16be91351badd24cdce741f892a6532e3"
|
||||
integrity sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==
|
||||
resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-4.0.0.tgz#a06534d370e7c977a939cd1d11f7f0ab8f1fed13"
|
||||
integrity sha512-XSnEewXmQ+veP7xX2dS5Q4yZAvO40cBN2MWkJ7D/6sW4Dg6wYBNwM1Vrnz1FhH5AdeLIlUXRI9e28z1YZi71NQ==
|
||||
dependencies:
|
||||
unique-slug "^5.0.0"
|
||||
|
||||
unique-slug@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-5.0.0.tgz#ca72af03ad0dbab4dad8aa683f633878b1accda8"
|
||||
integrity sha512-9OdaqO5kwqR+1kVgHAhsp5vPNU0hnxRa26rBFNfNgM7M6pNtgzeBn3s/xbyCQL3dcjzOatcef6UUHpB/6MaETg==
|
||||
dependencies:
|
||||
imurmurhash "^0.1.4"
|
||||
|
||||
@@ -4994,10 +5032,10 @@ which@^2.0.1, which@^2.0.2:
|
||||
dependencies:
|
||||
isexe "^2.0.0"
|
||||
|
||||
which@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/which/-/which-4.0.0.tgz#cd60b5e74503a3fbcfbf6cd6b4138a8bae644c1a"
|
||||
integrity sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==
|
||||
which@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/which/-/which-5.0.0.tgz#d93f2d93f79834d4363c7d0c23e00d07c466c8d6"
|
||||
integrity sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==
|
||||
dependencies:
|
||||
isexe "^3.1.1"
|
||||
|
||||
@@ -5054,6 +5092,11 @@ yallist@^4.0.0:
|
||||
resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
|
||||
integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
|
||||
|
||||
yallist@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/yallist/-/yallist-5.0.0.tgz#00e2de443639ed0d78fd87de0d27469fbcffb533"
|
||||
integrity sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==
|
||||
|
||||
yargs-parser@^20.2.2:
|
||||
version "20.2.9"
|
||||
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee"
|
||||
|
||||
Reference in New Issue
Block a user