import EventEmitter from 'eventemitter3' import * as CSS from 'csstype' import { LSPluginCaller } from './LSPlugin.caller' import { LSPluginFileStorage } from './modules/LSPlugin.Storage' export type PluginLocalIdentity = string export type ThemeOptions = { name: string url: string description?: string mode?: 'dark' | 'light' [key: string]: any } export type StyleString = string; export type StyleOptions = { key?: string style: StyleString } export type UIFrameAttrs = { draggable: boolean resizable: boolean [key: string]: any } export type UIBaseOptions = { key?: string replace?: boolean template: string } export type UIPathIdentity = { /** * DOM selector */ path: string } export type UISlotIdentity = { /** * Slot key */ slot: string } export type UISlotOptions = UIBaseOptions & UISlotIdentity export type UIPathOptions = UIBaseOptions & UIPathIdentity export type UIOptions = UIPathOptions | UISlotOptions export interface LSPluginPkgConfig { id: PluginLocalIdentity mode: 'shadow' | 'iframe' themes: Array icon: string } export interface LSPluginBaseInfo { id: string // should be unique mode: 'shadow' | 'iframe' settings: { disabled: boolean [key: string]: any }, [key: string]: any } export type IHookEvent = { [key: string]: any } export type IUserOffHook = () => void export type IUserHook = (callback: (e: IHookEvent & E) => void) => IUserOffHook export type IUserSlotHook = (callback: (e: IHookEvent & UISlotIdentity & E) => void) => void export type EntityID = number export type BlockUUID = string export type BlockUUIDTuple = ['uuid', BlockUUID] export type IEntityID = { id: EntityID } export type IBatchBlock = { content: string, properties?: Record, children?: Array } export interface AppUserInfo { [key: string]: any } /** * User's app configurations */ export interface AppUserConfigs { preferredThemeMode: 'dark' | 'light' preferredFormat: 'markdown' | 'org' preferredDateFormat: string preferredLanguage: string preferredWorkflow: string [key: string]: any } /** * In Logseq, a graph represents a repository of connected pages and blocks */ export interface AppGraphInfo { name: string url: string path: string [key: string]: any } /** * Block - Logseq's fundamental data structure. */ export interface BlockEntity { id: EntityID // db id uuid: BlockUUID left: IEntityID format: 'markdown' | 'org' parent: IEntityID unordered: boolean content: string page: IEntityID // optional fields in dummy page anchor?: string body?: any children?: Array container?: string file?: IEntityID level?: number meta?: { timestamps: any, properties: any, startPos: number, endPos: number } title?: Array [key: string]: any } /** * Page is just a block with some specific properties. */ export interface PageEntity { id: EntityID uuid: BlockUUID name: string originalName: string 'journal?': boolean file?: IEntityID namespace?: IEntityID format?: 'markdown' | 'org' journalDay?: number } export type BlockIdentity = BlockUUID | Pick export type BlockPageName = string export type PageIdentity = BlockPageName | BlockIdentity export type SlashCommandActionCmd = 'editor/input' | 'editor/hook' | 'editor/clear-current-slash' | 'editor/restore-saved-cursor' export type SlashCommandAction = [cmd: SlashCommandActionCmd, ...args: any] export type BlockCommandCallback = (e: IHookEvent & { uuid: BlockUUID }) => Promise export type BlockCursorPosition = { left: number, top: number, height: number, pos: number, rect: DOMRect } /** * App level APIs */ export interface IAppProxy { getUserInfo: () => Promise getUserConfigs: () => Promise // native relaunch: () => Promise quit: () => Promise openExternalLink: (url: string) => Promise // graph getCurrentGraph: () => Promise // router pushState: (k: string, params?: Record, query?: Record) => void replaceState: (k: string, params?: Record, query?: Record) => void // ui showMsg: (content: string, status?: 'success' | 'warning' | string) => void setZoomFactor: (factor: number) => void registerUIItem: ( type: 'toolbar' | 'pagebar', opts: { key: string, template: string } ) => void registerPageMenuItem: ( tag: string, action: (e: IHookEvent & { page: string }) => void ) => void // hook events onCurrentGraphChanged: IUserHook onThemeModeChanged: IUserHook<{ mode: 'dark' | 'light' }> onBlockRendererSlotted: IUserSlotHook<{ uuid: BlockUUID }> onMacroRendererSlotted: IUserSlotHook<{ payload: { arguments: Array, uuid: string, [key: string]: any } }> onPageHeadActionsSlotted: IUserSlotHook onRouteChanged: IUserHook<{ path: string, template: string }> onSidebarVisibleChanged: IUserHook<{ visible: boolean }> } /** * Editor related APIs */ export interface IEditorProxy extends Record { /** * register a custom command which will be added to the Logseq slash command list * * @param tag - displayed name of command * @param action - can be a single callback function to run when the command is called, or an array of fixed commands with arguments * * @example * ```ts * logseq.Editor.registerSlashCommand("Say Hi", () => { * console.log('Hi!') * }) * ``` * * @example * ```ts * logseq.Editor.registerSlashCommand("💥 Big Bang", [ * ["editor/hook", "customCallback"], * ["editor/clear-current-slash"], * ]); * ``` */ registerSlashCommand: ( tag: string, action: BlockCommandCallback | Array ) => unknown /** * register a custom command in the block context menu (triggered by right clicking the block dot) * @param tag - displayed name of command * @param action - can be a single callback function to run when the command is called */ registerBlockContextMenuItem: ( tag: string, action: BlockCommandCallback ) => unknown // block related APIs checkEditing: () => Promise /** * insert a string at the current cursor */ insertAtEditingCursor: (content: string) => Promise restoreEditingCursor: () => Promise exitEditingMode: (selectBlock?: boolean) => Promise getEditingCursorPosition: () => Promise getEditingBlockContent: () => Promise getCurrentPage: () => Promise getCurrentBlock: () => Promise /** * get all blocks of the current page as a tree structure * * @example * ```ts * const blocks = await logseq.Editor.getCurrentPageBlocksTree() * initMindMap(blocks) * ``` */ getCurrentPageBlocksTree: () => Promise> /** * get all blocks for the specified page * * @param srcPage - the page name or uuid */ getPageBlocksTree: (srcPage: PageIdentity) => Promise> insertBlock: ( srcBlock: BlockIdentity, content: string, opts?: Partial<{ before: boolean; sibling: boolean; properties: {} }> ) => Promise insertBatchBlock: ( srcBlock: BlockIdentity, batch: IBatchBlock | Array, opts?: Partial<{ before: boolean, sibling: boolean }> ) => Promise | null> updateBlock: ( srcBlock: BlockIdentity, content: string, opts?: Partial<{ properties: {} }> ) => Promise removeBlock: ( srcBlock: BlockIdentity ) => Promise getBlock: ( srcBlock: BlockIdentity | EntityID, opts?: Partial<{ includeChildren: boolean }> ) => Promise getPage: ( srcPage: PageIdentity | EntityID, opts?: Partial<{ includeChildren: boolean }> ) => Promise createPage: ( pageName: BlockPageName, properties?: {}, opts?: Partial<{ redirect: boolean, createFirstBlock: boolean, format: BlockEntity['format'] }> ) => Promise deletePage: ( pageName: BlockPageName ) => Promise renamePage: (oldName: string, newName: string) => Promise getAllPages: (repo?: string) => Promise getPreviousSiblingBlock: ( srcBlock: BlockIdentity ) => Promise getNextSiblingBlock: (srcBlock: BlockIdentity) => Promise moveBlock: ( srcBlock: BlockIdentity, targetBlock: BlockIdentity, opts?: Partial<{ before: boolean; children: boolean }> ) => Promise editBlock: (srcBlock: BlockIdentity, opts?: { pos: number }) => Promise upsertBlockProperty: ( block: BlockIdentity, key: string, value: any ) => Promise removeBlockProperty: (block: BlockIdentity, key: string) => Promise getBlockProperty: (block: BlockIdentity, key: string) => Promise getBlockProperties: (block: BlockIdentity) => Promise scrollToBlockInPage: ( pageName: BlockPageName, blockId: BlockIdentity ) => void } /** * Datascript related APIs */ export interface IDBProxy { /** * Run a DSL query * @link https://logseq.github.io/#/page/queries * @param dsl */ q: (dsl: string) => Promise | null> /** * Run a datascript query */ datascriptQuery: (query: string) => Promise } export interface ILSPluginThemeManager extends EventEmitter { themes: Map> registerTheme (id: PluginLocalIdentity, opt: ThemeOptions): Promise unregisterTheme (id: PluginLocalIdentity): Promise selectTheme (opt?: ThemeOptions): Promise } export type LSPluginUserEvents = 'ui:visible:changed' | 'settings:changed' export interface ILSPluginUser extends EventEmitter { /** * Connection status with the main app */ connected: boolean /** * Duplex message caller */ caller: LSPluginCaller /** * The plugin configurations from package.json */ baseInfo: LSPluginBaseInfo /** * The plugin user settings */ settings?: LSPluginBaseInfo['settings'] /** * The main Logseq app is ready to run the plugin * * @param model - same as the model in `provideModel` */ ready (model?: Record): Promise /** * @param callback - a function to run when the main Logseq app is ready */ ready (callback?: (e: any) => void | {}): Promise ready ( model?: Record, callback?: (e: any) => void | {} ): Promise beforeunload: (callback: () => Promise) => void /** * Create a object to hold the methods referenced in `provideUI` * * @example * ```ts * logseq.provideModel({ * openCalendar () { * console.log('Open the calendar!') * } * }) * ``` */ provideModel (model: Record): this /** * Set the theme for the main Logseq app */ provideTheme (theme: ThemeOptions): this /** * Inject custom css for the main Logseq app * * @example * ```ts * logseq.provideStyle(` * @import url("https://at.alicdn.com/t/font_2409735_r7em724douf.css"); * ) * ``` * * @example * ```ts * * ``` */ provideStyle (style: StyleString | StyleOptions): this /** * Inject custom UI at specific DOM node. * Event handlers can not be passed by string, so you need to create them in `provideModel` * * @example * ```ts * logseq.provideUI({ * key: 'open-calendar', * path: '#search', * template: ` * * * * ` * }) * ``` */ provideUI (ui: UIOptions): this updateSettings (attrs: Record): void setMainUIAttrs (attrs: Record): void /** * Set the style for the plugin's UI * * @example * ```ts * logseq.setMainUIInlineStyle({ * position: 'fixed', * zIndex: 11, * }) * ``` */ setMainUIInlineStyle (style: CSS.Properties): void /** * show the plugin's UI */ showMainUI (): void /** * hide the plugin's UI */ hideMainUI (opts?: { restoreEditingCursor: boolean }): void /** * toggle the plugin's UI */ toggleMainUI (): void isMainUIVisible: boolean App: IAppProxy & Record Editor: IEditorProxy & Record DB: IDBProxy FileStorage: LSPluginFileStorage }