LSPlugin.ts 28 KB

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