// Plugin & Theme system types // ─── Common ────────────────────────────────────────────────── export type Disposable = { dispose: () => void }; export type MaybePromise = T | Promise; export type PluginType = 'ui-extension' | 'sidebar-app' | 'hook' | 'theme'; export type PluginStatus = 'installed' | 'enabled' | 'running' | 'disabled' | 'error'; export type ThemeVariant = 'light' | 'dark'; // ─── Manifests ─────────────────────────────────────────────── /** * Advanced theme fields ("Theme API v2"). All optional and additive - a * legacy theme that ships only `:root`/`.dark` CSS continues to work. * * When `apiVersion >= 2` (or any of `tokens`/`extends`/`derive`/`density`/ * `radii`/`typography` is present), the theme compiler runs at install time * and produces a single CSS string from the structured fields, optionally * concatenated with a hand-written `theme.css` for fine-grained overrides. */ export interface ThemeTokenSet { /** Tokens applied regardless of variant (emitted into `:root`). */ common?: Record; /** Tokens applied in light mode (emitted into `:root`). */ light?: Record; /** Tokens applied in dark mode (emitted into `.dark`). */ dark?: Record; } export type ThemeDensity = 'compact' | 'normal' | 'touch'; export interface ThemeRadii { sm?: string; md?: string; lg?: string; xl?: string; full?: string; } export interface ThemeTypography { fontSans?: string; fontMono?: string; fontDisplay?: string; baseFontSize?: string; } export interface ThemeManifest { id: string; name: string; version: string; author: string; description: string; type: 'theme'; preview?: string; variants: ThemeVariant[]; minAppVersion?: string; // ─── Advanced (Theme API v2) ───────────────────────────────── /** Theme API version. Defaults to 1 (raw-CSS only). */ apiVersion?: 1 | 2; /** Inherit tokens/CSS from another installed (or built-in) theme by id. */ extends?: string; /** Structured colour tokens - compiled into CSS at install time. */ tokens?: ThemeTokenSet; /** When true, missing standard tokens are derived (e.g. *-foreground from contrast). */ derive?: boolean; /** Default UI density preset (compact / normal / touch). */ density?: ThemeDensity; /** Border-radius scale, emitted as `--radius-*` vars. */ radii?: ThemeRadii; /** Font stacks + base size, emitted as `--font-*` vars. */ typography?: ThemeTypography; } export interface PluginManifest { id: string; name: string; version: string; author: string; description: string; type: Exclude; permissions: string[]; entrypoint: string; minAppVersion?: string; settingsSchema?: Record; /** * Bundled translations shipped inside the plugin ZIP. * Keyed by BCP-47 locale tag ("en", "de", "fr-CA", …). * The loader auto-registers these before calling activate(), * so plugins can use api.i18n.t() without calling addTranslations() first. */ locales?: Record>; /** * External origins this plugin may embed in iframes (e.g. for YouTube, * Vimeo, Jitsi). Each entry is a single CSP origin like * "https://www.youtube-nocookie.com" * "https://*.example.com:8443" * Validated at install time and merged into the host CSP `frame-src`. */ frameOrigins?: string[]; } export interface SettingFieldSchema { type: 'boolean' | 'string' | 'number' | 'select'; label: string; description?: string; default: unknown; options?: string[]; min?: number; max?: number; } // ─── Installed Items ───────────────────────────────────────── export interface InstalledTheme { id: string; name: string; version: string; author: string; description: string; preview?: string; // data: URI or blob URL css: string; // compiled CSS text - what gets injected /** * Optional "skin" CSS shipped by Theme API v2 themes that need to restyle * actual UI components (toolbars, lists, buttons, etc.) - not just colour * tokens. Injected into a separate `