LSPlugin.ts 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032
  1. import * as CSS from 'csstype'
  2. import EventEmitter from 'eventemitter3'
  3. import { LSPluginCaller } from './LSPlugin.caller'
  4. import { LSPluginExperiments } from './modules/LSPlugin.Experiments'
  5. import { IAsyncStorage, LSPluginFileStorage } from './modules/LSPlugin.Storage'
  6. import { LSPluginRequest } from './modules/LSPlugin.Request'
  7. export type WithOptional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
  8. export type PluginLocalIdentity = string
  9. export type ThemeMode = 'light' | 'dark'
  10. export interface LegacyTheme {
  11. name: string
  12. url: string
  13. description?: string
  14. mode?: ThemeMode
  15. pid: PluginLocalIdentity
  16. }
  17. export interface Theme extends LegacyTheme {
  18. mode: ThemeMode
  19. }
  20. export type StyleString = string
  21. export type StyleOptions = {
  22. key?: string
  23. style: StyleString
  24. }
  25. export type UIContainerAttrs = {
  26. draggable: boolean
  27. resizable: boolean
  28. [key: string]: any
  29. }
  30. export type UIBaseOptions = {
  31. key?: string
  32. replace?: boolean
  33. template: string | null
  34. style?: CSS.Properties
  35. attrs?: Record<string, string>
  36. close?: 'outside' | string
  37. reset?: boolean // reset slot content or not
  38. }
  39. export type UIPathIdentity = {
  40. /**
  41. * DOM selector
  42. */
  43. path: string
  44. }
  45. export type UISlotIdentity = {
  46. /**
  47. * Slot key
  48. */
  49. slot: string
  50. }
  51. export type UISlotOptions = UIBaseOptions & UISlotIdentity
  52. export type UIPathOptions = UIBaseOptions & UIPathIdentity
  53. export type UIOptions = UIBaseOptions | UIPathOptions | UISlotOptions
  54. export interface LSPluginPkgConfig {
  55. id: PluginLocalIdentity
  56. main: string
  57. entry: string // alias of main
  58. title: string
  59. mode: 'shadow' | 'iframe'
  60. themes: Theme[]
  61. icon: string
  62. [key: string]: any
  63. }
  64. export interface LSPluginBaseInfo {
  65. id: string // should be unique
  66. mode: 'shadow' | 'iframe'
  67. settings: {
  68. disabled: boolean
  69. [key: string]: any
  70. }
  71. [key: string]: any
  72. }
  73. export type IHookEvent = {
  74. [key: string]: any
  75. }
  76. export type IUserOffHook = () => void
  77. export type IUserHook<E = any, R = IUserOffHook> = (
  78. callback: (e: IHookEvent & E) => void
  79. ) => IUserOffHook
  80. export type IUserSlotHook<E = any> = (
  81. callback: (e: IHookEvent & UISlotIdentity & E) => void
  82. ) => void
  83. export type EntityID = number
  84. export type BlockUUID = string
  85. export type BlockUUIDTuple = ['uuid', BlockUUID]
  86. export type IEntityID = { id: EntityID; [key: string]: any }
  87. export type IBatchBlock = {
  88. content: string
  89. properties?: Record<string, any>
  90. children?: Array<IBatchBlock>
  91. }
  92. export type IDatom = [e: number, a: string, v: any, t: number, added: boolean]
  93. export type IGitResult = { stdout: string; stderr: string; exitCode: number }
  94. export interface AppUserInfo {
  95. [key: string]: any
  96. }
  97. export interface AppInfo {
  98. version: string
  99. [key: string]: any
  100. }
  101. /**
  102. * User's app configurations
  103. */
  104. export interface AppUserConfigs {
  105. preferredThemeMode: ThemeMode
  106. preferredFormat: 'markdown' | 'org'
  107. preferredDateFormat: string
  108. preferredStartOfWeek: string
  109. preferredLanguage: string
  110. preferredWorkflow: string
  111. currentGraph: string
  112. showBracket: boolean
  113. enabledFlashcards: boolean
  114. enabledJournals: boolean
  115. [key: string]: any
  116. }
  117. /**
  118. * In Logseq, a graph represents a repository of connected pages and blocks
  119. */
  120. export interface AppGraphInfo {
  121. name: string
  122. url: string
  123. path: string
  124. [key: string]: any
  125. }
  126. /**
  127. * Block - Logseq's fundamental data structure.
  128. */
  129. export interface BlockEntity {
  130. id: EntityID // db id
  131. uuid: BlockUUID
  132. left: IEntityID
  133. format: 'markdown' | 'org'
  134. parent: IEntityID
  135. unordered: boolean
  136. content: string
  137. page: IEntityID
  138. properties?: Record<string, any>
  139. // optional fields in dummy page
  140. anchor?: string
  141. body?: any
  142. children?: Array<BlockEntity | BlockUUIDTuple>
  143. container?: string
  144. file?: IEntityID
  145. level?: number
  146. meta?: { timestamps: any; properties: any; startPos: number; endPos: number }
  147. title?: Array<any>
  148. [key: string]: any
  149. }
  150. /**
  151. * Page is just a block with some specific properties.
  152. */
  153. export interface PageEntity {
  154. id: EntityID
  155. uuid: BlockUUID
  156. name: string
  157. originalName: string
  158. 'journal?': boolean
  159. file?: IEntityID
  160. namespace?: IEntityID
  161. children?: Array<PageEntity>
  162. properties?: Record<string, any>
  163. format?: 'markdown' | 'org'
  164. journalDay?: number
  165. updatedAt?: number
  166. [key: string]: any
  167. }
  168. export type BlockIdentity = BlockUUID | Pick<BlockEntity, 'uuid'>
  169. export type BlockPageName = string
  170. export type PageIdentity = BlockPageName | BlockIdentity
  171. export type SlashCommandActionCmd =
  172. | 'editor/input'
  173. | 'editor/hook'
  174. | 'editor/clear-current-slash'
  175. | 'editor/restore-saved-cursor'
  176. export type SlashCommandAction = [cmd: SlashCommandActionCmd, ...args: any]
  177. export type SimpleCommandCallback<E = any> = (e: IHookEvent & E) => void
  178. export type BlockCommandCallback = (
  179. e: IHookEvent & { uuid: BlockUUID }
  180. ) => Promise<void>
  181. export type BlockCursorPosition = {
  182. left: number
  183. top: number
  184. height: number
  185. pos: number
  186. rect: DOMRect
  187. }
  188. export type SimpleCommandKeybinding = {
  189. mode?: 'global' | 'non-editing' | 'editing'
  190. binding: string
  191. mac?: string // special for Mac OS
  192. }
  193. export type SettingSchemaDesc = {
  194. key: string
  195. type: 'string' | 'number' | 'boolean' | 'enum' | 'object' | 'heading'
  196. default: string | number | boolean | Array<any> | object | null
  197. title: string
  198. description: string // support markdown
  199. inputAs?: 'color' | 'date' | 'datetime-local' | 'range' | 'textarea'
  200. enumChoices?: Array<string>
  201. enumPicker?: 'select' | 'radio' | 'checkbox' // default: select
  202. }
  203. export type ExternalCommandType =
  204. | 'logseq.command/run'
  205. | 'logseq.editor/cycle-todo'
  206. | 'logseq.editor/down'
  207. | 'logseq.editor/up'
  208. | 'logseq.editor/expand-block-children'
  209. | 'logseq.editor/collapse-block-children'
  210. | 'logseq.editor/open-file-in-default-app'
  211. | 'logseq.editor/open-file-in-directory'
  212. | 'logseq.editor/select-all-blocks'
  213. | 'logseq.editor/toggle-open-blocks'
  214. | 'logseq.editor/zoom-in'
  215. | 'logseq.editor/zoom-out'
  216. | 'logseq.editor/indent'
  217. | 'logseq.editor/outdent'
  218. | 'logseq.editor/copy'
  219. | 'logseq.editor/cut'
  220. | 'logseq.go/home'
  221. | 'logseq.go/journals'
  222. | 'logseq.go/keyboard-shortcuts'
  223. | 'logseq.go/next-journal'
  224. | 'logseq.go/prev-journal'
  225. | 'logseq.go/search'
  226. | 'logseq.go/search-in-page'
  227. | 'logseq.go/tomorrow'
  228. | 'logseq.go/backward'
  229. | 'logseq.go/forward'
  230. | 'logseq.search/re-index'
  231. | 'logseq.sidebar/clear'
  232. | 'logseq.sidebar/open-today-page'
  233. | 'logseq.ui/goto-plugins'
  234. | 'logseq.ui/select-theme-color'
  235. | 'logseq.ui/toggle-brackets'
  236. | 'logseq.ui/toggle-cards'
  237. | 'logseq.ui/toggle-contents'
  238. | 'logseq.ui/toggle-document-mode'
  239. | 'logseq.ui/toggle-help'
  240. | 'logseq.ui/toggle-left-sidebar'
  241. | 'logseq.ui/toggle-right-sidebar'
  242. | 'logseq.ui/toggle-settings'
  243. | 'logseq.ui/toggle-theme'
  244. | 'logseq.ui/toggle-wide-mode'
  245. | 'logseq.command-palette/toggle'
  246. export type UserProxyTags = 'app' | 'editor' | 'db' | 'git' | 'ui' | 'assets'
  247. export type SearchIndiceInitStatus = boolean
  248. export type SearchBlockItem = { id: EntityID, uuid: BlockIdentity, content: string, page: EntityID }
  249. export type SearchPageItem = string
  250. export type SearchFileItem = string
  251. export interface IPluginSearchServiceHooks {
  252. name: string
  253. options?: Record<string, any>
  254. onQuery: (
  255. graph: string,
  256. key: string,
  257. opts: Partial<{ limit: number }>
  258. ) =>
  259. Promise<{
  260. graph: string,
  261. key: string,
  262. blocks?: Array<Partial<SearchBlockItem>>,
  263. pages?: Array<SearchPageItem>,
  264. files?: Array<SearchFileItem>
  265. }>
  266. onIndiceInit: (graph: string) => Promise<SearchIndiceInitStatus>
  267. onIndiceReset: (graph: string) => Promise<void>
  268. onBlocksChanged: (graph: string, changes: { added: Array<SearchBlockItem>, removed: Array<BlockEntity> }) => Promise<void>
  269. onGraphRemoved: (graph: string, opts?: {}) => Promise<any>
  270. }
  271. /**
  272. * App level APIs
  273. */
  274. export interface IAppProxy {
  275. /**
  276. * @added 0.0.4
  277. * @param key
  278. */
  279. getInfo: (key?: keyof AppInfo) => Promise<AppInfo | any>
  280. getUserInfo: () => Promise<AppUserInfo | null>
  281. getUserConfigs: () => Promise<AppUserConfigs>
  282. // services
  283. registerSearchService<T extends IPluginSearchServiceHooks>(s: T): void
  284. // commands
  285. registerCommand: (
  286. type: string,
  287. opts: {
  288. key: string
  289. label: string
  290. desc?: string
  291. palette?: boolean
  292. keybinding?: SimpleCommandKeybinding
  293. },
  294. action: SimpleCommandCallback
  295. ) => void
  296. registerCommandPalette: (
  297. opts: {
  298. key: string
  299. label: string
  300. keybinding?: SimpleCommandKeybinding
  301. },
  302. action: SimpleCommandCallback
  303. ) => void
  304. /**
  305. * Supported key names
  306. * @link https://gist.github.com/xyhp915/d1a6d151a99f31647a95e59cdfbf4ddc
  307. * @param keybinding
  308. * @param action
  309. */
  310. registerCommandShortcut: (
  311. keybinding: SimpleCommandKeybinding,
  312. action: SimpleCommandCallback
  313. ) => void
  314. invokeExternalCommand: (
  315. type: ExternalCommandType,
  316. ...args: Array<any>
  317. ) => Promise<void>
  318. /**
  319. * Get state from app store
  320. * valid state is here
  321. * https://github.com/logseq/logseq/blob/master/src/main/frontend/state.cljs#L27
  322. *
  323. * @example
  324. * ```ts
  325. * const isDocMode = await logseq.App.getStateFromStore('document/mode?')
  326. * ```
  327. * @param path
  328. */
  329. getStateFromStore: <T = any>(path: string | Array<string>) => Promise<T>
  330. setStateFromStore: (path: string | Array<string>, value: any) => Promise<void>
  331. // native
  332. relaunch: () => Promise<void>
  333. quit: () => Promise<void>
  334. openExternalLink: (url: string) => Promise<void>
  335. /**
  336. * @deprecated Using `logseq.Git.execCommand`
  337. * @link https://github.com/desktop/dugite/blob/master/docs/api/exec.md
  338. * @param args
  339. */
  340. execGitCommand: (args: string[]) => Promise<string>
  341. // graph
  342. getCurrentGraph: () => Promise<AppGraphInfo | null>
  343. getCurrentGraphConfigs: () => Promise<any>
  344. getCurrentGraphFavorites: () => Promise<Array<string> | null>
  345. getCurrentGraphRecent: () => Promise<Array<string> | null>
  346. // router
  347. pushState: (
  348. k: string,
  349. params?: Record<string, any>,
  350. query?: Record<string, any>
  351. ) => void
  352. replaceState: (
  353. k: string,
  354. params?: Record<string, any>,
  355. query?: Record<string, any>
  356. ) => void
  357. // ui
  358. queryElementById: (id: string) => Promise<string | boolean>
  359. /**
  360. * @added 0.0.5
  361. * @param selector
  362. */
  363. queryElementRect: (selector: string) => Promise<DOMRectReadOnly | null>
  364. /**
  365. * @deprecated Use `logseq.UI.showMsg` instead
  366. * @param content
  367. * @param status
  368. */
  369. showMsg: (
  370. content: string,
  371. status?: 'success' | 'warning' | 'error' | string
  372. ) => void
  373. setZoomFactor: (factor: number) => void
  374. setFullScreen: (flag: boolean | 'toggle') => void
  375. setLeftSidebarVisible: (flag: boolean | 'toggle') => void
  376. setRightSidebarVisible: (flag: boolean | 'toggle') => void
  377. clearRightSidebarBlocks: (opts?: { close: boolean }) => void
  378. registerUIItem: (
  379. type: 'toolbar' | 'pagebar',
  380. opts: { key: string; template: string }
  381. ) => void
  382. registerPageMenuItem: (
  383. tag: string,
  384. action: (e: IHookEvent & { page: string }) => void
  385. ) => void
  386. // hook events
  387. onCurrentGraphChanged: IUserHook
  388. onGraphAfterIndexed: IUserHook<{ repo: string }>
  389. onThemeModeChanged: IUserHook<{ mode: 'dark' | 'light' }>
  390. onThemeChanged: IUserHook<Partial<{ name: string, mode: string, pid: string, url: string }>>
  391. onBlockRendererSlotted: IUserSlotHook<{ uuid: BlockUUID }>
  392. /**
  393. * provide ui slot to block `renderer` macro for `{{renderer arg1, arg2}}`
  394. *
  395. * @example https://github.com/logseq/logseq-plugin-samples/tree/master/logseq-pomodoro-timer
  396. * @example
  397. * ```ts
  398. * // e.g. {{renderer :h1, hello world, green}}
  399. *
  400. * logseq.App.onMacroRendererSlotted(({ slot, payload: { arguments } }) => {
  401. * let [type, text, color] = arguments
  402. * if (type !== ':h1') return
  403. * logseq.provideUI({
  404. * key: 'h1-playground',
  405. * slot, template: `
  406. * <h2 style="color: ${color || 'red'}">${text}</h2>
  407. * `,
  408. * })
  409. * })
  410. * ```
  411. */
  412. onMacroRendererSlotted: IUserSlotHook<{
  413. payload: { arguments: Array<string>; uuid: string; [key: string]: any }
  414. }>
  415. onPageHeadActionsSlotted: IUserSlotHook
  416. onRouteChanged: IUserHook<{ path: string; template: string }>
  417. onSidebarVisibleChanged: IUserHook<{ visible: boolean }>
  418. // internal
  419. _installPluginHook: (pid: string, hook: string) => void
  420. _uninstallPluginHook: (pid: string, hookOrAll: string | boolean) => void
  421. }
  422. /**
  423. * Editor related APIs
  424. */
  425. export interface IEditorProxy extends Record<string, any> {
  426. /**
  427. * register a custom command which will be added to the Logseq slash command list
  428. * @param tag - displayed name of command
  429. * @param action - can be a single callback function to run when the command is called, or an array of fixed commands with arguments
  430. *
  431. *
  432. * @example https://github.com/logseq/logseq-plugin-samples/tree/master/logseq-slash-commands
  433. *
  434. * @example
  435. * ```ts
  436. * logseq.Editor.registerSlashCommand("Say Hi", () => {
  437. * console.log('Hi!')
  438. * })
  439. * ```
  440. *
  441. * @example
  442. * ```ts
  443. * logseq.Editor.registerSlashCommand("💥 Big Bang", [
  444. * ["editor/hook", "customCallback"],
  445. * ["editor/clear-current-slash"],
  446. * ]);
  447. * ```
  448. */
  449. registerSlashCommand: (
  450. tag: string,
  451. action: BlockCommandCallback | Array<SlashCommandAction>
  452. ) => unknown
  453. /**
  454. * register a custom command in the block context menu (triggered by right-clicking the block dot)
  455. * @param label - displayed name of command
  456. * @param action - can be a single callback function to run when the command is called
  457. */
  458. registerBlockContextMenuItem: (
  459. label: string,
  460. action: BlockCommandCallback
  461. ) => unknown
  462. /**
  463. * Current it's only available for pdf viewer
  464. * @param label - displayed name of command
  465. * @param action - callback for the clickable item
  466. * @param opts - clearSelection: clear highlight selection when callback invoked
  467. */
  468. registerHighlightContextMenuItem: (
  469. label: string,
  470. action: SimpleCommandCallback,
  471. opts?: {
  472. clearSelection: boolean
  473. }
  474. ) => unknown
  475. // block related APIs
  476. checkEditing: () => Promise<BlockUUID | boolean>
  477. insertAtEditingCursor: (content: string) => Promise<void>
  478. restoreEditingCursor: () => Promise<void>
  479. exitEditingMode: (selectBlock?: boolean) => Promise<void>
  480. getEditingCursorPosition: () => Promise<BlockCursorPosition | null>
  481. getEditingBlockContent: () => Promise<string>
  482. getCurrentPage: () => Promise<PageEntity | BlockEntity | null>
  483. getCurrentBlock: () => Promise<BlockEntity | null>
  484. getSelectedBlocks: () => Promise<Array<BlockEntity> | null>
  485. /**
  486. * get all blocks of the current page as a tree structure
  487. *
  488. * @example
  489. * ```ts
  490. * const blocks = await logseq.Editor.getCurrentPageBlocksTree()
  491. * initMindMap(blocks)
  492. * ```
  493. */
  494. getCurrentPageBlocksTree: () => Promise<Array<BlockEntity>>
  495. /**
  496. * get all blocks for the specified page
  497. *
  498. * @param srcPage - the page name or uuid
  499. */
  500. getPageBlocksTree: (srcPage: PageIdentity) => Promise<Array<BlockEntity>>
  501. /**
  502. * get all page/block linked references
  503. * @param srcPage
  504. */
  505. getPageLinkedReferences: (
  506. srcPage: PageIdentity
  507. ) => Promise<Array<[page: PageEntity, blocks: Array<BlockEntity>]> | null>
  508. /**
  509. * get flatten pages from top namespace
  510. * @param namespace
  511. */
  512. getPagesFromNamespace: (
  513. namespace: BlockPageName
  514. ) => Promise<Array<PageEntity> | null>
  515. /**
  516. * construct pages tree from namespace pages
  517. * @param namespace
  518. */
  519. getPagesTreeFromNamespace: (
  520. namespace: BlockPageName
  521. ) => Promise<Array<PageEntity> | null>
  522. /**
  523. * Create a unique UUID string which can then be assigned to a block.
  524. * @added 0.0.8
  525. */
  526. newBlockUUID: () => Promise<string>
  527. /**
  528. * @example https://github.com/logseq/logseq-plugin-samples/tree/master/logseq-reddit-hot-news
  529. *
  530. * @param srcBlock
  531. * @param content
  532. * @param opts
  533. */
  534. insertBlock: (
  535. srcBlock: BlockIdentity,
  536. content: string,
  537. opts?: Partial<{
  538. before: boolean
  539. sibling: boolean
  540. isPageBlock: boolean
  541. focus: boolean
  542. customUUID: string
  543. properties: {}
  544. }>
  545. ) => Promise<BlockEntity | null>
  546. insertBatchBlock: (
  547. srcBlock: BlockIdentity,
  548. batch: IBatchBlock | Array<IBatchBlock>,
  549. opts?: Partial<{ before: boolean; sibling: boolean }>
  550. ) => Promise<Array<BlockEntity> | null>
  551. updateBlock: (
  552. srcBlock: BlockIdentity,
  553. content: string,
  554. opts?: Partial<{ properties: {} }>
  555. ) => Promise<void>
  556. removeBlock: (srcBlock: BlockIdentity) => Promise<void>
  557. getBlock: (
  558. srcBlock: BlockIdentity | EntityID,
  559. opts?: Partial<{ includeChildren: boolean }>
  560. ) => Promise<BlockEntity | null>
  561. /**
  562. * @example
  563. *
  564. * ```ts
  565. * logseq.Editor.setBlockCollapsed('uuid', true)
  566. * logseq.Editor.setBlockCollapsed('uuid', 'toggle')
  567. * ```
  568. * @param uuid
  569. * @param opts
  570. */
  571. setBlockCollapsed: (
  572. uuid: BlockUUID,
  573. opts: { flag: boolean | 'toggle' } | boolean | 'toggle'
  574. ) => Promise<void>
  575. getPage: (
  576. srcPage: PageIdentity | EntityID,
  577. opts?: Partial<{ includeChildren: boolean }>
  578. ) => Promise<PageEntity | null>
  579. createPage: (
  580. pageName: BlockPageName,
  581. properties?: {},
  582. opts?: Partial<{
  583. redirect: boolean
  584. createFirstBlock: boolean
  585. format: BlockEntity['format']
  586. journal: boolean
  587. }>
  588. ) => Promise<PageEntity | null>
  589. deletePage: (pageName: BlockPageName) => Promise<void>
  590. renamePage: (oldName: string, newName: string) => Promise<void>
  591. getAllPages: (repo?: string) => Promise<PageEntity[] | null>
  592. prependBlockInPage: (
  593. page: PageIdentity,
  594. content: string,
  595. opts?: Partial<{ properties: {} }>
  596. ) => Promise<BlockEntity | null>
  597. appendBlockInPage: (
  598. page: PageIdentity,
  599. content: string,
  600. opts?: Partial<{ properties: {} }>
  601. ) => Promise<BlockEntity | null>
  602. getPreviousSiblingBlock: (
  603. srcBlock: BlockIdentity
  604. ) => Promise<BlockEntity | null>
  605. getNextSiblingBlock: (srcBlock: BlockIdentity) => Promise<BlockEntity | null>
  606. moveBlock: (
  607. srcBlock: BlockIdentity,
  608. targetBlock: BlockIdentity,
  609. opts?: Partial<{ before: boolean; children: boolean }>
  610. ) => Promise<void>
  611. editBlock: (srcBlock: BlockIdentity, opts?: { pos: number }) => Promise<void>
  612. selectBlock: (srcBlock: BlockIdentity) => Promise<void>
  613. upsertBlockProperty: (
  614. block: BlockIdentity,
  615. key: string,
  616. value: any
  617. ) => Promise<void>
  618. removeBlockProperty: (block: BlockIdentity, key: string) => Promise<void>
  619. getBlockProperty: (block: BlockIdentity, key: string) => Promise<any>
  620. getBlockProperties: (block: BlockIdentity) => Promise<any>
  621. scrollToBlockInPage: (
  622. pageName: BlockPageName,
  623. blockId: BlockIdentity,
  624. opts?: { replaceState: boolean }
  625. ) => void
  626. openInRightSidebar: (uuid: BlockUUID) => void
  627. /**
  628. * @example https://github.com/logseq/logseq-plugin-samples/tree/master/logseq-a-translator
  629. */
  630. onInputSelectionEnd: IUserHook<{
  631. caret: any
  632. point: { x: number; y: number }
  633. start: number
  634. end: number
  635. text: string
  636. }>
  637. }
  638. /**
  639. * Datascript related APIs
  640. */
  641. export interface IDBProxy {
  642. /**
  643. * Run a DSL query
  644. * @link https://docs.logseq.com/#/page/queries
  645. * @param dsl
  646. */
  647. q: <T = any>(dsl: string) => Promise<Array<T> | null>
  648. /**
  649. * Run a datascript query
  650. */
  651. datascriptQuery: <T = any>(query: string, ...inputs: Array<any>) => Promise<T>
  652. /**
  653. * Hook all transaction data of DB
  654. *
  655. * @added 0.0.2
  656. */
  657. onChanged: IUserHook<{
  658. blocks: Array<BlockEntity>
  659. txData: Array<IDatom>
  660. txMeta?: { outlinerOp: string; [key: string]: any }
  661. }>
  662. /**
  663. * Subscribe a specific block changed event
  664. *
  665. * @added 0.0.2
  666. */
  667. onBlockChanged(
  668. uuid: BlockUUID,
  669. callback: (
  670. block: BlockEntity,
  671. txData: Array<IDatom>,
  672. txMeta?: { outlinerOp: string; [key: string]: any }
  673. ) => void
  674. ): IUserOffHook
  675. }
  676. /**
  677. * Git related APIS
  678. */
  679. export interface IGitProxy {
  680. /**
  681. * @added 0.0.2
  682. * @link https://github.com/desktop/dugite/blob/master/docs/api/exec.md
  683. * @param args
  684. */
  685. execCommand: (args: string[]) => Promise<IGitResult>
  686. loadIgnoreFile: () => Promise<string>
  687. saveIgnoreFile: (content: string) => Promise<void>
  688. }
  689. /**
  690. * UI related APIs
  691. */
  692. export type UIMsgOptions = {
  693. key: string
  694. timeout: number // milliseconds. `0` indicate that keep showing
  695. }
  696. export type UIMsgKey = UIMsgOptions['key']
  697. export interface IUIProxy {
  698. /**
  699. * @added 0.0.2
  700. *
  701. * @param content
  702. * @param status
  703. * @param opts
  704. */
  705. showMsg: (
  706. content: string,
  707. status?: 'success' | 'warning' | 'error' | string,
  708. opts?: Partial<UIMsgOptions>
  709. ) => Promise<UIMsgKey>
  710. closeMsg: (key: UIMsgKey) => void
  711. }
  712. /**
  713. * Assets related APIs
  714. */
  715. export interface IAssetsProxy {
  716. /**
  717. * @added 0.0.2
  718. * @param exts
  719. */
  720. listFilesOfCurrentGraph(exts?: string | string[]): Promise<Array<{
  721. path: string
  722. size: number
  723. accessTime: number
  724. modifiedTime: number
  725. changeTime: number
  726. birthTime: number
  727. }>>
  728. /**
  729. * @example https://github.com/logseq/logseq/pull/6488
  730. * @added 0.0.10
  731. */
  732. makeSandboxStorage(): IAsyncStorage
  733. }
  734. export interface ILSPluginThemeManager {
  735. get themes(): Map<PluginLocalIdentity, Theme[]>
  736. registerTheme(id: PluginLocalIdentity, opt: Theme): Promise<void>
  737. unregisterTheme(id: PluginLocalIdentity, effect?: boolean): Promise<void>
  738. selectTheme(
  739. opt: Theme | LegacyTheme,
  740. options: { effect?: boolean; emit?: boolean }
  741. ): Promise<void>
  742. }
  743. export type LSPluginUserEvents = 'ui:visible:changed' | 'settings:changed'
  744. export interface ILSPluginUser extends EventEmitter<LSPluginUserEvents> {
  745. /**
  746. * Connection status with the main app
  747. */
  748. connected: boolean
  749. /**
  750. * Duplex message caller
  751. */
  752. caller: LSPluginCaller
  753. /**
  754. * The plugin configurations from package.json
  755. */
  756. baseInfo: LSPluginBaseInfo
  757. /**
  758. * The plugin user settings
  759. */
  760. settings?: LSPluginBaseInfo['settings']
  761. /**
  762. * The main Logseq app is ready to run the plugin
  763. *
  764. * @param model - same as the model in `provideModel`
  765. */
  766. ready(model?: Record<string, any>): Promise<any>
  767. /**
  768. * @param callback - a function to run when the main Logseq app is ready
  769. */
  770. ready(callback?: (e: any) => void | {}): Promise<any>
  771. ready(
  772. model?: Record<string, any>,
  773. callback?: (e: any) => void | {}
  774. ): Promise<any>
  775. beforeunload: (callback: () => Promise<void>) => void
  776. /**
  777. * Create a object to hold the methods referenced in `provideUI`
  778. *
  779. * @example
  780. * ```ts
  781. * logseq.provideModel({
  782. * openCalendar () {
  783. * console.log('Open the calendar!')
  784. * }
  785. * })
  786. * ```
  787. */
  788. provideModel(model: Record<string, any>): this
  789. /**
  790. * Set the theme for the main Logseq app
  791. */
  792. provideTheme(theme: Theme): this
  793. /**
  794. * Inject custom css for the main Logseq app
  795. *
  796. * @example https://github.com/logseq/logseq-plugin-samples/tree/master/logseq-awesome-fonts
  797. * @example
  798. * ```ts
  799. * logseq.provideStyle(`
  800. * @import url("https://at.alicdn.com/t/font_2409735_r7em724douf.css");
  801. * )
  802. * ```
  803. */
  804. provideStyle(style: StyleString | StyleOptions): this
  805. /**
  806. * Inject custom UI at specific DOM node.
  807. * Event handlers can not be passed by string, so you need to create them in `provideModel`
  808. *
  809. * @example https://github.com/logseq/logseq-plugin-samples/tree/master/logseq-a-translator
  810. * @example
  811. * ```ts
  812. * logseq.provideUI({
  813. * key: 'open-calendar',
  814. * path: '#search',
  815. * template: `
  816. * <a data-on-click="openCalendar" onclick="alert('abc')' style="opacity: .6; display: inline-flex; padding-left: 3px;'>
  817. * <i class="iconfont icon-Calendaralt2"></i>
  818. * </a>
  819. * `
  820. * })
  821. * ```
  822. */
  823. provideUI(ui: UIOptions): this
  824. /**
  825. * @example https://github.com/logseq/logseq-plugin-samples/tree/master/logseq-awesome-fonts
  826. *
  827. * @param schemas
  828. */
  829. useSettingsSchema(schemas: Array<SettingSchemaDesc>): this
  830. /**
  831. * @example https://github.com/logseq/logseq-plugin-samples/tree/master/logseq-awesome-fonts
  832. *
  833. * @param attrs
  834. */
  835. updateSettings(attrs: Record<string, any>): void
  836. onSettingsChanged<T = any>(cb: (a: T, b: T) => void): IUserOffHook
  837. showSettingsUI(): void
  838. hideSettingsUI(): void
  839. setMainUIAttrs(attrs: Record<string, any>): void
  840. /**
  841. * Set the style for the plugin's UI
  842. *
  843. * @example https://github.com/logseq/logseq-plugin-samples/tree/master/logseq-awesome-fonts
  844. * @example
  845. * ```ts
  846. * logseq.setMainUIInlineStyle({
  847. * position: 'fixed',
  848. * zIndex: 11,
  849. * })
  850. * ```
  851. */
  852. setMainUIInlineStyle(style: CSS.Properties): void
  853. /**
  854. * show the plugin's UI
  855. */
  856. showMainUI(opts?: { autoFocus: boolean }): void
  857. /**
  858. * hide the plugin's UI
  859. */
  860. hideMainUI(opts?: { restoreEditingCursor: boolean }): void
  861. /**
  862. * toggle the plugin's UI
  863. */
  864. toggleMainUI(): void
  865. isMainUIVisible: boolean
  866. resolveResourceFullUrl(filePath: string): string
  867. App: IAppProxy & Record<string, any>
  868. Editor: IEditorProxy & Record<string, any>
  869. DB: IDBProxy
  870. Git: IGitProxy
  871. UI: IUIProxy
  872. Assets: IAssetsProxy
  873. Request: LSPluginRequest
  874. FileStorage: LSPluginFileStorage
  875. Experiments: LSPluginExperiments
  876. }