mirror of https://github.com/logseq/logseq
Enhance: plugin APIs (#10399)
* enhance(plugin): call apis with the sdk ns * enhance(plugin): types * enhance(api): get value from the computed style * enhance(api): types * enhance(plugin): types * enhance(plugin): types * fix: lint * fix(apis): incorrect shortcut command registion for block editing mode #10392 * fix(api): types * enhance(apis): support register shortcuts with multi binding vals * fix(plugins): normalize command key to make the internal keyword legal * chore(plugin): build libs core * chore(plugin): bump version * enhance(apis): normalize apis cljs data * chore(plugin): update libs user sdk * chore(plugin): CHANGELOG.md * fix: typo * fix(ux): support querying plugins with right space charspull/10693/head
parent
eb22435280
commit
036df25a17
|
@ -4,8 +4,20 @@ All notable changes to this project will be documented in this file.
|
|||
|
||||
## [Unreleased]
|
||||
|
||||
## [0.0.15]
|
||||
## [0.0.16]
|
||||
### Added
|
||||
- Support api of `logseq.UI.queryElementRect: (selector: string) => Promise<DOMRectReadOnly | null>`
|
||||
- Support api of `logseq.UI.queryElementById: (id: string) => Promise<string | boolean>`
|
||||
- Support api of `logseq.UI.checkSlotValid: (slot: UISlotIdentity['slot']) => Promise<boolean>`
|
||||
- Support api of `logseq.UI.resolveThemeCssPropsVals: (props: string | Array<string>) => Promise<any>`
|
||||
- Support api of `logseq.Assets.builtInOpen(path: string): Promise<boolean | undefined>`
|
||||
|
||||
### Fixed
|
||||
- fix Plugin can't register command shortcut with editing mode [#10392](https://github.com/logseq/logseq/issues/10392)
|
||||
- fix [Plugin API] [Keymap] Command without keybinding can't be present in Keymap [#10466](https://github.com/logseq/logseq/issues/10466)
|
||||
- fix [Possible DATA LOSS] [Plugin API] [Keymap] Any plugin could break the global config.edn [#10465](https://github.com/logseq/logseq/issues/10465)
|
||||
|
||||
## [0.0.15]
|
||||
### Added
|
||||
- Support a plug-in flag for the plugin slash commands item
|
||||
- Support api of `logseq.App.setCurrentGraphConfigs: (configs: {}) => Promise<void>`
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@logseq/libs",
|
||||
"version": "0.0.15",
|
||||
"version": "0.0.16",
|
||||
"description": "Logseq SDK libraries",
|
||||
"main": "dist/lsplugin.user.js",
|
||||
"typings": "index.d.ts",
|
||||
|
|
|
@ -54,6 +54,7 @@ const DIR_PLUGINS = 'plugins'
|
|||
declare global {
|
||||
interface Window {
|
||||
LSPluginCore: LSPluginCore
|
||||
DOMPurify: typeof DOMPurify
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -308,23 +309,25 @@ function initProviderHandlers(pluginLocal: PluginLocal) {
|
|||
// provider:ui
|
||||
pluginLocal.on(_('ui'), (ui: UIOptions) => {
|
||||
pluginLocal._onHostMounted(() => {
|
||||
pluginLocal._dispose(
|
||||
setupInjectedUI.call(
|
||||
pluginLocal,
|
||||
ui,
|
||||
Object.assign(
|
||||
{
|
||||
'data-ref': pluginLocal.id,
|
||||
},
|
||||
ui.attrs || {}
|
||||
),
|
||||
({ el, float }) => {
|
||||
if (!float) return
|
||||
const identity = el.dataset.identity
|
||||
pluginLocal.layoutCore.move_container_to_top(identity)
|
||||
}
|
||||
)
|
||||
const ret = setupInjectedUI.call(
|
||||
pluginLocal,
|
||||
ui,
|
||||
Object.assign(
|
||||
{
|
||||
'data-ref': pluginLocal.id,
|
||||
},
|
||||
ui.attrs || {}
|
||||
),
|
||||
({ el, float }) => {
|
||||
if (!float) return
|
||||
const identity = el.dataset.identity
|
||||
pluginLocal.layoutCore.move_container_to_top(identity)
|
||||
}
|
||||
)
|
||||
|
||||
if (typeof ret === 'function') {
|
||||
pluginLocal._dispose(ret)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
@ -346,8 +349,6 @@ function initApiProxyHandlers(pluginLocal: PluginLocal) {
|
|||
}
|
||||
}
|
||||
|
||||
const { _sync } = payload
|
||||
|
||||
if (pluginLocal.shadow) {
|
||||
if (payload.actor) {
|
||||
payload.actor.resolve(ret)
|
||||
|
@ -355,6 +356,8 @@ function initApiProxyHandlers(pluginLocal: PluginLocal) {
|
|||
return
|
||||
}
|
||||
|
||||
const { _sync } = payload
|
||||
|
||||
if (_sync != null) {
|
||||
const reply = (result: any) => {
|
||||
pluginLocal.caller?.callUserModel(LSPMSG_SYNC, {
|
||||
|
@ -430,7 +433,7 @@ class PluginLocal extends EventEmitter<
|
|||
|
||||
async _setupUserSettings(reload?: boolean) {
|
||||
const { _options } = this
|
||||
const logger = (this._logger = new PluginLogger('Loader'))
|
||||
const logger = (this._logger = new PluginLogger(`Loader:${this.debugTag}`))
|
||||
|
||||
if (_options.settings && !reload) {
|
||||
return
|
||||
|
@ -532,7 +535,7 @@ class PluginLocal extends EventEmitter<
|
|||
const localRoot = (this._localRoot = safetyPathNormalize(url))
|
||||
const logseq: Partial<LSPluginPkgConfig> = pkg.logseq || {}
|
||||
|
||||
// Pick legal attrs
|
||||
// Pick legal attrs
|
||||
;[
|
||||
'name',
|
||||
'author',
|
||||
|
@ -642,10 +645,10 @@ class PluginLocal extends EventEmitter<
|
|||
<meta charset="UTF-8">
|
||||
<title>logseq plugin entry</title>
|
||||
${
|
||||
IS_DEV
|
||||
? `<script src="${sdkPathRoot}/lsplugin.user.js?v=${tag}"></script>`
|
||||
: `<script src="https://cdn.jsdelivr.net/npm/@logseq/libs/dist/lsplugin.user.min.js?v=${tag}"></script>`
|
||||
}
|
||||
IS_DEV
|
||||
? `<script src="${sdkPathRoot}/lsplugin.user.js?v=${tag}"></script>`
|
||||
: `<script src="https://cdn.jsdelivr.net/npm/@logseq/libs/dist/lsplugin.user.min.js?v=${tag}"></script>`
|
||||
}
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
@ -866,7 +869,7 @@ class PluginLocal extends EventEmitter<
|
|||
|
||||
this._dispose(cleanInjectedScripts.bind(this))
|
||||
} catch (e) {
|
||||
this.logger?.error('[Load Plugin]', e, true)
|
||||
this.logger.error('load', e, true)
|
||||
|
||||
this.dispose().catch(null)
|
||||
this._status = PluginLocalLoadStatus.ERROR
|
||||
|
@ -924,7 +927,7 @@ class PluginLocal extends EventEmitter<
|
|||
)
|
||||
this.emit('beforeunload', eventBeforeUnload)
|
||||
} catch (e) {
|
||||
this.logger.error('[beforeunload]', e)
|
||||
this.logger.error('beforeunload', e)
|
||||
}
|
||||
|
||||
await this.dispose()
|
||||
|
@ -932,7 +935,7 @@ class PluginLocal extends EventEmitter<
|
|||
|
||||
this.emit('unloaded')
|
||||
} catch (e) {
|
||||
this.logger.error('[unload Error]', e)
|
||||
this.logger.error('unload', e)
|
||||
} finally {
|
||||
this._status = PluginLocalLoadStatus.UNLOADED
|
||||
}
|
||||
|
@ -1038,7 +1041,7 @@ class PluginLocal extends EventEmitter<
|
|||
|
||||
get debugTag() {
|
||||
const name = this._options?.name
|
||||
return `#${this._id} ${name ?? ''}`
|
||||
return `#${this._id} - ${name ?? ''}`
|
||||
}
|
||||
|
||||
get localRoot(): string {
|
||||
|
@ -1103,8 +1106,7 @@ class LSPluginCore
|
|||
| 'beforereload'
|
||||
| 'reloaded'
|
||||
>
|
||||
implements ILSPluginThemeManager
|
||||
{
|
||||
implements ILSPluginThemeManager {
|
||||
private _isRegistering = false
|
||||
private _readyIndicator?: DeferredActor
|
||||
private readonly _hostMountedActor: DeferredActor = deferred()
|
||||
|
@ -1566,12 +1568,12 @@ class LSPluginCore
|
|||
await this.saveUserPreferences(
|
||||
theme.mode
|
||||
? {
|
||||
themes: {
|
||||
...this._userPreferences.themes,
|
||||
mode: theme.mode,
|
||||
[theme.mode]: theme,
|
||||
},
|
||||
}
|
||||
themes: {
|
||||
...this._userPreferences.themes,
|
||||
mode: theme.mode,
|
||||
[theme.mode]: theme,
|
||||
},
|
||||
}
|
||||
: { theme: theme }
|
||||
)
|
||||
}
|
||||
|
|
|
@ -192,7 +192,7 @@ export interface BlockEntity {
|
|||
level?: number
|
||||
meta?: { timestamps: any; properties: any; startPos: number; endPos: number }
|
||||
title?: Array<any>
|
||||
marker?: string
|
||||
marker?: string
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -235,9 +235,10 @@ export type BlockCursorPosition = {
|
|||
rect: DOMRect
|
||||
}
|
||||
|
||||
export type Keybinding = string | Array<string>
|
||||
export type SimpleCommandKeybinding = {
|
||||
mode?: 'global' | 'non-editing' | 'editing'
|
||||
binding: string
|
||||
binding: Keybinding
|
||||
mac?: string // special for Mac OS
|
||||
}
|
||||
|
||||
|
@ -469,25 +470,6 @@ export interface IAppProxy {
|
|||
removeTemplate: (name: string) => Promise<any>
|
||||
insertTemplate: (target: BlockUUID, name: string) => Promise<any>
|
||||
|
||||
// ui
|
||||
queryElementById: (id: string) => Promise<string | boolean>
|
||||
|
||||
/**
|
||||
* @added 0.0.5
|
||||
* @param selector
|
||||
*/
|
||||
queryElementRect: (selector: string) => Promise<DOMRectReadOnly | null>
|
||||
|
||||
/**
|
||||
* @deprecated Use `logseq.UI.showMsg` instead
|
||||
* @param content
|
||||
* @param status
|
||||
*/
|
||||
showMsg: (
|
||||
content: string,
|
||||
status?: 'success' | 'warning' | 'error' | string
|
||||
) => void
|
||||
|
||||
setZoomFactor: (factor: number) => void
|
||||
setFullScreen: (flag: boolean | 'toggle') => void
|
||||
setLeftSidebarVisible: (flag: boolean | 'toggle') => void
|
||||
|
@ -891,20 +873,16 @@ export type UIMsgOptions = {
|
|||
export type UIMsgKey = UIMsgOptions['key']
|
||||
|
||||
export interface IUIProxy {
|
||||
/**
|
||||
* @added 0.0.2
|
||||
*
|
||||
* @param content
|
||||
* @param status
|
||||
* @param opts
|
||||
*/
|
||||
showMsg: (
|
||||
content: string,
|
||||
status?: 'success' | 'warning' | 'error' | string,
|
||||
opts?: Partial<UIMsgOptions>
|
||||
) => Promise<UIMsgKey>
|
||||
|
||||
closeMsg: (key: UIMsgKey) => void
|
||||
queryElementRect: (selector: string) => Promise<DOMRectReadOnly | null>
|
||||
queryElementById: (id: string) => Promise<string | boolean>
|
||||
checkSlotValid: (slot: UISlotIdentity['slot']) => Promise<boolean>
|
||||
resolveThemeCssPropsVals: (props: string | Array<string>) => Promise<Record<string, string | undefined> | null>
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -938,6 +916,13 @@ export interface IAssetsProxy {
|
|||
* @param path
|
||||
*/
|
||||
makeUrl(path: string): Promise<string>
|
||||
|
||||
/**
|
||||
* try to open asset type file in Logseq app
|
||||
* @added 0.0.16
|
||||
* @param path
|
||||
*/
|
||||
builtInOpen(path: string): Promise<boolean | undefined>
|
||||
}
|
||||
|
||||
export interface ILSPluginThemeManager {
|
||||
|
|
|
@ -4,7 +4,7 @@ import {
|
|||
mergeSettingsWithSchema,
|
||||
PluginLogger,
|
||||
safeSnakeCase,
|
||||
safetyPathJoin,
|
||||
safetyPathJoin, normalizeKeyStr,
|
||||
} from './helpers'
|
||||
import { LSPluginCaller } from './LSPlugin.caller'
|
||||
import * as callableAPIs from './callable.apis'
|
||||
|
@ -77,12 +77,21 @@ function registerSimpleCommand (
|
|||
},
|
||||
action: SimpleCommandCallback
|
||||
) {
|
||||
const { key, label, desc, palette, keybinding, extras } = opts
|
||||
|
||||
if (typeof action !== 'function') {
|
||||
this.logger.error(`${key || label}: command action should be function.`)
|
||||
return false
|
||||
}
|
||||
|
||||
const { key, label, desc, palette, keybinding, extras } = opts
|
||||
const eventKey = `SimpleCommandHook${key}${++registeredCmdUid}`
|
||||
const normalizedKey = normalizeKeyStr(key)
|
||||
|
||||
if (!normalizedKey) {
|
||||
this.logger.error(`${label}: command key is required.`)
|
||||
return false
|
||||
}
|
||||
|
||||
const eventKey = `SimpleCommandHook${normalizedKey}${++registeredCmdUid}`
|
||||
|
||||
this.Editor['on' + eventKey](action)
|
||||
|
||||
|
@ -92,7 +101,7 @@ function registerSimpleCommand (
|
|||
this.baseInfo.id,
|
||||
// [cmd, action]
|
||||
[
|
||||
{ key, label, type, desc, keybinding, extras },
|
||||
{ key: normalizedKey, label, type, desc, keybinding, extras },
|
||||
['editor/hook', eventKey],
|
||||
],
|
||||
palette,
|
||||
|
@ -171,7 +180,7 @@ const app: Partial<IAppProxy> = {
|
|||
|
||||
const { binding } = keybinding
|
||||
const group = '$shortcut$'
|
||||
const key = group + safeSnakeCase(binding)
|
||||
const key = opts.key || (group + safeSnakeCase(binding?.toString()))
|
||||
|
||||
return registerSimpleCommand.call(
|
||||
this,
|
||||
|
|
|
@ -2,7 +2,7 @@ import { SettingSchemaDesc, StyleString, UIOptions } from './LSPlugin'
|
|||
import { PluginLocal } from './LSPlugin.core'
|
||||
import * as nodePath from 'path'
|
||||
import DOMPurify from 'dompurify'
|
||||
import merge from 'deepmerge';
|
||||
import merge from 'deepmerge'
|
||||
import { snakeCase } from 'snake-case'
|
||||
import * as callables from './callable.apis'
|
||||
import EventEmitter from 'eventemitter3'
|
||||
|
@ -211,12 +211,23 @@ export function deferred<T = any>(timeout?: number, tag?: string) {
|
|||
|
||||
export function invokeHostExportedApi(method: string, ...args: Array<any>) {
|
||||
method = method?.startsWith('_call') ? method : method?.replace(/^[_$]+/, '')
|
||||
const method1 = safeSnakeCase(method)
|
||||
let method1 = safeSnakeCase(method)
|
||||
|
||||
// @ts-ignore
|
||||
const nsSDK = window.logseq?.sdk
|
||||
const supportedNS = nsSDK && Object.keys(nsSDK)
|
||||
let nsTarget = {}
|
||||
const ns0 = method1?.split('_')?.[0]
|
||||
|
||||
if (ns0 && supportedNS.includes(ns0)) {
|
||||
method1 = method1.replace(new RegExp(`^${ns0}_`), '')
|
||||
nsTarget = nsSDK?.[ns0]
|
||||
}
|
||||
|
||||
const logseqHostExportedApi = Object.assign(
|
||||
// @ts-ignore
|
||||
window.logseq?.api || {},
|
||||
callables
|
||||
{}, window.logseq?.api,
|
||||
nsTarget, callables
|
||||
)
|
||||
|
||||
const fn =
|
||||
|
@ -266,9 +277,9 @@ export function setupInjectedStyle(
|
|||
el.textContent = style
|
||||
|
||||
attrs &&
|
||||
Object.entries(attrs).forEach(([k, v]) => {
|
||||
el.setAttribute(k, v)
|
||||
})
|
||||
Object.entries(attrs).forEach(([k, v]) => {
|
||||
el.setAttribute(k, v)
|
||||
})
|
||||
|
||||
document.head.append(el)
|
||||
|
||||
|
@ -313,7 +324,7 @@ export function setupInjectedUI(
|
|||
console.error(
|
||||
`${this.debugTag} can not resolve selector target ${selector}`
|
||||
)
|
||||
return
|
||||
return false
|
||||
}
|
||||
|
||||
if (ui.template) {
|
||||
|
@ -344,22 +355,22 @@ export function setupInjectedUI(
|
|||
|
||||
// update attributes
|
||||
attrs &&
|
||||
Object.entries(attrs).forEach(([k, v]) => {
|
||||
el.setAttribute(k, v)
|
||||
})
|
||||
Object.entries(attrs).forEach(([k, v]) => {
|
||||
el.setAttribute(k, v)
|
||||
})
|
||||
|
||||
let positionDirty = el.dataset.dx != null
|
||||
ui.style &&
|
||||
Object.entries(ui.style).forEach(([k, v]) => {
|
||||
if (
|
||||
positionDirty &&
|
||||
['left', 'top', 'bottom', 'right', 'width', 'height'].includes(k)
|
||||
) {
|
||||
return
|
||||
}
|
||||
Object.entries(ui.style).forEach(([k, v]) => {
|
||||
if (
|
||||
positionDirty &&
|
||||
['left', 'top', 'bottom', 'right', 'width', 'height'].includes(k)
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
el.style[k] = v
|
||||
})
|
||||
el.style[k] = v
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -379,14 +390,14 @@ export function setupInjectedUI(
|
|||
content.innerHTML = ui.template
|
||||
|
||||
attrs &&
|
||||
Object.entries(attrs).forEach(([k, v]) => {
|
||||
el.setAttribute(k, v)
|
||||
})
|
||||
Object.entries(attrs).forEach(([k, v]) => {
|
||||
el.setAttribute(k, v)
|
||||
})
|
||||
|
||||
ui.style &&
|
||||
Object.entries(ui.style).forEach(([k, v]) => {
|
||||
el.style[k] = v
|
||||
})
|
||||
Object.entries(ui.style).forEach(([k, v]) => {
|
||||
el.style[k] = v
|
||||
})
|
||||
|
||||
let teardownUI: () => void
|
||||
let disposeFloat: () => void
|
||||
|
@ -399,11 +410,11 @@ export function setupInjectedUI(
|
|||
el.classList.add('lsp-ui-float-container', 'visible')
|
||||
disposeFloat =
|
||||
(pl._setupResizableContainer(el, key),
|
||||
pl._setupDraggableContainer(el, {
|
||||
key,
|
||||
close: () => teardownUI(),
|
||||
title: attrs?.title,
|
||||
}))
|
||||
pl._setupDraggableContainer(el, {
|
||||
key,
|
||||
close: () => teardownUI(),
|
||||
title: attrs?.title,
|
||||
}))
|
||||
}
|
||||
|
||||
if (!!slot && ui.reset) {
|
||||
|
@ -542,3 +553,8 @@ export function mergeSettingsWithSchema(
|
|||
// shadow copy
|
||||
return Object.assign(defaults, settings)
|
||||
}
|
||||
|
||||
export function normalizeKeyStr(s: string) {
|
||||
if (typeof s !== 'string') return
|
||||
return s.trim().replace(/\s/g, '_').toLowerCase()
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -0,0 +1 @@
|
|||
/*! @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 */
|
|
@ -54,7 +54,7 @@
|
|||
(try
|
||||
(let [graph-path (state/get-graph-path)
|
||||
_ (when (string/blank? graph-path)
|
||||
(utils/send-to-renderer "getCurrentGraph" {})
|
||||
(utils/send-to-renderer :setCurrentGraph {})
|
||||
(throw (js/Error. "Empty graph path")))
|
||||
p (.join node-path graph-path ".git")]
|
||||
(when (and (fs/existsSync p)
|
||||
|
|
|
@ -83,10 +83,10 @@
|
|||
(fn []
|
||||
(state/pub-event! [:modal/set-git-username-and-email])))
|
||||
|
||||
(safe-api-call "getCurrentGraph"
|
||||
(safe-api-call "setCurrentGraph"
|
||||
(fn []
|
||||
(when-let [graph (state/get-current-repo)]
|
||||
(ipc/ipc "setCurrentGraph" graph))))
|
||||
(ipc/ipc :setCurrentGraph graph))))
|
||||
|
||||
(safe-api-call "redirect"
|
||||
(fn [data]
|
||||
|
|
|
@ -373,7 +373,7 @@
|
|||
(some-> (js/document.querySelector ".cp__plugins-page") (.focus))
|
||||
(reset! *search-key nil))))
|
||||
:on-change #(let [^js target (.-target %)]
|
||||
(reset! *search-key (util/trim-safe (.-value target))))
|
||||
(reset! *search-key (some-> (.-value target) (string/triml))))
|
||||
:value (or search-key "")}]])
|
||||
|
||||
(rum/defc panel-tab-developer
|
||||
|
|
|
@ -35,7 +35,10 @@
|
|||
(get @state/state :command-palette/commands)))
|
||||
|
||||
(defn history
|
||||
([] (or (storage/get "commands-history") []))
|
||||
([] (or (try (storage/get "commands-history")
|
||||
(catch js/Error e
|
||||
(log/error :commands-history e)))
|
||||
[]))
|
||||
([vals] (storage/set "commands-history" vals)))
|
||||
|
||||
(defn- assoc-invokes [cmds]
|
||||
|
|
|
@ -25,12 +25,13 @@
|
|||
(defn- normalize-keyword-for-json
|
||||
[input]
|
||||
(when input
|
||||
(walk/postwalk
|
||||
(fn [a]
|
||||
(cond
|
||||
(keyword? a) (csk/->camelCase (name a))
|
||||
(uuid? a) (str a)
|
||||
:else a)) input)))
|
||||
(let [f (fn [[k v]] (if (keyword? k) [(csk/->camelCase (name k)) v] [k v]))]
|
||||
(walk/postwalk
|
||||
(fn [x]
|
||||
(cond
|
||||
(map? x) (into {} (map f x))
|
||||
(uuid? x) (str x)
|
||||
:else x)) input))))
|
||||
|
||||
(defn invoke-exported-api
|
||||
[type & args]
|
||||
|
@ -297,7 +298,8 @@
|
|||
[pid key keybinding]
|
||||
(let [id (keyword (str "plugin." pid "/" key))
|
||||
binding (:binding keybinding)
|
||||
binding (some->> (if (string? binding) [binding] (seq binding))
|
||||
binding (some->> (if (string? binding) [binding] (vec binding))
|
||||
(remove string/blank?)
|
||||
(map shortcut-utils/undecorate-binding))
|
||||
binding (if util/mac?
|
||||
(or (:mac keybinding) binding) binding)
|
||||
|
@ -495,7 +497,7 @@
|
|||
payload)
|
||||
(if (keyword? plugin-id) (name plugin-id) plugin-id))
|
||||
(catch :default e
|
||||
(js/console.error "[Hook Plugin Err]" e)))))
|
||||
(log/error :invoke-hook-exception e)))))
|
||||
|
||||
(defn hook-plugin-app
|
||||
([type payload] (hook-plugin-app type payload nil))
|
||||
|
|
|
@ -925,15 +925,17 @@
|
|||
(def *shortcut-cmds (atom {}))
|
||||
|
||||
(defn add-shortcut!
|
||||
[handler-id id shortcut-map]
|
||||
(swap! *config assoc-in [handler-id id] shortcut-map)
|
||||
(swap! *shortcut-cmds assoc id (:cmd shortcut-map))
|
||||
(let [plugin? (str/starts-with? (str id) ":plugin.")
|
||||
category (or (:category shortcut-map)
|
||||
(if plugin?
|
||||
:shortcut.category/plugins
|
||||
:shortcut.category/others))]
|
||||
(swap! *category update category #(conj % id))))
|
||||
([handler-id id shortcut-map] (add-shortcut! handler-id id shortcut-map false))
|
||||
([handler-id id shortcut-map config-only?]
|
||||
(swap! *config assoc-in [handler-id id] shortcut-map)
|
||||
(when-not config-only?
|
||||
(swap! *shortcut-cmds assoc id (:cmd shortcut-map))
|
||||
(let [plugin? (str/starts-with? (str id) ":plugin.")
|
||||
category (or (:category shortcut-map)
|
||||
(if plugin?
|
||||
:shortcut.category/plugins
|
||||
:shortcut.category/others))]
|
||||
(swap! *category update category #(conj % id))))))
|
||||
|
||||
(defn remove-shortcut!
|
||||
[handler-id id]
|
||||
|
|
|
@ -93,7 +93,8 @@
|
|||
(when-let [handler (get-handler-by-id handler-id)]
|
||||
(when-let [ks (dh/shortcut-binding shortcut-id)]
|
||||
(doseq [k ks]
|
||||
(.unregisterShortcut ^js handler (shortcut-utils/undecorate-binding k))))
|
||||
(.unregisterShortcut ^js handler (shortcut-utils/undecorate-binding k)))))
|
||||
(when shortcut-id
|
||||
(shortcut-config/remove-shortcut! handler-id shortcut-id)))
|
||||
|
||||
(defn uninstall-shortcut-handler!
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
[logseq.sdk.core]
|
||||
[logseq.sdk.utils :as sdk-utils]
|
||||
[logseq.sdk.ui :as sdk-ui]
|
||||
[logseq.sdk.git :as sdk-git]
|
||||
[logseq.sdk.assets :as sdk-assets]
|
||||
[clojure.string :as string]
|
||||
[datascript.core :as d]
|
||||
|
@ -31,6 +30,7 @@
|
|||
[frontend.modules.outliner.tree :as outliner-tree]
|
||||
[frontend.handler.command-palette :as palette-handler]
|
||||
[frontend.modules.shortcut.core :as st]
|
||||
[frontend.modules.shortcut.config :as shortcut-config]
|
||||
[electron.listener :as el]
|
||||
[frontend.state :as state]
|
||||
[frontend.util :as util]
|
||||
|
@ -352,7 +352,7 @@
|
|||
cmd (assoc cmd :key (string/replace (:key cmd) ":" "-"))
|
||||
key (:key cmd)
|
||||
keybinding (:keybinding cmd)
|
||||
palette-cmd (and palette? (plugin-handler/simple-cmd->palette-cmd pid cmd action))
|
||||
palette-cmd (plugin-handler/simple-cmd->palette-cmd pid cmd action)
|
||||
action' #(state/pub-event! [:exec-plugin-cmd {:type type :key key :pid pid :cmd cmd :action action}])]
|
||||
|
||||
;; handle simple commands
|
||||
|
@ -369,8 +369,16 @@
|
|||
(palette-handler/invoke-command palette-cmd)
|
||||
(action')))
|
||||
[mode-id id shortcut-map] (update shortcut-args 2 merge cmd {:fn dispatch-cmd :cmd palette-cmd})]
|
||||
(println :shortcut/register-shortcut [mode-id id shortcut-map])
|
||||
(st/register-shortcut! mode-id id shortcut-map)))))))
|
||||
|
||||
(cond
|
||||
;; FIX ME: move to register logic
|
||||
(= mode-id :shortcut.handler/block-editing-only)
|
||||
(shortcut-config/add-shortcut! mode-id id shortcut-map)
|
||||
|
||||
:else
|
||||
(do
|
||||
(println :shortcut/register-shortcut [mode-id id shortcut-map])
|
||||
(st/register-shortcut! mode-id id shortcut-map)))))))))
|
||||
|
||||
(defn ^:export unregister_plugin_simple_command
|
||||
[pid]
|
||||
|
@ -378,10 +386,10 @@
|
|||
(plugin-handler/unregister-plugin-simple-command pid)
|
||||
|
||||
;; remove palette commands
|
||||
(let [palette-matched (->> (palette-handler/get-commands)
|
||||
(filter #(string/includes? (str (:id %)) (str "plugin." pid))))]
|
||||
(when (seq palette-matched)
|
||||
(doseq [cmd palette-matched]
|
||||
(let [cmds-matched (->> (vals @shortcut-config/*shortcut-cmds)
|
||||
(filter #(string/includes? (str (:id %)) (str "plugin." pid))))]
|
||||
(when (seq cmds-matched)
|
||||
(doseq [cmd cmds-matched]
|
||||
(palette-handler/unregister (:id cmd))
|
||||
;; remove keybinding commands
|
||||
(when (seq (:shortcut cmd))
|
||||
|
@ -897,19 +905,10 @@
|
|||
(when-let [args (and args (seq (bean/->clj args)))]
|
||||
(shell/run-git-command! args)))
|
||||
|
||||
;; git
|
||||
(def ^:export git_exec_command sdk-git/exec_command)
|
||||
(def ^:export git_load_ignore_file sdk-git/load_ignore_file)
|
||||
(def ^:export git_save_ignore_file sdk-git/save_ignore_file)
|
||||
|
||||
;; ui
|
||||
(def ^:export show_msg sdk-ui/-show_msg)
|
||||
(def ^:export ui_show_msg sdk-ui/show_msg)
|
||||
(def ^:export ui_close_msg sdk-ui/close_msg)
|
||||
|
||||
;; assets
|
||||
(def ^:export assets_list_files_of_current_graph sdk-assets/list_files_of_current_graph)
|
||||
(def ^:export assets_make_url sdk-assets/make_url)
|
||||
(def ^:export make_asset_url sdk-assets/make_url)
|
||||
|
||||
;; experiments
|
||||
|
@ -998,16 +997,6 @@
|
|||
(p/then #(bean/->js %))))
|
||||
|
||||
;; helpers
|
||||
(defn ^:export query_element_by_id
|
||||
[id]
|
||||
(when-let [^js el (gdom/getElement id)]
|
||||
(if el (str (.-tagName el) "#" id) false)))
|
||||
|
||||
(defn ^:export query_element_rect
|
||||
[selector]
|
||||
(when-let [^js el (js/document.querySelector selector)]
|
||||
(bean/->js (.toJSON (.getBoundingClientRect el)))))
|
||||
|
||||
(defn ^:export set_focused_settings
|
||||
[pid]
|
||||
(when-let [plugin (state/get-plugin-by-id pid)]
|
||||
|
|
|
@ -2,7 +2,10 @@
|
|||
(:require [electron.ipc :as ipc]
|
||||
[cljs-bean.core :as bean]
|
||||
[promesa.core :as p]
|
||||
[frontend.handler.editor :as editor-handler]))
|
||||
[frontend.handler.editor :as editor-handler]
|
||||
[frontend.extensions.pdf.assets :as pdf-assets]
|
||||
[frontend.state :as state]
|
||||
[frontend.util :as util]))
|
||||
|
||||
(def ^:export make_url editor-handler/make-asset-url)
|
||||
|
||||
|
@ -10,3 +13,12 @@
|
|||
[^js exts]
|
||||
(p/let [files (ipc/ipc :getAssetsFiles {:exts exts})]
|
||||
(bean/->js files)))
|
||||
|
||||
(defn ^:export built_in_open
|
||||
[asset-file]
|
||||
(when-let [ext (util/trim-safe (util/get-file-ext asset-file))]
|
||||
(cond
|
||||
(contains? #{"pdf"} ext)
|
||||
(state/set-current-pdf! (pdf-assets/inflate-asset asset-file))
|
||||
|
||||
:else false)))
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
(ns logseq.sdk.ui
|
||||
(:require [frontend.handler.notification :as notification]
|
||||
[cljs-bean.core :as bean]
|
||||
[goog.dom :as gdom]
|
||||
[sci.core :as sci]
|
||||
[frontend.util :as util]
|
||||
[clojure.string :as string]))
|
||||
|
||||
(defn- parse-hiccup-ui
|
||||
|
@ -19,9 +21,9 @@
|
|||
(let [{:keys [key timeout]} (bean/->clj opts)
|
||||
hiccup? (and (string? content) (string/starts-with? (string/triml content) "[:"))
|
||||
content (if hiccup? (parse-hiccup-ui content) content)
|
||||
uid (when (string? key) (keyword key))
|
||||
clear? (not= timeout 0)
|
||||
key' (notification/show! content (keyword status) clear? uid timeout nil)]
|
||||
uid (when (string? key) (keyword key))
|
||||
clear? (not= timeout 0)
|
||||
key' (notification/show! content (keyword status) clear? uid timeout nil)]
|
||||
(name key'))))
|
||||
|
||||
(defn ^:export show_msg
|
||||
|
@ -31,4 +33,30 @@
|
|||
(defn ^:export close_msg
|
||||
[key]
|
||||
(when (string? key)
|
||||
(notification/clear! (keyword key)) nil))
|
||||
(notification/clear! (keyword key)) nil))
|
||||
|
||||
(defn ^:export query_element_rect
|
||||
[selector]
|
||||
(when-let [^js el (js/document.querySelector selector)]
|
||||
(bean/->js (.toJSON (.getBoundingClientRect el)))))
|
||||
|
||||
(defn ^:export query_element_by_id
|
||||
[id]
|
||||
(when-let [^js el (gdom/getElement id)]
|
||||
(if el (str (.-tagName el) "#" id) false)))
|
||||
|
||||
(defn ^:export check_slot_valid
|
||||
[slot]
|
||||
(when (string? slot)
|
||||
(boolean (query_element_by_id slot))))
|
||||
|
||||
(defn ^:export resolve_theme_css_props_vals
|
||||
[props]
|
||||
(when-let [props (if (string? props) [props] (bean/->clj props))]
|
||||
(let [^js s (js/window.getComputedStyle js/document.body)]
|
||||
(some->> (for [prop props]
|
||||
(when (string? prop)
|
||||
[prop (util/trim-safe (.getPropertyValue s prop))]))
|
||||
(remove empty?)
|
||||
(into {})
|
||||
(bean/->js)))))
|
Loading…
Reference in New Issue