LSPlugin.ts 27 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108
  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. /**
  546. * get all blocks of the current page as a tree structure
  547. *
  548. * @example
  549. * ```ts
  550. * const blocks = await logseq.Editor.getCurrentPageBlocksTree()
  551. * initMindMap(blocks)
  552. * ```
  553. */
  554. getCurrentPageBlocksTree: () => Promise<Array<BlockEntity>>
  555. /**
  556. * get all blocks for the specified page
  557. *
  558. * @param srcPage - the page name or uuid
  559. */
  560. getPageBlocksTree: (srcPage: PageIdentity) => Promise<Array<BlockEntity>>
  561. /**
  562. * get all page/block linked references
  563. * @param srcPage
  564. */
  565. getPageLinkedReferences: (
  566. srcPage: PageIdentity
  567. ) => Promise<Array<[page: PageEntity, blocks: Array<BlockEntity>]> | null>
  568. /**
  569. * get flatten pages from top namespace
  570. * @param namespace
  571. */
  572. getPagesFromNamespace: (
  573. namespace: BlockPageName
  574. ) => Promise<Array<PageEntity> | null>
  575. /**
  576. * construct pages tree from namespace pages
  577. * @param namespace
  578. */
  579. getPagesTreeFromNamespace: (
  580. namespace: BlockPageName
  581. ) => Promise<Array<PageEntity> | null>
  582. /**
  583. * Create a unique UUID string which can then be assigned to a block.
  584. * @added 0.0.8
  585. */
  586. newBlockUUID: () => Promise<string>
  587. /**
  588. * @example https://github.com/logseq/logseq-plugin-samples/tree/master/logseq-reddit-hot-news
  589. *
  590. * @param srcBlock
  591. * @param content
  592. * @param opts
  593. */
  594. insertBlock: (
  595. srcBlock: BlockIdentity,
  596. content: string,
  597. opts?: Partial<{
  598. before: boolean
  599. sibling: boolean
  600. isPageBlock: boolean
  601. focus: boolean
  602. customUUID: string
  603. properties: {}
  604. }>
  605. ) => Promise<BlockEntity | null>
  606. /**
  607. * @example https://github.com/logseq/logseq-plugin-samples/tree/master/logseq-reddit-hot-news
  608. *
  609. * `keepUUID` will allow you to set a custom UUID for blocks by setting their properties.id
  610. */
  611. insertBatchBlock: (
  612. srcBlock: BlockIdentity,
  613. batch: IBatchBlock | Array<IBatchBlock>,
  614. opts?: Partial<{ before: boolean; sibling: boolean; keepUUID: boolean }>
  615. ) => Promise<Array<BlockEntity> | null>
  616. updateBlock: (
  617. srcBlock: BlockIdentity,
  618. content: string,
  619. opts?: Partial<{ properties: {} }>
  620. ) => Promise<void>
  621. removeBlock: (srcBlock: BlockIdentity) => Promise<void>
  622. getBlock: (
  623. srcBlock: BlockIdentity | EntityID,
  624. opts?: Partial<{ includeChildren: boolean }>
  625. ) => Promise<BlockEntity | null>
  626. /**
  627. * @example
  628. *
  629. * ```ts
  630. * logseq.Editor.setBlockCollapsed('uuid', true)
  631. * logseq.Editor.setBlockCollapsed('uuid', 'toggle')
  632. * ```
  633. * @param uuid
  634. * @param opts
  635. */
  636. setBlockCollapsed: (
  637. uuid: BlockUUID,
  638. opts: { flag: boolean | 'toggle' } | boolean | 'toggle'
  639. ) => Promise<void>
  640. getPage: (
  641. srcPage: PageIdentity | EntityID,
  642. opts?: Partial<{ includeChildren: boolean }>
  643. ) => Promise<PageEntity | null>
  644. createPage: (
  645. pageName: BlockPageName,
  646. properties?: {},
  647. opts?: Partial<{
  648. redirect: boolean
  649. createFirstBlock: boolean
  650. format: BlockEntity['format']
  651. journal: boolean
  652. }>
  653. ) => Promise<PageEntity | null>
  654. deletePage: (pageName: BlockPageName) => Promise<void>
  655. renamePage: (oldName: string, newName: string) => Promise<void>
  656. getAllPages: (repo?: string) => Promise<PageEntity[] | null>
  657. prependBlockInPage: (
  658. page: PageIdentity,
  659. content: string,
  660. opts?: Partial<{ properties: {} }>
  661. ) => Promise<BlockEntity | null>
  662. appendBlockInPage: (
  663. page: PageIdentity,
  664. content: string,
  665. opts?: Partial<{ properties: {} }>
  666. ) => Promise<BlockEntity | null>
  667. getPreviousSiblingBlock: (
  668. srcBlock: BlockIdentity
  669. ) => Promise<BlockEntity | null>
  670. getNextSiblingBlock: (srcBlock: BlockIdentity) => Promise<BlockEntity | null>
  671. moveBlock: (
  672. srcBlock: BlockIdentity,
  673. targetBlock: BlockIdentity,
  674. opts?: Partial<{ before: boolean; children: boolean }>
  675. ) => Promise<void>
  676. editBlock: (srcBlock: BlockIdentity, opts?: { pos: number }) => Promise<void>
  677. selectBlock: (srcBlock: BlockIdentity) => Promise<void>
  678. saveFocusedCodeEditorContent: () => Promise<void>
  679. upsertBlockProperty: (
  680. block: BlockIdentity,
  681. key: string,
  682. value: any
  683. ) => Promise<void>
  684. removeBlockProperty: (block: BlockIdentity, key: string) => Promise<void>
  685. getBlockProperty: (block: BlockIdentity, key: string) => Promise<any>
  686. getBlockProperties: (block: BlockIdentity) => Promise<any>
  687. scrollToBlockInPage: (
  688. pageName: BlockPageName,
  689. blockId: BlockIdentity,
  690. opts?: { replaceState: boolean }
  691. ) => void
  692. openInRightSidebar: (id: BlockUUID | EntityID) => void
  693. /**
  694. * @example https://github.com/logseq/logseq-plugin-samples/tree/master/logseq-a-translator
  695. */
  696. onInputSelectionEnd: IUserHook<{
  697. caret: any
  698. point: { x: number; y: number }
  699. start: number
  700. end: number
  701. text: string
  702. }>
  703. }
  704. /**
  705. * Datascript related APIs
  706. */
  707. export interface IDBProxy {
  708. /**
  709. * Run a DSL query
  710. * @link https://docs.logseq.com/#/page/queries
  711. * @param dsl
  712. */
  713. q: <T = any>(dsl: string) => Promise<Array<T> | null>
  714. /**
  715. * Run a datascript query
  716. */
  717. datascriptQuery: <T = any>(query: string, ...inputs: Array<any>) => Promise<T>
  718. /**
  719. * Hook all transaction data of DB
  720. *
  721. * @added 0.0.2
  722. */
  723. onChanged: IUserHook<{
  724. blocks: Array<BlockEntity>
  725. txData: Array<IDatom>
  726. txMeta?: { outlinerOp: string; [key: string]: any }
  727. }>
  728. /**
  729. * Subscribe a specific block changed event
  730. *
  731. * @added 0.0.2
  732. */
  733. onBlockChanged(
  734. uuid: BlockUUID,
  735. callback: (
  736. block: BlockEntity,
  737. txData: Array<IDatom>,
  738. txMeta?: { outlinerOp: string; [key: string]: any }
  739. ) => void
  740. ): IUserOffHook
  741. }
  742. /**
  743. * Git related APIS
  744. */
  745. export interface IGitProxy {
  746. /**
  747. * @added 0.0.2
  748. * @link https://github.com/desktop/dugite/blob/master/docs/api/exec.md
  749. * @param args
  750. */
  751. execCommand: (args: string[]) => Promise<IGitResult>
  752. loadIgnoreFile: () => Promise<string>
  753. saveIgnoreFile: (content: string) => Promise<void>
  754. }
  755. /**
  756. * UI related APIs
  757. */
  758. export type UIMsgOptions = {
  759. key: string
  760. timeout: number // milliseconds. `0` indicate that keep showing
  761. }
  762. export type UIMsgKey = UIMsgOptions['key']
  763. export interface IUIProxy {
  764. showMsg: (
  765. content: string,
  766. status?: 'success' | 'warning' | 'error' | string,
  767. opts?: Partial<UIMsgOptions>
  768. ) => Promise<UIMsgKey>
  769. closeMsg: (key: UIMsgKey) => void
  770. queryElementRect: (selector: string) => Promise<DOMRectReadOnly | null>
  771. queryElementById: (id: string) => Promise<string | boolean>
  772. checkSlotValid: (slot: UISlotIdentity['slot']) => Promise<boolean>
  773. resolveThemeCssPropsVals: (props: string | Array<string>) => Promise<Record<string, string | undefined> | null>
  774. }
  775. /**
  776. * Assets related APIs
  777. */
  778. export interface IAssetsProxy {
  779. /**
  780. * @added 0.0.2
  781. * @param exts
  782. */
  783. listFilesOfCurrentGraph(exts?: string | string[]): Promise<
  784. Array<{
  785. path: string
  786. size: number
  787. accessTime: number
  788. modifiedTime: number
  789. changeTime: number
  790. birthTime: number
  791. }>
  792. >
  793. /**
  794. * @example https://github.com/logseq/logseq/pull/6488
  795. * @added 0.0.10
  796. */
  797. makeSandboxStorage(): IAsyncStorage
  798. /**
  799. * make assets scheme url based on current graph
  800. * @added 0.0.15
  801. * @param path
  802. */
  803. makeUrl(path: string): Promise<string>
  804. /**
  805. * try to open asset type file in Logseq app
  806. * @added 0.0.16
  807. * @param path
  808. */
  809. builtInOpen(path: string): Promise<boolean | undefined>
  810. }
  811. export interface ILSPluginThemeManager {
  812. get themes(): Map<PluginLocalIdentity, Theme[]>
  813. registerTheme(id: PluginLocalIdentity, opt: Theme): Promise<void>
  814. unregisterTheme(id: PluginLocalIdentity, effect?: boolean): Promise<void>
  815. selectTheme(
  816. opt: Theme | LegacyTheme,
  817. options: { effect?: boolean; emit?: boolean }
  818. ): Promise<void>
  819. }
  820. export type LSPluginUserEvents = 'ui:visible:changed' | 'settings:changed'
  821. export interface ILSPluginUser extends EventEmitter<LSPluginUserEvents> {
  822. /**
  823. * Connection status with the main app
  824. */
  825. connected: boolean
  826. /**
  827. * Duplex message caller
  828. */
  829. caller: LSPluginCaller
  830. /**
  831. * The plugin configurations from package.json
  832. */
  833. baseInfo: LSPluginBaseInfo
  834. /**
  835. * The plugin user settings
  836. */
  837. settings?: LSPluginBaseInfo['settings']
  838. /**
  839. * The main Logseq app is ready to run the plugin
  840. *
  841. * @param model - same as the model in `provideModel`
  842. */
  843. ready(model?: Record<string, any>): Promise<any>
  844. /**
  845. * @param callback - a function to run when the main Logseq app is ready
  846. */
  847. ready(callback?: (e: any) => void | {}): Promise<any>
  848. ready(
  849. model?: Record<string, any>,
  850. callback?: (e: any) => void | {}
  851. ): Promise<any>
  852. beforeunload: (callback: () => Promise<void>) => void
  853. /**
  854. * Create a object to hold the methods referenced in `provideUI`
  855. *
  856. * @example
  857. * ```ts
  858. * logseq.provideModel({
  859. * openCalendar () {
  860. * console.log('Open the calendar!')
  861. * }
  862. * })
  863. * ```
  864. */
  865. provideModel(model: Record<string, any>): this
  866. /**
  867. * Set the theme for the main Logseq app
  868. */
  869. provideTheme(theme: Theme): this
  870. /**
  871. * Inject custom css for the main Logseq app
  872. *
  873. * @example https://github.com/logseq/logseq-plugin-samples/tree/master/logseq-awesome-fonts
  874. * @example
  875. * ```ts
  876. * logseq.provideStyle(`
  877. * @import url("https://at.alicdn.com/t/font_2409735_r7em724douf.css");
  878. * )
  879. * ```
  880. */
  881. provideStyle(style: StyleString | StyleOptions): this
  882. /**
  883. * Inject custom UI at specific DOM node.
  884. * Event handlers can not be passed by string, so you need to create them in `provideModel`
  885. *
  886. * @example https://github.com/logseq/logseq-plugin-samples/tree/master/logseq-a-translator
  887. * @example
  888. * ```ts
  889. * logseq.provideUI({
  890. * key: 'open-calendar',
  891. * path: '#search',
  892. * template: `
  893. * <a data-on-click="openCalendar" onclick="alert('abc')' style="opacity: .6; display: inline-flex; padding-left: 3px;'>
  894. * <i class="iconfont icon-Calendaralt2"></i>
  895. * </a>
  896. * `
  897. * })
  898. * ```
  899. */
  900. provideUI(ui: UIOptions): this
  901. /**
  902. * @example https://github.com/logseq/logseq-plugin-samples/tree/master/logseq-awesome-fonts
  903. *
  904. * @param schemas
  905. */
  906. useSettingsSchema(schemas: Array<SettingSchemaDesc>): this
  907. /**
  908. * @example https://github.com/logseq/logseq-plugin-samples/tree/master/logseq-awesome-fonts
  909. *
  910. * @param attrs
  911. */
  912. updateSettings(attrs: Record<string, any>): void
  913. onSettingsChanged<T = any>(cb: (a: T, b: T) => void): IUserOffHook
  914. showSettingsUI(): void
  915. hideSettingsUI(): void
  916. setMainUIAttrs(attrs: Record<string, any>): void
  917. /**
  918. * Set the style for the plugin's UI
  919. *
  920. * @example https://github.com/logseq/logseq-plugin-samples/tree/master/logseq-awesome-fonts
  921. * @example
  922. * ```ts
  923. * logseq.setMainUIInlineStyle({
  924. * position: 'fixed',
  925. * zIndex: 11,
  926. * })
  927. * ```
  928. */
  929. setMainUIInlineStyle(style: CSS.Properties): void
  930. /**
  931. * show the plugin's UI
  932. */
  933. showMainUI(opts?: { autoFocus: boolean }): void
  934. /**
  935. * hide the plugin's UI
  936. */
  937. hideMainUI(opts?: { restoreEditingCursor: boolean }): void
  938. /**
  939. * toggle the plugin's UI
  940. */
  941. toggleMainUI(): void
  942. isMainUIVisible: boolean
  943. resolveResourceFullUrl(filePath: string): string
  944. App: IAppProxy
  945. Editor: IEditorProxy
  946. DB: IDBProxy
  947. Git: IGitProxy
  948. UI: IUIProxy
  949. Assets: IAssetsProxy
  950. Request: LSPluginRequest
  951. FileStorage: LSPluginFileStorage
  952. Experiments: LSPluginExperiments
  953. }