import type { PluginNoteKind, PluginContentDocument, PluginDiagramGraph, PluginDiagramEditInput, PluginTableDocument, PluginTableEditInput, PluginPosterDocument, PluginPosterEditInput, PluginInfographicDocument, PluginInfographicEditInput } from "./structured-notes"; export * from "./structured-notes"; export declare const PLUGIN_API_VERSION: "2"; export declare const THEME_API_VERSION: "1"; export declare const PLUGIN_PERMISSIONS: readonly ["notes:read", "notes:write", "notes:delete", "metadata:read", "metadata:write", "resources:read", "resources:write", "templates:read", "templates:write", "network", "network:public", "ai:generate", "storage", "secrets", "schedules", "editor:read", "editor:write", "ui:commands", "ui:navigation", "ui:notices", "ui:panels", "ui:embeds"]; export type PluginPermission = (typeof PLUGIN_PERMISSIONS)[number]; export type ExtensionPlatform = "web" | "desktop" | "android" | "ios"; export interface LocalizedExtensionMetadata { name?: string; description?: string; } export type ExtensionLocales = Record; export interface PluginManifest { type: "plugin"; id: string; name: string; version: string; apiVersion: typeof PLUGIN_API_VERSION; /** Plugins must delegate ordinary persistent configuration to EdgeEver. */ settingsUi: "host"; description?: string; /** Localized marketplace and manager copy keyed by a BCP 47 language tag. */ locales?: ExtensionLocales; author?: string; entry: string; platforms?: ExtensionPlatform[]; permissions: PluginPermission[]; networkHosts?: string[]; settings?: PluginSettingsSchema; } export interface PluginSettingListItem { title: string; description?: string; } export interface PluginSettingList { title?: string; actionLabel?: string; items: PluginSettingListItem[]; } /** * Declarative setting metadata. EdgeEver owns the layout, controls, validation, * state feedback, and responsive behavior; plugins cannot supply presentation code or styles. */ interface PluginSettingBase { key: string; label: string; description?: string; locales?: Record; }>; required?: boolean; /** Host-rendered read-only items, opened from a small entry next to the field. */ list?: PluginSettingList; } export type PluginSettingField = (PluginSettingBase & { type: "text"; default?: string; placeholder?: string; }) | (PluginSettingBase & { type: "secret"; placeholder?: string; }) | (PluginSettingBase & { type: "number"; default?: number; min?: number; max?: number; step?: number; }) | (PluginSettingBase & { type: "boolean"; default?: boolean; }) | (PluginSettingBase & { type: "select"; default?: string; options: Array<{ value: string; label: string; }>; }); export interface PluginSettingsSchema { /** Host-rendered fields in display order. Arbitrary UI markup and styling are intentionally unsupported. */ fields: PluginSettingField[]; } export type PluginSettingValue = string | number | boolean; export declare const PLUGIN_API_ERROR_CODES: readonly ["NOTE_CONFLICT", "RESOURCE_CONFLICT", "INVALID_MARKDOWN_EDIT", "STRUCTURED_NOTE_REQUIRED", "INVALID_STRUCTURED_EDIT", "NOTE_TYPE_MISMATCH", "TABLE_DATA_CHANGES_REQUIRED", "HOST_UPGRADE_REQUIRED"]; export type PluginApiErrorCode = (typeof PLUGIN_API_ERROR_CODES)[number]; export interface PluginApiError extends Error { code: PluginApiErrorCode; } export declare const THEME_TOKEN_NAMES: readonly ["color.background", "color.surface", "color.surfaceMuted", "color.text", "color.textMuted", "color.border", "color.accent", "color.accentForeground", "color.success", "color.warning", "color.danger", "font.body", "font.mono", "font.size", "lineHeight.body", "radius.medium", "density.scale", "editor.contentWidth"]; export type ThemeTokenName = (typeof THEME_TOKEN_NAMES)[number]; export type ThemeTokens = Partial>; export interface ThemeManifest { type: "theme"; id: string; name: string; version: string; themeApiVersion: typeof THEME_API_VERSION; description?: string; /** Localized marketplace and manager copy keyed by a BCP 47 language tag. */ locales?: ExtensionLocales; author?: string; modes: Array<"light" | "dark">; light: ThemeTokens; dark?: ThemeTokens; } export type ExtensionManifest = PluginManifest | ThemeManifest; export declare const MARKETPLACE_REGISTRY_VERSION: "1"; export interface MarketplaceEntry { id: string; name: string; description: string; locales?: ExtensionLocales; author: string; publisher?: "edgeever"; category: string; repositoryUrl: string; distribution: { type: "github"; repositoryUrl: string; } | { type: "manifest"; manifestUrl: string; }; verification: { version: string; checksums?: { manifestJson?: string; mainJs?: string; stylesCss?: string; }; }; /** SPDX license identifier recorded when a community plugin was admitted. */ licenseSpdx?: string; /** 40-character commit recorded for a community plugin release. */ sourceRevision?: string; /** Plugin API version this community entry was admitted against. */ apiVersion?: string; /** Theme API version this community entry was admitted against. */ themeApiVersion?: string; /** Capability snapshot used to detect a later permission expansion. */ admitted?: { permissions: string[]; networkHosts: string[]; }; /** * Set only by the client after a signed community registry is merged. * Remote files cannot claim this field. */ listing?: "community"; } export interface MarketplaceRevocation { id: string; reason: string; revokedAt: string; } export interface MarketplaceRegistry { registryVersion: typeof MARKETPLACE_REGISTRY_VERSION; updatedAt: string; entries: MarketplaceEntry[]; revocations?: MarketplaceRevocation[]; } export interface PluginNoteSummary { kind: PluginNoteKind; id: string; notebookId: string; title: string | null; excerpt: string; tags: string[]; isPinned: boolean; createdAt: string; updatedAt: string; } export interface PluginNote extends PluginNoteSummary { /** Full persisted document; read-only through this API. Special payloads also live in contentMarkdown. */ contentJson: PluginContentDocument; revision: number; contentMarkdown: string; contentText: string; contentHash: string; } /** * A replacement range in a note's Markdown source. Offsets use JavaScript * UTF-16 string indices and ranges are half-open: [from, to). */ export interface PluginMarkdownEdit { from: number; to: number; insert: string; } export interface PluginMarkdownEditInput { expectedRevision: number; expectedContentHash: string; edits: PluginMarkdownEdit[]; } export interface PluginNoteQuery { notebookId?: string; text?: string; tags?: string[]; sort?: "updated-desc" | "created-desc" | "title-asc"; limit?: number; offset?: number; } export interface PluginNoteCreateInput { notebookId: string; title?: string; contentMarkdown?: string; tags?: string[]; } export interface PluginNoteUpdateInput { title?: string; contentMarkdown?: string; tags?: string[]; } export interface PluginNoteQueryResult { notes: PluginNoteSummary[]; totalCount: number; nextOffset: number | null; } export interface PluginNoteContentQueryResult { notes: PluginNote[]; totalCount: number; nextOffset: number | null; } export interface PluginNotebook { id: string; parentId: string | null; name: string; memoCount: number; } export interface PluginNoteRevision { id: string; noteId: string; revision: number; title: string | null; tags: string[]; contentMarkdown: string; contentText: string; createdAt: string; } export interface PluginResource { id: string; noteId: string; kind: "image" | "attachment"; mimeType: string | null; filename: string | null; byteSize: number; contentHash: string | null; width: number | null; height: number | null; createdAt: string; updatedAt: string; url: string; } export interface PluginTag { name: string; noteCount: number; } export interface PluginTemplate { id: string; name: string; description: string | null; title: string | null; contentMarkdown: string; tags: string[]; createdAt: string; updatedAt: string; } export type PluginEventMap = { "settings.changed": { key: string; }; "note.created": { note: PluginNote; }; "note.updated": { note: PluginNote; }; "note.deleted": { noteId: string; }; "tag.changed": { previousName?: string; name?: string; deleted?: boolean; }; "template.created": { template: PluginTemplate; }; "template.updated": { template: PluginTemplate; }; "template.deleted": { templateId: string; }; "resource.created": { resource: PluginResource; }; "resource.updated": { resource: PluginResource; }; "resource.deleted": { resourceId: string; }; "workspace.sync-queue-changed": Record; "workspace.synced": { bootstrapped: boolean; changed: number; }; }; export interface PluginCommand { id: string; title: string; /** * When false, the command stays in the plugin toolbar menu but is omitted from * marketplace and plugin-manager cards. Defaults to true. */ listed?: boolean; /** * When false, the command is omitted from the plugin toolbar menu. * Use this for a card-only launcher that already has a dashboard panel in the menu. * Defaults to true. */ menu?: boolean; run: () => void | Promise; } export type PluginScheduleMissedRunPolicy = "run-once" | "skip"; export interface PluginScheduleInput { /** Stable plugin-local identifier. Reusing it updates the same persistent schedule. */ key: string; name: string; commandId: string; cronExpression: string; timezone?: string; missedRunPolicy?: PluginScheduleMissedRunPolicy; isEnabled?: boolean; } export interface PluginSchedule extends Required> { timezone: string; missedRunPolicy: PluginScheduleMissedRunPolicy; isEnabled: boolean; runsOnThisDevice: boolean; lastRun: { status: "running" | "succeeded" | "failed"; scheduledFor: string; startedAt: string; finishedAt: string | null; errorMessage: string | null; } | null; } export interface PluginEditorSelection { noteId: string; from: number; to: number; empty: boolean; text: string; contentMarkdown: string; } export interface PluginEditorDocument { noteId: string; contentMarkdown: string; hasUnsavedChanges: boolean; } export interface PluginOpenNoteOptions { /** Opens in-note search and reveals the first exact text match. */ search?: string; } export type PluginJsonValue = null | boolean | number | string | PluginJsonValue[] | { [key: string]: PluginJsonValue; }; export type PluginPanelPresentation = "dialog" | "fullscreen"; export type PluginPanelPurpose = "workflow" | "dashboard" | "preview" | "onboarding"; export interface PluginPanelOpenOptions { state?: PluginJsonValue; } export type PluginPanelActionVariant = "default" | "primary" | "ghost"; export interface PluginPanelAction { id: string; label: string; variant?: PluginPanelActionVariant; disabled?: boolean; } export interface PluginPanelSelectOption { value: string; label: string; } export type PluginPanelToolbarItem = { type: "search"; key: string; placeholder?: string; value?: string; } | { type: "tabs"; key: string; value?: string; options: PluginPanelSelectOption[]; } | { type: "select"; key: string; label?: string; value?: string; options: PluginPanelSelectOption[]; } | { type: "button"; key: string; label: string; variant?: PluginPanelActionVariant; disabled?: boolean; }; export interface PluginPanelEmptyState { title: string; description?: string; action?: PluginPanelAction; } /** * Host-rendered panel chrome. Plugins describe intent; EdgeEver owns layout and controls. * Callbacks stay in-memory and are not serialized with panel open state. */ export interface PluginPanelChrome { header?: { title?: string; /** Pass `null` to hide the host's default panel description. */ description?: string | null; actions?: PluginPanelAction[]; }; toolbar?: PluginPanelToolbarItem[]; empty?: PluginPanelEmptyState | null; onAction?: (id: string) => void; onChange?: (key: string, value: string) => void; } export interface PluginPanelShell { set(chrome: PluginPanelChrome): void; } export interface PluginPanelMountContext { state: PluginJsonValue | null; requestClose(): Promise; /** Host-rendered header, toolbar, and empty state. `set` is a no-op when the host has no chrome adapter. */ shell: PluginPanelShell; } export type PluginPanelCloseDecision = boolean | { title: string; message: string; confirmLabel?: string; }; export interface PluginEmbedInput { type: string; resourceId: string; previewResourceId?: string; title?: string; data?: PluginJsonValue; } export interface PluginEmbedInstance extends PluginEmbedInput { id: string; pluginId: string; previewResourceId: string; title: string; data: PluginJsonValue; } export interface PluginEmbedRenderer { type: string; mount(container: HTMLElement, embed: PluginEmbedInstance): void | (() => void) | Promise void)>; } export interface PluginPanel { id: string; title: string; /** Business purpose of this panel. Custom settings pages are intentionally unsupported. */ purpose: PluginPanelPurpose; presentation?: PluginPanelPresentation; mount(container: HTMLElement, context: PluginPanelMountContext): void | (() => void) | Promise void)>; beforeClose?(): PluginPanelCloseDecision | Promise; } export interface PluginContext { pluginId: string; ai: { status(): Promise<{ configured: boolean; modelName?: string; }>; generate(input: { system: string; prompt: string; maxOutputTokens?: number; signal?: AbortSignal; }): Promise<{ text: string; }>; transcribeResource(noteId: string, resourceId: string): Promise<{ text: string; resourceId: string; filename: string; }>; transcribeMedia(media: Blob, options?: { signal?: AbortSignal; }): Promise<{ text: string; }>; }; notes: { query(input?: PluginNoteQuery): Promise; queryContent(input?: PluginNoteQuery): Promise; get(noteId: string): Promise; editMarkdown(noteId: string, input: PluginMarkdownEditInput): Promise; create(input: PluginNoteCreateInput): Promise; update(noteId: string, input: PluginNoteUpdateInput): Promise; delete(noteId: string, options?: { permanent?: boolean; }): Promise; move(noteIds: string[], notebookId: string): Promise; pin(noteIds: string[], isPinned: boolean): Promise; restore(noteId: string): Promise; revisions: { list(noteId: string): Promise; restore(noteId: string, revisionId: string): Promise; }; }; diagrams: { get(noteId: string, options?: { includeLayout?: boolean; }): Promise<{ note: PluginNote; diagram: PluginDiagramGraph; editToken: string; }>; edit(noteId: string, input: PluginDiagramEditInput): Promise<{ note: PluginNote; diagram: PluginDiagramGraph; editToken: string; dryRun: boolean; }>; }; tables: { get(noteId: string): Promise<{ note: PluginNote; table: PluginTableDocument; editToken: string; }>; edit(noteId: string, input: PluginTableEditInput): Promise<{ note: PluginNote; table: PluginTableDocument; editToken: string; dryRun: boolean; changedCellCount: number; removedAttachmentCount: number; }>; }; posters: { capabilities(): Promise<{ templates: string[]; platforms: Array<{ id: string; width: number; height: number; }>; fonts: string[]; editableFields: string[]; limits: { maxElements: number; maxOperations: number; canvasMin: number; canvasMax: number; }; }>; get(noteId: string): Promise<{ note: PluginNote; poster: PluginPosterDocument; editToken: string; }>; edit(noteId: string, input: PluginPosterEditInput): Promise<{ note: PluginNote; poster: PluginPosterDocument; editToken: string; dryRun: boolean; changed: boolean; }>; }; infographics: { capabilities(): Promise<{ templates: string[]; themes: string[]; maxOperations: number; }>; get(noteId: string): Promise<{ note: PluginNote; infographic: PluginInfographicDocument; data: Record; template: string; theme?: string; editToken: string; }>; edit(noteId: string, input: PluginInfographicEditInput): Promise<{ note: PluginNote; infographic: PluginInfographicDocument; editToken: string; dryRun: boolean; changed: boolean; }>; }; notebooks: { list(): Promise; create(input: { name: string; parentId?: string | null; }): Promise; update(notebookId: string, input: { name?: string; parentId?: string | null; sortOrder?: number; }): Promise; delete(notebookId: string): Promise; }; tags: { list(): Promise; rename(name: string, nextName: string): Promise; delete(name: string): Promise; }; templates: { list(): Promise; create(input: { name: string; description?: string | null; noteId?: string; title?: string | null; contentMarkdown?: string; tags?: string[]; }): Promise; update(templateId: string, input: { name?: string; description?: string | null; title?: string | null; contentMarkdown?: string; tags?: string[]; }): Promise; delete(templateId: string): Promise; use(templateId: string, notebookId: string): Promise; }; commands: { register(command: PluginCommand): () => void; }; schedules: { /** Creates or updates one persistent schedule owned by this plugin. Desktop only. */ upsert(input: PluginScheduleInput): Promise; list(): Promise; remove(key: string): Promise; }; events: { on(event: K, listener: (payload: PluginEventMap[K]) => void): () => void; }; storage: { get(key: string): Promise; set(key: string, value: T): Promise; remove(key: string): Promise; }; secrets: { get(key: string): Promise; set(key: string, value: string): Promise; remove(key: string): Promise; }; editor: { getSelection(): Promise; getDocument(): Promise; editMarkdown(edits: PluginMarkdownEdit[]): Promise; insertEmbed(input: PluginEmbedInput): Promise; embeds: { register(renderer: PluginEmbedRenderer): () => void; }; replaceSelection(contentMarkdown: string): Promise; insertAtCursor(contentMarkdown: string): Promise; }; resources: { list(noteId?: string): Promise; read(resourceId: string): Promise; upload(noteId: string, file: File): Promise; update(resourceId: string, input: { file: File; expectedContentHash: string; }): Promise; rename(resourceId: string, filename: string): Promise; delete(resourceId: string): Promise; }; settings: { get(key: string): Promise; set(key: string, value: PluginSettingValue): Promise; remove(key: string): Promise; }; network: { /** direct preserves browser fetch. public uses authenticated, bounded HTTPS GET/HEAD transport. */ fetch(input: string, init?: RequestInit & { transport?: "direct" | "public"; }): Promise; }; ui: { showNotice(message: string): void; openNote(noteId: string, options?: PluginOpenNoteOptions): Promise; panels: { register(panel: PluginPanel): () => void; open(panelId: string, options?: PluginPanelOpenOptions): Promise; }; }; } export interface EdgeEverPlugin { activate(context: PluginContext): void | (() => void) | Promise void)>; deactivate?(): void | Promise; } export declare const definePlugin: (plugin: T) => T; export declare const defineTheme: (theme: T) => T; /** Strips unknown fields and clamps sizes so host chrome rendering stays bounded. */ export declare const normalizePluginPanelChrome: (value: PluginPanelChrome | null | undefined) => PluginPanelChrome; export declare const parseExtensionManifest: (value: unknown) => ExtensionManifest; export declare const isReservedMarketplacePluginId: (id: string) => boolean; export declare const parseMarketplaceRegistry: (value: unknown) => MarketplaceRegistry; /** * Overlay a signed community registry onto the bundled official list. * Official entries, their repository URLs, and `publisher: "edgeever"` stay * on the bundled list. A signature does not make a remote entry official. */ export declare const mergeCommunityMarketplace: (bundled: MarketplaceRegistry, community: MarketplaceRegistry | null, revokedIds: ReadonlySet) => MarketplaceRegistry; /** Revocation ids only grow. A later record may replace the reason, never remove the id. */ export declare const rememberMarketplaceRevocations: (existing: readonly MarketplaceRevocation[], incoming: readonly MarketplaceRevocation[]) => MarketplaceRevocation[];