Skip to content

Commit

Permalink
Enhance/plugin APIs (#7555)
Browse files Browse the repository at this point in the history
* feat: WIP native cli command support

* Add :shell/command-whitelist option

* Integrate cli to code block

* Add :code-block/command-whitelist option

* fix: size of icon

* improve(shell): cache user shell whitelist on application configures file

* improve(electron): promisify run cli command

* chore(libs): update version

* fix(plugin): incorrect payload of pdf highlights section hook

* improve(plugin): block renderer with specific block uuid

* improve(plugin): expose logger for user lib

* improve(plugin): block hooks type

* improve(plugin): block slot hook with specific block

* improve(plugin): auto generate key for provide UI options

* improve(plugin): style of injected ui container

* improve(plugin): types

* improve(plugin): async messaging api from host to plugin

* improve(plugin): add types

* improve(apis): get external plugin metadata

* improve(apis): invoke external plugin impls

* improve(apis): call external plugin impls for simple commands

* enhance(apis): datascript query api for predicate inputs

* enhance(apis): datascript query api for predicate inputs

* fix(apis): redundant args of datascript query api

* enhance(plugins): position of float ui container

* enhance(plugins): style of setting options

* enhance(plugins): layouts data for float ui

* chore(plugins): update CHANGELOG.md

* improve(apis): add types

* chore: fix some inclusive terms

* improve(apis): types

* chore(plugins): update CHANGELOG.md

* chore(plugins): build libs

* chore: update CHANGELOG.md

* chore: remove experiemental alda integration

* fix(lint): remove unused methods

Co-authored-by: Tienson Qin <tiensonqin@gmail.com>
Co-authored-by: Andelf <andelf@gmail.com>
  • Loading branch information
3 people committed Dec 19, 2022
1 parent dfe1d65 commit 0203179
Show file tree
Hide file tree
Showing 28 changed files with 791 additions and 427 deletions.
21 changes: 20 additions & 1 deletion libs/CHANGELOG.md
Expand Up @@ -3,8 +3,27 @@
All notable changes to this project will be documented in this file.

## [Unreleased]
## [0.0.13]

## [0.0.11]
### Added
- Support block content slot hook `App.onBlockRendererSlotted` with a specific block UUID
- Support plugins calling each other `App.invokeExternalPlugin` with key of models & commands.
E.g. (It is recommended that the caller plugin upgrade the SDK to the latest.)
```typescript
// Defined at https://github.com/xyhp915/logseq-journals-calendar/blob/main/src/main.js#L74
await logseq.App.invokeExternalPlugin('logseq-journals-calendar.models.goToToday')

// Defined at https://github.com/vipzhicheng/logseq-plugin-vim-shortcuts/blob/bec05aeee8/src/keybindings/down.ts#L20
await logseq.App.invokeExternalPlugin('logseq-plugin-vim-shortcuts.commands.vim-shortcut-down-0')
```
- Support api of `Editor.saveFocusedCodeEditorContent` [#FQ](https://github.com/logseq/logseq/issues/7714)
- Support predicate for `DB.datascriptQuery` inputs

### Fixed
- Incorrect hook payload from `Editor.registerHighlightContextMenuItem`
- Auto generate key if not exist for `provideUI` options

## [0.0.12]

### Added

Expand Down
2 changes: 1 addition & 1 deletion libs/package.json
@@ -1,6 +1,6 @@
{
"name": "@logseq/libs",
"version": "0.0.11",
"version": "0.0.13",
"description": "Logseq SDK libraries",
"main": "dist/lsplugin.user.js",
"typings": "index.d.ts",
Expand Down
37 changes: 27 additions & 10 deletions libs/src/LSPlugin.caller.ts
Expand Up @@ -38,7 +38,7 @@ class LSPluginCaller extends EventEmitter {
payload: any,
actor?: DeferredActor
) => Promise<any>
private _callUserModel?: (type: string, payload: any) => Promise<any>
private _callUserModel?: (type: string, ...payloads: any[]) => Promise<any>

private _debugTag = ''

Expand Down Expand Up @@ -205,8 +205,13 @@ class LSPluginCaller extends EventEmitter {
return this._call?.call(this, type, payload, actor)
}

async callUserModel(type: string, payload: any = {}) {
return this._callUserModel?.call(this, type, payload)
async callUserModel(type: string, ...args: any[]) {
return this._callUserModel?.apply(this, [type, ...args])
}

async callUserModelAsync(type: string, ...args: any[]) {
type = AWAIT_LSPMSGFn(type)
return this._callUserModel?.apply(this, [type, ...args])
}

// run in host
Expand Down Expand Up @@ -235,12 +240,21 @@ class LSPluginCaller extends EventEmitter {
const mainLayoutInfo = (await this._pluginLocal._loadLayoutsData())?.$$0
if (mainLayoutInfo) {
cnt.dataset.inited_layout = 'true'
const { width, height, left, top } = mainLayoutInfo
let { width, height, left, top, vw, vh } = mainLayoutInfo

left = Math.max(left, 0)
left = (typeof vw === 'number') ?
`${Math.min(left * 100 / vw, 99)}%` : `${left}px`

// 45 is height of headbar
top = Math.max(top, 45)
top = (typeof vh === 'number') ?
`${Math.min(top * 100 / vh, 99)}%` : `${top}px`

Object.assign(cnt.style, {
width: width + 'px',
height: height + 'px',
left: left + 'px',
top: top + 'px',
left, top
})
}
} catch (e) {
Expand Down Expand Up @@ -292,12 +306,15 @@ class LSPluginCaller extends EventEmitter {
})
}

this._callUserModel = async (type, payload: any) => {
this._callUserModel = async (type, ...payloads: any[]) => {
if (type.startsWith(FLAG_AWAIT)) {
// TODO: attach payload with method call
return await refChild.get(type.replace(FLAG_AWAIT, ''))
// TODO: attach arguments with method call
return await refChild.get(
type.replace(FLAG_AWAIT, ''),
...payloads
)
} else {
refChild.call(type, payload)
refChild.call(type, payloads?.[0])
}
}

Expand Down
77 changes: 20 additions & 57 deletions libs/src/LSPlugin.core.ts
Expand Up @@ -19,7 +19,7 @@ import {
cleanInjectedScripts,
safeSnakeCase,
injectTheme,
cleanInjectedUI,
cleanInjectedUI, PluginLogger,
} from './helpers'
import * as pluginHelpers from './helpers'
import Debug from 'debug'
Expand Down Expand Up @@ -132,59 +132,6 @@ class PluginSettings extends EventEmitter<'change' | 'reset'> {
}
}

class PluginLogger extends EventEmitter<'change'> {
private _logs: Array<[type: string, payload: any]> = []

constructor(private readonly _tag: string) {
super()
}

write(type: string, payload: any[], inConsole?: boolean) {
if (payload?.length && (true === payload[payload.length - 1])) {
inConsole = true
payload.pop()
}

const msg = payload.reduce((ac, it) => {
if (it && it instanceof Error) {
ac += `${it.message} ${it.stack}`
} else {
ac += it.toString()
}
return ac
}, `[${this._tag}][${new Date().toLocaleTimeString()}] `)

this._logs.push([type, msg])

if (inConsole) {
console?.['ERROR' === type ? 'error' : 'debug'](`${type}: ${msg}`)
}

this.emit('change')
}

clear() {
this._logs = []
this.emit('change')
}

info(...args: any[]) {
this.write('INFO', args)
}

error(...args: any[]) {
this.write('ERROR', args)
}

warn(...args: any[]) {
this.write('WARN', args)
}

toJSON() {
return this._logs
}
}

interface UserPreferences {
theme: LegacyTheme
themes: {
Expand All @@ -204,7 +151,6 @@ interface PluginLocalOptions {
mode: 'shadow' | 'iframe'
settingsSchema?: SettingSchemaDesc[]
settings?: PluginSettings
logger?: PluginLogger
effect?: boolean
theme?: boolean

Expand Down Expand Up @@ -460,6 +406,7 @@ class PluginLocal extends EventEmitter<'loaded'
private _localRoot?: string
private _dotSettingsFile?: string
private _caller?: LSPluginCaller
private _logger?: PluginLogger

/**
* @param _options
Expand All @@ -483,7 +430,7 @@ class PluginLocal extends EventEmitter<'loaded'

async _setupUserSettings(reload?: boolean) {
const { _options } = this
const logger = (_options.logger = new PluginLogger('Loader'))
const logger = (this._logger = new PluginLogger('Loader'))

if (_options.settings && !reload) {
return
Expand Down Expand Up @@ -1056,13 +1003,17 @@ class PluginLocal extends EventEmitter<'loaded'
}

get logger() {
return this.options.logger
return this._logger
}

get disabled() {
return this.settings?.get('disabled')
}

get theme() {
return this.options.theme
}

get caller() {
return this._caller
}
Expand Down Expand Up @@ -1123,6 +1074,8 @@ class PluginLocal extends EventEmitter<'loaded'
json.usf = this.dotSettingsFile
json.iir = this.isInstalledInDotRoot
json.lsr = this._resolveResourceFullUrl('/')
json.settings = json.settings?.toJSON()

return json
}
}
Expand Down Expand Up @@ -1535,6 +1488,16 @@ class LSPluginCore
return this._registeredThemes
}

get enabledPlugins() {
return [...this.registeredPlugins.entries()].reduce((a, b) => {
let p = b?.[1]
if (p?.disabled !== true) {
a.set(b?.[0], p)
}
return a
}, new Map())
}

async registerTheme(id: PluginLocalIdentity, opt: Theme): Promise<void> {
debug('Register theme #', id, opt)

Expand Down
38 changes: 36 additions & 2 deletions libs/src/LSPlugin.ts
Expand Up @@ -102,6 +102,10 @@ export type IUserHook<E = any, R = IUserOffHook> = (
export type IUserSlotHook<E = any> = (
callback: (e: IHookEvent & UISlotIdentity & E) => void
) => void
export type IUserConditionSlotHook<C = any, E = any> = (
condition: C,
callback: (e: IHookEvent & UISlotIdentity & E) => void
) => void

export type EntityID = number
export type BlockUUID = string
Expand Down Expand Up @@ -366,11 +370,33 @@ export interface IAppProxy {
action: SimpleCommandCallback
) => void

/**
* Supported all registered palette commands
* @param type
* @param args
*/
invokeExternalCommand: (
type: ExternalCommandType,
...args: Array<any>
) => Promise<void>

/**
* Call external plugin command provided by models or registerd commands
* @added 0.0.13
* @param type `xx-plugin-id.commands.xx-key`, `xx-plugin-id.models.xx-key`
* @param args
*/
invokeExternalPlugin: (
type: string,
...args: Array<any>
) => Promise<unknown>

/**
* @added 0.0.13
* @param pid
*/
getExternalPlugin: (pid: string) => Promise<{} | null>

/**
* Get state from app store
* valid state is here
Expand Down Expand Up @@ -455,7 +481,13 @@ export interface IAppProxy {
onGraphAfterIndexed: IUserHook<{ repo: string }>
onThemeModeChanged: IUserHook<{ mode: 'dark' | 'light' }>
onThemeChanged: IUserHook<Partial<{ name: string, mode: string, pid: string, url: string }>>
onBlockRendererSlotted: IUserSlotHook<{ uuid: BlockUUID }>

/**
* provide ui slot to specific block with UUID
*
* @added 0.0.13
*/
onBlockRendererSlotted: IUserConditionSlotHook<BlockUUID, Omit<BlockEntity, 'children' | 'page'>>

/**
* provide ui slot to block `renderer` macro for `{{renderer arg1, arg2}}`
Expand Down Expand Up @@ -636,7 +668,7 @@ export interface IEditorProxy extends Record<string, any> {

/**
* @example https://github.com/logseq/logseq-plugin-samples/tree/master/logseq-reddit-hot-news
*
*
* `keepUUID` will allow you to set a custom UUID for blocks by setting their properties.id
*/
insertBatchBlock: (
Expand Down Expand Up @@ -722,6 +754,8 @@ export interface IEditorProxy extends Record<string, any> {
editBlock: (srcBlock: BlockIdentity, opts?: { pos: number }) => Promise<void>
selectBlock: (srcBlock: BlockIdentity) => Promise<void>

saveFocusedCodeEditorContent: () => Promise<void>

upsertBlockProperty: (
block: BlockIdentity,
key: string,
Expand Down

0 comments on commit 0203179

Please sign in to comment.