docs(sdk): éclate et étend la documentation SDK par sujet
Remplace le README monolithique par un point d'entrée vers des pages dédiées (manifest, activation/contexte, menus, commandes/feedback, layouts React, fenêtres, services, packaging/distribution) pour couvrir #142-#144 et faciliter la navigation. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
60
docs/activation-context.md
Normal file
60
docs/activation-context.md
Normal file
@ -0,0 +1,60 @@
|
||||
# Activation And Context
|
||||
|
||||
The plugin entrypoint exports `activate(ctx)`.
|
||||
|
||||
```ts
|
||||
import type { ActivateContext, IdeAPluginModule } from "@idea/plugin-sdk";
|
||||
|
||||
export function activate(ctx: ActivateContext): void {
|
||||
ctx.logger.info("activated", { pluginId: ctx.pluginId });
|
||||
}
|
||||
|
||||
export default { activate } satisfies IdeAPluginModule;
|
||||
```
|
||||
|
||||
IdeA accepts either a named `activate` export or a default export containing
|
||||
`activate`.
|
||||
|
||||
## Activation Scope
|
||||
|
||||
`activationScope` defaults to `"app"`. App-scoped plugins activate during app
|
||||
bootstrap. Project-scoped plugins are kept pending until a project is focused,
|
||||
then activated once for the app session.
|
||||
|
||||
Choose `"project"` only when `activate(ctx)` must immediately read project
|
||||
state. Menu commands and layouts can usually stay app-scoped and check for a
|
||||
focused project at invocation/render time.
|
||||
|
||||
## Context Fields
|
||||
|
||||
- `pluginId`, `pluginDisplayName`, `version`: host-provided identity.
|
||||
- `logger`: `debug`, `info`, `warn`, `error`.
|
||||
- `subscriptions`: push disposables returned by command/layout/watch
|
||||
registrations.
|
||||
- `commands`: command registry for declared menu commands.
|
||||
- `layouts`: layout registry for declared layout types.
|
||||
- `menu`: marker for the plugin-owned menu surface.
|
||||
- `storage`: plugin-owned persistent key/value storage.
|
||||
- `services`: public host service facade for plugins declaring `ui` or `tooling`
|
||||
capabilities.
|
||||
|
||||
The context never exposes internal IdeA gateways or Tauri commands. Use
|
||||
`ctx.services` and the registration APIs instead.
|
||||
|
||||
## Disposal
|
||||
|
||||
Push every returned disposable to `ctx.subscriptions`:
|
||||
|
||||
```ts
|
||||
const disposable = ctx.commands?.registerCommand("com.example.run", run);
|
||||
if (disposable) ctx.subscriptions.push(disposable);
|
||||
```
|
||||
|
||||
IdeA disposes these handles best-effort when the plugin is unloaded or the app
|
||||
session ends.
|
||||
|
||||
## Storage
|
||||
|
||||
Use `ctx.storage` for plugin-owned counters, flags, preferences and small caches.
|
||||
Use workspace/config services only for project-owned files or configuration that
|
||||
the user expects to see in the project.
|
||||
@ -27,8 +27,8 @@ menu item -> command id -> registered command handler -> optional task -> feedba
|
||||
|
||||
Check preconditions before calling `runCommand()`:
|
||||
|
||||
- `ctx.services` exists. Plugins need the `tooling` capability for the service
|
||||
facade.
|
||||
- `ctx.services` exists. Plugins need the `ui` or `tooling` capability for the
|
||||
service facade.
|
||||
- `ctx.services.workspace.getCurrentProject()` returned a project, or the caller
|
||||
supplied a valid `projectId`.
|
||||
- Required executables, environment variables and workspace files were validated,
|
||||
|
||||
85
docs/layouts-react.md
Normal file
85
docs/layouts-react.md
Normal file
@ -0,0 +1,85 @@
|
||||
# Layouts With React
|
||||
|
||||
Plugin layouts are real React components. They can use JSX and hooks.
|
||||
|
||||
```tsx
|
||||
import type { PluginLayoutProps } from "@idea/plugin-sdk";
|
||||
import { useState } from "react";
|
||||
|
||||
export function Dashboard(props: PluginLayoutProps): React.ReactElement {
|
||||
const [localClicks, setLocalClicks] = useState(0);
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
setLocalClicks((value) => value + 1);
|
||||
props.setState({ localClicks: localClicks + 1 });
|
||||
}}
|
||||
>
|
||||
Clicked {localClicks}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Register the component for a manifest-declared layout type:
|
||||
|
||||
```ts
|
||||
ctx.layouts?.register({
|
||||
type: "hello-plugin.dashboard",
|
||||
component: Dashboard
|
||||
});
|
||||
```
|
||||
|
||||
## Props
|
||||
|
||||
- `projectId`: project hosting the layout.
|
||||
- `nodeId`: stable host node id for this layout instance.
|
||||
- `layoutType`: manifest layout `type`.
|
||||
- `state`: JSON-serializable host-persisted state.
|
||||
- `setState(next)`: replace host-persisted state.
|
||||
- `availability`: currently `"available"` while mounted.
|
||||
|
||||
Call `setState` from user actions, effects or async callbacks. Do not call it
|
||||
unconditionally during render.
|
||||
|
||||
## Host React
|
||||
|
||||
Plugins must import React normally:
|
||||
|
||||
```ts
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
```
|
||||
|
||||
At runtime IdeA resolves these bare imports to the host instance:
|
||||
|
||||
- `react`
|
||||
- `react-dom`
|
||||
- `react-dom/client`
|
||||
- `react/jsx-runtime`
|
||||
- `react/jsx-dev-runtime`
|
||||
|
||||
Do not bundle your own copy of React or ReactDOM into the plugin. Declare them
|
||||
as `peerDependencies` and `devDependencies` for local typechecking/builds.
|
||||
|
||||
```json
|
||||
{
|
||||
"peerDependencies": {
|
||||
"react": "^18.2.0 || ^19.0.0",
|
||||
"react-dom": "^18.2.0 || ^19.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^19.0.0",
|
||||
"@types/react-dom": "^19.0.0",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Build
|
||||
|
||||
Use `jsx: "react-jsx"` in `tsconfig`. A plain multi-file ESM `tsc` build is
|
||||
supported as long as every emitted file imported by `main` is included in the
|
||||
archive.
|
||||
68
docs/manifest.md
Normal file
68
docs/manifest.md
Normal file
@ -0,0 +1,68 @@
|
||||
# Manifest
|
||||
|
||||
Every plugin package has an `idea-plugin.json` file at the archive root.
|
||||
|
||||
```json
|
||||
{
|
||||
"ideaPluginManifestVersion": 1,
|
||||
"id": "com.example.hello-plugin",
|
||||
"displayName": "Hello Plugin",
|
||||
"publisher": "Example",
|
||||
"version": "0.1.0",
|
||||
"description": "Example IdeA plugin.",
|
||||
"main": "dist/index.js",
|
||||
"engines": {
|
||||
"idea": ">=0.1.0"
|
||||
},
|
||||
"trustLevel": "full",
|
||||
"capabilities": ["ui", "tooling"],
|
||||
"activationScope": "app",
|
||||
"contributes": {
|
||||
"menus": [],
|
||||
"menuItems": [],
|
||||
"layouts": [],
|
||||
"mcpServers": []
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Required Fields
|
||||
|
||||
- `ideaPluginManifestVersion`: currently `1`.
|
||||
- `id`: stable lowercase id using letters, digits, dots and dashes. It must start
|
||||
and end with an alphanumeric character.
|
||||
- `displayName`: human-readable plugin name.
|
||||
- `version`: semver version.
|
||||
- `main`: package-relative ESM entrypoint loaded by IdeA.
|
||||
- `trustLevel`: currently `"full"`.
|
||||
|
||||
## Optional Fields
|
||||
|
||||
- `description`, `publisher`.
|
||||
- `engines.idea`: host compatibility hint.
|
||||
- `activationScope`: `"app"` by default, or `"project"` when activation needs a
|
||||
focused project immediately.
|
||||
- `capabilities`: `"ui"`, `"tooling"` and/or `"mcp"`.
|
||||
|
||||
## Contributions
|
||||
|
||||
`contributes.layouts` is the only surface for plugin layouts. The same layout
|
||||
contribution can be mounted inside an IdeA layout cell or opened in a detached OS
|
||||
window. Do not add a separate manifest contribution type for windows.
|
||||
|
||||
Contribution ids are part of the runtime contract:
|
||||
|
||||
- commands can only register ids declared by `contributes.menuItems[*].command`;
|
||||
- layouts can only register `type` values declared by `contributes.layouts`;
|
||||
- `services.windows.open({ layoutType })` only accepts a layout type declared by
|
||||
the calling plugin.
|
||||
|
||||
## Validation
|
||||
|
||||
Use the SDK validator in tests or build tooling:
|
||||
|
||||
```ts
|
||||
import { assertPluginManifest } from "@idea/plugin-sdk";
|
||||
|
||||
assertPluginManifest(JSON.parse(manifestText));
|
||||
```
|
||||
56
docs/menus.md
Normal file
56
docs/menus.md
Normal file
@ -0,0 +1,56 @@
|
||||
# Menus
|
||||
|
||||
Menus are declared in the manifest and implemented by registering command
|
||||
handlers during activation.
|
||||
|
||||
```json
|
||||
{
|
||||
"contributes": {
|
||||
"menus": [
|
||||
{
|
||||
"id": "hello-plugin.menu",
|
||||
"label": "Hello Plugin",
|
||||
"topLevel": true,
|
||||
"order": 100
|
||||
}
|
||||
],
|
||||
"menuItems": [
|
||||
{
|
||||
"id": "hello-plugin.open.item",
|
||||
"targetMenuId": "hello-plugin.menu",
|
||||
"label": "Open Dashboard",
|
||||
"command": "hello-plugin.open",
|
||||
"order": 10,
|
||||
"when": "projectOpen"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Then register the command id:
|
||||
|
||||
```ts
|
||||
export function activate(ctx: ActivateContext): void {
|
||||
const disposable = ctx.commands?.registerCommand("hello-plugin.open", async () => {
|
||||
await ctx.services?.windows.open({ layoutType: "hello-plugin.dashboard" });
|
||||
});
|
||||
if (disposable) ctx.subscriptions.push(disposable);
|
||||
}
|
||||
```
|
||||
|
||||
## Rules
|
||||
|
||||
- A command can only be registered if at least one manifest menu item declares
|
||||
that exact `command` id.
|
||||
- Missing command handlers are a no-op when the user clicks the menu item.
|
||||
- A handler owns precondition checks and feedback. See
|
||||
[Commands And Feedback](commands-and-feedback.md).
|
||||
- Menu item `when` expressions are evaluated by the host; unsupported or false
|
||||
conditions hide/disable the item according to host policy.
|
||||
|
||||
## Targets
|
||||
|
||||
`targetMenuId` can refer to a plugin top-level menu id or a host menu id exposed
|
||||
by IdeA. Prefer a plugin top-level menu for plugin-specific workflows and host
|
||||
menus only when the action naturally belongs beside native actions.
|
||||
76
docs/packaging-distribution.md
Normal file
76
docs/packaging-distribution.md
Normal file
@ -0,0 +1,76 @@
|
||||
# Packaging And Distribution
|
||||
|
||||
A plugin archive is a ZIP file whose root contains `idea-plugin.json`.
|
||||
|
||||
```text
|
||||
hello-plugin-0.1.0.zip
|
||||
├── idea-plugin.json
|
||||
├── README.md
|
||||
└── dist/
|
||||
├── index.js
|
||||
├── constants.js
|
||||
└── core/
|
||||
├── layout.js
|
||||
├── storage.js
|
||||
└── workspace.js
|
||||
```
|
||||
|
||||
The manifest `main` field must point to an emitted file inside the archive:
|
||||
|
||||
```json
|
||||
{
|
||||
"main": "dist/index.js"
|
||||
}
|
||||
```
|
||||
|
||||
## Module Resolution
|
||||
|
||||
IdeA loads `main` as ESM and serves package-relative imports from the plugin
|
||||
package. This is supported:
|
||||
|
||||
```js
|
||||
import { Dashboard } from "./core/layout.js";
|
||||
```
|
||||
|
||||
For React, import bare host modules normally:
|
||||
|
||||
```js
|
||||
import { useState } from "react";
|
||||
import { jsx } from "react/jsx-runtime";
|
||||
```
|
||||
|
||||
IdeA resolves React/ReactDOM bare imports to the host instance. Other bare
|
||||
dependencies are not host-resolved. Bundle or vendor third-party dependencies
|
||||
other than React/ReactDOM into package-relative files.
|
||||
|
||||
## TypeScript Build
|
||||
|
||||
The hello plugin uses plain `tsc`:
|
||||
|
||||
```sh
|
||||
npm run build
|
||||
npm run build:hello-plugin
|
||||
```
|
||||
|
||||
For React layouts, configure JSX:
|
||||
|
||||
```json
|
||||
{
|
||||
"compilerOptions": {
|
||||
"jsx": "react-jsx",
|
||||
"module": "NodeNext",
|
||||
"moduleResolution": "NodeNext"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Archive Build
|
||||
|
||||
The SDK example can be packaged with:
|
||||
|
||||
```sh
|
||||
npm run package:hello-plugin
|
||||
```
|
||||
|
||||
The resulting archive has no wrapping parent directory and is ready for IdeA's
|
||||
plugin installer.
|
||||
68
docs/services.md
Normal file
68
docs/services.md
Normal file
@ -0,0 +1,68 @@
|
||||
# Services
|
||||
|
||||
`ctx.services` is the stable public host facade. It is available to plugins that
|
||||
declare `ui` or `tooling` capabilities.
|
||||
|
||||
```json
|
||||
{
|
||||
"capabilities": ["ui", "tooling"]
|
||||
}
|
||||
```
|
||||
|
||||
## Workspace
|
||||
|
||||
`services.workspace` reads the focused/current project, project context and
|
||||
project-owned files. Paths are relative to the project root; hosts reject
|
||||
absolute paths and traversal outside the workspace.
|
||||
|
||||
Key APIs:
|
||||
|
||||
- `getCurrentProject()`
|
||||
- `getProjectRoot(projectId?)`
|
||||
- `readProjectContext(projectId?)`
|
||||
- `updateProjectContext(content, projectId?)`
|
||||
- `readTextFile(path, projectId?)`
|
||||
- `writeTextFile(path, content, projectId?)`
|
||||
- `readBinaryFile(path, projectId?)`
|
||||
- `writeBinaryFile(path, bytes, projectId?)`
|
||||
- `listDirectory(path?, projectId?)`
|
||||
- `stat(path, projectId?)`
|
||||
- `watch(path, handler, projectId?)`
|
||||
- `queryStructure(query?)`
|
||||
|
||||
## Tasks
|
||||
|
||||
`services.tasks` launches and inspects host-managed command tasks.
|
||||
|
||||
Use `runCommand()` only after preconditions are satisfied. `ownerAgentId` must be
|
||||
a real agent id when work belongs to an agent workflow.
|
||||
|
||||
## Tooling
|
||||
|
||||
`services.tooling.diagnose()` checks executables, environment values and files
|
||||
from the host-controlled runtime.
|
||||
|
||||
## Events
|
||||
|
||||
`services.events.subscribe()` provides best-effort bounded subscriptions to
|
||||
public plugin events such as workspace file changes and background task changes.
|
||||
Dispose subscriptions when no longer needed.
|
||||
|
||||
## Config
|
||||
|
||||
`services.config` reads and updates structured JSON documents in project-owned
|
||||
locations. Use it for configuration the user expects to review/version.
|
||||
|
||||
## Terminal
|
||||
|
||||
`services.terminal` opens, reattaches and closes terminal sessions:
|
||||
|
||||
- `open({ cwd?, rows?, cols?, onData? })`
|
||||
- `reattach(sessionId, { onData? })`
|
||||
- `close(sessionId)`
|
||||
|
||||
## Windows
|
||||
|
||||
`services.windows.open({ layoutType, state? })` opens a detached OS window for
|
||||
one of the calling plugin's declared layout contributions. See
|
||||
[Windows](windows.md).
|
||||
46
docs/windows.md
Normal file
46
docs/windows.md
Normal file
@ -0,0 +1,46 @@
|
||||
# Windows
|
||||
|
||||
Plugin windows are detached OS windows that host an existing
|
||||
`contributes.layouts` layout. There is no separate window contribution type.
|
||||
|
||||
```ts
|
||||
await ctx.services?.windows.open({
|
||||
layoutType: "hello-plugin.dashboard",
|
||||
state: { openedFrom: "menu" }
|
||||
});
|
||||
```
|
||||
|
||||
## Contract
|
||||
|
||||
- `layoutType` must match a layout type declared by the calling plugin.
|
||||
- The host validates the plugin is runtime-active and the layout exists before
|
||||
opening or focusing the window.
|
||||
- Reopening the same plugin/layout pair focuses the existing window.
|
||||
- `state` is JSON-serializable initial window state. The mounted component can
|
||||
later call `setState(next)` to update its local host state.
|
||||
- The window follows IdeA's focused project, matching detached native panel
|
||||
behavior. If no project is focused, the host shows a neutral shell until one
|
||||
is focused.
|
||||
|
||||
## Return Value
|
||||
|
||||
```ts
|
||||
const win = await ctx.services.windows.open({ layoutType: "hello-plugin.dashboard" });
|
||||
|
||||
win.label;
|
||||
win.alreadyOpen;
|
||||
win.surface.layoutType;
|
||||
```
|
||||
|
||||
The returned `label` is a host-owned window identity. Treat it as opaque.
|
||||
|
||||
## Typical Menu Flow
|
||||
|
||||
```ts
|
||||
ctx.commands?.registerCommand("hello-plugin.open-dashboard", async () => {
|
||||
return ctx.services?.windows.open({
|
||||
layoutType: "hello-plugin.dashboard",
|
||||
state: { source: "menu" }
|
||||
});
|
||||
});
|
||||
```
|
||||
Reference in New Issue
Block a user