// Zumba plugin interface. Bump the version on any breaking change;
// the host refuses plugins whose manifest `api` doesn't match its major.minor.
package zumba:plugin@0.2.0;

interface types {
    record command {
        id: string,
        title: string,
        /// e.g. "cmd-alt-h"; the user's keymap wins on conflicts.
        keybinding: option<string>,
    }

    record theme {
        name: string,
        /// A gpui-component theme file (same JSON as ~/.config/zumba/themes/*.json).
        json: string,
    }

    record lsp-server {
        language: string,
        extensions: list<string>,
        command: string,
        args: list<string>,
    }

    /// An ACP agent the editor can launch (stdio, newline JSON-RPC).
    record agent {
        id: string,
        name: string,
        command: string,
        args: list<string>,
    }

    record panel {
        id: string,
        title: string,
    }

    /// Everything a plugin adds. Each kind needs its capability in the manifest;
    /// contributions without it are dropped by the host.
    record contributions {
        commands: list<command>,
        themes: list<theme>,
        lsp-servers: list<lsp-server>,
        agents: list<agent>,
        panels: list<panel>,
    }

    record context {
        /// Absolute workspace root.
        workspace: string,
        /// Workspace-relative path of the active file, if any.
        file: option<string>,
        selection: string,
        language: option<string>,
        /// The active editor's text (unsaved edits included) and the cursor as a byte offset in it.
        text: string,
        cursor: u32,
        /// Clipboard text; only for plugins with the `clipboard:read` capability.
        clipboard: option<string>,
    }

    record http-request {
        method: string,
        url: string,
        headers: list<tuple<string, string>>,
        body: option<string>,
    }

    record http-response {
        status: u16,
        /// e.g. "HTTP/1.1 200 OK"
        status-line: string,
        headers: list<tuple<string, string>>,
        body: string,
        elapsed-ms: u32,
    }

    variant action {
        notify(string),
        /// Replaces the selection in the active editor (needs `editor:write`).
        insert-text(string),
        /// Workspace-relative path.
        open-file(string),
        /// Asks the editor to re-render a panel.
        refresh-panel(string),
        /// Opens (shows) one of the plugin's panels and re-renders it.
        show-panel(string),
    }

    record button { id: string, label: string }
    record input { id: string, placeholder: string, value: string }
    record list-view { id: string, items: list<string>, selected: option<u32> }

    /// Panels are a flat column of these.
    variant node {
        heading(string),
        text(string),
        button(button),
        input(input),
        %list(list-view),
        separator,
    }

    variant event {
        click(string),
        /// (input id, value)
        change(tuple<string, string>),
        submit(tuple<string, string>),
        /// (list id, index)
        select(tuple<string, u32>),
    }
}

interface host {
    use types.{http-request, http-response};
    log: func(msg: string);
    /// Workspace-relative; needs `fs:read`.
    read-file: func(path: string) -> result<string, string>;
    /// Workspace-relative; needs `fs:write`.
    write-file: func(path: string, contents: string) -> result<_, string>;
    /// Workspace-relative paths of files under `dir` (recursive, gitignore-aware); needs `fs:read`.
    list-files: func(dir: string) -> result<list<string>, string>;
    /// http(s) only; needs `http`. Times out after 8 s; bodies over 2 MB are cut.
    fetch: func(req: http-request) -> result<http-response, string>;
}

world plugin {
    use types.{contributions, context, action, node, event};
    import host;

    export activate: func() -> contributions;
    export run-command: func(id: string, ctx: context) -> result<list<action>, string>;
    export render-panel: func(id: string) -> list<node>;
    export panel-event: func(id: string, ev: event, ctx: context) -> list<action>;
}
