LSPlugin.ts 27 KB

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