mirror of
https://github.com/logseq/logseq.git
synced 2026-05-28 06:34:34 +00:00
Merge branch 'logseq:whiteboards' into whiteboards
This commit is contained in:
2
deps.edn
2
deps.edn
@@ -18,7 +18,7 @@
|
||||
cljs-http/cljs-http {:mvn/version "0.1.46"}
|
||||
org.babashka/sci {:mvn/version "0.3.2"}
|
||||
hickory/hickory {:git/url "https://github.com/logseq/hickory"
|
||||
:sha "9c2c2f1fc2c45efaad906e0faabc3201278deeaa"}
|
||||
:sha "86baee0319a66b7921719a5d2345e09734b23650"}
|
||||
hiccups/hiccups {:mvn/version "0.3.0"}
|
||||
tongue/tongue {:mvn/version "0.4.4"}
|
||||
org.clojure/core.async {:mvn/version "1.3.610"}
|
||||
|
||||
3
deps/graph-parser/.clj-kondo/config.edn
vendored
3
deps/graph-parser/.clj-kondo/config.edn
vendored
@@ -1,6 +1,7 @@
|
||||
{:linters
|
||||
{:consistent-alias
|
||||
{:aliases {datascript.core d
|
||||
{:aliases {clojure.string string
|
||||
datascript.core d
|
||||
logseq.graph-parser graph-parser
|
||||
logseq.graph-parser.text text
|
||||
logseq.graph-parser.block gp-block
|
||||
|
||||
16
deps/graph-parser/src/logseq/graph_parser.cljs
vendored
16
deps/graph-parser/src/logseq/graph_parser.cljs
vendored
@@ -24,14 +24,15 @@
|
||||
(db-set-file-content! conn file content)
|
||||
(let [format (gp-util/get-format file)
|
||||
file-content [{:file/path file}]
|
||||
tx (if (contains? gp-config/mldoc-support-formats format)
|
||||
{:keys [tx ast]}
|
||||
(if (contains? gp-config/mldoc-support-formats format)
|
||||
(let [extract-options' (merge {:block-pattern (gp-config/get-block-pattern format)
|
||||
:date-formatter "MMM do, yyyy"
|
||||
:supported-formats (gp-config/supported-formats)}
|
||||
extract-options
|
||||
{:db @conn})
|
||||
[pages blocks]
|
||||
(extract/extract-blocks-pages file content extract-options')
|
||||
{:keys [pages blocks ast]}
|
||||
(extract/extract file content extract-options')
|
||||
delete-blocks (delete-blocks-fn (first pages) file)
|
||||
block-ids (map (fn [block] {:block/uuid (:block/uuid block)}) blocks)
|
||||
block-refs-ids (->> (mapcat :block/refs blocks)
|
||||
@@ -44,13 +45,16 @@
|
||||
pages (extract/with-ref-pages pages blocks)
|
||||
pages-index (map #(select-keys % [:block/name]) pages)]
|
||||
;; does order matter?
|
||||
(concat file-content pages-index delete-blocks pages block-ids blocks))
|
||||
file-content)
|
||||
{:tx (concat file-content pages-index delete-blocks pages block-ids blocks)
|
||||
:ast ast})
|
||||
{:tx file-content})
|
||||
tx (concat tx [(cond-> {:file/path file}
|
||||
new?
|
||||
;; TODO: use file system timestamp?
|
||||
(assoc :file/created-at (date-time-util/time-ms)))])]
|
||||
(d/transact! conn (gp-util/remove-nils tx) (select-keys options [:new-graph? :from-disk?]))))
|
||||
{:tx
|
||||
(d/transact! conn (gp-util/remove-nils tx) (select-keys options [:new-graph? :from-disk?]))
|
||||
:ast ast}))
|
||||
|
||||
(defn filter-files
|
||||
"Filters files in preparation for parsing. Only includes files that are
|
||||
|
||||
@@ -46,8 +46,12 @@ TODO: Fail fast when process exits 1"
|
||||
[conn files {:keys [config] :as options}]
|
||||
(let [extract-options (merge {:date-formatter (gp-config/get-date-formatter config)}
|
||||
(select-keys options [:verbose]))]
|
||||
(doseq [{:file/keys [path content]} files]
|
||||
(graph-parser/parse-file conn path content {:extract-options extract-options}))))
|
||||
(mapv
|
||||
(fn [{:file/keys [path content]}]
|
||||
(let [{:keys [ast]}
|
||||
(graph-parser/parse-file conn path content {:extract-options extract-options})]
|
||||
{:file path :ast ast}))
|
||||
files)))
|
||||
|
||||
(defn parse-graph
|
||||
"Parses a given graph directory and returns a datascript connection and all
|
||||
@@ -61,8 +65,9 @@ TODO: Fail fast when process exits 1"
|
||||
([dir options]
|
||||
(let [files (or (:files options) (build-graph-files dir))
|
||||
conn (ldb/start-conn)
|
||||
config (read-config dir)]
|
||||
(when-not (:files options) (println "Parsing" (count files) "files..."))
|
||||
(parse-files conn files (merge options {:config config}))
|
||||
config (read-config dir)
|
||||
_ (when-not (:files options) (println "Parsing" (count files) "files..."))
|
||||
asts (parse-files conn files (merge options {:config config}))]
|
||||
{:conn conn
|
||||
:files (map :file/path files)})))
|
||||
:files (map :file/path files)
|
||||
:asts asts})))
|
||||
|
||||
@@ -138,16 +138,17 @@
|
||||
(catch :default e
|
||||
(log/error :exception e))))
|
||||
|
||||
(defn extract-blocks-pages
|
||||
(defn extract
|
||||
"Extracts pages, blocks and ast from given file"
|
||||
[file content {:keys [user-config verbose] :or {verbose true} :as options}]
|
||||
(if (string/blank? content)
|
||||
[]
|
||||
(let [format (gp-util/get-format file)
|
||||
_ (when verbose (println "Parsing start: " file))
|
||||
ast (gp-mldoc/->edn content (gp-mldoc/default-config format
|
||||
;; {:parse_outline_only? true}
|
||||
)
|
||||
user-config)]
|
||||
;; {:parse_outline_only? true}
|
||||
)
|
||||
user-config)]
|
||||
(when verbose (println "Parsing finished: " file))
|
||||
(let [first-block (ffirst ast)
|
||||
properties (let [properties (and (gp-property/properties-ast? first-block)
|
||||
@@ -165,10 +166,11 @@
|
||||
(update properties :filters
|
||||
(fn [v]
|
||||
(string/replace (or v "") "\\" "")))
|
||||
properties)))]
|
||||
(extract-pages-and-blocks
|
||||
format ast properties
|
||||
file content options)))))
|
||||
properties)))
|
||||
[pages blocks] (extract-pages-and-blocks format ast properties file content options)]
|
||||
{:pages pages
|
||||
:blocks blocks
|
||||
:ast ast}))))
|
||||
|
||||
(defn- with-block-uuid
|
||||
[pages]
|
||||
|
||||
@@ -1,13 +1,25 @@
|
||||
(ns logseq.graph-parser.cli-test
|
||||
(:require [cljs.test :refer [deftest]]
|
||||
(:require [cljs.test :refer [deftest is testing]]
|
||||
[logseq.graph-parser.cli :as gp-cli]
|
||||
[logseq.graph-parser.test.docs-graph-helper :as docs-graph-helper]))
|
||||
[logseq.graph-parser.test.docs-graph-helper :as docs-graph-helper]
|
||||
[clojure.string :as string]))
|
||||
|
||||
;; Integration test that test parsing a large graph like docs
|
||||
(deftest ^:integration parse-graph
|
||||
(let [graph-dir "test/docs"
|
||||
_ (docs-graph-helper/clone-docs-repo-if-not-exists graph-dir)
|
||||
{:keys [conn files]} (gp-cli/parse-graph graph-dir)
|
||||
db @conn]
|
||||
{:keys [conn files asts]} (gp-cli/parse-graph graph-dir)]
|
||||
|
||||
(docs-graph-helper/docs-graph-assertions db files)))
|
||||
(docs-graph-helper/docs-graph-assertions @conn files)
|
||||
|
||||
(testing "Asts"
|
||||
(is (seq asts) "Asts returned are non-zero")
|
||||
(is (= files (map :file asts))
|
||||
"There's an ast returned for every file processed")
|
||||
(is (empty? (remove #(or
|
||||
(seq (:ast %))
|
||||
;; logseq files don't have ast
|
||||
;; could also used gp-config but API isn't public yet
|
||||
(string/includes? (:file %) (str graph-dir "/logseq/")))
|
||||
asts))
|
||||
"Parsed files shouldn't have empty asts"))))
|
||||
|
||||
@@ -5,16 +5,15 @@
|
||||
|
||||
(defn- extract
|
||||
[text]
|
||||
(let [result (extract/extract-blocks-pages "a.md" text {:block-pattern "-"})
|
||||
result (last result)
|
||||
lefts (map (juxt :block/parent :block/left) result)]
|
||||
(let [{:keys [blocks]} (extract/extract "a.md" text {:block-pattern "-"})
|
||||
lefts (map (juxt :block/parent :block/left) blocks)]
|
||||
(if (not= (count lefts) (count (distinct lefts)))
|
||||
(do
|
||||
(pprint/pprint (map (fn [x] (select-keys x [:block/uuid :block/level :block/content :block/left])) result))
|
||||
(pprint/pprint (map (fn [x] (select-keys x [:block/uuid :block/level :block/content :block/left])) blocks))
|
||||
(throw (js/Error. ":block/parent && :block/left conflicts")))
|
||||
(mapv :block/content result))))
|
||||
(mapv :block/content blocks))))
|
||||
|
||||
(deftest test-extract-blocks-pages
|
||||
(deftest test-extract
|
||||
[]
|
||||
(is (= ["a" "b" "c"]
|
||||
(extract
|
||||
|
||||
@@ -223,5 +223,5 @@ test('invalid page props #3944', async ({ page, block }) => {
|
||||
await block.mustFill('public:: true\nsize:: 65535')
|
||||
await page.press('textarea >> nth=0', 'Enter')
|
||||
// Force rendering property block
|
||||
await block.clickNext()
|
||||
await block.enterNext()
|
||||
})
|
||||
|
||||
@@ -33,21 +33,21 @@ test('drop to left center', async ({ page }) => {
|
||||
})
|
||||
|
||||
|
||||
test('drop to upper left', async ({ page }) => {
|
||||
test('drop to upper left', async ({ page, block }) => {
|
||||
await createRandomPage(page)
|
||||
|
||||
await page.fill('textarea >> nth=0', 'block a')
|
||||
await enterNextBlock(page)
|
||||
await block.mustFill('block a')
|
||||
await block.enterNext()
|
||||
|
||||
await page.fill('textarea >> nth=0', 'block b')
|
||||
await page.press('textarea >> nth=0', 'Escape')
|
||||
await block.mustFill('block b')
|
||||
await block.escapeEditing()
|
||||
|
||||
const bullet = page.locator('span.bullet-container >> nth=-1')
|
||||
const where = page.locator('.ls-block >> nth=0')
|
||||
await bullet.dragTo(where, {
|
||||
targetPosition: {
|
||||
x: 30,
|
||||
y: 5
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -14,8 +14,6 @@
|
||||
504EC30F1FED79650016851F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 504EC30E1FED79650016851F /* Assets.xcassets */; };
|
||||
504EC3121FED79650016851F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 504EC3101FED79650016851F /* LaunchScreen.storyboard */; };
|
||||
50B271D11FEDC1A000F3C39B /* public in Resources */ = {isa = PBXBuildFile; fileRef = 50B271D01FEDC1A000F3C39B /* public */; };
|
||||
5FD5BB71278579F5008E6875 /* DownloadiCloudFiles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FD5BB70278579F5008E6875 /* DownloadiCloudFiles.swift */; };
|
||||
5FD5BB73278579FF008E6875 /* DownloadiCloudFiles.m in Sources */ = {isa = PBXBuildFile; fileRef = 5FD5BB72278579FF008E6875 /* DownloadiCloudFiles.m */; };
|
||||
5FF8632A283B5ADB0047731B /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FF86329283B5ADB0047731B /* Utils.swift */; };
|
||||
5FF8632C283B5BFD0047731B /* Utils.m in Sources */ = {isa = PBXBuildFile; fileRef = 5FF8632B283B5BFD0047731B /* Utils.m */; };
|
||||
5FFF7D6D27E343FA00B00DA8 /* ShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FFF7D6C27E343FA00B00DA8 /* ShareViewController.swift */; };
|
||||
@@ -70,8 +68,6 @@
|
||||
504EC3111FED79650016851F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||
504EC3131FED79650016851F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
50B271D01FEDC1A000F3C39B /* public */ = {isa = PBXFileReference; lastKnownFileType = folder; path = public; sourceTree = "<group>"; };
|
||||
5FD5BB70278579F5008E6875 /* DownloadiCloudFiles.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DownloadiCloudFiles.swift; sourceTree = "<group>"; };
|
||||
5FD5BB72278579FF008E6875 /* DownloadiCloudFiles.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DownloadiCloudFiles.m; sourceTree = "<group>"; };
|
||||
5FF86329283B5ADB0047731B /* Utils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Utils.swift; sourceTree = "<group>"; };
|
||||
5FF8632B283B5BFD0047731B /* Utils.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Utils.m; sourceTree = "<group>"; };
|
||||
5FFF7D6A27E343FA00B00DA8 /* ShareViewController.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = ShareViewController.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
@@ -144,8 +140,6 @@
|
||||
children = (
|
||||
5FF86329283B5ADB0047731B /* Utils.swift */,
|
||||
5FF8632B283B5BFD0047731B /* Utils.m */,
|
||||
5FD5BB72278579FF008E6875 /* DownloadiCloudFiles.m */,
|
||||
5FD5BB70278579F5008E6875 /* DownloadiCloudFiles.swift */,
|
||||
D32752BF2754C5AB0039291C /* AppDebug.entitlements */,
|
||||
D32752BC275496A60039291C /* App.entitlements */,
|
||||
50379B222058CBB4000EE86E /* capacitor.config.json */,
|
||||
@@ -359,13 +353,11 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
504EC3081FED79650016851F /* AppDelegate.swift in Sources */,
|
||||
5FD5BB71278579F5008E6875 /* DownloadiCloudFiles.swift in Sources */,
|
||||
FE443F1E27FF54AA007ECE65 /* Payload.swift in Sources */,
|
||||
5FF8632C283B5BFD0047731B /* Utils.m in Sources */,
|
||||
FE8C946B27FD762700C8017B /* FileSync.swift in Sources */,
|
||||
FE647FF427BDFEDE00F3206B /* FsWatcher.swift in Sources */,
|
||||
5FF8632A283B5ADB0047731B /* Utils.swift in Sources */,
|
||||
5FD5BB73278579FF008E6875 /* DownloadiCloudFiles.m in Sources */,
|
||||
D3D62A0A275C92880003FBDC /* FileContainer.swift in Sources */,
|
||||
D3D62A0C275C928F0003FBDC /* FileContainer.m in Sources */,
|
||||
FE443F1C27FF5420007ECE65 /* Extensions.swift in Sources */,
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
//
|
||||
// DownloadiCloudFiles.m
|
||||
// Logseq
|
||||
//
|
||||
// Created by leizhe on 2021/12/29.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <Capacitor/Capacitor.h>
|
||||
|
||||
CAP_PLUGIN(DownloadiCloudFiles, "DownloadiCloudFiles",
|
||||
CAP_PLUGIN_METHOD(syncGraph, CAPPluginReturnPromise);
|
||||
)
|
||||
@@ -1,109 +0,0 @@
|
||||
//
|
||||
// DownloadiCloudFiles.swift
|
||||
// Logseq
|
||||
//
|
||||
// Created by leizhe on 2021/12/29.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Capacitor
|
||||
|
||||
@objc(DownloadiCloudFiles)
|
||||
public class DownloadiCloudFiles: CAPPlugin, UIDocumentPickerDelegate {
|
||||
|
||||
public var _call: CAPPluginCall? = nil
|
||||
|
||||
let fileManager = FileManager.default
|
||||
var filesNeededToDownload = Set<String>()
|
||||
let extensions = [
|
||||
"md",
|
||||
"org",
|
||||
"css",
|
||||
"edn",
|
||||
"excalidraw"
|
||||
]
|
||||
|
||||
var containerUrl: URL? {
|
||||
return fileManager.url(forUbiquityContainerIdentifier: nil)?.appendingPathComponent("Documents")
|
||||
}
|
||||
|
||||
var isDirectory: ObjCBool = false
|
||||
var downloaded = false
|
||||
|
||||
@objc func syncGraph(_ call: CAPPluginCall) {
|
||||
|
||||
guard let graph = call.options["graph"] as? String else {
|
||||
call.reject("Missing graph name")
|
||||
return
|
||||
}
|
||||
|
||||
let ignores = [".git", ".trash", "bak", ".recycle"]
|
||||
|
||||
if let url = self.containerUrl?.appendingPathComponent(graph) {
|
||||
do {
|
||||
downloaded = try self.downloadAllFilesFromCloud(at: url, ignorePattern: ignores)
|
||||
handleDownloadFolderLoop()
|
||||
} catch {
|
||||
print(error.localizedDescription)
|
||||
}
|
||||
}
|
||||
call.resolve(["success": downloaded])
|
||||
}
|
||||
|
||||
func appendUndownloadedFile(at url: URL){
|
||||
var lastPathComponent = url.lastPathComponent
|
||||
lastPathComponent.removeFirst()
|
||||
let dirPath = url.deletingLastPathComponent().path
|
||||
let filePath = dirPath + "/" + lastPathComponent.replacingOccurrences(of: ".icloud", with: "")
|
||||
let neededToHandle = !extensions.allSatisfy{ !filePath.hasSuffix($0) }
|
||||
|
||||
if neededToHandle {
|
||||
filesNeededToDownload.insert(filePath)
|
||||
}
|
||||
}
|
||||
|
||||
func downloadAllFilesFromCloud(at url: URL, ignorePattern ignores: [String] = []) throws -> Bool {
|
||||
|
||||
let files = try fileManager.contentsOfDirectory(at: url, includingPropertiesForKeys: nil, options: [])
|
||||
|
||||
for file in files {
|
||||
if file.pathExtension.lowercased() == "icloud" {
|
||||
do {
|
||||
try fileManager.startDownloadingUbiquitousItem(at: file)
|
||||
appendUndownloadedFile(at: file)
|
||||
} catch {
|
||||
print("Unexpected error: \(error).")
|
||||
}
|
||||
|
||||
} else {
|
||||
if fileManager.fileExists(atPath: file.path, isDirectory:&isDirectory) {
|
||||
if isDirectory.boolValue && !ignores.contains(file.lastPathComponent) {
|
||||
if try downloadAllFilesFromCloud(at: file, ignorePattern: ignores) {
|
||||
downloaded = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return downloaded
|
||||
}
|
||||
|
||||
|
||||
func handleDownloadFolder() {
|
||||
for file in filesNeededToDownload {
|
||||
if fileManager.fileExists(atPath: file) {
|
||||
filesNeededToDownload.remove(file)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func handleDownloadFolderLoop () {
|
||||
while !filesNeededToDownload.isEmpty {
|
||||
let count = filesNeededToDownload.count
|
||||
let interval = min(Double(count) * 0.1, 2)
|
||||
Thread.sleep(forTimeInterval: interval)
|
||||
handleDownloadFolder()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,17 +11,26 @@ import MobileCoreServices
|
||||
@objc(FileContainer)
|
||||
public class FileContainer: CAPPlugin, UIDocumentPickerDelegate {
|
||||
|
||||
public var _call: CAPPluginCall? = nil
|
||||
|
||||
var containerUrl: URL? {
|
||||
var iCloudContainerUrl: URL? {
|
||||
return FileManager.default.url(forUbiquityContainerIdentifier: nil)?.appendingPathComponent("Documents")
|
||||
}
|
||||
|
||||
var localContainerUrl: URL? {
|
||||
return FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first
|
||||
}
|
||||
|
||||
@objc func ensureDocuments(_ call: CAPPluginCall) {
|
||||
self._call = call
|
||||
|
||||
// check for container existence
|
||||
if let url = self.containerUrl, !FileManager.default.fileExists(atPath: url.path, isDirectory: nil) {
|
||||
|
||||
validateDocuments(at: self.iCloudContainerUrl!)
|
||||
validateDocuments(at: self.localContainerUrl!)
|
||||
|
||||
call.resolve(["path": [self.iCloudContainerUrl?.path as Any,
|
||||
self.localContainerUrl?.path as Any]])
|
||||
}
|
||||
|
||||
func validateDocuments(at url: URL) {
|
||||
|
||||
if !FileManager.default.fileExists(atPath: url.path, isDirectory: nil) {
|
||||
do {
|
||||
print("the url = " + url.path)
|
||||
try FileManager.default.createDirectory(at: url, withIntermediateDirectories: true, attributes: nil)
|
||||
@@ -31,20 +40,19 @@ public class FileContainer: CAPPlugin, UIDocumentPickerDelegate {
|
||||
print(error.localizedDescription)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let str = ""
|
||||
guard let filename = self.containerUrl?.appendingPathComponent(".logseq") else {
|
||||
return
|
||||
}
|
||||
let filename = url.appendingPathComponent(".logseq", isDirectory: false)
|
||||
|
||||
if !FileManager.default.fileExists(atPath: filename.path) {
|
||||
do {
|
||||
try str.write(to: filename, atomically: true, encoding: String.Encoding.utf8)
|
||||
}
|
||||
catch {
|
||||
// failed to write file – bad permissions, bad filename, missing permissions, or more likely it can't be converted to the encoding
|
||||
print("write .logseq failed")
|
||||
print(error.localizedDescription)
|
||||
}
|
||||
}
|
||||
self._call?.resolve(["path": self.containerUrl?.path as Any])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@logseq/libs",
|
||||
"version": "0.0.6",
|
||||
"version": "0.0.7",
|
||||
"description": "Logseq SDK libraries",
|
||||
"main": "dist/lsplugin.user.js",
|
||||
"typings": "index.d.ts",
|
||||
@@ -15,9 +15,9 @@
|
||||
"fix": "prettier --write \"src/**/*.{ts, js}\""
|
||||
},
|
||||
"dependencies": {
|
||||
"csstype": "3.0.8",
|
||||
"debug": "4.3.1",
|
||||
"dompurify": "2.3.1",
|
||||
"csstype": "3.1.0",
|
||||
"debug": "4.3.4",
|
||||
"dompurify": "2.3.8",
|
||||
"eventemitter3": "4.0.7",
|
||||
"fast-deep-equal": "3.1.3",
|
||||
"lodash-es": "4.17.21",
|
||||
@@ -26,14 +26,14 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/debug": "^4.1.5",
|
||||
"@types/dompurify": "^2.2.1",
|
||||
"@types/lodash-es": "^4.17.4",
|
||||
"@types/dompurify": "2.3.3",
|
||||
"@types/lodash-es": "4.17.6",
|
||||
"prettier": "^2.6.2",
|
||||
"prettier-config-standard": "^5.0.0",
|
||||
"ts-loader": "^8.0.17",
|
||||
"typescript": "^4.2.2",
|
||||
"webpack": "^5.24.3",
|
||||
"webpack-bundle-analyzer": "^4.4.0",
|
||||
"webpack-cli": "^4.5.0"
|
||||
"ts-loader": "9.3.0",
|
||||
"typescript": "4.7.3",
|
||||
"webpack": "5.73.0",
|
||||
"webpack-bundle-analyzer": "4.5.0",
|
||||
"webpack-cli": "4.9.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -281,7 +281,7 @@ class LSPluginCaller extends EventEmitter {
|
||||
})
|
||||
|
||||
this._call = async (...args: any) => {
|
||||
// parent all will get message before handshaked
|
||||
// parent all will get message before handshake
|
||||
await refChild.call(LSPMSGFn(pl.id), {
|
||||
type: args[0],
|
||||
payload: Object.assign(args[1] || {}, {
|
||||
|
||||
@@ -1126,7 +1126,7 @@ class LSPluginCore
|
||||
| 'registered'
|
||||
| 'error'
|
||||
| 'unregistered'
|
||||
| 'theme-changed'
|
||||
| 'themes-changed'
|
||||
| 'theme-selected'
|
||||
| 'reset-custom-theme'
|
||||
| 'settings-changed'
|
||||
@@ -1510,7 +1510,7 @@ class LSPluginCore
|
||||
}
|
||||
|
||||
themes.push(opt)
|
||||
this.emit('theme-changed', this.themes, { id, ...opt })
|
||||
this.emit('themes-changed', this.themes, { id, ...opt })
|
||||
}
|
||||
|
||||
async selectTheme(
|
||||
@@ -1571,7 +1571,7 @@ class LSPluginCore
|
||||
}
|
||||
|
||||
this._registeredThemes.delete(id)
|
||||
this.emit('theme-changed', this.themes, { id })
|
||||
this.emit('themes-changed', this.themes, { id })
|
||||
if (effect && this._currentTheme?.pid === id) {
|
||||
this._currentTheme.eject()
|
||||
this._currentTheme = null
|
||||
|
||||
@@ -4,6 +4,9 @@ import EventEmitter from 'eventemitter3'
|
||||
import { LSPluginCaller } from './LSPlugin.caller'
|
||||
import { LSPluginExperiments } from './modules/LSPlugin.Experiments'
|
||||
import { LSPluginFileStorage } from './modules/LSPlugin.Storage'
|
||||
import { LSPluginRequest } from './modules/LSPlugin.Request'
|
||||
|
||||
export type WithOptional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
|
||||
|
||||
export type PluginLocalIdentity = string
|
||||
|
||||
@@ -407,6 +410,7 @@ export interface IAppProxy {
|
||||
// hook events
|
||||
onCurrentGraphChanged: IUserHook
|
||||
onThemeModeChanged: IUserHook<{ mode: 'dark' | 'light' }>
|
||||
onThemeChanged: IUserHook<Partial<{name: string, mode: string, pid: string, url: string}>>
|
||||
onBlockRendererSlotted: IUserSlotHook<{ uuid: BlockUUID }>
|
||||
|
||||
/**
|
||||
@@ -948,6 +952,7 @@ export interface ILSPluginUser extends EventEmitter<LSPluginUserEvents> {
|
||||
Git: IGitProxy
|
||||
UI: IUIProxy
|
||||
|
||||
Request: LSPluginRequest
|
||||
FileStorage: LSPluginFileStorage
|
||||
Experiments: LSPluginExperiments
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ import * as CSS from 'csstype'
|
||||
import EventEmitter from 'eventemitter3'
|
||||
import { LSPluginFileStorage } from './modules/LSPlugin.Storage'
|
||||
import { LSPluginExperiments } from './modules/LSPlugin.Experiments'
|
||||
import { LSPluginRequest } from './modules/LSPlugin.Request'
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
@@ -318,8 +319,7 @@ const KEY_MAIN_UI = 0
|
||||
*/
|
||||
export class LSPluginUser
|
||||
extends EventEmitter<LSPluginUserEvents>
|
||||
implements ILSPluginUser
|
||||
{
|
||||
implements ILSPluginUser {
|
||||
// @ts-ignore
|
||||
private _version: string = LIB_VERSION
|
||||
private _debugTag: string = ''
|
||||
@@ -333,6 +333,7 @@ export class LSPluginUser
|
||||
private _ui = new Map<number, uiState>()
|
||||
|
||||
private _mFileStorage: LSPluginFileStorage
|
||||
private _mRequest: LSPluginRequest
|
||||
private _mExperiments: LSPluginExperiments
|
||||
|
||||
/**
|
||||
@@ -673,6 +674,12 @@ export class LSPluginUser
|
||||
return m
|
||||
}
|
||||
|
||||
get Request(): LSPluginRequest {
|
||||
let m = this._mRequest
|
||||
if (!m) m = this._mRequest = new LSPluginRequest(this)
|
||||
return m
|
||||
}
|
||||
|
||||
get Experiments(): LSPluginExperiments {
|
||||
let m = this._mExperiments
|
||||
if (!m) m = this._mExperiments = new LSPluginExperiments(this)
|
||||
|
||||
@@ -24,7 +24,7 @@ export class LSPluginExperiments {
|
||||
)
|
||||
}
|
||||
|
||||
private invokeExperMethod(type: string, ...args: Array<any>) {
|
||||
public invokeExperMethod(type: string, ...args: Array<any>) {
|
||||
const host = this.ensureHostScope()
|
||||
type = safeSnakeCase(type)?.toLowerCase()
|
||||
return host.logseq.api['exper_' + type]?.apply(host, args)
|
||||
|
||||
145
libs/src/modules/LSPlugin.Request.ts
Normal file
145
libs/src/modules/LSPlugin.Request.ts
Normal file
@@ -0,0 +1,145 @@
|
||||
import { LSPluginUser, WithOptional } from '../LSPlugin.user'
|
||||
import { EventEmitter } from 'eventemitter3'
|
||||
|
||||
export type IRequestOptions<R = any> = {
|
||||
url: string
|
||||
abortable: boolean
|
||||
headers: Record<string, string>
|
||||
method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'
|
||||
data: Object | ArrayBuffer
|
||||
timeout: number
|
||||
returnType: 'json' | 'text' | 'base64' | 'arraybuffer'
|
||||
success: (result: R) => void
|
||||
fail: (err: any) => void
|
||||
final: () => void
|
||||
}
|
||||
|
||||
export type RequestTaskID = string | number
|
||||
|
||||
const CLIENT_MSG_CALLBACK = '#lsp#request#callback'
|
||||
const genTaskCallbackType = (id: RequestTaskID) => `task_callback_${id}`
|
||||
|
||||
/**
|
||||
* Request task
|
||||
*/
|
||||
export class LSPluginRequestTask<R = any> {
|
||||
private readonly _promise: Promise<R>
|
||||
private _aborted: boolean = false
|
||||
|
||||
constructor(
|
||||
private _client: LSPluginRequest,
|
||||
private _requestId: RequestTaskID,
|
||||
private _requestOptions: Partial<IRequestOptions> = {}
|
||||
) {
|
||||
|
||||
this._promise = new Promise<any>((resolve, reject) => {
|
||||
if (!this._requestId) {
|
||||
return reject(null)
|
||||
}
|
||||
|
||||
// task result listener
|
||||
this._client.once(
|
||||
genTaskCallbackType(this._requestId),
|
||||
(e) => {
|
||||
if (e && e instanceof Error) {
|
||||
reject(e)
|
||||
} else {
|
||||
resolve(e)
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
const { success, fail, final } = this._requestOptions
|
||||
|
||||
this._promise
|
||||
.then((res) => {
|
||||
success?.(res)
|
||||
})
|
||||
.catch((e) => {
|
||||
fail?.(e)
|
||||
})
|
||||
.finally(() => {
|
||||
final?.()
|
||||
})
|
||||
}
|
||||
|
||||
abort() {
|
||||
if (
|
||||
!this._requestOptions.abortable ||
|
||||
this._aborted
|
||||
) return
|
||||
|
||||
this._client.ctx._execCallableAPI(
|
||||
'http_request_abort',
|
||||
this._requestId
|
||||
)
|
||||
|
||||
this._aborted = true
|
||||
}
|
||||
|
||||
get promise(): Promise<R> {
|
||||
return this._promise
|
||||
}
|
||||
|
||||
get client(): LSPluginRequest {
|
||||
return this._client
|
||||
}
|
||||
|
||||
get requestId(): RequestTaskID {
|
||||
return this._requestId
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple request client
|
||||
*/
|
||||
export class LSPluginRequest extends EventEmitter {
|
||||
constructor(private _ctx: LSPluginUser) {
|
||||
super()
|
||||
|
||||
// request callback listener
|
||||
this.ctx.caller.on(
|
||||
CLIENT_MSG_CALLBACK,
|
||||
(e: any) => {
|
||||
const reqId = e?.requestId
|
||||
if (!reqId) return
|
||||
|
||||
this.emit(genTaskCallbackType(reqId), e?.payload)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
static createRequestTask(
|
||||
client: LSPluginRequest,
|
||||
requestID: RequestTaskID,
|
||||
requestOptions: Partial<IRequestOptions>
|
||||
) {
|
||||
return new LSPluginRequestTask(
|
||||
client, requestID, requestOptions
|
||||
)
|
||||
}
|
||||
|
||||
async _request<R extends {},
|
||||
T extends WithOptional<IRequestOptions<R>, keyof Omit<IRequestOptions, 'url'>>>(options: T):
|
||||
Promise<T extends Pick<IRequestOptions, 'abortable'> ? LSPluginRequestTask<R> : R> {
|
||||
const pid = this.ctx.baseInfo.id
|
||||
const { success, fail, final, ...requestOptions } = options
|
||||
const reqID = this.ctx.Experiments.invokeExperMethod('request', pid, requestOptions)
|
||||
|
||||
const task = LSPluginRequest.createRequestTask(
|
||||
this.ctx.Request,
|
||||
reqID, options
|
||||
)
|
||||
|
||||
if (!requestOptions.abortable) {
|
||||
return task.promise
|
||||
}
|
||||
|
||||
return task as any
|
||||
}
|
||||
|
||||
get ctx(): LSPluginUser {
|
||||
return this._ctx
|
||||
}
|
||||
}
|
||||
328
libs/yarn.lock
328
libs/yarn.lock
@@ -19,17 +19,17 @@
|
||||
dependencies:
|
||||
"@types/ms" "*"
|
||||
|
||||
"@types/dompurify@^2.2.1":
|
||||
version "2.2.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/dompurify/-/dompurify-2.2.3.tgz#6e89677a07902ac1b6821c345f34bd85da239b08"
|
||||
integrity sha512-CLtc2mZK8+axmrz1JqtpklO/Kvn38arGc8o1l3UVopZaXXuer9ONdZwJ/9f226GrhRLtUmLr9WrvZsRSNpS8og==
|
||||
"@types/dompurify@2.3.3":
|
||||
version "2.3.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/dompurify/-/dompurify-2.3.3.tgz#c24c92f698f77ed9cc9d9fa7888f90cf2bfaa23f"
|
||||
integrity sha512-nnVQSgRVuZ/843oAfhA25eRSNzUFcBPk/LOiw5gm8mD9/X7CNcbRkQu/OsjCewO8+VIYfPxUnXvPEVGenw14+w==
|
||||
dependencies:
|
||||
"@types/trusted-types" "*"
|
||||
|
||||
"@types/eslint-scope@^3.7.0":
|
||||
version "3.7.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.1.tgz#8dc390a7b4f9dd9f1284629efce982e41612116e"
|
||||
integrity sha512-SCFeogqiptms4Fg29WpOTk5nHIzfpKCemSN63ksBQYKTcXoJEmJagV+DhVmbapZzY4/5YaOV1nZwrsU79fFm1g==
|
||||
"@types/eslint-scope@^3.7.3":
|
||||
version "3.7.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.3.tgz#125b88504b61e3c8bc6f870882003253005c3224"
|
||||
integrity sha512-PB3ldyrcnAicT35TWPs5IcwKD8S333HMaa2VVv4+wdvebJkjWuW/xESoB8IwRcog8HYVYamb1g/R31Qv5Bx03g==
|
||||
dependencies:
|
||||
"@types/eslint" "*"
|
||||
"@types/estree" "*"
|
||||
@@ -42,20 +42,25 @@
|
||||
"@types/estree" "*"
|
||||
"@types/json-schema" "*"
|
||||
|
||||
"@types/estree@*", "@types/estree@^0.0.50":
|
||||
"@types/estree@*":
|
||||
version "0.0.50"
|
||||
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.50.tgz#1e0caa9364d3fccd2931c3ed96fdbeaa5d4cca83"
|
||||
integrity sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==
|
||||
|
||||
"@types/estree@^0.0.51":
|
||||
version "0.0.51"
|
||||
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.51.tgz#cfd70924a25a3fd32b218e5e420e6897e1ac4f40"
|
||||
integrity sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==
|
||||
|
||||
"@types/json-schema@*", "@types/json-schema@^7.0.8":
|
||||
version "7.0.9"
|
||||
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d"
|
||||
integrity sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==
|
||||
|
||||
"@types/lodash-es@^4.17.4":
|
||||
version "4.17.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/lodash-es/-/lodash-es-4.17.4.tgz#b2e440d2bf8a93584a9fd798452ec497986c9b97"
|
||||
integrity sha512-BBz79DCJbD2CVYZH67MBeHZRX++HF+5p8Mo5MzjZi64Wac39S3diedJYHZtScbRVf4DjZyN6LzA0SB0zy+HSSQ==
|
||||
"@types/lodash-es@4.17.6":
|
||||
version "4.17.6"
|
||||
resolved "https://registry.yarnpkg.com/@types/lodash-es/-/lodash-es-4.17.6.tgz#c2ed4c8320ffa6f11b43eb89e9eaeec65966a0a0"
|
||||
integrity sha512-R+zTeVUKDdfoRxpAryaQNRKk3105Rrgx2CFRClIgRGaqDTdjsm8h6IYA8ir584W3ePzkZfst5xIgDwYrlh9HLg==
|
||||
dependencies:
|
||||
"@types/lodash" "*"
|
||||
|
||||
@@ -200,22 +205,22 @@
|
||||
"@webassemblyjs/ast" "1.11.1"
|
||||
"@xtuc/long" "4.2.2"
|
||||
|
||||
"@webpack-cli/configtest@^1.0.4":
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-1.0.4.tgz#f03ce6311c0883a83d04569e2c03c6238316d2aa"
|
||||
integrity sha512-cs3XLy+UcxiP6bj0A6u7MLLuwdXJ1c3Dtc0RkKg+wiI1g/Ti1om8+/2hc2A2B60NbBNAbMgyBMHvyymWm/j4wQ==
|
||||
"@webpack-cli/configtest@^1.1.1":
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-1.1.1.tgz#9f53b1b7946a6efc2a749095a4f450e2932e8356"
|
||||
integrity sha512-1FBc1f9G4P/AxMqIgfZgeOTuRnwZMten8E7zap5zgpPInnCrP8D4Q81+4CWIch8i/Nf7nXjP0v6CjjbHOrXhKg==
|
||||
|
||||
"@webpack-cli/info@^1.3.0":
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-1.3.0.tgz#9d78a31101a960997a4acd41ffd9b9300627fe2b"
|
||||
integrity sha512-ASiVB3t9LOKHs5DyVUcxpraBXDOKubYu/ihHhU+t1UPpxsivg6Od2E2qU4gJCekfEddzRBzHhzA/Acyw/mlK/w==
|
||||
"@webpack-cli/info@^1.4.1":
|
||||
version "1.4.1"
|
||||
resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-1.4.1.tgz#2360ea1710cbbb97ff156a3f0f24556e0fc1ebea"
|
||||
integrity sha512-PKVGmazEq3oAo46Q63tpMr4HipI3OPfP7LiNOEJg963RMgT0rqheag28NCML0o3GIzA3DmxP1ZIAv9oTX1CUIA==
|
||||
dependencies:
|
||||
envinfo "^7.7.3"
|
||||
|
||||
"@webpack-cli/serve@^1.5.2":
|
||||
version "1.5.2"
|
||||
resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-1.5.2.tgz#ea584b637ff63c5a477f6f21604b5a205b72c9ec"
|
||||
integrity sha512-vgJ5OLWadI8aKjDlOH3rb+dYyPd2GTZuQC/Tihjct6F9GpXGZINo3Y/IVuZVTM1eDQB+/AOsjPUWH/WySDaXvw==
|
||||
"@webpack-cli/serve@^1.6.1":
|
||||
version "1.6.1"
|
||||
resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-1.6.1.tgz#0de2875ac31b46b6c5bb1ae0a7d7f0ba5678dffe"
|
||||
integrity sha512-gNGTiTrjEVQ0OcVnzsRSqTxaBSr+dmTfm+qJsCDluky8uhdLWep7Gcr62QsAKHTMxjCS/8nEITsmFAhfIx+QSw==
|
||||
|
||||
"@xtuc/ieee754@^1.2.0":
|
||||
version "1.2.0"
|
||||
@@ -264,11 +269,6 @@ ansi-styles@^4.1.0:
|
||||
dependencies:
|
||||
color-convert "^2.0.1"
|
||||
|
||||
big.js@^5.2.2:
|
||||
version "5.2.2"
|
||||
resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328"
|
||||
integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==
|
||||
|
||||
braces@^3.0.1:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
|
||||
@@ -331,31 +331,26 @@ color-name@~1.1.4:
|
||||
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
|
||||
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
|
||||
|
||||
colorette@^1.2.1, colorette@^1.3.0:
|
||||
colorette@^1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.3.0.tgz#ff45d2f0edb244069d3b772adeb04fed38d0a0af"
|
||||
integrity sha512-ecORCqbSFP7Wm8Y6lyqMJjexBQqXSF7SSeaTyGGphogUjBlFP9m9o08wy86HL2uB7fMTxtOUzLMk7ogKcxMg1w==
|
||||
|
||||
colorette@^2.0.14:
|
||||
version "2.0.17"
|
||||
resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.17.tgz#5dd4c0d15e2984b7433cb4a9f2ead45063b80c47"
|
||||
integrity sha512-hJo+3Bkn0NCHybn9Tu35fIeoOKGOk5OCC32y4Hz2It+qlCO2Q3DeQ1hRn/tDDMQKRYUEzqsl7jbF6dYKjlE60g==
|
||||
|
||||
commander@^2.20.0:
|
||||
version "2.20.3"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
|
||||
integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
|
||||
|
||||
commander@^6.2.0:
|
||||
version "6.2.1"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c"
|
||||
integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==
|
||||
|
||||
commander@^7.0.0:
|
||||
commander@^7.0.0, commander@^7.2.0:
|
||||
version "7.2.0"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7"
|
||||
integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==
|
||||
|
||||
core-util-is@~1.0.0:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
|
||||
integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
|
||||
|
||||
cross-spawn@^7.0.3:
|
||||
version "7.0.3"
|
||||
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
|
||||
@@ -365,22 +360,22 @@ cross-spawn@^7.0.3:
|
||||
shebang-command "^2.0.0"
|
||||
which "^2.0.1"
|
||||
|
||||
csstype@3.0.8:
|
||||
version "3.0.8"
|
||||
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.8.tgz#d2266a792729fb227cd216fb572f43728e1ad340"
|
||||
integrity sha512-jXKhWqXPmlUeoQnF/EhTtTl4C9SnrxSH/jZUih3jmO6lBKr99rP3/+FmrMj4EFpOXzMtXHAZkd3x0E6h6Fgflw==
|
||||
csstype@3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.0.tgz#4ddcac3718d787cf9df0d1b7d15033925c8f29f2"
|
||||
integrity sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA==
|
||||
|
||||
debug@4.3.1:
|
||||
version "4.3.1"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee"
|
||||
integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==
|
||||
debug@4.3.4:
|
||||
version "4.3.4"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
|
||||
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
|
||||
dependencies:
|
||||
ms "2.1.2"
|
||||
|
||||
dompurify@2.3.1:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.3.1.tgz#a47059ca21fd1212d3c8f71fdea6943b8bfbdf6a"
|
||||
integrity sha512-xGWt+NHAQS+4tpgbOAI08yxW0Pr256Gu/FNE2frZVTbgrBUn8M7tz7/ktS/LZ2MHeGqz6topj0/xY+y8R5FBFw==
|
||||
dompurify@2.3.8:
|
||||
version "2.3.8"
|
||||
resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.3.8.tgz#224fe9ae57d7ebd9a1ae1ac18c1c1ca3f532226f"
|
||||
integrity sha512-eVhaWoVibIzqdGYjwsBWodIQIaXFSB+cKDf4cfxLMsK0xiud6SE+/WCVx/Xw/UwQsa4cS3T2eITcdtmTg2UKcw==
|
||||
|
||||
dot-case@^3.0.4:
|
||||
version "3.0.4"
|
||||
@@ -400,24 +395,10 @@ electron-to-chromium@^1.3.811:
|
||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.814.tgz#418fad80c3276a46103ca72a21a8290620d83c4a"
|
||||
integrity sha512-0mH03cyjh6OzMlmjauGg0TLd87ErIJqWiYxMcOLKf5w6p0YEOl7DJAj7BDlXEFmCguY5CQaKVOiMjAMODO2XDw==
|
||||
|
||||
emojis-list@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78"
|
||||
integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==
|
||||
|
||||
enhanced-resolve@^4.0.0:
|
||||
version "4.5.0"
|
||||
resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz#2f3cfd84dbe3b487f18f2db2ef1e064a571ca5ec"
|
||||
integrity sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg==
|
||||
dependencies:
|
||||
graceful-fs "^4.1.2"
|
||||
memory-fs "^0.5.0"
|
||||
tapable "^1.0.0"
|
||||
|
||||
enhanced-resolve@^5.8.0:
|
||||
version "5.8.2"
|
||||
resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.8.2.tgz#15ddc779345cbb73e97c611cd00c01c1e7bf4d8b"
|
||||
integrity sha512-F27oB3WuHDzvR2DOGNTaYy0D5o0cnrv8TeI482VM4kYgQd/FT9lUQwuNsJ0oOHtBUq7eiW5ytqzp7nBFknL+GA==
|
||||
enhanced-resolve@^5.0.0, enhanced-resolve@^5.9.3:
|
||||
version "5.9.3"
|
||||
resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.9.3.tgz#44a342c012cbc473254af5cc6ae20ebd0aae5d88"
|
||||
integrity sha512-Bq9VSor+kjvW3f9/MiiR4eE3XYgOl7/rS8lnSxbRbF3kS0B2r+Y9w5krBWxZgDxASVZbdYrn5wT4j/Wb0J9qow==
|
||||
dependencies:
|
||||
graceful-fs "^4.2.4"
|
||||
tapable "^2.2.0"
|
||||
@@ -427,17 +408,10 @@ envinfo@^7.7.3:
|
||||
resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.8.1.tgz#06377e3e5f4d379fea7ac592d5ad8927e0c4d475"
|
||||
integrity sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==
|
||||
|
||||
errno@^0.1.3:
|
||||
version "0.1.8"
|
||||
resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f"
|
||||
integrity sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==
|
||||
dependencies:
|
||||
prr "~1.0.1"
|
||||
|
||||
es-module-lexer@^0.7.1:
|
||||
version "0.7.1"
|
||||
resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.7.1.tgz#c2c8e0f46f2df06274cdaf0dd3f3b33e0a0b267d"
|
||||
integrity sha512-MgtWFl5No+4S3TmhDmCz2ObFGm6lEpTnzbQi+Dd+pw4mlTIZTmM2iAs5gRlmx5zS9luzobCSBSI90JM/1/JgOw==
|
||||
es-module-lexer@^0.9.0:
|
||||
version "0.9.3"
|
||||
resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.9.3.tgz#6f13db00cc38417137daf74366f535c8eb438f19"
|
||||
integrity sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==
|
||||
|
||||
escalade@^3.1.1:
|
||||
version "3.1.1"
|
||||
@@ -544,6 +518,11 @@ graceful-fs@^4.1.2, graceful-fs@^4.2.4:
|
||||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a"
|
||||
integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==
|
||||
|
||||
graceful-fs@^4.2.9:
|
||||
version "4.2.10"
|
||||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c"
|
||||
integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==
|
||||
|
||||
gzip-size@^6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-6.0.0.tgz#065367fd50c239c0671cbcbad5be3e2eeb10e462"
|
||||
@@ -581,11 +560,6 @@ inherits@2.0.3:
|
||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
|
||||
integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
|
||||
|
||||
inherits@~2.0.3:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
|
||||
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
||||
|
||||
interpret@^2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/interpret/-/interpret-2.2.0.tgz#1a78a0b5965c40a5416d007ad6f50ad27c417df9"
|
||||
@@ -615,11 +589,6 @@ is-stream@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077"
|
||||
integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==
|
||||
|
||||
isarray@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
|
||||
integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
|
||||
|
||||
isexe@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
|
||||
@@ -639,23 +608,16 @@ jest-worker@^27.0.2:
|
||||
merge-stream "^2.0.0"
|
||||
supports-color "^8.0.0"
|
||||
|
||||
json-parse-better-errors@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9"
|
||||
integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==
|
||||
json-parse-even-better-errors@^2.3.1:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d"
|
||||
integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==
|
||||
|
||||
json-schema-traverse@^0.4.1:
|
||||
version "0.4.1"
|
||||
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
|
||||
integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
|
||||
|
||||
json5@^2.1.2:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3"
|
||||
integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==
|
||||
dependencies:
|
||||
minimist "^1.2.5"
|
||||
|
||||
kind-of@^6.0.2:
|
||||
version "6.0.3"
|
||||
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd"
|
||||
@@ -666,15 +628,6 @@ loader-runner@^4.2.0:
|
||||
resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.2.0.tgz#d7022380d66d14c5fb1d496b89864ebcfd478384"
|
||||
integrity sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw==
|
||||
|
||||
loader-utils@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.0.tgz#e4cace5b816d425a166b5f097e10cd12b36064b0"
|
||||
integrity sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==
|
||||
dependencies:
|
||||
big.js "^5.2.2"
|
||||
emojis-list "^3.0.0"
|
||||
json5 "^2.1.2"
|
||||
|
||||
locate-path@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0"
|
||||
@@ -706,14 +659,6 @@ lru-cache@^6.0.0:
|
||||
dependencies:
|
||||
yallist "^4.0.0"
|
||||
|
||||
memory-fs@^0.5.0:
|
||||
version "0.5.0"
|
||||
resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.5.0.tgz#324c01288b88652966d161db77838720845a8e3c"
|
||||
integrity sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==
|
||||
dependencies:
|
||||
errno "^0.1.3"
|
||||
readable-stream "^2.0.1"
|
||||
|
||||
merge-stream@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
|
||||
@@ -749,11 +694,6 @@ mimic-fn@^2.1.0:
|
||||
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
|
||||
integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
|
||||
|
||||
minimist@^1.2.5:
|
||||
version "1.2.6"
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44"
|
||||
integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==
|
||||
|
||||
ms@2.1.2:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
|
||||
@@ -867,21 +807,11 @@ prettier@^2.6.2:
|
||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.6.2.tgz#e26d71a18a74c3d0f0597f55f01fb6c06c206032"
|
||||
integrity sha512-PkUpF+qoXTqhOeWL9fu7As8LXsIUZ1WYaJiY/a7McAQzxjk82OF0tibkFXVCDImZtWxbvojFjerkiLb0/q8mew==
|
||||
|
||||
process-nextick-args@~2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
|
||||
integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
|
||||
|
||||
process@^0.11.1:
|
||||
version "0.11.10"
|
||||
resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
|
||||
integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI=
|
||||
|
||||
prr@~1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476"
|
||||
integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY=
|
||||
|
||||
punycode@^2.1.0:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
|
||||
@@ -894,19 +824,6 @@ randombytes@^2.1.0:
|
||||
dependencies:
|
||||
safe-buffer "^5.1.0"
|
||||
|
||||
readable-stream@^2.0.1:
|
||||
version "2.3.7"
|
||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
|
||||
integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
|
||||
dependencies:
|
||||
core-util-is "~1.0.0"
|
||||
inherits "~2.0.3"
|
||||
isarray "~1.0.0"
|
||||
process-nextick-args "~2.0.0"
|
||||
safe-buffer "~5.1.1"
|
||||
string_decoder "~1.1.1"
|
||||
util-deprecate "~1.0.1"
|
||||
|
||||
rechoir@^0.7.0:
|
||||
version "0.7.1"
|
||||
resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.7.1.tgz#9478a96a1ca135b5e88fc027f03ee92d6c645686"
|
||||
@@ -939,11 +856,6 @@ safe-buffer@^5.1.0:
|
||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
|
||||
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
|
||||
|
||||
safe-buffer@~5.1.0, safe-buffer@~5.1.1:
|
||||
version "5.1.2"
|
||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
|
||||
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
|
||||
|
||||
schema-utils@^3.0.0, schema-utils@^3.1.0:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.1.1.tgz#bc74c4b6b6995c1d88f76a8b77bea7219e0c8281"
|
||||
@@ -1026,13 +938,6 @@ source-map@~0.7.2:
|
||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383"
|
||||
integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==
|
||||
|
||||
string_decoder@~1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
|
||||
integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==
|
||||
dependencies:
|
||||
safe-buffer "~5.1.0"
|
||||
|
||||
strip-final-newline@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad"
|
||||
@@ -1052,11 +957,6 @@ supports-color@^8.0.0:
|
||||
dependencies:
|
||||
has-flag "^4.0.0"
|
||||
|
||||
tapable@^1.0.0:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2"
|
||||
integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==
|
||||
|
||||
tapable@^2.1.1, tapable@^2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.0.tgz#5c373d281d9c672848213d0e037d1c4165ab426b"
|
||||
@@ -1095,14 +995,13 @@ totalist@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/totalist/-/totalist-1.1.0.tgz#a4d65a3e546517701e3e5c37a47a70ac97fe56df"
|
||||
integrity sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g==
|
||||
|
||||
ts-loader@^8.0.17:
|
||||
version "8.3.0"
|
||||
resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-8.3.0.tgz#83360496d6f8004fab35825279132c93412edf33"
|
||||
integrity sha512-MgGly4I6cStsJy27ViE32UoqxPTN9Xly4anxxVyaIWR+9BGxboV4EyJBGfR3RePV7Ksjj3rHmPZJeIt+7o4Vag==
|
||||
ts-loader@9.3.0:
|
||||
version "9.3.0"
|
||||
resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-9.3.0.tgz#980f4dbfb60e517179e15e10ed98e454b132159f"
|
||||
integrity sha512-2kLLAdAD+FCKijvGKi9sS0OzoqxLCF3CxHpok7rVgCZ5UldRzH0TkbwG9XECKjBzHsAewntC5oDaI/FwKzEUog==
|
||||
dependencies:
|
||||
chalk "^4.1.0"
|
||||
enhanced-resolve "^4.0.0"
|
||||
loader-utils "^2.0.0"
|
||||
enhanced-resolve "^5.0.0"
|
||||
micromatch "^4.0.0"
|
||||
semver "^7.3.4"
|
||||
|
||||
@@ -1111,10 +1010,10 @@ tslib@^2.0.3:
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01"
|
||||
integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==
|
||||
|
||||
typescript@^4.2.2:
|
||||
version "4.3.5"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.5.tgz#4d1c37cc16e893973c45a06886b7113234f119f4"
|
||||
integrity sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==
|
||||
typescript@4.7.3:
|
||||
version "4.7.3"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.3.tgz#8364b502d5257b540f9de4c40be84c98e23a129d"
|
||||
integrity sha512-WOkT3XYvrpXx4vMMqlD+8R8R37fZkjyLGlxavMc4iB8lrl8L0DeTcHbYgw/v0N/z9wAFsgBhcsF0ruoySS22mA==
|
||||
|
||||
uri-js@^4.2.2:
|
||||
version "4.4.1"
|
||||
@@ -1123,11 +1022,6 @@ uri-js@^4.2.2:
|
||||
dependencies:
|
||||
punycode "^2.1.0"
|
||||
|
||||
util-deprecate@~1.0.1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
|
||||
|
||||
util@^0.10.3:
|
||||
version "0.10.4"
|
||||
resolved "https://registry.yarnpkg.com/util/-/util-0.10.4.tgz#3aa0125bfe668a4672de58857d3ace27ecb76901"
|
||||
@@ -1135,51 +1029,45 @@ util@^0.10.3:
|
||||
dependencies:
|
||||
inherits "2.0.3"
|
||||
|
||||
v8-compile-cache@^2.2.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee"
|
||||
integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==
|
||||
|
||||
watchpack@^2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.2.0.tgz#47d78f5415fe550ecd740f99fe2882323a58b1ce"
|
||||
integrity sha512-up4YAn/XHgZHIxFBVCdlMiWDj6WaLKpwVeGQk2I5thdYxF/KmF0aaz6TfJZ/hfl1h/XlcDr7k1KH7ThDagpFaA==
|
||||
watchpack@^2.3.1:
|
||||
version "2.4.0"
|
||||
resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d"
|
||||
integrity sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==
|
||||
dependencies:
|
||||
glob-to-regexp "^0.4.1"
|
||||
graceful-fs "^4.1.2"
|
||||
|
||||
webpack-bundle-analyzer@^4.4.0:
|
||||
version "4.4.2"
|
||||
resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.4.2.tgz#39898cf6200178240910d629705f0f3493f7d666"
|
||||
integrity sha512-PIagMYhlEzFfhMYOzs5gFT55DkUdkyrJi/SxJp8EF3YMWhS+T9vvs2EoTetpk5qb6VsCq02eXTlRDOydRhDFAQ==
|
||||
webpack-bundle-analyzer@4.5.0:
|
||||
version "4.5.0"
|
||||
resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.5.0.tgz#1b0eea2947e73528754a6f9af3e91b2b6e0f79d5"
|
||||
integrity sha512-GUMZlM3SKwS8Z+CKeIFx7CVoHn3dXFcUAjT/dcZQQmfSZGvitPfMob2ipjai7ovFFqPvTqkEZ/leL4O0YOdAYQ==
|
||||
dependencies:
|
||||
acorn "^8.0.4"
|
||||
acorn-walk "^8.0.0"
|
||||
chalk "^4.1.0"
|
||||
commander "^6.2.0"
|
||||
commander "^7.2.0"
|
||||
gzip-size "^6.0.0"
|
||||
lodash "^4.17.20"
|
||||
opener "^1.5.2"
|
||||
sirv "^1.0.7"
|
||||
ws "^7.3.1"
|
||||
|
||||
webpack-cli@^4.5.0:
|
||||
version "4.8.0"
|
||||
resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-4.8.0.tgz#5fc3c8b9401d3c8a43e2afceacfa8261962338d1"
|
||||
integrity sha512-+iBSWsX16uVna5aAYN6/wjhJy1q/GKk4KjKvfg90/6hykCTSgozbfz5iRgDTSJt/LgSbYxdBX3KBHeobIs+ZEw==
|
||||
webpack-cli@4.9.2:
|
||||
version "4.9.2"
|
||||
resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-4.9.2.tgz#77c1adaea020c3f9e2db8aad8ea78d235c83659d"
|
||||
integrity sha512-m3/AACnBBzK/kMTcxWHcZFPrw/eQuY4Df1TxvIWfWM2x7mRqBQCqKEd96oCUa9jkapLBaFfRce33eGDb4Pr7YQ==
|
||||
dependencies:
|
||||
"@discoveryjs/json-ext" "^0.5.0"
|
||||
"@webpack-cli/configtest" "^1.0.4"
|
||||
"@webpack-cli/info" "^1.3.0"
|
||||
"@webpack-cli/serve" "^1.5.2"
|
||||
colorette "^1.2.1"
|
||||
"@webpack-cli/configtest" "^1.1.1"
|
||||
"@webpack-cli/info" "^1.4.1"
|
||||
"@webpack-cli/serve" "^1.6.1"
|
||||
colorette "^2.0.14"
|
||||
commander "^7.0.0"
|
||||
execa "^5.0.0"
|
||||
fastest-levenshtein "^1.0.12"
|
||||
import-local "^3.0.2"
|
||||
interpret "^2.2.0"
|
||||
rechoir "^0.7.0"
|
||||
v8-compile-cache "^2.2.0"
|
||||
webpack-merge "^5.7.3"
|
||||
|
||||
webpack-merge@^5.7.3:
|
||||
@@ -1190,18 +1078,18 @@ webpack-merge@^5.7.3:
|
||||
clone-deep "^4.0.1"
|
||||
wildcard "^2.0.0"
|
||||
|
||||
webpack-sources@^3.2.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.0.tgz#b16973bcf844ebcdb3afde32eda1c04d0b90f89d"
|
||||
integrity sha512-fahN08Et7P9trej8xz/Z7eRu8ltyiygEo/hnRi9KqBUs80KeDcnf96ZJo++ewWd84fEf3xSX9bp4ZS9hbw0OBw==
|
||||
webpack-sources@^3.2.3:
|
||||
version "3.2.3"
|
||||
resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde"
|
||||
integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==
|
||||
|
||||
webpack@^5.24.3:
|
||||
version "5.51.1"
|
||||
resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.51.1.tgz#41bebf38dccab9a89487b16dbe95c22e147aac57"
|
||||
integrity sha512-xsn3lwqEKoFvqn4JQggPSRxE4dhsRcysWTqYABAZlmavcoTmwlOb9b1N36Inbt/eIispSkuHa80/FJkDTPos1A==
|
||||
webpack@5.73.0:
|
||||
version "5.73.0"
|
||||
resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.73.0.tgz#bbd17738f8a53ee5760ea2f59dce7f3431d35d38"
|
||||
integrity sha512-svjudQRPPa0YiOYa2lM/Gacw0r6PvxptHj4FuEKQ2kX05ZLkjbVc5MnPs6its5j7IZljnIqSVo/OsY2X0IpHGA==
|
||||
dependencies:
|
||||
"@types/eslint-scope" "^3.7.0"
|
||||
"@types/estree" "^0.0.50"
|
||||
"@types/eslint-scope" "^3.7.3"
|
||||
"@types/estree" "^0.0.51"
|
||||
"@webassemblyjs/ast" "1.11.1"
|
||||
"@webassemblyjs/wasm-edit" "1.11.1"
|
||||
"@webassemblyjs/wasm-parser" "1.11.1"
|
||||
@@ -1209,21 +1097,21 @@ webpack@^5.24.3:
|
||||
acorn-import-assertions "^1.7.6"
|
||||
browserslist "^4.14.5"
|
||||
chrome-trace-event "^1.0.2"
|
||||
enhanced-resolve "^5.8.0"
|
||||
es-module-lexer "^0.7.1"
|
||||
enhanced-resolve "^5.9.3"
|
||||
es-module-lexer "^0.9.0"
|
||||
eslint-scope "5.1.1"
|
||||
events "^3.2.0"
|
||||
glob-to-regexp "^0.4.1"
|
||||
graceful-fs "^4.2.4"
|
||||
json-parse-better-errors "^1.0.2"
|
||||
graceful-fs "^4.2.9"
|
||||
json-parse-even-better-errors "^2.3.1"
|
||||
loader-runner "^4.2.0"
|
||||
mime-types "^2.1.27"
|
||||
neo-async "^2.6.2"
|
||||
schema-utils "^3.1.0"
|
||||
tapable "^2.1.1"
|
||||
terser-webpack-plugin "^5.1.3"
|
||||
watchpack "^2.2.0"
|
||||
webpack-sources "^3.2.0"
|
||||
watchpack "^2.3.1"
|
||||
webpack-sources "^3.2.3"
|
||||
|
||||
which@^2.0.1:
|
||||
version "2.0.2"
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
/*! @license DOMPurify 2.3.1 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/2.3.1/LICENSE */
|
||||
/*! @license DOMPurify 2.3.8 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/2.3.8/LICENSE */
|
||||
|
||||
@@ -37,7 +37,8 @@
|
||||
"@sentry/electron": "2.5.1",
|
||||
"posthog-js": "1.10.2",
|
||||
"@logseq/rsapi": "0.0.20",
|
||||
"electron-deeplink": "1.0.10"
|
||||
"electron-deeplink": "1.0.10",
|
||||
"abort-controller": "3.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@electron-forge/cli": "^6.0.0-beta.57",
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
["os" :as os]
|
||||
["diff-match-patch" :as google-diff]
|
||||
["/electron/utils" :as js-utils]
|
||||
["abort-controller" :as AbortController]
|
||||
[electron.fs-watcher :as watcher]
|
||||
[electron.configs :as cfgs]
|
||||
[promesa.core :as p]
|
||||
@@ -368,6 +369,49 @@
|
||||
(defmethod handle :uninstallMarketPlugin [_ [_ id]]
|
||||
(plugin/uninstall! id))
|
||||
|
||||
(def *request-abort-signals (atom {}))
|
||||
|
||||
(defmethod handle :httpRequest [_ [_ _req-id opts]]
|
||||
(let [{:keys [url abortable method data returnType headers]} opts]
|
||||
(when-let [[method type] (and (not (string/blank? url))
|
||||
[(keyword (string/upper-case (or method "GET")))
|
||||
(keyword (string/lower-case (or returnType "json")))])]
|
||||
(-> (utils/fetch url
|
||||
(-> {:method method
|
||||
:headers (and headers (bean/->js headers))}
|
||||
(merge (when (and (not (contains? #{:GET :HEAD} method)) data)
|
||||
;; TODO: support type of arrayBuffer
|
||||
{:body (js/JSON.stringify (bean/->js data))})
|
||||
|
||||
(when-let [^js controller (and abortable (AbortController.))]
|
||||
(swap! *request-abort-signals assoc _req-id controller)
|
||||
{:signal (.-signal controller)}))))
|
||||
(p/then (fn [^js res]
|
||||
(case type
|
||||
:json
|
||||
(.json res)
|
||||
|
||||
:arraybuffer
|
||||
(.arrayBuffer res)
|
||||
|
||||
:base64
|
||||
(-> (.buffer res)
|
||||
(p/then #(.toString % "base64")))
|
||||
|
||||
:text
|
||||
(.text res))))
|
||||
(p/catch
|
||||
(fn [^js e]
|
||||
;; TODO: handle special cases
|
||||
(throw e)))
|
||||
(p/finally
|
||||
(fn []
|
||||
(swap! *request-abort-signals dissoc _req-id)))))))
|
||||
|
||||
(defmethod handle :httpRequestAbort [_ [_ _req-id]]
|
||||
(when-let [^js controller (get @*request-abort-signals _req-id)]
|
||||
(.abort controller)))
|
||||
|
||||
(defmethod handle :quitAndInstall []
|
||||
(.quitAndInstall autoUpdater))
|
||||
|
||||
|
||||
@@ -274,7 +274,7 @@
|
||||
["Calculator" [[:editor/input "```calc\n\n```" {:backward-pos 4}]
|
||||
[:codemirror/focus]] "Insert a calculator"]
|
||||
|
||||
["draw" (draw-handler/initialize-excalidarw-file) "Draw a graph with Excalidraw"]
|
||||
["Draw" (draw-handler/initialize-excalidarw-file) "Draw a graph with Excalidraw"]
|
||||
|
||||
["Embed HTML " (->inline "html")]
|
||||
|
||||
@@ -399,25 +399,26 @@
|
||||
:as _option}]
|
||||
(let [selected? (not (string/blank? selected))
|
||||
input (gdom/getElement id)
|
||||
edit-content (gobj/get input "value")
|
||||
current-pos (cursor/pos input)
|
||||
prefix (subs edit-content 0 current-pos)
|
||||
postfix (if selected?
|
||||
(string/replace-first (subs edit-content current-pos)
|
||||
selected
|
||||
"")
|
||||
(subs edit-content current-pos))
|
||||
new-value (str prefix value postfix)
|
||||
new-pos (- (+ (count prefix)
|
||||
(count value)
|
||||
(or forward-pos 0))
|
||||
(or backward-pos 0))]
|
||||
(state/set-block-content-and-last-pos! id new-value new-pos)
|
||||
(cursor/move-cursor-to input new-pos)
|
||||
(when selected?
|
||||
(.setSelectionRange input new-pos (+ new-pos (count selected))))
|
||||
(when check-fn
|
||||
(check-fn new-value (dec (count prefix))))))
|
||||
edit-content (gobj/get input "value")]
|
||||
(when edit-content
|
||||
(let [current-pos (cursor/pos input)
|
||||
prefix (subs edit-content 0 current-pos)
|
||||
postfix (if selected?
|
||||
(string/replace-first (subs edit-content current-pos)
|
||||
selected
|
||||
"")
|
||||
(subs edit-content current-pos))
|
||||
new-value (str prefix value postfix)
|
||||
new-pos (- (+ (count prefix)
|
||||
(count value)
|
||||
(or forward-pos 0))
|
||||
(or backward-pos 0))]
|
||||
(state/set-block-content-and-last-pos! id new-value new-pos)
|
||||
(cursor/move-cursor-to input new-pos)
|
||||
(when selected?
|
||||
(.setSelectionRange input new-pos (+ new-pos (count selected))))
|
||||
(when check-fn
|
||||
(check-fn new-value (dec (count prefix))))))))
|
||||
|
||||
(defn delete-pair!
|
||||
[id]
|
||||
@@ -431,6 +432,20 @@
|
||||
(state/set-block-content-and-last-pos! id new-value new-pos)
|
||||
(cursor/move-cursor-to input new-pos)))
|
||||
|
||||
(defn delete-selection!
|
||||
[id]
|
||||
(let [input (gdom/getElement id)
|
||||
edit-content (gobj/get input "value")
|
||||
start (util/get-selection-start input)
|
||||
end (util/get-selection-end input)]
|
||||
(when-not (= start end)
|
||||
(let [prefix (subs edit-content 0 start)
|
||||
new-value (str prefix
|
||||
(subs edit-content end))
|
||||
new-pos (count prefix)]
|
||||
(state/set-block-content-and-last-pos! id new-value new-pos)
|
||||
(cursor/move-cursor-to input new-pos)))))
|
||||
|
||||
(defn get-matched-commands
|
||||
([text]
|
||||
(get-matched-commands text @*initial-commands))
|
||||
|
||||
@@ -1924,11 +1924,13 @@
|
||||
(if (and meta? (not (state/get-edit-input-id)))
|
||||
(do
|
||||
(util/stop e)
|
||||
(state/conj-selection-block! (gdom/getElement block-id) :down))
|
||||
(state/conj-selection-block! (gdom/getElement block-id) :down)
|
||||
(when (and block-id (not (state/get-selection-start-block)))
|
||||
(state/set-selection-start-block! block-id)))
|
||||
(when (contains? #{1 0} button)
|
||||
(when-not (target-forbidden-edit? target)
|
||||
(cond
|
||||
(and shift? (state/get-selection-start-block))
|
||||
(and shift? (state/get-selection-start-block-or-first))
|
||||
(editor-handler/highlight-selection-area! block-id)
|
||||
|
||||
shift?
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
[frontend.db.model :as db-model]
|
||||
[frontend.extensions.zotero :as zotero]
|
||||
[frontend.handler.editor :as editor-handler :refer [get-state]]
|
||||
[frontend.handler.paste :as paste-handler]
|
||||
[frontend.handler.editor.lifecycle :as lifecycle]
|
||||
[frontend.handler.page :as page-handler]
|
||||
[frontend.mixins :as mixins]
|
||||
@@ -530,7 +531,7 @@
|
||||
:minRows (if (state/enable-grammarly?) 2 1)
|
||||
:on-click (editor-handler/editor-on-click! id)
|
||||
:on-change (editor-handler/editor-on-change! block id search-timeout)
|
||||
:on-paste (editor-handler/editor-on-paste! id)
|
||||
:on-paste (paste-handler/editor-on-paste! id)
|
||||
:auto-focus false
|
||||
:class heading-class})
|
||||
|
||||
|
||||
@@ -135,7 +135,20 @@
|
||||
(rum/local false ::loading)
|
||||
[state _repo-url db-encrypted-secret close-fn]
|
||||
(let [secret (::secret state)
|
||||
loading (::loading state)]
|
||||
loading (::loading state)
|
||||
on-click-fn (fn []
|
||||
(reset! loading true)
|
||||
(let [value @secret]
|
||||
(when-not (string/blank? value) ; TODO: length or other checks
|
||||
(let [repo (state/get-current-repo)]
|
||||
(p/do!
|
||||
(-> (e/decrypt-with-passphrase value db-encrypted-secret)
|
||||
(p/then (fn [keys]
|
||||
(e/save-key-pair! repo keys)
|
||||
(close-fn true)
|
||||
(state/set-state! :encryption/graph-parsing? false)))
|
||||
(p/catch #(notification/show! "The password is not matched." :warning true))
|
||||
(p/finally #(reset! loading false))))))))]
|
||||
[:div
|
||||
[:div.sm:flex.sm:items-start
|
||||
[:div.mt-3.text-center.sm:mt-0.sm:text-left
|
||||
@@ -146,25 +159,16 @@
|
||||
{:type "password"
|
||||
:auto-focus true
|
||||
:on-change (fn [e]
|
||||
(reset! secret (util/evalue e)))}]
|
||||
(reset! secret (util/evalue e)))
|
||||
:on-key-down (fn [e]
|
||||
(when (= (.-key e) "Enter")
|
||||
(on-click-fn)))}]
|
||||
|
||||
[:div.mt-5.sm:mt-4.sm:flex.sm:flex-row-reverse
|
||||
[:span.flex.w-full.rounded-md.shadow-sm.sm:ml-3.sm:w-auto
|
||||
[:button.inline-flex.justify-center.w-full.rounded-md.border.border-transparent.px-4.py-2.bg-indigo-600.text-base.leading-6.font-medium.text-white.shadow-sm.hover:bg-indigo-500.focus:outline-none.focus:border-indigo-700.focus:shadow-outline-indigo.transition.ease-in-out.duration-150.sm:text-sm.sm:leading-5
|
||||
{:type "button"
|
||||
:on-click (fn []
|
||||
(reset! loading true)
|
||||
(let [value @secret]
|
||||
(when-not (string/blank? value) ; TODO: length or other checks
|
||||
(let [repo (state/get-current-repo)]
|
||||
(p/do!
|
||||
(-> (e/decrypt-with-passphrase value db-encrypted-secret)
|
||||
(p/then (fn [keys]
|
||||
(e/save-key-pair! repo keys)
|
||||
(close-fn true)
|
||||
(state/set-state! :encryption/graph-parsing? false)))
|
||||
(p/catch #(notification/show! "The password is not matched." :warning true))
|
||||
(p/finally #(reset! loading false))))))))}
|
||||
:on-click on-click-fn}
|
||||
(if @loading (ui/loading "Decrypting") "Decrypt")]]]]))
|
||||
|
||||
(defn encryption-input-secret-dialog
|
||||
|
||||
@@ -26,11 +26,10 @@
|
||||
(rum/defc home-button []
|
||||
(ui/with-shortcut :go/home "left"
|
||||
[:button.button.icon.inline
|
||||
{:href (rfe/href :home)
|
||||
:on-click #(do
|
||||
{:on-click #(do
|
||||
(when (mobile-util/native-iphone?)
|
||||
(state/set-left-sidebar-open! false))
|
||||
(route-handler/go-to-journals!))}
|
||||
(route-handler/redirect-to-home!))}
|
||||
(ui/icon "home" {:style {:fontSize ui/icon-size}})]))
|
||||
|
||||
(rum/defc login < rum/reactive
|
||||
@@ -229,9 +228,11 @@
|
||||
(not (mobile-util/native-platform?))
|
||||
(not config/publishing?))
|
||||
left-menu (left-menu-button {:on-click (fn []
|
||||
(open-fn)
|
||||
(state/set-left-sidebar-open!
|
||||
(not (:ui/left-sidebar-open? @state/state))))})]
|
||||
(open-fn)
|
||||
(state/set-left-sidebar-open!
|
||||
(not (:ui/left-sidebar-open? @state/state))))})
|
||||
custom-home-page? (and (state/custom-home-page?)
|
||||
(= (state/sub-default-home-page) (state/get-current-page)))]
|
||||
[:div.cp__header#head
|
||||
{:class (util/classnames [{:electron-mac electron-mac?
|
||||
:native-ios (mobile-util/native-ios?)
|
||||
@@ -254,7 +255,7 @@
|
||||
(state/pub-event! [:go/search]))}
|
||||
(ui/icon "search" {:style {:fontSize ui/icon-size}})]))])
|
||||
(when (mobile-util/native-platform?)
|
||||
(if (state/home?)
|
||||
(if (or (state/home?) custom-home-page?)
|
||||
left-menu
|
||||
(ui/with-shortcut :go/backward "bottom"
|
||||
[:button.it.navigation.nav-left.button.icon
|
||||
@@ -269,7 +270,8 @@
|
||||
(when plugin-handler/lsp-enabled?
|
||||
(plugins/hook-ui-items :toolbar))
|
||||
|
||||
(when (not= (state/get-current-route) :home)
|
||||
(when (and (not= (state/get-current-route) :home)
|
||||
(not custom-home-page?))
|
||||
(home-button))
|
||||
|
||||
(when (util/electron?)
|
||||
@@ -283,7 +285,7 @@
|
||||
{:on-click #(route-handler/redirect! {:to :repo-add})}
|
||||
(ui/icon "folder-plus")
|
||||
(when-not config/mobile?
|
||||
[:strong {:style {:margin-top (if electron-mac? 0 2)}}
|
||||
[:span.ml-1 {:style {:margin-top (if electron-mac? 0 2)}}
|
||||
(t :on-boarding/add-graph)])])
|
||||
|
||||
(when config/publishing?
|
||||
|
||||
@@ -54,7 +54,8 @@
|
||||
}
|
||||
|
||||
.ui-items-container .button {
|
||||
width: 2rem;
|
||||
width: auto;
|
||||
padding: 0 8px;
|
||||
}
|
||||
|
||||
svg.warning {
|
||||
@@ -134,7 +135,7 @@
|
||||
}
|
||||
|
||||
.is-electron.is-mac.is-fullscreen .cp__header > .l {
|
||||
padding-left: 0;
|
||||
padding-left: 1rem;
|
||||
}
|
||||
|
||||
.cp__header a, .cp__header svg {
|
||||
@@ -201,7 +202,12 @@
|
||||
}
|
||||
|
||||
.button.icon {
|
||||
@apply w-8 h-8 text-lg p-1;
|
||||
@apply w-8 h-8 p-1;
|
||||
}
|
||||
|
||||
.button.icon.add-graph-btn {
|
||||
width: unset;
|
||||
margin: 0 6px;
|
||||
}
|
||||
|
||||
.is-mac.is-electron :is(.cp__header, .cp__right-sidebar-topbar) :is(button, .button, a) {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -776,6 +776,43 @@
|
||||
@apply pr-3 opacity-30 hover:opacity-100 transition-opacity;
|
||||
}
|
||||
}
|
||||
|
||||
.toolbar-plugins-manager {
|
||||
&-trigger {
|
||||
.dropdown-wrapper {
|
||||
max-height: 80vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.menu-link {
|
||||
padding: 3px 5px;
|
||||
}
|
||||
|
||||
.item-wrap {
|
||||
padding-right: 28px;
|
||||
font-size: 13px;
|
||||
position: relative;
|
||||
|
||||
div[data-injected-ui] .ti {
|
||||
position: relative;
|
||||
bottom: -1px;
|
||||
}
|
||||
}
|
||||
|
||||
.pin {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
height: 100%;
|
||||
padding: 0 6px;
|
||||
|
||||
&.pinned {
|
||||
color: var(--ls-link-ref-text-color);
|
||||
opacity: 90;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.lsp-frame-readme {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
[rum.core :as rum]))
|
||||
|
||||
(rum/defc container
|
||||
[{:keys [t route theme on-click current-repo nfs-granted? db-restoring?
|
||||
[{:keys [route theme on-click current-repo nfs-granted? db-restoring?
|
||||
settings-open? sidebar-open? system-theme? sidebar-blocks-len]} child]
|
||||
(let [mounted-fn (use-mounted)
|
||||
[restored-sidebar? set-restored-sidebar?] (rum/use-state false)]
|
||||
@@ -37,8 +37,9 @@
|
||||
|
||||
(rum/use-effect!
|
||||
#(when lsp-enabled?
|
||||
(plugin-handler/setup-install-listener! t))
|
||||
[t])
|
||||
(plugin-handler/setup-install-listener!)
|
||||
(plugin-handler/load-plugin-preferences))
|
||||
[])
|
||||
|
||||
(rum/use-effect!
|
||||
(fn []
|
||||
|
||||
@@ -144,6 +144,12 @@
|
||||
(when-let [conn (get-db repo false)]
|
||||
(repo-listen-to-tx! repo conn)))
|
||||
|
||||
(defn relisten-and-persist!
|
||||
[repo]
|
||||
(when-let [conn (get-db repo false)]
|
||||
(d/unlisten! conn :persistence)
|
||||
(listen-and-persist! repo)))
|
||||
|
||||
(defn start-db-conn!
|
||||
([repo]
|
||||
(start-db-conn! repo {}))
|
||||
|
||||
@@ -170,6 +170,21 @@
|
||||
;; (sort-by last)
|
||||
(reverse))))
|
||||
|
||||
(defn get-files-v2
|
||||
[repo]
|
||||
(when-let [db (conn/get-db repo)]
|
||||
(->> (d/q
|
||||
'[:find ?file ?path
|
||||
;; ?modified-at
|
||||
:where
|
||||
[?file :file/path ?path]
|
||||
;; [?file :file/last-modified-at ?modified-at]
|
||||
]
|
||||
db)
|
||||
(seq)
|
||||
;; (sort-by last)
|
||||
(reverse))))
|
||||
|
||||
(defn get-files-blocks
|
||||
[repo-url paths]
|
||||
(let [paths (set paths)
|
||||
|
||||
@@ -3608,6 +3608,7 @@
|
||||
:settings-page/tab-version-control "Sürüm denetimi"
|
||||
:settings-page/tab-advanced "Gelişmiş"
|
||||
:settings-page/plugin-system "Eklenti sistemi"
|
||||
:settings-page/enable-flashcards "Bilgi kartları"
|
||||
:settings-page/network-proxy "Ağ ara sunucusu"
|
||||
:logseq "Logseq"
|
||||
:on "AÇIK"
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
right: 0;
|
||||
/* margin-top: 4px; */
|
||||
z-index: 9999;
|
||||
display: none;
|
||||
padding: 4px 6px;
|
||||
line-height: 1em;
|
||||
opacity: .8;
|
||||
@@ -42,12 +41,6 @@
|
||||
font-family: Fira Code, Monaco, Menlo, Consolas, 'COURIER NEW', monospace;
|
||||
}
|
||||
}
|
||||
|
||||
@screen md {
|
||||
&-lang {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.CodeMirror {
|
||||
|
||||
@@ -152,6 +152,7 @@
|
||||
(defn resolve-ref-page
|
||||
[pdf-current]
|
||||
(let [page-name (:key pdf-current)
|
||||
page-name (string/trim page-name)
|
||||
page-name (str "hls__" page-name)
|
||||
page (db-model/get-page page-name)
|
||||
url (:url pdf-current)
|
||||
|
||||
@@ -2,15 +2,15 @@
|
||||
(:require ["@capacitor/filesystem" :refer [Encoding Filesystem]]
|
||||
[cljs-bean.core :as bean]
|
||||
[clojure.string :as string]
|
||||
[frontend.db :as db]
|
||||
[frontend.encrypt :as encrypt]
|
||||
[frontend.fs.protocol :as protocol]
|
||||
[frontend.mobile.util :as mobile-util]
|
||||
[frontend.state :as state]
|
||||
[frontend.util :as util]
|
||||
[lambdaisland.glogi :as log]
|
||||
[promesa.core :as p]
|
||||
[rum.core :as rum]
|
||||
[frontend.state :as state]
|
||||
[frontend.db :as db]
|
||||
[frontend.encrypt :as encrypt]))
|
||||
[rum.core :as rum]))
|
||||
|
||||
(when (mobile-util/native-ios?)
|
||||
(defn iOS-ensure-documents!
|
||||
@@ -184,11 +184,6 @@
|
||||
[path localDocumentsPath]
|
||||
(string/includes? path localDocumentsPath))
|
||||
|
||||
(defn- iCloud-container-path?
|
||||
"Check whether `path' is logseq's iCloud container path on iOS"
|
||||
[path]
|
||||
(string/includes? path "iCloud~com~logseq~logseq"))
|
||||
|
||||
(rum/defc instruction
|
||||
[]
|
||||
[:div.instruction
|
||||
@@ -280,16 +275,10 @@
|
||||
{:keys [path localDocumentsPath]} (p/chain
|
||||
(.pickFolder mobile-util/folder-picker)
|
||||
#(js->clj % :keywordize-keys true))
|
||||
_ (when (mobile-util/native-ios?)
|
||||
(cond
|
||||
(not (or (local-container-path? path localDocumentsPath)
|
||||
(iCloud-container-path? path)))
|
||||
(state/pub-event! [:modal/show-instruction])
|
||||
|
||||
(iCloud-container-path? path)
|
||||
(mobile-util/sync-icloud-repo path)
|
||||
|
||||
:else nil))
|
||||
_ (when (and (mobile-util/native-ios?)
|
||||
(not (or (local-container-path? path localDocumentsPath)
|
||||
(mobile-util/iCloud-container-path? path))))
|
||||
(state/pub-event! [:modal/show-instruction]))
|
||||
files (readdir path)
|
||||
files (js->clj files :keywordize-keys true)]
|
||||
(into [] (concat [{:path path}] files))))
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
[frontend.handler.ui :as ui-handler]
|
||||
[frontend.handler.user :as user-handler]
|
||||
[frontend.extensions.srs :as srs]
|
||||
[frontend.mobile.core :as mobile]
|
||||
[frontend.mobile.util :as mobile-util]
|
||||
[frontend.idb :as idb]
|
||||
[frontend.modules.instrumentation.core :as instrument]
|
||||
@@ -111,7 +110,8 @@
|
||||
|
||||
(watch-for-date!)
|
||||
(file-handler/watch-for-current-graph-dir!)
|
||||
(state/pub-event! [:graph/ready (state/get-current-repo)])))
|
||||
(state/pub-event! [:graph/ready (state/get-current-repo)])
|
||||
(state/pub-event! [:graph/restored (state/get-current-repo)])))
|
||||
(p/catch (fn [error]
|
||||
(log/error :exception error)))))
|
||||
|
||||
@@ -184,8 +184,6 @@
|
||||
(instrument/init)
|
||||
(set-network-watcher!)
|
||||
|
||||
(mobile/init!)
|
||||
|
||||
(util/indexeddb-check?
|
||||
(fn [_error]
|
||||
(notification/show! "Sorry, it seems that your browser doesn't support IndexedDB, we recommend to use latest Chrome(Chromium) or Firefox(Non-private mode)." :error false)
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
(ns frontend.handler.editor
|
||||
(:require ["/frontend/utils" :as utils]
|
||||
["path" :as path]
|
||||
[cljs.core.match :refer [match]]
|
||||
(:require ["path" :as path]
|
||||
[clojure.set :as set]
|
||||
[clojure.string :as string]
|
||||
[clojure.walk :as w]
|
||||
@@ -56,8 +54,7 @@
|
||||
[promesa.core :as p]
|
||||
[logseq.graph-parser.util :as gp-util]
|
||||
[logseq.graph-parser.mldoc :as gp-mldoc]
|
||||
[logseq.graph-parser.block :as gp-block]
|
||||
[frontend.extensions.html-parser :as html-parser]))
|
||||
[logseq.graph-parser.block :as gp-block]))
|
||||
|
||||
;; FIXME: should support multiple images concurrently uploading
|
||||
|
||||
@@ -65,7 +62,7 @@
|
||||
(defonce *asset-uploading-process (atom 0))
|
||||
(defonce *selected-text (atom nil))
|
||||
|
||||
(defn- get-selection-and-format
|
||||
(defn get-selection-and-format
|
||||
[]
|
||||
(when-let [block (state/get-edit-block)]
|
||||
(when (:block/uuid block)
|
||||
@@ -977,6 +974,16 @@
|
||||
(into [] (state/get-export-block-text-remove-options)))]
|
||||
[top-level-block-uuids content]))
|
||||
|
||||
(defn- get-all-blocks-by-ids
|
||||
[repo ids]
|
||||
(loop [ids ids
|
||||
result []]
|
||||
(if (seq ids)
|
||||
(let [blocks (db/get-block-and-children repo (first ids))
|
||||
result (vec (concat result blocks))]
|
||||
(recur (remove (set (map :block/uuid result)) (rest ids)) result))
|
||||
result)))
|
||||
|
||||
(defn copy-selection-blocks
|
||||
[html?]
|
||||
(when-let [blocks (seq (state/get-selection-blocks))]
|
||||
@@ -988,7 +995,7 @@
|
||||
(when block
|
||||
(let [html (export/export-blocks-as-html repo top-level-block-uuids)]
|
||||
(common-handler/copy-to-clipboard-without-id-property! (:block/format block) content (when html? html)))
|
||||
(state/set-copied-blocks content ids)
|
||||
(state/set-copied-blocks! content (get-all-blocks-by-ids repo top-level-block-uuids))
|
||||
(notification/show! "Copied!" :success)))))
|
||||
|
||||
(defn copy-block-refs
|
||||
@@ -1071,7 +1078,6 @@
|
||||
sorted-blocks (mapcat (fn [block]
|
||||
(tree/get-sorted-block-and-children repo (:db/id block)))
|
||||
top-level-blocks)]
|
||||
(state/set-copied-full-blocks nil sorted-blocks)
|
||||
(delete-blocks! repo (map :block/uuid sorted-blocks) sorted-blocks dom-blocks))))))
|
||||
|
||||
(def url-regex
|
||||
@@ -1202,7 +1208,7 @@
|
||||
[_top-level-block-uuids md-content] (compose-copied-blocks-contents repo [block-id])
|
||||
html (export/export-blocks-as-html repo [block-id])
|
||||
sorted-blocks (tree/get-sorted-block-and-children repo (:db/id block))]
|
||||
(state/set-copied-full-blocks md-content sorted-blocks)
|
||||
(state/set-copied-blocks! md-content sorted-blocks)
|
||||
(common-handler/copy-to-clipboard-without-id-property! (:block/format block) md-content html)
|
||||
(delete-block-aux! block true))))
|
||||
|
||||
@@ -1213,7 +1219,7 @@
|
||||
|
||||
(defn highlight-selection-area!
|
||||
[end-block]
|
||||
(when-let [start-block (state/get-selection-start-block)]
|
||||
(when-let [start-block (state/get-selection-start-block-or-first)]
|
||||
(let [blocks (util/get-nodes-between-two-nodes start-block end-block "ls-block")
|
||||
direction (util/get-direction-between-two-nodes start-block end-block "ls-block")
|
||||
|
||||
@@ -1941,7 +1947,8 @@
|
||||
(paste-block-cleanup block page exclude-properties format content-update-fn))
|
||||
blocks)
|
||||
result (outliner-core/insert-blocks! blocks' target-block {:sibling? sibling?
|
||||
:outliner-op :paste})]
|
||||
:outliner-op :paste
|
||||
:replace-empty-target? true})]
|
||||
(edit-last-block-after-inserted! result))))))
|
||||
|
||||
(defn- block-tree->blocks
|
||||
@@ -2848,157 +2855,6 @@
|
||||
(edit-box-on-change! e block id)
|
||||
(util/scroll-editor-cursor input)))))
|
||||
|
||||
(defn- paste-text-parseable
|
||||
[format text]
|
||||
(when-let [editing-block (state/get-edit-block)]
|
||||
(let [page-id (:db/id (:block/page editing-block))
|
||||
blocks (block/extract-blocks
|
||||
(mldoc/->edn text (gp-mldoc/default-config format)) text true format)
|
||||
blocks' (gp-block/with-parent-and-left page-id blocks)]
|
||||
(paste-blocks blocks' {}))))
|
||||
|
||||
(defn- paste-segmented-text
|
||||
[format text]
|
||||
(let [paragraphs (string/split text #"(?:\r?\n){2,}")
|
||||
updated-paragraphs
|
||||
(string/join "\n"
|
||||
(mapv (fn [p] (->> (string/trim p)
|
||||
((fn [p]
|
||||
(if (util/safe-re-find (if (= format :org)
|
||||
#"\s*\*+\s+"
|
||||
#"\s*-\s+") p)
|
||||
p
|
||||
(str (if (= format :org) "* " "- ") p))))))
|
||||
paragraphs))]
|
||||
(paste-text-parseable format updated-paragraphs)))
|
||||
|
||||
(defn- get-all-blocks-by-ids
|
||||
[repo ids]
|
||||
(loop [ids ids
|
||||
result []]
|
||||
(if (seq ids)
|
||||
(let [blocks (db/get-block-and-children repo (first ids))
|
||||
result (vec (concat result blocks))]
|
||||
(recur (remove (set (map :block/uuid result)) (rest ids)) result))
|
||||
result)))
|
||||
|
||||
(defn wrap-macro-url
|
||||
[url]
|
||||
(cond
|
||||
(boolean (text-util/get-matched-video url))
|
||||
(util/format "{{video %s}}" url)
|
||||
|
||||
(string/includes? url "twitter.com")
|
||||
(util/format "{{twitter %s}}" url)
|
||||
|
||||
:else
|
||||
(do
|
||||
(notification/show! (util/format "No macro is available for %s" url) :warning)
|
||||
nil)))
|
||||
|
||||
(defn- paste-copied-blocks-or-text
|
||||
[initial-text text e]
|
||||
(let [copied-blocks (state/get-copied-blocks)
|
||||
copied-block-ids (:copy/block-ids copied-blocks)
|
||||
copied-graph (:copy/graph copied-blocks)
|
||||
input (state/get-input)]
|
||||
(cond
|
||||
;; Internal blocks by either copy or cut blocks
|
||||
(and
|
||||
(= copied-graph (state/get-current-repo))
|
||||
(or (seq copied-block-ids)
|
||||
(seq (:copy/full-blocks copied-blocks)))
|
||||
initial-text
|
||||
;; not copied from the external clipboard
|
||||
(= (string/replace (string/trim initial-text) "\r" "")
|
||||
(string/replace (string/trim (or (:copy/content copied-blocks) "")) "\r" "")))
|
||||
(do
|
||||
(util/stop e)
|
||||
(let [blocks (or
|
||||
(:copy/full-blocks copied-blocks)
|
||||
(get-all-blocks-by-ids (state/get-current-repo) copied-block-ids))]
|
||||
(when (seq blocks)
|
||||
(state/set-copied-full-blocks! blocks)
|
||||
(paste-blocks blocks {}))))
|
||||
|
||||
(and (gp-util/url? text)
|
||||
(not (string/blank? (util/get-selected-text))))
|
||||
(do
|
||||
(util/stop e)
|
||||
(html-link-format! text))
|
||||
|
||||
(and (text/block-ref? text)
|
||||
(wrapped-by? input "((" "))"))
|
||||
(do
|
||||
(util/stop e)
|
||||
(commands/simple-insert! (state/get-edit-input-id) (text/get-block-ref text) nil))
|
||||
|
||||
:else
|
||||
;; from external
|
||||
(let [format (or (db/get-page-format (state/get-current-page)) :markdown)]
|
||||
(util/stop e)
|
||||
(match [format
|
||||
(nil? (util/safe-re-find #"(?m)^\s*(?:[-+*]|#+)\s+" text))
|
||||
(nil? (util/safe-re-find #"(?m)^\s*\*+\s+" text))
|
||||
(nil? (util/safe-re-find #"(?:\r?\n){2,}" text))]
|
||||
[:markdown false _ _]
|
||||
(paste-text-parseable format text)
|
||||
|
||||
[:org _ false _]
|
||||
(paste-text-parseable format text)
|
||||
|
||||
[:markdown true _ false]
|
||||
(paste-segmented-text format text)
|
||||
|
||||
[:markdown true _ true]
|
||||
(commands/simple-insert! (state/get-edit-input-id) text nil)
|
||||
|
||||
[:org _ true false]
|
||||
(paste-segmented-text format text)
|
||||
|
||||
[:org _ true true]
|
||||
(commands/simple-insert! (state/get-edit-input-id) text nil))))))
|
||||
|
||||
(defn paste-text-in-one-block-at-point
|
||||
[]
|
||||
(utils/getClipText
|
||||
(fn [clipboard-data]
|
||||
(when-let [_ (state/get-input)]
|
||||
(let [data (or (when (gp-util/url? clipboard-data)
|
||||
(wrap-macro-url clipboard-data))
|
||||
clipboard-data)]
|
||||
(insert data true))))
|
||||
(fn [error]
|
||||
(js/console.error error))))
|
||||
|
||||
(defn editor-on-paste!
|
||||
[id]
|
||||
(fn [e]
|
||||
(state/set-state! :editor/on-paste? true)
|
||||
(let [clipboard-data (gobj/get e "clipboardData")
|
||||
html (.getData clipboard-data "text/html")
|
||||
edit-block (state/get-edit-block)
|
||||
format (or (:block/format edit-block) :markdown)
|
||||
initial-text (.getData clipboard-data "text")
|
||||
text (or (when-not (string/blank? html)
|
||||
(html-parser/convert format html))
|
||||
initial-text)
|
||||
input (state/get-input)]
|
||||
(if-not (string/blank? text)
|
||||
(if (or (thingatpt/markdown-src-at-point input)
|
||||
(thingatpt/org-admonition&src-at-point input))
|
||||
(when-not (mobile-util/native-ios?)
|
||||
(util/stop e)
|
||||
(paste-text-in-one-block-at-point))
|
||||
(paste-copied-blocks-or-text initial-text text e))
|
||||
(let [_handled
|
||||
(let [clipboard-data (gobj/get e "clipboardData")
|
||||
files (.-files clipboard-data)]
|
||||
(when-let [file (first files)]
|
||||
(when-let [block (state/get-edit-block)]
|
||||
(upload-asset id #js[file] (:block/format block) *asset-uploading? true))))]
|
||||
(util/stop e))))))
|
||||
|
||||
(defn- cut-blocks-and-clear-selections!
|
||||
[copy?]
|
||||
(cut-selection-blocks copy?)
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
(ns frontend.handler.events
|
||||
(:refer-clojure :exclude [run!])
|
||||
(:require [clojure.core.async :as async]
|
||||
(:require ["@capacitor/filesystem" :refer [Directory Filesystem]]
|
||||
[clojure.core.async :as async]
|
||||
[clojure.set :as set]
|
||||
[clojure.string :as string]
|
||||
[datascript.core :as d]
|
||||
[frontend.commands :as commands]
|
||||
[frontend.components.diff :as diff]
|
||||
[frontend.components.encryption :as encryption]
|
||||
@@ -13,7 +15,9 @@
|
||||
[frontend.config :as config]
|
||||
[frontend.context.i18n :refer [t]]
|
||||
[frontend.db :as db]
|
||||
[logseq.db.schema :as db-schema]
|
||||
[frontend.db.conn :as conn]
|
||||
[frontend.db.model :as db-model]
|
||||
[frontend.db.persist :as db-persist]
|
||||
[frontend.encrypt :as encrypt]
|
||||
[frontend.extensions.srs :as srs]
|
||||
[frontend.fs :as fs]
|
||||
@@ -32,15 +36,18 @@
|
||||
[frontend.handler.search :as search-handler]
|
||||
[frontend.handler.ui :as ui-handler]
|
||||
[frontend.handler.web.nfs :as nfs-handler]
|
||||
[frontend.mobile.core :as mobile]
|
||||
[frontend.mobile.util :as mobile-util]
|
||||
[frontend.modules.instrumentation.posthog :as posthog]
|
||||
[frontend.modules.outliner.file :as outliner-file]
|
||||
[frontend.modules.shortcut.core :as st]
|
||||
[frontend.search :as search-db]
|
||||
[frontend.state :as state]
|
||||
[frontend.ui :as ui]
|
||||
[frontend.util :as util]
|
||||
[frontend.util.persist-var :as persist-var]
|
||||
[goog.dom :as gdom]
|
||||
[logseq.db.schema :as db-schema]
|
||||
[promesa.core :as p]
|
||||
[rum.core :as rum]))
|
||||
|
||||
@@ -63,19 +70,25 @@
|
||||
(sync/sync-start)
|
||||
))
|
||||
|
||||
(defn- graph-switch [graph]
|
||||
(state/set-current-repo! graph)
|
||||
;; load config
|
||||
(common-handler/reset-config! graph nil)
|
||||
(st/refresh!)
|
||||
(when-not (= :draw (state/get-current-route))
|
||||
(route-handler/redirect-to-home!))
|
||||
(when-let [dir-name (config/get-repo-dir graph)]
|
||||
(fs/watch-dir! dir-name))
|
||||
(srs/update-cards-due-count!)
|
||||
(state/pub-event! [:graph/ready graph])
|
||||
(defn- graph-switch
|
||||
([graph]
|
||||
(graph-switch graph false))
|
||||
([graph skip-ios-check?]
|
||||
(if (and (mobile-util/native-ios?) (not skip-ios-check?))
|
||||
(state/pub-event! [:validate-appId graph-switch graph])
|
||||
(do
|
||||
(state/set-current-repo! graph)
|
||||
;; load config
|
||||
(common-handler/reset-config! graph nil)
|
||||
(st/refresh!)
|
||||
(when-not (= :draw (state/get-current-route))
|
||||
(route-handler/redirect-to-home!))
|
||||
(when-let [dir-name (config/get-repo-dir graph)]
|
||||
(fs/watch-dir! dir-name))
|
||||
(srs/update-cards-due-count!)
|
||||
(state/pub-event! [:graph/ready graph])
|
||||
|
||||
(file-sync-stop-when-switch-graph))
|
||||
(file-sync-stop-when-switch-graph)))))
|
||||
|
||||
(def persist-db-noti-m
|
||||
{:before #(notification/show!
|
||||
@@ -330,6 +343,60 @@
|
||||
(when-let [right-sidebar-node (gdom/getElementByClass "sidebar-item-list")]
|
||||
(set! (.. right-sidebar-node -style -paddingBottom) "150px")))))
|
||||
|
||||
(defn update-file-path [deprecated-repo current-repo deprecated-app-id current-app-id]
|
||||
(let [files (db-model/get-files-v2 deprecated-repo)
|
||||
conn (conn/get-db deprecated-repo false)
|
||||
tx (mapv (fn [[id path]]
|
||||
(let [new-path (string/replace path deprecated-app-id current-app-id)]
|
||||
{:db/id id
|
||||
:file/path new-path}))
|
||||
files)]
|
||||
(d/transact! conn tx)
|
||||
(reset! conn/conns
|
||||
(update-keys @conn/conns
|
||||
(fn [key] (if (string/includes? key deprecated-repo)
|
||||
(string/replace key deprecated-repo current-repo)
|
||||
key))))))
|
||||
|
||||
(defn get-ios-app-id
|
||||
[repo-url]
|
||||
(when repo-url
|
||||
(let [app-id (-> (first (string/split repo-url "/Documents"))
|
||||
(string/split "/")
|
||||
last)]
|
||||
app-id)))
|
||||
|
||||
(defmethod handle :validate-appId [[_ graph-switch-f graph]]
|
||||
(when-let [deprecated-repo (or graph (state/get-current-repo))]
|
||||
;; Installation is will not be changed for iCloud
|
||||
(if (mobile-util/iCloud-container-path? deprecated-repo)
|
||||
(when graph-switch-f (graph-switch-f graph true))
|
||||
(p/let [deprecated-app-id (get-ios-app-id deprecated-repo)
|
||||
current-document-url (.getUri Filesystem #js {:path ""
|
||||
:directory (.-Documents Directory)})
|
||||
current-app-id (-> (js->clj current-document-url :keywordize-keys true)
|
||||
get-ios-app-id)]
|
||||
(if (= deprecated-app-id current-app-id)
|
||||
(when graph-switch-f (graph-switch-f graph true))
|
||||
(do
|
||||
(.unwatch mobile-util/fs-watcher)
|
||||
(let [current-repo (string/replace deprecated-repo deprecated-app-id current-app-id)
|
||||
current-repo-dir (config/get-repo-dir current-repo)]
|
||||
(try
|
||||
(update-file-path deprecated-repo current-repo deprecated-app-id current-app-id)
|
||||
(db-persist/delete-graph! deprecated-repo)
|
||||
(search-db/remove-db! deprecated-repo)
|
||||
(state/delete-repo! {:url deprecated-repo})
|
||||
(state/add-repo! {:url current-repo :nfs? true})
|
||||
(catch :default e
|
||||
(js/console.error e)))
|
||||
(state/set-current-repo! current-repo)
|
||||
(db/relisten-and-persist! current-repo)
|
||||
(db/persist-if-idle! current-repo)
|
||||
(file-handler/restore-config! current-repo false)
|
||||
(.watch mobile-util/fs-watcher #js {:path current-repo-dir})
|
||||
(when graph-switch-f (graph-switch-f current-repo true)))))))))
|
||||
|
||||
(defmethod handle :plugin/consume-updates [[_ id pending? updated?]]
|
||||
(let [downloading? (:plugin/updates-downloading? @state/state)]
|
||||
|
||||
@@ -441,6 +508,9 @@
|
||||
(route-handler/redirect! {:to :whiteboard
|
||||
:path-params {:name link}}))
|
||||
|
||||
(defmethod handle :graph/restored [[_ _graph]]
|
||||
(mobile/init!))
|
||||
|
||||
(defmethod handle :graph/dir-gone [[_ dir]]
|
||||
(state/pub-event! [:notification/show
|
||||
{:content (str "The directory " dir " has been renamed or deleted, the editor will be disabled for this graph, you can unlink the graph.")
|
||||
@@ -474,3 +544,13 @@
|
||||
:error error}])))))
|
||||
(recur))
|
||||
chan))
|
||||
|
||||
(comment
|
||||
(let [{:keys [deprecated-app-id current-app-id]} {:deprecated-app-id "AFDADF9A-7466-4ED8-B74F-AAAA0D4565B9", :current-app-id "7563518E-0EFD-4AD2-8577-10CFFD6E4596"}]
|
||||
(def deprecated-app-id deprecated-app-id)
|
||||
(def current-app-id current-app-id))
|
||||
(def deprecated-repo (state/get-current-repo))
|
||||
(def new-repo (string/replace deprecated-repo deprecated-app-id current-app-id))
|
||||
|
||||
(update-file-path deprecated-repo new-repo deprecated-app-id current-app-id)
|
||||
)
|
||||
|
||||
@@ -130,18 +130,19 @@
|
||||
file)
|
||||
file (gp-util/path-normalize file)
|
||||
new? (nil? (db/entity [:file/path file]))]
|
||||
(graph-parser/parse-file
|
||||
(db/get-db repo-url false)
|
||||
file
|
||||
content
|
||||
(merge options
|
||||
{:new? new?
|
||||
:delete-blocks-fn (partial get-delete-blocks repo-url)
|
||||
:extract-options {:user-config (state/get-config)
|
||||
:date-formatter (state/get-date-formatter)
|
||||
:page-name-order (state/page-name-order)
|
||||
:block-pattern (config/get-block-pattern (gp-util/get-format file))
|
||||
:supported-formats (gp-config/supported-formats)}})))))
|
||||
(:tx
|
||||
(graph-parser/parse-file
|
||||
(db/get-db repo-url false)
|
||||
file
|
||||
content
|
||||
(merge options
|
||||
{:new? new?
|
||||
:delete-blocks-fn (partial get-delete-blocks repo-url)
|
||||
:extract-options {:user-config (state/get-config)
|
||||
:date-formatter (state/get-date-formatter)
|
||||
:page-name-order (state/page-name-order)
|
||||
:block-pattern (config/get-block-pattern (gp-util/get-format file))
|
||||
:supported-formats (gp-config/supported-formats)}}))))))
|
||||
|
||||
;; TODO: Remove this function in favor of `alter-files`
|
||||
(defn alter-file
|
||||
|
||||
175
src/main/frontend/handler/paste.cljs
Normal file
175
src/main/frontend/handler/paste.cljs
Normal file
@@ -0,0 +1,175 @@
|
||||
(ns frontend.handler.paste
|
||||
(:require [frontend.state :as state]
|
||||
[frontend.db :as db]
|
||||
[frontend.format.block :as block]
|
||||
[logseq.graph-parser.util :as gp-util]
|
||||
[logseq.graph-parser.mldoc :as gp-mldoc]
|
||||
[logseq.graph-parser.block :as gp-block]
|
||||
[clojure.string :as string]
|
||||
[frontend.util :as util]
|
||||
[frontend.handler.editor :as editor-handler]
|
||||
[frontend.extensions.html-parser :as html-parser]
|
||||
[goog.object :as gobj]
|
||||
[frontend.mobile.util :as mobile-util]
|
||||
[frontend.util.thingatpt :as thingatpt]
|
||||
["/frontend/utils" :as utils]
|
||||
[frontend.commands :as commands]
|
||||
[cljs.core.match :refer [match]]
|
||||
[logseq.graph-parser.text :as text]
|
||||
[frontend.handler.notification :as notification]
|
||||
[frontend.util.text :as text-util]
|
||||
[frontend.format.mldoc :as mldoc]
|
||||
[lambdaisland.glogi :as log]))
|
||||
|
||||
(defn- paste-text-parseable
|
||||
[format text]
|
||||
(when-let [editing-block (state/get-edit-block)]
|
||||
(let [page-id (:db/id (:block/page editing-block))
|
||||
blocks (block/extract-blocks
|
||||
(mldoc/->edn text (gp-mldoc/default-config format)) text true format)
|
||||
blocks' (gp-block/with-parent-and-left page-id blocks)]
|
||||
(editor-handler/paste-blocks blocks' {}))))
|
||||
|
||||
(defn- paste-segmented-text
|
||||
[format text]
|
||||
(let [paragraphs (string/split text #"(?:\r?\n){2,}")
|
||||
updated-paragraphs
|
||||
(string/join "\n"
|
||||
(mapv (fn [p] (->> (string/trim p)
|
||||
((fn [p]
|
||||
(if (util/safe-re-find (if (= format :org)
|
||||
#"\s*\*+\s+"
|
||||
#"\s*-\s+") p)
|
||||
p
|
||||
(str (if (= format :org) "* " "- ") p))))))
|
||||
paragraphs))]
|
||||
(paste-text-parseable format updated-paragraphs)))
|
||||
|
||||
(defn- wrap-macro-url
|
||||
[url]
|
||||
(cond
|
||||
(boolean (text-util/get-matched-video url))
|
||||
(util/format "{{video %s}}" url)
|
||||
|
||||
(string/includes? url "twitter.com")
|
||||
(util/format "{{twitter %s}}" url)
|
||||
|
||||
:else
|
||||
(do
|
||||
(notification/show! (util/format "No macro is available for %s" url) :warning)
|
||||
nil)))
|
||||
|
||||
(defn- paste-copied-blocks-or-text
|
||||
[text e html]
|
||||
(util/stop e)
|
||||
(let [copied-blocks (state/get-copied-blocks)
|
||||
input (state/get-input)
|
||||
text (string/replace text "\r\n" "\n") ;; Fix for Windows platform
|
||||
internal-paste? (and
|
||||
(seq (:copy/blocks copied-blocks))
|
||||
;; not copied from the external clipboard
|
||||
(= text (:copy/content copied-blocks)))]
|
||||
(if internal-paste?
|
||||
(let [blocks (:copy/blocks copied-blocks)]
|
||||
(when (seq blocks)
|
||||
(editor-handler/paste-blocks blocks {})))
|
||||
(let [{:keys [value]} (editor-handler/get-selection-and-format)]
|
||||
(cond
|
||||
(and (or (gp-util/url? text)
|
||||
(and value (gp-util/url? (string/trim value))))
|
||||
(not (string/blank? (util/get-selected-text))))
|
||||
(editor-handler/html-link-format! text)
|
||||
|
||||
(and (text/block-ref? text)
|
||||
(editor-handler/wrapped-by? input "((" "))"))
|
||||
(commands/simple-insert! (state/get-edit-input-id) (text/get-block-ref text) nil)
|
||||
|
||||
:else
|
||||
;; from external
|
||||
(let [format (or (db/get-page-format (state/get-current-page)) :markdown)
|
||||
html-text (let [result (when-not (string/blank? html)
|
||||
(try
|
||||
(html-parser/convert format html)
|
||||
(catch :default e
|
||||
(log/error :exception e)
|
||||
nil)))]
|
||||
(if (string/blank? result) nil result))
|
||||
text (or html-text text)
|
||||
input-id (state/get-edit-input-id)
|
||||
replace-text-f (fn []
|
||||
(commands/delete-selection! input-id)
|
||||
(commands/simple-insert! input-id text nil))]
|
||||
(match [format
|
||||
(nil? (util/safe-re-find #"(?m)^\s*(?:[-+*]|#+)\s+" text))
|
||||
(nil? (util/safe-re-find #"(?m)^\s*\*+\s+" text))
|
||||
(nil? (util/safe-re-find #"(?:\r?\n){2,}" text))]
|
||||
[:markdown false _ _]
|
||||
(paste-text-parseable format text)
|
||||
|
||||
[:org _ false _]
|
||||
(paste-text-parseable format text)
|
||||
|
||||
[:markdown true _ false]
|
||||
(paste-segmented-text format text)
|
||||
|
||||
[:markdown true _ true]
|
||||
(replace-text-f)
|
||||
|
||||
[:org _ true false]
|
||||
(paste-segmented-text format text)
|
||||
|
||||
[:org _ true true]
|
||||
(replace-text-f))))))))
|
||||
|
||||
(defn paste-text-in-one-block-at-point
|
||||
[]
|
||||
(utils/getClipText
|
||||
(fn [clipboard-data]
|
||||
(when-let [_ (state/get-input)]
|
||||
(let [data (or (when (gp-util/url? clipboard-data)
|
||||
(wrap-macro-url clipboard-data))
|
||||
clipboard-data)]
|
||||
(editor-handler/insert data true))))
|
||||
(fn [error]
|
||||
(js/console.error error))))
|
||||
|
||||
(defn- paste-text-or-blocks-aux
|
||||
[input e text html]
|
||||
(if (or (thingatpt/markdown-src-at-point input)
|
||||
(thingatpt/org-admonition&src-at-point input))
|
||||
(when-not (mobile-util/native-ios?)
|
||||
(util/stop e)
|
||||
(paste-text-in-one-block-at-point))
|
||||
(paste-copied-blocks-or-text text e html)))
|
||||
|
||||
(defn editor-on-paste!
|
||||
([id]
|
||||
(editor-on-paste! id false))
|
||||
([id raw-paste?]
|
||||
(fn [e]
|
||||
(state/set-state! :editor/on-paste? true)
|
||||
(let [input (state/get-input)]
|
||||
(if raw-paste?
|
||||
(utils/getClipText
|
||||
(fn [clipboard-data]
|
||||
(when-let [_ (state/get-input)]
|
||||
(let [text (or (when (gp-util/url? clipboard-data)
|
||||
(wrap-macro-url clipboard-data))
|
||||
clipboard-data)]
|
||||
(paste-text-or-blocks-aux input e text nil))))
|
||||
(fn [error]
|
||||
(js/console.error error)))
|
||||
(let [clipboard-data (gobj/get e "clipboardData")
|
||||
html (when-not raw-paste? (.getData clipboard-data "text/html"))
|
||||
text (.getData clipboard-data "text")]
|
||||
(if-not (string/blank? text)
|
||||
(paste-text-or-blocks-aux input e text html)
|
||||
(when id
|
||||
(let [_handled
|
||||
(let [clipboard-data (gobj/get e "clipboardData")
|
||||
files (.-files clipboard-data)]
|
||||
(when-let [file (first files)]
|
||||
(when-let [block (state/get-edit-block)]
|
||||
(editor-handler/upload-asset id #js[file] (:block/format block)
|
||||
editor-handler/*asset-uploading? true))))]
|
||||
(util/stop e))))))))))
|
||||
@@ -14,6 +14,7 @@
|
||||
[clojure.string :as string]
|
||||
[lambdaisland.glogi :as log]
|
||||
[frontend.components.svg :as svg]
|
||||
[frontend.context.i18n :refer [t]]
|
||||
[frontend.format :as format]))
|
||||
|
||||
(defonce lsp-enabled?
|
||||
@@ -42,6 +43,23 @@
|
||||
(defonce stats-url (str central-endpoint "stats.json"))
|
||||
(declare select-a-plugin-theme)
|
||||
|
||||
(defn load-plugin-preferences
|
||||
[]
|
||||
(-> (invoke-exported-api "load_user_preferences")
|
||||
(p/then #(bean/->clj %))
|
||||
(p/then #(state/set-state! :plugin/preferences %))
|
||||
(p/catch
|
||||
#(js/console.error %))))
|
||||
|
||||
(defn save-plugin-preferences!
|
||||
([input] (save-plugin-preferences! input true))
|
||||
([input reload-state?]
|
||||
(when-let [^js input (and (map? input) (bean/->js input))]
|
||||
(p/then
|
||||
(js/LSPluginCore.saveUserPreferences input)
|
||||
#(when reload-state?
|
||||
(load-plugin-preferences))))))
|
||||
|
||||
(defn gh-repo-url [repo]
|
||||
(str "https://github.com/" repo))
|
||||
|
||||
@@ -55,14 +73,17 @@
|
||||
(if (or refresh? (nil? (:plugin/marketplace-pkgs @state/state)))
|
||||
(p/create
|
||||
(fn [resolve reject]
|
||||
(-> (ipc/ipc :httpFetchJSON plugins-url)
|
||||
(p/then (fn [res]
|
||||
(let [on-ok (fn [res]
|
||||
(if-let [res (and res (bean/->clj res))]
|
||||
(let [pkgs (:packages res)]
|
||||
(state/set-state! :plugin/marketplace-pkgs pkgs)
|
||||
(resolve pkgs))
|
||||
(reject nil))))
|
||||
(p/catch reject))))
|
||||
(reject nil)))]
|
||||
(if (state/http-proxy-enabled-or-val?)
|
||||
(-> (ipc/ipc :httpFetchJSON plugins-url)
|
||||
(p/then on-ok)
|
||||
(p/catch reject))
|
||||
(util/fetch plugins-url on-ok reject)))))
|
||||
(p/resolved (:plugin/marketplace-pkgs @state/state))))
|
||||
|
||||
(defn load-marketplace-stats
|
||||
@@ -70,8 +91,7 @@
|
||||
(if (or refresh? (nil? (:plugin/marketplace-stats @state/state)))
|
||||
(p/create
|
||||
(fn [resolve reject]
|
||||
(-> (ipc/ipc :httpFetchJSON stats-url)
|
||||
(p/then (fn [^js res]
|
||||
(let [on-ok (fn [^js res]
|
||||
(if-let [res (and res (bean/->clj res))]
|
||||
(do
|
||||
(state/set-state!
|
||||
@@ -82,8 +102,12 @@
|
||||
(reduce (fn [a b] (+ a (get b 2))) 0 (:releases stat)))])
|
||||
res)))
|
||||
(resolve nil))
|
||||
(reject nil))))
|
||||
(p/catch reject))))
|
||||
(reject nil)))]
|
||||
(if (state/http-proxy-enabled-or-val?)
|
||||
(-> (ipc/ipc :httpFetchJSON stats-url)
|
||||
(p/then on-ok)
|
||||
(p/catch reject))
|
||||
(util/fetch stats-url on-ok reject)))))
|
||||
(p/resolved nil)))
|
||||
|
||||
(defn installed?
|
||||
@@ -159,7 +183,7 @@
|
||||
(filter #(has-setting-schema? (:id %)) plugins)))
|
||||
|
||||
(defn setup-install-listener!
|
||||
[t]
|
||||
[]
|
||||
(let [channel (name :lsp-installed)
|
||||
listener (fn [^js _ ^js e]
|
||||
(js/console.debug :lsp-installed e)
|
||||
@@ -299,11 +323,13 @@
|
||||
(swap! state/state medley/dissoc-in [:plugin/simple-commands (keyword pid)]))
|
||||
|
||||
(defn register-plugin-ui-item
|
||||
[pid {:keys [type] :as opts}]
|
||||
[pid {:keys [key type] :as opts}]
|
||||
(when-let [pid (keyword pid)]
|
||||
(when (contains? (:plugin/installed-plugins @state/state) pid)
|
||||
(swap! state/state update-in [:plugin/installed-ui-items pid]
|
||||
(fnil conj []) [type opts pid])
|
||||
(let [items (or (get-in @state/state [:plugin/installed-ui-items pid]) [])
|
||||
items (filter #(not= key (:key (second %))) items)]
|
||||
(swap! state/state assoc-in [:plugin/installed-ui-items pid]
|
||||
(conj items [type opts pid])))
|
||||
true)))
|
||||
|
||||
(defn unregister-plugin-ui-items
|
||||
@@ -531,6 +557,24 @@
|
||||
(into {} (map (fn [v] [(keyword (:id v)) v]) plugins)))
|
||||
(state/pub-event! [:plugin/consume-updates]))))
|
||||
|
||||
(defn call-plugin
|
||||
[^js pl type payload]
|
||||
(when pl
|
||||
(.call (.-caller pl) (name type) (bean/->js payload))))
|
||||
|
||||
(defn request-callback
|
||||
[^js pl req-id payload]
|
||||
(call-plugin pl :#lsp#request#callback {:requestId req-id :payload payload}))
|
||||
|
||||
(defn op-pinned-toolbar-item!
|
||||
[key op]
|
||||
(let [pinned (state/sub [:plugin/preferences :pinnedToolbarItems])
|
||||
pinned (into #{} pinned)]
|
||||
(when-let [op-fn (case op
|
||||
:add conj
|
||||
:remove disj)]
|
||||
(save-plugin-preferences! {:pinnedToolbarItems (op-fn pinned (name key))}))))
|
||||
|
||||
;; components
|
||||
(rum/defc lsp-indicator < rum/reactive
|
||||
[]
|
||||
@@ -596,7 +640,7 @@
|
||||
(clear-commands! pid)
|
||||
(unregister-plugin-themes pid)))
|
||||
|
||||
(.on "theme-changed" (fn [^js themes]
|
||||
(.on "themes-changed" (fn [^js themes]
|
||||
(swap! state/state assoc :plugin/installed-themes
|
||||
(vec (mapcat (fn [[pid vs]] (mapv #(assoc % :pid pid) (bean/->clj vs))) (bean/->clj themes))))))
|
||||
|
||||
@@ -607,6 +651,7 @@
|
||||
(when mode
|
||||
(state/set-custom-theme! mode theme)
|
||||
(state/set-theme-mode! mode))
|
||||
(hook-plugin-app :theme-changed theme)
|
||||
(state/set-state! :plugin/selected-theme url))))
|
||||
|
||||
(.on "reset-custom-theme" (fn [^js themes]
|
||||
|
||||
@@ -336,7 +336,9 @@
|
||||
(db-persist/delete-graph! url)
|
||||
(search/remove-db! url)
|
||||
(state/delete-repo! repo)
|
||||
(when graph-exists? (ipc/ipc "graphUnlinked" repo))))]
|
||||
(when graph-exists? (ipc/ipc "graphUnlinked" repo))
|
||||
(when (= (state/get-current-repo) url)
|
||||
(state/set-current-repo! (:url (first (state/get-repos)))))))]
|
||||
(when (or (config/local-db? url) (= url "local"))
|
||||
(p/let [_ (idb/clear-local-db! url)] ; clear file handles
|
||||
(delete-db-f)))))
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
(:require ["@capacitor/app" :refer [^js App]]
|
||||
["@capacitor/keyboard" :refer [^js Keyboard]]
|
||||
[clojure.string :as string]
|
||||
[frontend.fs.capacitor-fs :as fs]
|
||||
[frontend.fs.capacitor-fs :as mobile-fs]
|
||||
[frontend.handler.editor :as editor-handler]
|
||||
[frontend.mobile.deeplink :as deeplink]
|
||||
[frontend.mobile.intent :as intent]
|
||||
@@ -20,8 +20,10 @@
|
||||
(defn- ios-init
|
||||
"Initialize iOS-specified event listeners"
|
||||
[]
|
||||
(let [path (fs/iOS-ensure-documents!)]
|
||||
(let [path (mobile-fs/iOS-ensure-documents!)]
|
||||
(println "iOS container path: " path))
|
||||
|
||||
(state/pub-event! [:validate-appId])
|
||||
|
||||
(.addEventListener js/window
|
||||
"load"
|
||||
@@ -29,7 +31,7 @@
|
||||
(when @*url
|
||||
(js/setTimeout #(deeplink/deeplink @*url)
|
||||
1000))))
|
||||
|
||||
|
||||
(mobile-util/check-ios-zoomed-display)
|
||||
|
||||
(.removeAllListeners mobile-util/file-sync)
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
|
||||
(defonce folder-picker (registerPlugin "FolderPicker"))
|
||||
(when (native-ios?)
|
||||
(defonce download-icloud-files (registerPlugin "DownloadiCloudFiles"))
|
||||
(defonce ios-utils (registerPlugin "Utils"))
|
||||
(defonce ios-file-container (registerPlugin "FileContainer"))
|
||||
(defonce file-sync (registerPlugin "FileSync")))
|
||||
@@ -32,14 +31,6 @@
|
||||
(when (native-platform?)
|
||||
(defonce fs-watcher (registerPlugin "FsWatcher")))
|
||||
|
||||
(defn sync-icloud-repo [repo-dir]
|
||||
(let [repo-name (-> (string/split repo-dir "Documents/")
|
||||
last
|
||||
string/trim
|
||||
js/decodeURI)]
|
||||
(.syncGraph download-icloud-files
|
||||
(clj->js {:graph repo-name}))))
|
||||
|
||||
(defn hide-splash []
|
||||
(.hide SplashScreen))
|
||||
|
||||
@@ -92,4 +83,10 @@
|
||||
#(js->clj % :keywordize-keys true))]
|
||||
(when (:isZoomed is-zoomed?)
|
||||
(let [^js cl (.-classList js/document.documentElement)]
|
||||
(.add cl "is-zoomed-native-ios")))))
|
||||
(.add cl "is-zoomed-native-ios")))))
|
||||
|
||||
(defn iCloud-container-path?
|
||||
"Check whether `path' is logseq's iCloud container path on iOS"
|
||||
[path]
|
||||
(string/includes? path "iCloud~com~logseq~logseq"))
|
||||
|
||||
|
||||
@@ -370,20 +370,29 @@
|
||||
|
||||
;;; ### insert-blocks, delete-blocks, move-blocks
|
||||
|
||||
(defn- fix-top-level-blocks
|
||||
(defn fix-top-level-blocks
|
||||
"Blocks with :block/level"
|
||||
[blocks]
|
||||
(loop [blocks blocks
|
||||
last-top-level-block nil
|
||||
result []]
|
||||
(if-let [block (first blocks)]
|
||||
(if (= 1 (:block/level block))
|
||||
(let [block' (assoc block
|
||||
:block/left {:db/id (:db/id last-top-level-block)}
|
||||
:block/parent (:block/parent last-top-level-block))]
|
||||
(recur (rest blocks) block (conj result block')))
|
||||
(recur (rest blocks) last-top-level-block (conj result block)))
|
||||
result)))
|
||||
(let [top-level-blocks (filter #(= (:block/level %) 1) blocks)
|
||||
id->block (zipmap (map :db/id top-level-blocks) top-level-blocks)
|
||||
uuid->block (zipmap (map :block/uuid top-level-blocks) top-level-blocks)]
|
||||
(if (every? (fn [block]
|
||||
(let [left (:block/left block)
|
||||
id (if (map? left) (:db/id left) (second left))]
|
||||
(some? (or (get id->block id) (get uuid->block id))))) (rest top-level-blocks))
|
||||
;; no need to fix
|
||||
blocks
|
||||
(loop [blocks blocks
|
||||
last-top-level-block nil
|
||||
result []]
|
||||
(if-let [block (first blocks)]
|
||||
(if (= 1 (:block/level block))
|
||||
(let [block' (assoc block
|
||||
:block/left {:db/id (:db/id last-top-level-block)}
|
||||
:block/parent (:block/parent last-top-level-block))]
|
||||
(recur (rest blocks) block (conj result block')))
|
||||
(recur (rest blocks) last-top-level-block (conj result block)))
|
||||
result)))))
|
||||
|
||||
(defn- insert-blocks-aux
|
||||
[blocks target-block {:keys [sibling? replace-empty-target? keep-uuid? move? outliner-op]}]
|
||||
@@ -469,7 +478,7 @@
|
||||
(> (count blocks) 1)
|
||||
(not move?)))
|
||||
blocks' (blocks-with-level blocks)
|
||||
blocks' (if (= outliner-op ::paste)
|
||||
blocks' (if (= outliner-op :paste)
|
||||
(fix-top-level-blocks blocks')
|
||||
blocks')
|
||||
insert-opts {:sibling? sibling?
|
||||
@@ -509,13 +518,18 @@
|
||||
:blocks tx}))))
|
||||
|
||||
(defn- build-move-blocks-next-tx
|
||||
[blocks]
|
||||
[blocks non-consecutive-blocks?]
|
||||
(let [id->blocks (zipmap (map :db/id blocks) blocks)
|
||||
top-level-blocks (get-top-level-blocks blocks)
|
||||
top-level-blocks-ids (set (map :db/id top-level-blocks))
|
||||
right-block (get-right-sibling (:db/id (last top-level-blocks)))]
|
||||
(when (and right-block
|
||||
(not (contains? top-level-blocks-ids (:db/id right-block))))
|
||||
(not (contains? top-level-blocks-ids (:db/id right-block)))
|
||||
(or (and
|
||||
non-consecutive-blocks?
|
||||
(not= (:db/id (last top-level-blocks))
|
||||
(:db/id (:block/left right-block))))
|
||||
true))
|
||||
{:db/id (:db/id right-block)
|
||||
:block/left (loop [block (:block/left right-block)]
|
||||
(if (contains? top-level-blocks-ids (:db/id block))
|
||||
@@ -535,20 +549,21 @@
|
||||
|
||||
(defn- fix-non-consecutive-blocks
|
||||
[blocks target-block sibling?]
|
||||
(let [page-blocks (group-by :block/page blocks)]
|
||||
(->>
|
||||
(mapcat (fn [[_page blocks]]
|
||||
(let [blocks (db-model/sort-page-random-blocks blocks)
|
||||
non-consecutive-blocks (->> (conj (db-model/get-non-consecutive-blocks blocks) (last blocks))
|
||||
(util/distinct-by :db/id))]
|
||||
(when (seq non-consecutive-blocks)
|
||||
(mapv (fn [block]
|
||||
(when-let [right (get-right-sibling (:db/id block))]
|
||||
(when-let [new-left (find-new-left right (distinct (map :db/id blocks)) target-block block sibling?)]
|
||||
{:db/id (:db/id right)
|
||||
:block/left (:db/id new-left)})))
|
||||
non-consecutive-blocks)))) page-blocks)
|
||||
(remove nil?))))
|
||||
(when (> (count blocks) 1)
|
||||
(let [page-blocks (group-by :block/page blocks)]
|
||||
(->>
|
||||
(mapcat (fn [[_page blocks]]
|
||||
(let [blocks (db-model/sort-page-random-blocks blocks)
|
||||
non-consecutive-blocks (->> (conj (db-model/get-non-consecutive-blocks blocks) (last blocks))
|
||||
(util/distinct-by :db/id))]
|
||||
(when (seq non-consecutive-blocks)
|
||||
(mapv (fn [block]
|
||||
(when-let [right (get-right-sibling (:db/id block))]
|
||||
(when-let [new-left (find-new-left right (distinct (map :db/id blocks)) target-block block sibling?)]
|
||||
{:db/id (:db/id right)
|
||||
:block/left (:db/id new-left)})))
|
||||
non-consecutive-blocks)))) page-blocks)
|
||||
(remove nil?)))))
|
||||
|
||||
(defn- delete-block
|
||||
"Delete block from the tree."
|
||||
@@ -618,20 +633,20 @@
|
||||
{:tx-data @txs-state}))
|
||||
|
||||
(defn- move-to-original-position?
|
||||
[blocks target-block sibling?]
|
||||
(let [non-consecutive-blocks (db-model/get-non-consecutive-blocks blocks)]
|
||||
(and (empty? non-consecutive-blocks)
|
||||
(= (:db/id (:block/left (first blocks))) (:db/id target-block))
|
||||
(not= (= (:db/id (:block/parent (first blocks)))
|
||||
(:db/id target-block))
|
||||
sibling?))))
|
||||
[blocks target-block sibling? non-consecutive-blocks?]
|
||||
(and (not non-consecutive-blocks?)
|
||||
(= (:db/id (:block/left (first blocks))) (:db/id target-block))
|
||||
(not= (= (:db/id (:block/parent (first blocks)))
|
||||
(:db/id target-block))
|
||||
sibling?)))
|
||||
|
||||
(defn move-blocks
|
||||
"Move `blocks` to `target-block` as siblings or children."
|
||||
[blocks target-block {:keys [sibling? outliner-op]}]
|
||||
[:pre [(seq blocks)
|
||||
(s/valid? ::block-map-or-entity target-block)]]
|
||||
(let [original-position? (move-to-original-position? blocks target-block sibling?)]
|
||||
(let [non-consecutive-blocks? (seq (db-model/get-non-consecutive-blocks blocks))
|
||||
original-position? (move-to-original-position? blocks target-block sibling? non-consecutive-blocks?)]
|
||||
(when (and (not (contains? (set (map :db/id blocks)) (:db/id target-block)))
|
||||
(not original-position?))
|
||||
(let [parents (->> (db/get-block-parents (state/get-current-repo) (:block/uuid target-block))
|
||||
@@ -648,7 +663,7 @@
|
||||
target-page (or (:db/id (:block/page target-block))
|
||||
(:db/id target-block))
|
||||
not-same-page? (not= first-block-page target-page)
|
||||
move-blocks-next-tx [(build-move-blocks-next-tx blocks)]
|
||||
move-blocks-next-tx [(build-move-blocks-next-tx blocks non-consecutive-blocks?)]
|
||||
children-page-tx (when not-same-page?
|
||||
(let [children-ids (mapcat #(db/get-block-children-ids (state/get-current-repo) (:block/uuid %)) blocks)]
|
||||
(map (fn [uuid] {:block/uuid uuid
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
[frontend.extensions.pdf.utils :as pdf-utils]
|
||||
[frontend.handler.config :as config-handler]
|
||||
[frontend.handler.editor :as editor-handler]
|
||||
[frontend.handler.paste :as paste-handler]
|
||||
[frontend.handler.history :as history]
|
||||
[frontend.handler.page :as page-handler]
|
||||
[frontend.handler.route :as route-handler]
|
||||
@@ -149,7 +150,7 @@
|
||||
:fn editor-handler/copy-current-block-embed}
|
||||
|
||||
:editor/paste-text-in-one-block-at-point {:binding "mod+shift+v"
|
||||
:fn editor-handler/paste-text-in-one-block-at-point}
|
||||
:fn (fn [_state e] ((paste-handler/editor-on-paste! nil true) e))}
|
||||
|
||||
:editor/insert-youtube-timestamp {:binding "mod+shift+y"
|
||||
:fn commands/insert-youtube-timestamp}
|
||||
|
||||
@@ -971,6 +971,7 @@
|
||||
:command.date-picker/next-week "Tarih seçici: Sonraki haftayı seç"
|
||||
:command.pdf/previous-page "Geçerli pdf belgesinin önceki sayfası"
|
||||
:command.pdf/next-page "Geçerli pdf belgesinin sonraki sayfası"
|
||||
:command.pdf/close "Geçerli pdf görüntüleyiciyi kapat"
|
||||
:command.auto-complete/complete "Otomatik tamamlama: Seçili öğeyi seç"
|
||||
:command.auto-complete/prev "Otomatik tamamlama: Önceki öğeyi seç"
|
||||
:command.auto-complete/next "Otomatik tamamlama: Sonraki öğeyi seç"
|
||||
@@ -994,6 +995,7 @@
|
||||
:command.editor/strike-through "Üstü çizili"
|
||||
:command.editor/clear-block "Tüm blok içeriğini sil"
|
||||
:command.editor/kill-line-before "İmleç konumundan önceki satırı sil"
|
||||
:command.editor/copy-embed "Geçerli bloğu işaret eden bir blok eklemesi kopyala"
|
||||
:command.editor/kill-line-after "İmleç konumundan sonraki satırı sil"
|
||||
:command.editor/beginning-of-block "İmleci bir bloğun başına taşı"
|
||||
:command.editor/end-of-block "İmleci bir bloğun sonuna taşı"
|
||||
@@ -1191,4 +1193,4 @@
|
||||
:command.editor/copy-text "선택한 영역을 텍스트로 복사"
|
||||
:command.pdf/close "PDF 닫기"
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
@@ -160,6 +160,7 @@
|
||||
:plugin/enabled (and (util/electron?)
|
||||
;; true false :theme-only
|
||||
((fnil identity true) (storage/get :lsp-core-enabled)))
|
||||
:plugin/preferences nil
|
||||
:plugin/indicator-text nil
|
||||
:plugin/installed-plugins {}
|
||||
:plugin/installed-themes []
|
||||
@@ -193,8 +194,8 @@
|
||||
|
||||
;; copied blocks
|
||||
:copy/blocks {:copy/content nil
|
||||
:copy/block-ids nil
|
||||
:copy/graph nil}
|
||||
:copy/graph nil
|
||||
:copy/blocks nil}
|
||||
|
||||
:copy/export-block-text-indent-style (or (storage/get :copy/export-block-text-indent-style)
|
||||
"dashes")
|
||||
@@ -532,9 +533,7 @@
|
||||
(->> (remove #(= (:url repo)
|
||||
(:url %))
|
||||
repos)
|
||||
(util/distinct-by :url))))
|
||||
(when (= (get-current-repo) (:url repo))
|
||||
(set-current-repo! (:url (first (get-repos))))))
|
||||
(util/distinct-by :url)))))
|
||||
|
||||
(defn set-timestamp-block!
|
||||
[value]
|
||||
@@ -703,6 +702,12 @@
|
||||
(uuid id)))
|
||||
(distinct)))
|
||||
|
||||
(defn get-selection-start-block-or-first
|
||||
[]
|
||||
(or (get-selection-start-block)
|
||||
(some-> (first (get-selection-blocks))
|
||||
(gobj/get "id"))))
|
||||
|
||||
(defn in-selection-mode?
|
||||
[]
|
||||
(:selection/mode @state))
|
||||
@@ -1436,22 +1441,11 @@
|
||||
[]
|
||||
(:copy/blocks @state))
|
||||
|
||||
(defn set-copied-blocks
|
||||
[content ids]
|
||||
(set-state! :copy/blocks {:copy/graph (get-current-repo)
|
||||
:copy/content content
|
||||
:copy/block-ids ids
|
||||
:copy/full-blocks nil}))
|
||||
|
||||
(defn set-copied-full-blocks
|
||||
(defn set-copied-blocks!
|
||||
[content blocks]
|
||||
(set-state! :copy/blocks {:copy/graph (get-current-repo)
|
||||
:copy/content (or content (get-in @state [:copy/blocks :copy/content]))
|
||||
:copy/full-blocks blocks}))
|
||||
|
||||
(defn set-copied-full-blocks!
|
||||
[blocks]
|
||||
(set-state! [:copy/blocks :copy/full-blocks] blocks))
|
||||
:copy/blocks blocks}))
|
||||
|
||||
(defn get-export-block-text-indent-style []
|
||||
(:copy/export-block-text-indent-style @state))
|
||||
@@ -1698,6 +1692,11 @@
|
||||
(if (fn? m) m
|
||||
(fn [old-value] (merge old-value m)))))
|
||||
|
||||
(defn http-proxy-enabled-or-val? []
|
||||
(when-let [agent-opts (sub [:electron/user-cfgs :settings/agent])]
|
||||
(when (every? not-empty (vals agent-opts))
|
||||
(str (:protocol agent-opts) "://" (:host agent-opts) ":" (:port agent-opts)))))
|
||||
|
||||
(defn enable-encryption?
|
||||
[repo]
|
||||
(:feature/enable-encryption?
|
||||
|
||||
@@ -100,11 +100,11 @@
|
||||
;; public exports
|
||||
(rum/defcs dropdown < (mixins/modal :open?)
|
||||
[state content-fn modal-content-fn
|
||||
& [{:keys [modal-class z-index]
|
||||
& [{:keys [modal-class z-index trigger-class]
|
||||
:or {z-index 999}}]]
|
||||
(let [{:keys [open?]} state
|
||||
modal-content (modal-content-fn state)]
|
||||
[:div.relative.ui__dropdown-trigger {:style {:z-index z-index}}
|
||||
[:div.relative.ui__dropdown-trigger {:style {:z-index z-index} :class trigger-class}
|
||||
(content-fn state)
|
||||
(css-transition
|
||||
{:in @open? :timeout 0}
|
||||
@@ -125,20 +125,21 @@
|
||||
(fn [{:keys [close-fn]}]
|
||||
[:div.py-1.rounded-md.shadow-xs
|
||||
(when links-header links-header)
|
||||
(for [{:keys [options title icon hr hover-detail]} (if (fn? links) (links) links)]
|
||||
(for [{:keys [options title icon hr hover-detail item]} (if (fn? links) (links) links)]
|
||||
(let [new-options
|
||||
(merge options
|
||||
{:title hover-detail
|
||||
{:title hover-detail
|
||||
:on-click (fn [e]
|
||||
(when-let [on-click-fn (:on-click options)]
|
||||
(on-click-fn e))
|
||||
(close-fn))})
|
||||
(when-not (false? (when-let [on-click-fn (:on-click options)]
|
||||
(on-click-fn e)))
|
||||
(close-fn)))})
|
||||
child (if hr
|
||||
nil
|
||||
[:div.flex.items-center
|
||||
(when icon icon)
|
||||
[:div {:style {:margin-right "8px"
|
||||
:margin-left "4px"}} title]])]
|
||||
(or item
|
||||
[:div.flex.items-center
|
||||
(when icon icon)
|
||||
[:div {:style {:margin-right "8px"
|
||||
:margin-left "4px"}} title]]))]
|
||||
(if hr
|
||||
[:hr.my-1 {:key "dropdown-hr"}]
|
||||
(rum/with-key
|
||||
@@ -296,7 +297,7 @@
|
||||
(when-let [custom-theme (state/sub [:ui/custom-theme (keyword theme)])]
|
||||
(when-let [url (:url custom-theme)]
|
||||
(js/LSPluginCore.selectTheme (bean/->js custom-theme)
|
||||
(bean/->js {:emit false}))
|
||||
(bean/->js {:emit true}))
|
||||
(state/set-state! :plugin/selected-theme url)))))
|
||||
|
||||
(defn setup-system-theme-effect!
|
||||
|
||||
@@ -808,6 +808,22 @@
|
||||
(plugin-handler/register-extensions-enhancer
|
||||
(keyword pid) type {:enhancer enhancer})))
|
||||
|
||||
(defonce *request-k (volatile! 0))
|
||||
|
||||
(defn ^:export exper_request
|
||||
[pid ^js options]
|
||||
(when-let [^js _pl (plugin-handler/get-plugin-inst pid)]
|
||||
(let [req-id (vreset! *request-k (inc @*request-k))
|
||||
req-cb #(plugin-handler/request-callback _pl req-id %)]
|
||||
(-> (ipc/ipc :httpRequest req-id options)
|
||||
(p/then #(req-cb %))
|
||||
(p/catch #(req-cb %)))
|
||||
req-id)))
|
||||
|
||||
(defn ^:export http_request_abort
|
||||
[req-id]
|
||||
(ipc/ipc :httpRequestAbort req-id))
|
||||
|
||||
;; helpers
|
||||
(defn ^:export query_element_by_id
|
||||
[id]
|
||||
|
||||
@@ -215,6 +215,37 @@
|
||||
(outliner-core/indent-outdent-blocks! [(get-block 13) (get-block 14)] false))
|
||||
(is (= [2 12 13 14 16] (get-children 22)))))
|
||||
|
||||
(deftest test-fix-top-level-blocks
|
||||
(testing "no need to fix"
|
||||
(let [blocks [{:block/uuid #uuid "62aa668b-e258-445d-aef6-5510054ff495",
|
||||
:block/properties {},
|
||||
:block/left #:db{:id 144},
|
||||
:block/format :markdown,
|
||||
:block/level 1,
|
||||
:block/content "a",
|
||||
:db/id 145,
|
||||
:block/parent #:db{:id 144},
|
||||
:block/page #:db{:id 144}}
|
||||
{:block/uuid #uuid "62aa668d-65d1-440c-849b-a0717f691193",
|
||||
:block/properties {},
|
||||
:block/left #:db{:id 145},
|
||||
:block/format :markdown,
|
||||
:block/level 1,
|
||||
:block/content "b",
|
||||
:db/id 146,
|
||||
:block/parent #:db{:id 144},
|
||||
:block/page #:db{:id 144}}
|
||||
{:block/uuid #uuid "62aa668e-f866-48ee-b8fe-737e101c548d",
|
||||
:block/properties {},
|
||||
:block/left #:db{:id 146},
|
||||
:block/format :markdown,
|
||||
:block/level 1,
|
||||
:block/content "c",
|
||||
:db/id 147,
|
||||
:block/parent #:db{:id 144},
|
||||
:block/page #:db{:id 144}}]]
|
||||
(= blocks (outliner-core/fix-top-level-blocks blocks)))))
|
||||
|
||||
(deftest test-outdent-blocks
|
||||
(testing "
|
||||
[1 [[2 [[3]
|
||||
@@ -416,7 +447,9 @@
|
||||
(defn transact-random-tree!
|
||||
[]
|
||||
(let [tree (gen-safe-tree)]
|
||||
(transact-tree! tree)))
|
||||
(if (seq tree)
|
||||
(transact-tree! tree)
|
||||
(transact-random-tree!))))
|
||||
|
||||
(defn get-datoms
|
||||
[]
|
||||
@@ -458,9 +491,6 @@
|
||||
(set/union old (set (map :block/uuid blocks)))))
|
||||
(insert-blocks! blocks (get-random-block)))
|
||||
(let [total (get-blocks-count)]
|
||||
;; (when (not= total (count @*random-blocks))
|
||||
;; (defonce wrong-db (db/get-db test-db))
|
||||
;; (defonce random-blocks @*random-blocks))
|
||||
(is (= total (count @*random-blocks))))))))
|
||||
|
||||
(deftest ^:long random-deletes
|
||||
@@ -518,16 +548,17 @@
|
||||
*random-blocks (atom c1)]
|
||||
(dotimes [_i 100]
|
||||
;; (prn "Random move indent/outdent: " i)
|
||||
(let [blocks (gen-blocks)]
|
||||
(let [new-blocks (gen-blocks)]
|
||||
(swap! *random-blocks (fn [old]
|
||||
(set/union old (set (map :block/uuid blocks)))))
|
||||
(insert-blocks! blocks (get-random-block)))
|
||||
(let [blocks (get-random-successive-blocks)]
|
||||
(when (seq blocks)
|
||||
(outliner-tx/transact! {:graph test-db}
|
||||
(outliner-core/indent-outdent-blocks! blocks (gen/generate gen/boolean)))
|
||||
(let [total (get-blocks-count)]
|
||||
(is (= total (count @*random-blocks))))))))))
|
||||
(set/union old (set (map :block/uuid new-blocks)))))
|
||||
(insert-blocks! new-blocks (get-random-block))
|
||||
(let [blocks (get-random-successive-blocks)
|
||||
indent? (gen/generate gen/boolean)]
|
||||
(when (seq blocks)
|
||||
(outliner-tx/transact! {:graph test-db}
|
||||
(outliner-core/indent-outdent-blocks! blocks indent?))
|
||||
(let [total (get-blocks-count)]
|
||||
(is (= total (count @*random-blocks)))))))))))
|
||||
|
||||
(deftest ^:long random-mixed-ops
|
||||
(testing "Random mixed operations"
|
||||
|
||||
@@ -5,7 +5,8 @@ import {
|
||||
AppCanvas,
|
||||
AppProvider,
|
||||
TLReactCallbacks,
|
||||
TLReactComponents, TLReactToolConstructor
|
||||
TLReactComponents,
|
||||
TLReactToolConstructor,
|
||||
} from '@tldraw/react'
|
||||
import * as React from 'react'
|
||||
import { AppUI } from '~components/AppUI'
|
||||
@@ -25,7 +26,7 @@ import {
|
||||
PencilTool,
|
||||
PolygonTool,
|
||||
TextTool,
|
||||
YouTubeTool
|
||||
YouTubeTool,
|
||||
} from '~lib/tools'
|
||||
|
||||
const components: TLReactComponents<Shape> = {
|
||||
@@ -54,16 +55,18 @@ interface LogseqTldrawProps {
|
||||
onPersist?: TLReactCallbacks<Shape>['onPersist']
|
||||
}
|
||||
|
||||
export const App = function App(props: LogseqTldrawProps): JSX.Element {
|
||||
export const App = function App({
|
||||
searchHandler,
|
||||
PageComponent,
|
||||
...props
|
||||
}: LogseqTldrawProps): JSX.Element {
|
||||
const onFileDrop = useFileDrop()
|
||||
const onPaste = usePaste()
|
||||
|
||||
const Page = React.useMemo(() => React.memo(props.PageComponent), [])
|
||||
|
||||
const Page = React.useMemo(() => React.memo(PageComponent), [])
|
||||
return (
|
||||
<LogseqContext.Provider value={{ Page, search: props.searchHandler }}>
|
||||
<LogseqContext.Provider value={{ Page, search: searchHandler }}>
|
||||
<AppProvider
|
||||
onMount={props.onMount}
|
||||
Shapes={shapes}
|
||||
Tools={tools}
|
||||
onFileDrop={onFileDrop}
|
||||
|
||||
@@ -8,18 +8,19 @@ import {
|
||||
uniqueId,
|
||||
} from '@tldraw/core'
|
||||
import type { TLReactCallbacks } from '@tldraw/react'
|
||||
import { transaction } from 'mobx'
|
||||
import * as React from 'react'
|
||||
import type { Shape } from '~lib'
|
||||
import { LogseqPortalShape, Shape } from '~lib'
|
||||
|
||||
export function usePaste() {
|
||||
return React.useCallback<TLReactCallbacks<Shape>['onFileDrop']>(async (app, { point }) => {
|
||||
return React.useCallback<TLReactCallbacks<Shape>['onPaste']>(async (app, { point }) => {
|
||||
const assetId = uniqueId()
|
||||
interface ImageAsset extends TLAsset {
|
||||
size: number[]
|
||||
}
|
||||
|
||||
const assetsToCreate: ImageAsset[] = []
|
||||
const shapesToCreate: TLShapeModel[] = []
|
||||
const shapesToCreate: Shape['props'][] = []
|
||||
const bindingsToCreate: TLBinding[] = []
|
||||
|
||||
async function handleImage(item: ClipboardItem) {
|
||||
@@ -47,64 +48,83 @@ export function usePaste() {
|
||||
}
|
||||
|
||||
async function handleLogseqShapes(item: ClipboardItem) {
|
||||
const plainTextType = item.types.find(type => type.startsWith('text/plain'))
|
||||
if (plainTextType) {
|
||||
const blob = await item.getType(plainTextType)
|
||||
const rawText = await blob.text()
|
||||
const data = JSON.parse(rawText)
|
||||
if (data.type === 'logseq/whiteboard-shapes') {
|
||||
const shapes = data.shapes as TLShapeModel[]
|
||||
const commonBounds = BoundsUtils.getCommonBounds(
|
||||
shapes.map(shape => ({
|
||||
minX: shape.point?.[0] ?? point[0],
|
||||
minY: shape.point?.[1] ?? point[1],
|
||||
width: shape.size?.[0] ?? 4,
|
||||
height: shape.size?.[1] ?? 4,
|
||||
maxX: (shape.point?.[0] ?? point[0]) + (shape.size?.[0] ?? 4),
|
||||
maxY: (shape.point?.[1] ?? point[1]) + (shape.size?.[1] ?? 4),
|
||||
}))
|
||||
)
|
||||
const clonedShapes = shapes.map((shape: TLShapeModel) => {
|
||||
return {
|
||||
...shape,
|
||||
if (item.types.includes('text/plain')) {
|
||||
const blob = await item.getType('text/plain')
|
||||
const rawText = (await blob.text()).trim()
|
||||
|
||||
try {
|
||||
const data = JSON.parse(rawText)
|
||||
if (data.type === 'logseq/whiteboard-shapes') {
|
||||
const shapes = data.shapes as TLShapeModel[]
|
||||
const commonBounds = BoundsUtils.getCommonBounds(
|
||||
shapes.map(shape => ({
|
||||
minX: shape.point?.[0] ?? point[0],
|
||||
minY: shape.point?.[1] ?? point[1],
|
||||
width: shape.size?.[0] ?? 4,
|
||||
height: shape.size?.[1] ?? 4,
|
||||
maxX: (shape.point?.[0] ?? point[0]) + (shape.size?.[0] ?? 4),
|
||||
maxY: (shape.point?.[1] ?? point[1]) + (shape.size?.[1] ?? 4),
|
||||
}))
|
||||
)
|
||||
const clonedShapes = shapes.map(shape => {
|
||||
return {
|
||||
...shape,
|
||||
id: uniqueId(),
|
||||
parentId: app.currentPageId,
|
||||
point: [
|
||||
point[0] + shape.point![0] - commonBounds.minX,
|
||||
point[1] + shape.point![1] - commonBounds.minY,
|
||||
],
|
||||
}
|
||||
})
|
||||
// @ts-expect-error - This is a valid shape
|
||||
shapesToCreate.push(...clonedShapes)
|
||||
|
||||
// Try to rebinding the shapes to the new assets
|
||||
shapesToCreate.forEach((s, idx) => {
|
||||
if (s.handles) {
|
||||
Object.values(s.handles).forEach(h => {
|
||||
if (h.bindingId) {
|
||||
// try to bind the new shape
|
||||
const binding = app.currentPage.bindings[h.bindingId]
|
||||
// if the copied binding from/to is in the source
|
||||
const oldFromIdx = shapes.findIndex(s => s.id === binding.fromId)
|
||||
const oldToIdx = shapes.findIndex(s => s.id === binding.toId)
|
||||
if (binding && oldFromIdx !== -1 && oldToIdx !== -1) {
|
||||
const newBinding: TLBinding = {
|
||||
...binding,
|
||||
id: uniqueId(),
|
||||
fromId: shapesToCreate[oldFromIdx].id,
|
||||
toId: shapesToCreate[oldToIdx].id,
|
||||
}
|
||||
bindingsToCreate.push(newBinding)
|
||||
h.bindingId = newBinding.id
|
||||
} else {
|
||||
h.bindingId = undefined
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
return true
|
||||
}
|
||||
} catch {
|
||||
const blockRefEg = '((62af02d0-0443-42e8-a284-946c162b0f89))'
|
||||
if (/^\(\(.*\)\)$/.test(rawText) && rawText.length === blockRefEg.length) {
|
||||
const blockRef = rawText.slice(2, -2)
|
||||
shapesToCreate.push({
|
||||
...LogseqPortalShape.defaultProps,
|
||||
id: uniqueId(),
|
||||
parentId: app.currentPageId,
|
||||
point: [
|
||||
point[0] + shape.point![0] - commonBounds.minX,
|
||||
point[1] + shape.point![1] - commonBounds.minY,
|
||||
],
|
||||
}
|
||||
})
|
||||
shapesToCreate.push(...clonedShapes)
|
||||
|
||||
// Try to rebinding the shapes to the new assets
|
||||
shapesToCreate.forEach((s, idx) => {
|
||||
if (s.handles) {
|
||||
Object.values(s.handles).forEach(h => {
|
||||
if (h.bindingId) {
|
||||
// try to bind the new shape
|
||||
const binding = app.currentPage.bindings[h.bindingId]
|
||||
// if the copied binding from/to is in the source
|
||||
const oldFromIdx = shapes.findIndex(s => s.id === binding.fromId)
|
||||
const oldToIdx = shapes.findIndex(s => s.id === binding.toId)
|
||||
if (binding && oldFromIdx !== -1 && oldToIdx !== -1) {
|
||||
const newBinding: TLBinding = {
|
||||
...binding,
|
||||
id: uniqueId(),
|
||||
fromId: shapesToCreate[oldFromIdx].id,
|
||||
toId: shapesToCreate[oldToIdx].id,
|
||||
}
|
||||
bindingsToCreate.push(newBinding)
|
||||
h.bindingId = newBinding.id
|
||||
} else {
|
||||
h.bindingId = undefined
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
point: [point[0], point[1]],
|
||||
size: [600, 400],
|
||||
pageId: blockRef,
|
||||
blockType: 'B',
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// TODO: supporting other pasting formats
|
||||
@@ -131,10 +151,16 @@ export function usePaste() {
|
||||
})),
|
||||
...shapesToCreate,
|
||||
]
|
||||
app.createAssets(assetsToCreate)
|
||||
app.createShapes(allShapesToAdd)
|
||||
app.currentPage.updateBindings(Object.fromEntries(bindingsToCreate.map(b => [b.id, b])))
|
||||
|
||||
app.setSelectedShapes(allShapesToAdd.map(s => s.id))
|
||||
app.transaction(() => {
|
||||
if (assetsToCreate.length > 0) {
|
||||
app.createAssets(assetsToCreate)
|
||||
}
|
||||
if (allShapesToAdd.length > 0) {
|
||||
app.createShapes(allShapesToAdd)
|
||||
}
|
||||
app.currentPage.updateBindings(Object.fromEntries(bindingsToCreate.map(b => [b.id, b])))
|
||||
app.setSelectedShapes(allShapesToAdd.map(s => s.id))
|
||||
})
|
||||
}, [])
|
||||
}
|
||||
|
||||
@@ -18,8 +18,9 @@ const HEADER_HEIGHT = 40
|
||||
export interface LogseqPortalShapeProps extends TLBoxShapeProps, CustomStyleProps {
|
||||
type: 'logseq-portal'
|
||||
pageId: string // page name or UUID
|
||||
collapsed: boolean
|
||||
collapsedHeight: number
|
||||
blockType?: 'P' | 'B'
|
||||
collapsed?: boolean
|
||||
collapsedHeight?: number
|
||||
}
|
||||
|
||||
interface LogseqQuickSearchProps {
|
||||
@@ -125,6 +126,14 @@ export class LogseqPortalShape extends TLBoxShape<LogseqPortalShapeProps> {
|
||||
}
|
||||
}
|
||||
|
||||
static isPageOrBlock(id: string): 'P' | 'B' | false {
|
||||
const blockRefEg = '((62af02d0-0443-42e8-a284-946c162b0f89))'
|
||||
if (id) {
|
||||
return /^\(\(.*\)\)$/.test(id) && id.length === blockRefEg.length ? 'B' : 'P'
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
ReactContextBar = observer(() => {
|
||||
return (
|
||||
<>
|
||||
@@ -203,9 +212,11 @@ export class LogseqPortalShape extends TLBoxShape<LogseqPortalShapeProps> {
|
||||
|
||||
const onPageNameChanged = React.useCallback((id: string) => {
|
||||
transaction(() => {
|
||||
app.history.resume()
|
||||
this.update({
|
||||
pageId: id,
|
||||
size: [600, 320],
|
||||
blockType: 'page',
|
||||
})
|
||||
this.setDraft(false)
|
||||
app.setActivatedShapes([])
|
||||
@@ -254,7 +265,7 @@ export class LogseqPortalShape extends TLBoxShape<LogseqPortalShapeProps> {
|
||||
'--ls-title-text-color': stroke,
|
||||
}}
|
||||
>
|
||||
<LogseqPortalShapeHeader type="P" pageId={pageId} />
|
||||
<LogseqPortalShapeHeader type={this.props.blockType ?? 'P'} pageId={pageId} />
|
||||
{(!this.props.collapsed || isActivated) && (
|
||||
<div
|
||||
style={{
|
||||
|
||||
@@ -5,6 +5,7 @@ import { App as TldrawApp } from 'tldraw-logseq'
|
||||
const storingKey = 'playground.index'
|
||||
|
||||
const onPersist = app => {
|
||||
console.log('onPersist', app)
|
||||
window.sessionStorage.setItem(storingKey, JSON.stringify(app.serialized))
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import Vec from '@tldraw/vec'
|
||||
import type { TLApp, TLPage, TLShapeModel, TLShape } from '~lib'
|
||||
import type { TLEventMap } from '~types'
|
||||
import { BoundsUtils } from '~utils'
|
||||
@@ -132,6 +133,19 @@ export class TLApi<S extends TLShape = TLShape, K extends TLEventMap = TLEventMa
|
||||
return this
|
||||
}
|
||||
|
||||
cameraToCenter = (): this => {
|
||||
const { shapes } = this.app.currentPage
|
||||
// Viewport should be focused to existing shapes
|
||||
const commonBounds = BoundsUtils.getCommonBounds(shapes.map(shape => shape.bounds))
|
||||
this.app.viewport.update({
|
||||
point: Vec.add(Vec.neg(BoundsUtils.getBoundsCenter(commonBounds)), [
|
||||
this.app.viewport.currentView.width / 2,
|
||||
this.app.viewport.currentView.height/ 2,
|
||||
]),
|
||||
})
|
||||
return this
|
||||
}
|
||||
|
||||
/** Zoom to fit the current selection in the viewport. */
|
||||
zoomToSelection = (): this => {
|
||||
const { selectionBounds } = this.app
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { Vec } from '@tldraw/vec'
|
||||
import { action, computed, makeObservable, observable, toJS } from 'mobx'
|
||||
import { action, computed, makeObservable, observable, toJS, transaction } from 'mobx'
|
||||
import { BoundsUtils, KeyUtils } from '~utils'
|
||||
import {
|
||||
TLSelectTool,
|
||||
@@ -191,6 +191,7 @@ export class TLApp<
|
||||
loadDocumentModel(model: TLDocumentModel<S>): this {
|
||||
this.history.deserialize(model)
|
||||
if (model.assets) this.addAssets(model.assets)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
@@ -719,6 +720,15 @@ export class TLApp<
|
||||
return Shape
|
||||
}
|
||||
|
||||
transaction = (fn: () => void) => {
|
||||
transaction(() => {
|
||||
this.history.pause()
|
||||
fn()
|
||||
this.history.resume()
|
||||
this.persist()
|
||||
})
|
||||
}
|
||||
|
||||
/* ------------------ Subscriptions ----------------- */
|
||||
|
||||
private subscriptions = new Set<TLSubscription<S, K, this, any>>([])
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { TLApp, TLPage, TLDocumentModel, TLShape } from '~lib'
|
||||
import type { TLEventMap } from '~types'
|
||||
import { deepEqual } from '~utils'
|
||||
|
||||
export class TLHistory<S extends TLShape = TLShape, K extends TLEventMap = TLEventMap> {
|
||||
constructor(app: TLApp<S, K>) {
|
||||
@@ -12,7 +13,7 @@ export class TLHistory<S extends TLShape = TLShape, K extends TLEventMap = TLEve
|
||||
isPaused = true
|
||||
|
||||
get creating() {
|
||||
return this.app.selectedTool.currentState.id === 'creating';
|
||||
return this.app.selectedTool.currentState.id === 'creating'
|
||||
}
|
||||
|
||||
pause = () => {
|
||||
@@ -38,6 +39,9 @@ export class TLHistory<S extends TLShape = TLShape, K extends TLEventMap = TLEve
|
||||
|
||||
const { serialized } = this.app
|
||||
|
||||
// Do not persist if the serialized state is the same as the last one
|
||||
if (deepEqual(this.stack[this.pointer], serialized)) return
|
||||
|
||||
if (this.pointer < this.stack.length) {
|
||||
this.stack = this.stack.slice(0, this.pointer + 1)
|
||||
}
|
||||
|
||||
@@ -280,6 +280,7 @@ export class TLPage<S extends TLShape = TLShape, E extends TLEventMap = TLEventM
|
||||
|
||||
if (!deepEqual(updated, curr) || shapesToDelete.length) {
|
||||
transaction(() => {
|
||||
this.app.history.resume()
|
||||
this.update({
|
||||
bindings: updated.bindings,
|
||||
})
|
||||
|
||||
@@ -53,6 +53,7 @@ export class PointingCanvasState<
|
||||
point: [...this.app.inputs.originPoint],
|
||||
})
|
||||
shape.setDraft(true)
|
||||
this.app.history.pause()
|
||||
this.app.setActivatedShapes([shape.id])
|
||||
this.app.currentPage.addShapes(shape)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import * as React from 'react'
|
||||
import { TLViewport, TLBounds, debounce } from '@tldraw/core'
|
||||
import { useApp } from './useApp'
|
||||
|
||||
const getNearestScrollableContainer = (element: HTMLElement): HTMLElement | Document => {
|
||||
let parent = element.parentElement
|
||||
@@ -25,6 +26,7 @@ export function useResizeObserver<T extends HTMLElement>(
|
||||
viewport: TLViewport,
|
||||
onBoundsChange?: (bounds: TLBounds) => void
|
||||
) {
|
||||
const app = useApp()
|
||||
const rIsMounted = React.useRef(false)
|
||||
|
||||
// When the element resizes, update the bounds (stored in inputs)
|
||||
@@ -81,5 +83,8 @@ export function useResizeObserver<T extends HTMLElement>(
|
||||
|
||||
React.useLayoutEffect(() => {
|
||||
updateBounds()
|
||||
setTimeout(() => {
|
||||
app.api.cameraToCenter()
|
||||
})
|
||||
}, [ref])
|
||||
}
|
||||
|
||||
@@ -6811,11 +6811,6 @@ react-grid-layout@0.16.6:
|
||||
react-draggable "3.x"
|
||||
react-resizable "1.x"
|
||||
|
||||
react-icon-base@2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/react-icon-base/-/react-icon-base-2.1.0.tgz#a196e33fdf1e7aaa1fda3aefbb68bdad9e82a79d"
|
||||
integrity sha512-9wwKJa2LB8ujtJB5MAXYYEM7JfYThZTj0YnfGxzLLWkifaLIGc7iTde2EpJ7ka5MjneRHnlxbIn5VV9k2WjUVA==
|
||||
|
||||
react-icon-base@^2.1.2:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/react-icon-base/-/react-icon-base-2.1.2.tgz#a17101dad9c1192652356096860a9ab43a0766c7"
|
||||
|
||||
Reference in New Issue
Block a user