Merge branch 'logseq:whiteboards' into whiteboards

This commit is contained in:
sawhney17
2022-06-21 21:54:01 +04:00
committed by GitHub
67 changed files with 1677 additions and 1281 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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 */,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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 닫기"
}
})
})

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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))
})
}, [])
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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