LSPlugin.ts 23 KB

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