From 7e2e66ea2db0772214b42e1ff293e5b57aa1c0fd Mon Sep 17 00:00:00 2001 From: Albus Date: Wed, 1 Jul 2026 03:25:25 +0000 Subject: [PATCH] chore: routine joplin plugin framework update --- api/Global.d.ts | 6 +- api/Joplin.d.ts | 15 ++- api/JoplinAi.d.ts | 80 +++++++++++++++ api/JoplinClipboard.d.ts | 18 +++- api/JoplinContentScripts.d.ts | 4 +- api/JoplinData.d.ts | 5 +- api/JoplinFs.d.ts | 22 ++++ api/JoplinImaging.d.ts | 10 +- api/JoplinSettings.d.ts | 2 +- api/JoplinViews.d.ts | 4 +- api/JoplinViewsDialogs.d.ts | 6 +- api/JoplinViewsEditor.d.ts | 7 +- api/JoplinViewsMenuItems.d.ts | 3 +- api/JoplinViewsMenus.d.ts | 3 +- api/JoplinViewsPanels.d.ts | 7 +- api/JoplinViewsToolbarButtons.d.ts | 3 +- api/JoplinWindow.d.ts | 9 +- api/JoplinWorkspace.d.ts | 7 +- api/noteListType.d.ts | 6 +- api/noteListType.ts | 10 +- api/types.ts | 155 ++++++++++++++++++++++++----- package.json | 2 +- 22 files changed, 325 insertions(+), 59 deletions(-) create mode 100644 api/JoplinAi.d.ts create mode 100644 api/JoplinFs.d.ts diff --git a/api/Global.d.ts b/api/Global.d.ts index 686ccf1..56bb162 100644 --- a/api/Global.d.ts +++ b/api/Global.d.ts @@ -1,5 +1,7 @@ import Plugin from '../Plugin'; import Joplin from './Joplin'; +import BasePlatformImplementation from '../BasePlatformImplementation'; +import type { Store } from 'redux'; /** * @ignore */ @@ -8,7 +10,7 @@ import Joplin from './Joplin'; */ export default class Global { private joplin_; - constructor(implementation: any, plugin: Plugin, store: any); + constructor(implementation: BasePlatformImplementation, plugin: Plugin, store: Store); get joplin(): Joplin; - get process(): any; + get process(): NodeJS.Process; } diff --git a/api/Joplin.d.ts b/api/Joplin.d.ts index 15c672c..ca1617e 100644 --- a/api/Joplin.d.ts +++ b/api/Joplin.d.ts @@ -12,6 +12,9 @@ import JoplinClipboard from './JoplinClipboard'; import JoplinWindow from './JoplinWindow'; import BasePlatformImplementation from '../BasePlatformImplementation'; import JoplinImaging from './JoplinImaging'; +import JoplinFs from './JoplinFs'; +import JoplinAi from './JoplinAi'; +import type { Store } from 'redux'; /** * This is the main entry point to the Joplin API. You can access various services using the provided accessors. * @@ -28,6 +31,7 @@ export default class Joplin { private data_; private plugins_; private imaging_; + private fs_; private workspace_; private filters_; private commands_; @@ -37,11 +41,13 @@ export default class Joplin { private contentScripts_; private clipboard_; private window_; + private ai_; private implementation_; - constructor(implementation: BasePlatformImplementation, plugin: Plugin, store: any); + constructor(implementation: BasePlatformImplementation, plugin: Plugin, store: Store); get data(): JoplinData; get clipboard(): JoplinClipboard; get imaging(): JoplinImaging; + get fs(): JoplinFs; get window(): JoplinWindow; get plugins(): JoplinPlugins; get workspace(): JoplinWorkspace; @@ -57,6 +63,13 @@ export default class Joplin { get views(): JoplinViews; get interop(): JoplinInterop; get settings(): JoplinSettings; + /** + * Access to AI features: chat completions and semantic search over the + * local embeddings index. See {@link JoplinAi}. + * + * desktop + */ + get ai(): JoplinAi; /** * It is not possible to bundle native packages with a plugin, because they * need to work cross-platforms. Instead access to certain useful native diff --git a/api/JoplinAi.d.ts b/api/JoplinAi.d.ts new file mode 100644 index 0000000..9e611b2 --- /dev/null +++ b/api/JoplinAi.d.ts @@ -0,0 +1,80 @@ +import { ChatMessage, ChatOptions, SearchOptions, SearchResult } from './types'; +/** + * Provides access to AI models configured by the user. The active provider + * (Joplin Cloud AI, OpenAI-compatible, or Anthropic) and the model are picked + * by the user in the Joplin settings — plugins inherit whichever is active. + * + * AI is disabled by default. The user must enable it in the settings, and + * separately grant permission to use a remote (cloud-hosted) provider before + * any plugin call will succeed. + * + * If the user is signed into Joplin Cloud, AI works zero-config — they only + * need to flip the master toggle on. + * + * desktop + */ +export default class JoplinAi { + /** + * Sends a chat completion request to the active AI provider and returns the + * assistant's text response. + * + * The active provider and model are controlled by the user in Settings → + * AI. Plugins should not assume any particular provider or model. + * + * This call throws when: + * + * - AI features are disabled (`AI features are disabled`). + * - The active provider is remote and the user has not allowed remote + * providers (`Remote AI access is not allowed`). + * - The provider is misconfigured, e.g. missing API key or model name + * (`*provider* has no API key configured`). + * - The provider returns an HTTP error (the message includes the status + * and any detail returned by the provider). + * + * Plugins should catch these errors and present a user-friendly message + * pointing the user at the Joplin settings. + * + * @example + * ```typescript + * const reply = await joplin.ai.chat([ + * { role: 'system', content: 'You are a concise assistant.' }, + * { role: 'user', content: 'Summarise this note: ...' }, + * ]); + * console.log(reply); + * ``` + */ + chat(messages: ChatMessage[], options?: ChatOptions): Promise; + /** + * Runs a semantic search against the locally-indexed embeddings and + * returns matching chunks ranked by similarity. + * + * The `query` is either plain text (which gets embedded internally) or + * `{ noteId }`, which reuses the note's already-indexed chunks as the + * query — useful for "find related notes" / tag suggestion / semantic + * graph use cases without spending another embedding pass. + * + * The `scope` restricts the search: `'all'` (default), `'note'`, + * `'folder'` (by folder id), or `'tag'` (by tag id). + * Trashed and conflict notes are excluded from results. + * + * The `relevance` preset controls how strict the match is: + * `'strict' | 'normal' | 'loose'`. Joplin owns the mapping from preset + * to model-specific (k, minScore) — plugins write against the preset + * and stay compatible when the bundled model changes. + * + * Throws when AI features are disabled or no embedding provider is + * active (e.g. ONNX failed to load on this platform). + * + * @example + * ```typescript + * const results = await joplin.ai.search({ + * query: { text: 'pizza dough hydration' }, + * relevance: 'normal', + * }); + * for (const r of results) { + * console.log(r.score, r.noteId, r.chunkText.slice(0, 80)); + * } + * ``` + */ + search(options: SearchOptions): Promise; +} diff --git a/api/JoplinClipboard.d.ts b/api/JoplinClipboard.d.ts index 6d2baa8..60c4c1c 100644 --- a/api/JoplinClipboard.d.ts +++ b/api/JoplinClipboard.d.ts @@ -1,8 +1,23 @@ import { ClipboardContent } from './types'; +interface ElectronClipboardLike { + readText(): string; + writeText(text: string): void; + readHTML(): string; + writeHTML(html: string): void; + readImage(): { + toDataURL(): string; + } | null; + writeImage(image: unknown): void; + availableFormats(): string[]; + write(data: Record): void; +} +interface ElectronNativeImageLike { + createFromDataURL(dataUrl: string): unknown; +} export default class JoplinClipboard { private electronClipboard_; private electronNativeImage_; - constructor(electronClipboard: any, electronNativeImage: any); + constructor(electronClipboard: ElectronClipboardLike, electronNativeImage: ElectronNativeImageLike); readText(): Promise; writeText(text: string): Promise; /** desktop */ @@ -43,3 +58,4 @@ export default class JoplinClipboard { */ write(content: ClipboardContent): Promise; } +export {}; diff --git a/api/JoplinContentScripts.d.ts b/api/JoplinContentScripts.d.ts index adf4e8d..d391ce0 100644 --- a/api/JoplinContentScripts.d.ts +++ b/api/JoplinContentScripts.d.ts @@ -1,4 +1,4 @@ -import Plugin from '../Plugin'; +import Plugin, { MessageListenerCallback } from '../Plugin'; import { ContentScriptType } from './types'; export default class JoplinContentScripts { private plugin; @@ -37,5 +37,5 @@ export default class JoplinContentScripts { * [postMessage * demo](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/post_messages) */ - onMessage(contentScriptId: string, callback: any): Promise; + onMessage(contentScriptId: string, callback: MessageListenerCallback): Promise; } diff --git a/api/JoplinData.d.ts b/api/JoplinData.d.ts index 026150a..e76db36 100644 --- a/api/JoplinData.d.ts +++ b/api/JoplinData.d.ts @@ -1,4 +1,5 @@ import { ModelType } from '../../../BaseModel'; +import { RequestFile } from '../../rest/Api'; import Plugin from '../Plugin'; import { Path } from './types'; /** @@ -45,8 +46,8 @@ export default class JoplinData { private serializeApiBody; private pathToString; get(path: Path, query?: any): Promise; - post(path: Path, query?: any, body?: any, files?: any[]): Promise; - put(path: Path, query?: any, body?: any, files?: any[]): Promise; + post(path: Path, query?: any, body?: any, files?: RequestFile[]): Promise; + put(path: Path, query?: any, body?: any, files?: RequestFile[]): Promise; delete(path: Path, query?: any): Promise; itemType(itemId: string): Promise; resourcePath(resourceId: string): Promise; diff --git a/api/JoplinFs.d.ts b/api/JoplinFs.d.ts new file mode 100644 index 0000000..238d5da --- /dev/null +++ b/api/JoplinFs.d.ts @@ -0,0 +1,22 @@ +export interface ArchiveEntry { + entryName: string; + name: string; +} +/** + * Provides file system utilities for plugins. + * + * desktop + */ +export default class JoplinFs { + /** + * Extracts an archive to the specified directory. Currently only ZIP files + * are supported. + * + * desktop + * + * @param sourcePath Path to the archive file to extract + * @param destinationPath Path to the directory where the contents should be extracted + * @returns List of entries extracted from the archive + */ + archiveExtract(sourcePath: string, destinationPath: string): Promise; +} diff --git a/api/JoplinImaging.d.ts b/api/JoplinImaging.d.ts index 8d878b5..7c5d469 100644 --- a/api/JoplinImaging.d.ts +++ b/api/JoplinImaging.d.ts @@ -1,3 +1,4 @@ +import { ResourceEntity } from '../../database/types'; import { Rectangle } from './types'; export interface CreateFromBufferOptions { width?: number; @@ -65,7 +66,10 @@ export default class JoplinImaging { createFromPdfResource(resourceId: string, options?: CreateFromPdfOptions): Promise; getPdfInfoFromPath(path: string): Promise; getPdfInfoFromResource(resourceId: string): Promise; - getSize(handle: Handle): Promise; + getSize(handle: Handle): Promise<{ + width: number; + height: number; + }>; resize(handle: Handle, options?: ResizeOptions): Promise; crop(handle: Handle, rectangle: Rectangle): Promise; toPngFile(handle: Handle, filePath: string): Promise; @@ -78,12 +82,12 @@ export default class JoplinImaging { * Creates a new Joplin resource from the image data. The image will be * first converted to a JPEG. */ - toJpgResource(handle: Handle, resourceProps: any, quality?: number): Promise; + toJpgResource(handle: Handle, resourceProps: Partial, quality?: number): Promise; /** * Creates a new Joplin resource from the image data. The image will be * first converted to a PNG. */ - toPngResource(handle: Handle, resourceProps: any): Promise; + toPngResource(handle: Handle, resourceProps: Partial): Promise; /** * Image data is not automatically deleted by Joplin so make sure you call * this method on the handle once you are done. diff --git a/api/JoplinSettings.d.ts b/api/JoplinSettings.d.ts index 13cdca2..a3aa324 100644 --- a/api/JoplinSettings.d.ts +++ b/api/JoplinSettings.d.ts @@ -40,7 +40,7 @@ export default class JoplinSettings { /** * Gets setting values (only applies to setting you registered from your plugin) */ - values(keys: string[] | string): Promise>; + values(keys: string[] | string): Promise>; /** * Gets a setting value (only applies to setting you registered from your plugin). * diff --git a/api/JoplinViews.d.ts b/api/JoplinViews.d.ts index 364e82a..237286b 100644 --- a/api/JoplinViews.d.ts +++ b/api/JoplinViews.d.ts @@ -1,4 +1,6 @@ +import { JoplinViews as JoplinViewsImplementation } from '../BasePlatformImplementation'; import Plugin from '../Plugin'; +import { PluginStore } from '../ViewController'; import JoplinViewsDialogs from './JoplinViewsDialogs'; import JoplinViewsMenuItems from './JoplinViewsMenuItems'; import JoplinViewsMenus from './JoplinViewsMenus'; @@ -32,7 +34,7 @@ export default class JoplinViews { private editors_; private noteList_; private implementation_; - constructor(implementation: any, plugin: Plugin, store: any); + constructor(implementation: JoplinViewsImplementation, plugin: Plugin, store: PluginStore); get dialogs(): JoplinViewsDialogs; get panels(): JoplinViewsPanels; get editors(): JoplinViewsEditors; diff --git a/api/JoplinViewsDialogs.d.ts b/api/JoplinViewsDialogs.d.ts index 55db518..a331545 100644 --- a/api/JoplinViewsDialogs.d.ts +++ b/api/JoplinViewsDialogs.d.ts @@ -1,5 +1,7 @@ import Plugin from '../Plugin'; import { ButtonSpec, ViewHandle, DialogResult, Toast } from './types'; +import { JoplinViewsDialogs as JoplinViewsDialogsImplementation, ShowOpenDialogOptions } from '../BasePlatformImplementation'; +import { PluginStore } from '../ViewController'; /** * Allows creating and managing dialogs. A dialog is modal window that * contains a webview and a row of buttons. You can update the @@ -33,7 +35,7 @@ export default class JoplinViewsDialogs { private store; private plugin; private implementation_; - constructor(implementation: any, plugin: Plugin, store: any); + constructor(implementation: JoplinViewsDialogsImplementation, plugin: Plugin, store: PluginStore); private controller; /** * Creates a new dialog @@ -54,7 +56,7 @@ export default class JoplinViewsDialogs { * * desktop */ - showOpenDialog(options: any): Promise; + showOpenDialog(options: ShowOpenDialogOptions): Promise; /** * Sets the dialog HTML content */ diff --git a/api/JoplinViewsEditor.d.ts b/api/JoplinViewsEditor.d.ts index 512c596..72bd953 100644 --- a/api/JoplinViewsEditor.d.ts +++ b/api/JoplinViewsEditor.d.ts @@ -1,4 +1,5 @@ -import Plugin from '../Plugin'; +import Plugin, { MessageListenerCallback } from '../Plugin'; +import { PluginStore } from '../ViewController'; import { ActivationCheckCallback, ViewHandle, UpdateCallback, EditorPluginCallbacks } from './types'; interface SaveNoteOptions { /** @@ -55,7 +56,7 @@ export default class JoplinViewsEditors { private plugin; private activationCheckHandlers_; private unhandledActivationCheck_; - constructor(plugin: Plugin, store: any); + constructor(plugin: Plugin, store: PluginStore); private controller; /** * Registers a new editor plugin. Joplin will call the provided callback to create new editor views @@ -79,7 +80,7 @@ export default class JoplinViewsEditors { /** * See [[JoplinViewPanels]] */ - onMessage(handle: ViewHandle, callback: Function): Promise; + onMessage(handle: ViewHandle, callback: MessageListenerCallback): Promise; /** * Saves the content of the editor, without calling `onUpdate` for editors in the same window. */ diff --git a/api/JoplinViewsMenuItems.d.ts b/api/JoplinViewsMenuItems.d.ts index 5e236b1..d8b46f5 100644 --- a/api/JoplinViewsMenuItems.d.ts +++ b/api/JoplinViewsMenuItems.d.ts @@ -1,5 +1,6 @@ import { CreateMenuItemOptions, MenuItemLocation } from './types'; import Plugin from '../Plugin'; +import { PluginStore } from '../ViewController'; /** * Allows creating and managing menu items. * @@ -10,7 +11,7 @@ import Plugin from '../Plugin'; export default class JoplinViewsMenuItems { private store; private plugin; - constructor(plugin: Plugin, store: any); + constructor(plugin: Plugin, store: PluginStore); /** * Creates a new menu item and associate it with the given command. You can specify under which menu the item should appear using the `location` parameter. */ diff --git a/api/JoplinViewsMenus.d.ts b/api/JoplinViewsMenus.d.ts index 474830d..67f6d6d 100644 --- a/api/JoplinViewsMenus.d.ts +++ b/api/JoplinViewsMenus.d.ts @@ -1,5 +1,6 @@ import { MenuItem, MenuItemLocation } from './types'; import Plugin from '../Plugin'; +import { PluginStore } from '../ViewController'; /** * Allows creating menus. * @@ -10,7 +11,7 @@ import Plugin from '../Plugin'; export default class JoplinViewsMenus { private store; private plugin; - constructor(plugin: Plugin, store: any); + constructor(plugin: Plugin, store: PluginStore); private registerCommandAccelerators; /** * Creates a new menu from the provided menu items and place it at the given location. As of now, it is only possible to place the diff --git a/api/JoplinViewsPanels.d.ts b/api/JoplinViewsPanels.d.ts index 881dbb0..73259da 100644 --- a/api/JoplinViewsPanels.d.ts +++ b/api/JoplinViewsPanels.d.ts @@ -1,4 +1,5 @@ -import Plugin from '../Plugin'; +import Plugin, { MessageListenerCallback } from '../Plugin'; +import { PluginStore } from '../ViewController'; import { ViewHandle } from './types'; /** * Allows creating and managing view panels. View panels allow displaying any HTML @@ -17,7 +18,7 @@ import { ViewHandle } from './types'; export default class JoplinViewsPanels { private store; private plugin; - constructor(plugin: Plugin, store: any); + constructor(plugin: Plugin, store: PluginStore); private controller; /** * Creates a new panel @@ -50,7 +51,7 @@ export default class JoplinViewsPanels { * demo](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/post_messages) for more details. * */ - onMessage(handle: ViewHandle, callback: Function): Promise; + onMessage(handle: ViewHandle, callback: MessageListenerCallback): Promise; /** * Sends a message to the webview. * diff --git a/api/JoplinViewsToolbarButtons.d.ts b/api/JoplinViewsToolbarButtons.d.ts index ba17c83..c1d12c2 100644 --- a/api/JoplinViewsToolbarButtons.d.ts +++ b/api/JoplinViewsToolbarButtons.d.ts @@ -1,5 +1,6 @@ import { ToolbarButtonLocation } from './types'; import Plugin from '../Plugin'; +import { PluginStore } from '../ViewController'; /** * Allows creating and managing toolbar buttons. * @@ -8,7 +9,7 @@ import Plugin from '../Plugin'; export default class JoplinViewsToolbarButtons { private store; private plugin; - constructor(plugin: Plugin, store: any); + constructor(plugin: Plugin, store: PluginStore); /** * Creates a new toolbar button and associate it with the given command. */ diff --git a/api/JoplinWindow.d.ts b/api/JoplinWindow.d.ts index 4cbdc64..ed9b3cb 100644 --- a/api/JoplinWindow.d.ts +++ b/api/JoplinWindow.d.ts @@ -1,7 +1,13 @@ import Plugin from '../Plugin'; +type DispatchStore = { + dispatch: (action: { + type: string; + [k: string]: unknown; + }) => void; +}; export default class JoplinWindow { private store_; - constructor(_plugin: Plugin, store: any); + constructor(_plugin: Plugin, store: DispatchStore); /** * Loads a chrome CSS file. It will apply to the window UI elements, except * for the note viewer. It is the same as the "Custom stylesheet for @@ -21,3 +27,4 @@ export default class JoplinWindow { */ loadNoteCssFile(filePath: string): Promise; } +export {}; diff --git a/api/JoplinWorkspace.d.ts b/api/JoplinWorkspace.d.ts index 9799f6f..e89f7e6 100644 --- a/api/JoplinWorkspace.d.ts +++ b/api/JoplinWorkspace.d.ts @@ -1,5 +1,6 @@ import Plugin from '../Plugin'; -import { FolderEntity } from '../../database/types'; +import { PluginStore } from '../ViewController'; +import { FolderEntity, NoteEntity } from '../../database/types'; import { Disposable, EditContextMenuFilterObject, FilterHandler } from './types'; declare enum ItemChangeEventType { Create = 1, @@ -40,7 +41,7 @@ type ResourceChangeHandler = WorkspaceEventHandler; export default class JoplinWorkspace { private store; private plugin; - constructor(plugin: Plugin, store: any); + constructor(plugin: Plugin, store: PluginStore); /** * Called when a new note or notes are selected. */ @@ -83,7 +84,7 @@ export default class JoplinWorkspace { * * On desktop, this returns the selected note in the focused window. */ - selectedNote(): Promise; + selectedNote(): Promise; /** * Gets the currently selected folder. In some cases, for example during * search or when viewing a tag, no folder is actually selected in the user diff --git a/api/noteListType.d.ts b/api/noteListType.d.ts index 2fc14ee..7862a63 100644 --- a/api/noteListType.d.ts +++ b/api/noteListType.d.ts @@ -1,5 +1,5 @@ import { Size } from './types'; -type ListRendererDatabaseDependency = 'folder.created_time' | 'folder.deleted_time' | 'folder.encryption_applied' | 'folder.encryption_cipher_text' | 'folder.icon' | 'folder.id' | 'folder.is_shared' | 'folder.master_key_id' | 'folder.parent_id' | 'folder.share_id' | 'folder.title' | 'folder.updated_time' | 'folder.user_created_time' | 'folder.user_data' | 'folder.user_updated_time' | 'folder.type_' | 'note.altitude' | 'note.application_data' | 'note.author' | 'note.body' | 'note.conflict_original_id' | 'note.created_time' | 'note.deleted_time' | 'note.encryption_applied' | 'note.encryption_cipher_text' | 'note.id' | 'note.is_conflict' | 'note.is_shared' | 'note.is_todo' | 'note.latitude' | 'note.longitude' | 'note.markup_language' | 'note.master_key_id' | 'note.order' | 'note.parent_id' | 'note.share_id' | 'note.source' | 'note.source_application' | 'note.source_url' | 'note.title' | 'note.todo_completed' | 'note.todo_due' | 'note.updated_time' | 'note.user_created_time' | 'note.user_data' | 'note.user_updated_time' | 'note.type_'; +type ListRendererDatabaseDependency = 'folder.created_time' | 'folder.deleted_time' | 'folder.encryption_applied' | 'folder.encryption_cipher_text' | 'folder.icon' | 'folder.id' | 'folder.is_shared' | 'folder.master_key_id' | 'folder.parent_id' | 'folder.share_id' | 'folder.title' | 'folder.updated_time' | 'folder.user_created_time' | 'folder.user_data' | 'folder.user_updated_time' | 'folder.type_' | 'note.altitude' | 'note.application_data' | 'note.author' | 'note.body' | 'note.conflict_original_id' | 'note.created_time' | 'note.deleted_time' | 'note.encryption_applied' | 'note.encryption_cipher_text' | 'note.extracted_resource_ids' | 'note.id' | 'note.is_conflict' | 'note.is_locked' | 'note.is_shared' | 'note.is_todo' | 'note.latitude' | 'note.longitude' | 'note.markup_language' | 'note.master_key_id' | 'note.order' | 'note.parent_id' | 'note.share_id' | 'note.source' | 'note.source_application' | 'note.source_url' | 'note.title' | 'note.todo_completed' | 'note.todo_due' | 'note.updated_time' | 'note.user_created_time' | 'note.user_data' | 'note.user_updated_time' | 'note.type_'; export declare enum ItemFlow { TopToBottom = "topToBottom", LeftToRight = "leftToRight" @@ -30,9 +30,9 @@ export type OnClickHandler = (event: OnClickEvent) => Promise; * The `item.*` properties are specific to the rendered item. The most important being * `item.selected`, which you can use to display the selected note in a different way. */ -export type ListRendererDependency = ListRendererDatabaseDependency | 'item.index' | 'item.selected' | 'item.size.height' | 'item.size.width' | 'note.folder.title' | 'note.isWatched' | 'note.tags' | 'note.todoStatusText' | 'note.titleHtml'; +export type ListRendererDependency = ListRendererDatabaseDependency | 'item.index' | 'item.selected' | 'item.size.height' | 'item.size.width' | 'note.checkboxes' | 'note.folder.title' | 'note.isWatched' | 'note.tags' | 'note.todoStatusText' | 'note.titleHtml'; export type ListRendererItemValueTemplates = Record; -export declare const columnNames: readonly ["note.folder.title", "note.is_todo", "note.latitude", "note.longitude", "note.source_url", "note.tags", "note.title", "note.todo_completed", "note.todo_due", "note.user_created_time", "note.user_updated_time"]; +export declare const columnNames: readonly ["note.checkboxes", "note.folder.title", "note.is_todo", "note.latitude", "note.longitude", "note.source_url", "note.tags", "note.title", "note.todo_completed", "note.todo_due", "note.user_created_time", "note.user_updated_time"]; export type ColumnName = typeof columnNames[number]; export interface ListRenderer { /** diff --git a/api/noteListType.ts b/api/noteListType.ts index ad00453..cf09294 100644 --- a/api/noteListType.ts +++ b/api/noteListType.ts @@ -3,7 +3,7 @@ import { Size } from './types'; // AUTO-GENERATED by generate-database-type -type ListRendererDatabaseDependency = 'folder.created_time' | 'folder.deleted_time' | 'folder.encryption_applied' | 'folder.encryption_cipher_text' | 'folder.icon' | 'folder.id' | 'folder.is_shared' | 'folder.master_key_id' | 'folder.parent_id' | 'folder.share_id' | 'folder.title' | 'folder.updated_time' | 'folder.user_created_time' | 'folder.user_data' | 'folder.user_updated_time' | 'folder.type_' | 'note.altitude' | 'note.application_data' | 'note.author' | 'note.body' | 'note.conflict_original_id' | 'note.created_time' | 'note.deleted_time' | 'note.encryption_applied' | 'note.encryption_cipher_text' | 'note.id' | 'note.is_conflict' | 'note.is_shared' | 'note.is_todo' | 'note.latitude' | 'note.longitude' | 'note.markup_language' | 'note.master_key_id' | 'note.order' | 'note.parent_id' | 'note.share_id' | 'note.source' | 'note.source_application' | 'note.source_url' | 'note.title' | 'note.todo_completed' | 'note.todo_due' | 'note.updated_time' | 'note.user_created_time' | 'note.user_data' | 'note.user_updated_time' | 'note.type_'; +type ListRendererDatabaseDependency = 'folder.created_time' | 'folder.deleted_time' | 'folder.encryption_applied' | 'folder.encryption_cipher_text' | 'folder.icon' | 'folder.id' | 'folder.is_shared' | 'folder.master_key_id' | 'folder.parent_id' | 'folder.share_id' | 'folder.title' | 'folder.updated_time' | 'folder.user_created_time' | 'folder.user_data' | 'folder.user_updated_time' | 'folder.type_' | 'note.altitude' | 'note.application_data' | 'note.author' | 'note.body' | 'note.conflict_original_id' | 'note.created_time' | 'note.deleted_time' | 'note.encryption_applied' | 'note.encryption_cipher_text' | 'note.extracted_resource_ids' | 'note.id' | 'note.is_conflict' | 'note.is_locked' | 'note.is_shared' | 'note.is_todo' | 'note.latitude' | 'note.longitude' | 'note.markup_language' | 'note.master_key_id' | 'note.order' | 'note.parent_id' | 'note.share_id' | 'note.source' | 'note.source_application' | 'note.source_url' | 'note.title' | 'note.todo_completed' | 'note.todo_due' | 'note.updated_time' | 'note.user_created_time' | 'note.user_data' | 'note.user_updated_time' | 'note.type_'; // AUTO-GENERATED by generate-database-type export enum ItemFlow { @@ -11,12 +11,12 @@ export enum ItemFlow { LeftToRight = 'leftToRight', } -// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied +// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Plugin-API output map; values are heterogeneous (HTML strings, formatted numbers, booleans) and indexed dynamically per-plugin export type RenderNoteView = Record; export interface OnChangeEvent { elementId: string; - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Plugin API: value depends on the input element type value: any; noteId: string; } @@ -25,7 +25,7 @@ export interface OnClickEvent { elementId: string; } -// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied +// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Plugin-API callback; props is a per-renderer subset of the note shape declared via itemProps, indexed dynamically export type OnRenderNoteHandler = (props: any)=> Promise; export type OnChangeHandler = (event: OnChangeEvent)=> Promise; export type OnClickHandler = (event: OnClickEvent)=> Promise; @@ -50,6 +50,7 @@ export type ListRendererDependency = 'item.selected' | 'item.size.height' | 'item.size.width' | + 'note.checkboxes' | 'note.folder.title' | 'note.isWatched' | 'note.tags' | @@ -59,6 +60,7 @@ export type ListRendererDependency = export type ListRendererItemValueTemplates = Record; export const columnNames = [ + 'note.checkboxes', 'note.folder.title', 'note.is_todo', 'note.latitude', diff --git a/api/types.ts b/api/types.ts index 3911a38..58c1614 100644 --- a/api/types.ts +++ b/api/types.ts @@ -26,7 +26,7 @@ export interface Command { /** * Code to be ran when the command is executed. It may return a result. */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Plugin commands accept arbitrary args and return arbitrary results; this is part of the public plugin API execute(...args: any[]): Promise; /** @@ -116,13 +116,13 @@ export interface ExportModule { /** * Called when an item needs to be processed. An "item" can be any Joplin object, such as a note, a folder, a notebook, etc. */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Plugin API: item type depends on itemType (NoteEntity, FolderEntity, ResourceEntity, etc.); plugin authors discriminate at use site onProcessItem(context: ExportContext, itemType: number, item: any): Promise; /** * Called when a resource file needs to be exported. */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- See onProcessItem; resource here is a ResourceEntity but the plugin API keeps it loosely typed onProcessResource(context: ExportContext, resource: any, filePath: string): Promise; /** @@ -186,13 +186,13 @@ export interface ExportContext { /** * You can attach your own custom data using this property - it will then be passed to each event handler, allowing you to keep state from one event to the next. */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Plugin API: userData is arbitrary per-plugin state userData?: any; } export interface ImportContext { sourcePath: string; - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Plugin API: import options are arbitrary per-importer options: any; warnings: string[]; } @@ -202,7 +202,7 @@ export interface ImportContext { // ================================================================= export interface Script { - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Plugin API: event payload shape depends on the host context onStart?(event: any): Promise; } @@ -308,7 +308,7 @@ export interface MenuItem { * Arguments that should be passed to the command. They will be as rest * parameters. */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Plugin API: command args depend on the command commandArgs?: any[]; /** @@ -362,13 +362,13 @@ export type ViewHandle = string; export interface EditorCommand { name: string; - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Plugin API: command value depends on the command value?: any; } export interface DialogResult { id: ButtonId; - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Plugin API: form data shape depends on the dialog formData?: any; } @@ -434,8 +434,30 @@ export interface EditorPluginCallbacks { export type VisibleHandler = ()=> Promise; +/** + * Identifies the type of element that was right-clicked in the editor context menu. + */ +export enum ContextMenuItemType { + None = '', + Image = 'image', + Resource = 'resource', + Text = 'text', + Link = 'link', + NoteLink = 'noteLink', +} + export interface EditContextMenuFilterObject { items: MenuItem[]; + /** + * Context about what was right-clicked. Plugins should use this instead of + * checking the editor cursor position, as the cursor may not reflect the + * actual click location. + */ + context?: { + resourceId?: string; + itemType?: ContextMenuItemType; + textToCopy?: string; + }; } export interface EditorActivationCheckFilterObject { @@ -497,7 +519,7 @@ export enum SettingStorage { // Redefine a simplified interface to mask internal details // and to remove function calls as they would have to be async. export interface SettingItem { - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Setting values are heterogeneous per setting (string/number/bool/Record/Array); plugin authors narrow at use site value: any; type: SettingItemType; @@ -534,8 +556,7 @@ export interface SettingItem { * This property is required when `isEnum` is `true`. In which case, it * should contain a map of value => label. */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied - options?: Record; + options?: Record; /** * Reserved property. Not used at the moment. @@ -616,7 +637,7 @@ export interface ClipboardContent { // Content Script types // ================================================================= -// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied +// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Plugin API: messages between content scripts and plugins are arbitrary serialisable data export type PostMessageHandler = (message: any)=> Promise; /** @@ -640,38 +661,38 @@ export interface ContentScriptContext { } export interface ContentScriptModuleLoadedEvent { - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Plugin API: userData is arbitrary per-plugin state userData?: any; } export interface ContentScriptModule { onLoaded?: (event: ContentScriptModuleLoadedEvent)=> void; - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Plugin entry point returns a plugin-specific module (markdown-it plugin, CodeMirror plugin, etc.); shape varies per content script type plugin: ()=> any; assets?: ()=> void; } export interface MarkdownItContentScriptModule extends Omit { - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- markdown-it and options are external library types not imported here; plugin authors annotate concretely plugin: (markdownIt: any, options: any)=> any; } -// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied +// eslint-disable-next-line @typescript-eslint/no-explicit-any -- CodeMirror command callbacks accept and return arbitrary values; matches CM6 Command type type EditorCommandCallback = (...args: any[])=> any; export interface CodeMirrorControl { /** Points to a CodeMirror 6 EditorView instance. */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- CM6 EditorView is an external library type not imported here editor: any; - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- CM6 module namespace; types come from the external library cm6: any; /** `extension` should be a [CodeMirror 6 extension](https://codemirror.net/docs/ref/#state.Extension). */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- CM6 Extension type comes from the external library addExtension(extension: any|any[]): void; supportsCommand(name: string): boolean; - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- See EditorCommandCallback execCommand(name: string, ...args: any[]): any; registerCommand(name: string, callback: EditorCommandCallback): void; @@ -685,13 +706,13 @@ export interface CodeMirrorControl { * * Using `autocompletion({ override: [ ... ]})` causes errors when done by multiple plugins. */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- CM6 CompletionSource and Extension types come from the external library completionSource(completionSource: any): any; /** * Creates an extension that enables or disables [`languageData`-based autocompletion](https://codemirror.net/docs/ref/#autocomplete.autocompletion^config.override). */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- See completionSource above enableLanguageDataAutocomplete: { of: (enabled: boolean)=> any }; /** @@ -938,3 +959,91 @@ export enum ContentScriptType { */ CodeMirrorPlugin = 'codeMirrorPlugin', } + +// ================================================================= +// AI API types +// ================================================================= + +/** + * Role of a chat message. `system` messages set the assistant's behaviour, + * `user` messages come from the end user, and `assistant` messages are model + * responses fed back as conversation history. + */ +export type ChatMessageRole = 'system' | 'user' | 'assistant'; + +/** + * A single message in a chat conversation. + */ +export interface ChatMessage { + role: ChatMessageRole; + content: string; +} + +/** + * Optional parameters for a chat call. The active model and provider are + * controlled by the user in the Joplin settings — plugins cannot pick a model. + */ +export interface ChatOptions { + /** Sampling temperature, typically between 0 and 1. Provider default if omitted. */ + temperature?: number; + /** Maximum number of tokens to generate. Provider default if omitted. */ + maxTokens?: number; +} + +/** + * Relevance preset for semantic search. Maps internally to model-specific + * `(k, minScore)` tuning — the preset is the public contract so plugins keep + * working when the bundled embedding model changes. + */ +export type SearchRelevance = 'strict' | 'normal' | 'loose'; + +/** + * Where to look for matches. + * + * - `all`: every indexed note (default). + * - `note`: a single note (rarely useful directly — mainly an internal + * building block). + * - `folder`: all notes in the given folder (a "notebook" in the UI). + * - `tag`: all notes tagged with the given tag. + * + * Trashed and conflict notes are always excluded. + */ +export type SearchScope = + | { type: 'all' } + | { type: 'note'; noteId: string } + | { type: 'folder'; folderId: string } + | { type: 'tag'; tagId: string }; + +/** + * What to search for: free text (embedded internally), or an existing note + * whose stored chunks are reused as the query — useful for "related notes", + * tag suggestions, and graph-style use cases without a second embedding pass. + */ +export type SearchQuery = + | { text: string } + | { noteId: string }; + +/** + * Parameters for {@link JoplinAi.search}. + */ +export interface SearchOptions { + query: SearchQuery; + scope?: SearchScope; + relevance?: SearchRelevance; +} + +/** + * A single hit from {@link JoplinAi.search}. + */ +export interface SearchResult { + noteId: string; + chunkIndex: number; + chunkText: string; + /** + * Cosine similarity in `[0, 1]`. Higher means more similar. Plugins should + * use this for ranking but not as an absolute threshold — that's what the + * `relevance` preset is for. + */ + score: number; +} + diff --git a/package.json b/package.json index 88530ee..caca106 100644 --- a/package.json +++ b/package.json @@ -60,4 +60,4 @@ "files": [ "publish" ] -} +} \ No newline at end of file