wings-plugin-sdk 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +18 -0
- package/README.md +69 -2
- package/bin.mjs +21 -0
- package/build.d.ts +11 -0
- package/build.mjs +50 -0
- package/package.json +48 -4
- package/react.d.ts +6 -0
- package/react.js +11 -0
- package/wings.d.ts +219 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Josip Papež
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
|
6
|
+
associated documentation files (the "Software"), to deal in the Software without restriction, including
|
|
7
|
+
without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
|
|
9
|
+
following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial
|
|
12
|
+
portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
|
|
15
|
+
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
|
|
16
|
+
EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
17
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
18
|
+
USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,70 @@
|
|
|
1
|
-
#
|
|
1
|
+
# wings-plugin-sdk
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Types, a React hook and a build command for [Wings](https://github.com/josippapez/Wings) plugins.
|
|
4
|
+
|
|
5
|
+
Wings itself gives each plugin the `wings` object at runtime. This package doesn't contain it: it types it, and
|
|
6
|
+
bundles your TypeScript and React into the plain scripts Wings loads.
|
|
7
|
+
|
|
8
|
+
## Set up
|
|
9
|
+
|
|
10
|
+
```sh
|
|
11
|
+
npm install --save-dev wings-plugin-sdk typescript
|
|
12
|
+
npm install --save-dev react react-dom @types/react @types/react-dom # only for React pages
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
`tsconfig.json`:
|
|
16
|
+
|
|
17
|
+
```json
|
|
18
|
+
{
|
|
19
|
+
"compilerOptions": {
|
|
20
|
+
"target": "ES2022",
|
|
21
|
+
"module": "ESNext",
|
|
22
|
+
"moduleResolution": "bundler",
|
|
23
|
+
"jsx": "react-jsx",
|
|
24
|
+
"strict": true,
|
|
25
|
+
"noEmit": true,
|
|
26
|
+
"types": ["wings-plugin-sdk"]
|
|
27
|
+
},
|
|
28
|
+
"include": ["src"]
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Build
|
|
33
|
+
|
|
34
|
+
Put the code in `src/`, next to `wings-plugin.json`. Every file at the top of `src/` builds to the file of the
|
|
35
|
+
same name in the plugin folder, so `src/main.ts` becomes `main.js` and `src/sidebar.tsx` becomes `sidebar.js`.
|
|
36
|
+
Folders inside `src/` hold the modules they import.
|
|
37
|
+
|
|
38
|
+
```sh
|
|
39
|
+
npx wings-plugin build # writes main.js, sidebar.js, ...
|
|
40
|
+
npx wings-plugin build --check # fails if a built file is older than its source
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Commit the built files. Wings installs a plugin from GitHub as the files in the repo, without building it.
|
|
44
|
+
|
|
45
|
+
## React pages
|
|
46
|
+
|
|
47
|
+
A sidebar, popover or view page is your own HTML that loads its built script:
|
|
48
|
+
|
|
49
|
+
```html
|
|
50
|
+
<main id="app"></main>
|
|
51
|
+
<script src="sidebar.js"></script>
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
```tsx
|
|
55
|
+
import { useState } from "react";
|
|
56
|
+
import { createRoot } from "react-dom/client";
|
|
57
|
+
import { useBroadcast } from "wings-plugin-sdk/react";
|
|
58
|
+
|
|
59
|
+
function Sidebar() {
|
|
60
|
+
const [state, setState] = useState<unknown>(null);
|
|
61
|
+
useBroadcast((message) => setState(message));
|
|
62
|
+
return <pre>{JSON.stringify(state, null, 2)}</pre>;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
createRoot(document.getElementById("app")!).render(<Sidebar />);
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Every `wings.on*` call returns a function that removes the listener, so you can also return it from a
|
|
69
|
+
`useEffect` yourself. The API reference is
|
|
70
|
+
[plugins/README.md](https://github.com/josippapez/Wings/blob/main/plugins/README.md) in the Wings repo.
|
package/bin.mjs
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// wings-plugin build builds the plugin in this folder
|
|
3
|
+
// wings-plugin build --check fails if a built file is older than its source
|
|
4
|
+
import { relative } from "node:path";
|
|
5
|
+
import { buildPlugin, entries } from "./build.mjs";
|
|
6
|
+
|
|
7
|
+
const [command, ...flags] = process.argv.slice(2);
|
|
8
|
+
if (command !== "build") {
|
|
9
|
+
console.error("Usage: wings-plugin build [--check]");
|
|
10
|
+
process.exit(2);
|
|
11
|
+
}
|
|
12
|
+
const dir = process.cwd();
|
|
13
|
+
if (!entries(dir).length) {
|
|
14
|
+
console.error("No plugin to build here: wings-plugin build runs next to wings-plugin.json, with the code in src/.");
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
const check = flags.includes("--check");
|
|
18
|
+
const { built, stale } = await buildPlugin(dir, { check });
|
|
19
|
+
for (const { file, bytes } of built) console.log(`${relative(dir, file)} ${(bytes / 1024).toFixed(1)} KB`);
|
|
20
|
+
for (const file of stale) console.error(`${relative(dir, file)} is out of date. Run wings-plugin build and commit it.`);
|
|
21
|
+
if (stale.length) process.exit(1);
|
package/build.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** The entries of the plugin in `dir`: one for each file at the top of its `src/`. */
|
|
2
|
+
export function entries(dir: string): { src: string; out: string }[];
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Builds the plugin in `dir`: `src/main.ts` to `main.js`, `src/sidebar.tsx` to `sidebar.js`, and so on. With
|
|
6
|
+
* `check`, writes nothing and lists in `stale` the outputs that differ from what's on disk.
|
|
7
|
+
*/
|
|
8
|
+
export function buildPlugin(
|
|
9
|
+
dir: string,
|
|
10
|
+
options?: { check?: boolean; banner?: string },
|
|
11
|
+
): Promise<{ built: { file: string; bytes: number }[]; stale: string[] }>;
|
package/build.mjs
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// Builds a plugin's src/ into the files Wings loads. Every file at the top of src/ is one entry, so src/main.ts
|
|
2
|
+
// becomes main.js and src/sidebar.tsx becomes sidebar.js, next to wings-plugin.json. Folders inside src/ hold
|
|
3
|
+
// the modules they import. Wings runs plain scripts, so each file is one bundle with its imports, React too.
|
|
4
|
+
import * as esbuild from "esbuild";
|
|
5
|
+
import { existsSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
6
|
+
import { join, parse } from "node:path";
|
|
7
|
+
|
|
8
|
+
const BANNER = "// Built from src/ by wings-plugin build. Edit src/, not this file.";
|
|
9
|
+
|
|
10
|
+
/** The entries of the plugin in `dir`: `{ src, out }` for each file at the top of its src/. */
|
|
11
|
+
export function entries(dir) {
|
|
12
|
+
const src = join(dir, "src");
|
|
13
|
+
if (!existsSync(join(dir, "wings-plugin.json")) || !existsSync(src)) return [];
|
|
14
|
+
return readdirSync(src, { withFileTypes: true })
|
|
15
|
+
.filter((f) => f.isFile() && /\.(ts|tsx|js|jsx)$/.test(f.name) && !f.name.endsWith(".d.ts"))
|
|
16
|
+
.map((f) => ({ src: join(src, f.name), out: join(dir, `${parse(f.name).name}.js`) }));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Builds the plugin in `dir`. With `check`, writes nothing and returns the outputs that differ from what's on
|
|
21
|
+
* disk instead, so a release can refuse built files older than their source.
|
|
22
|
+
*/
|
|
23
|
+
export async function buildPlugin(dir, { check = false, banner = BANNER } = {}) {
|
|
24
|
+
const built = [];
|
|
25
|
+
const stale = [];
|
|
26
|
+
for (const { src, out } of entries(dir)) {
|
|
27
|
+
const result = await esbuild.build({
|
|
28
|
+
entryPoints: [src],
|
|
29
|
+
bundle: true,
|
|
30
|
+
format: "iife",
|
|
31
|
+
// WKWebView on the oldest macOS Wings supports.
|
|
32
|
+
target: "safari16",
|
|
33
|
+
jsx: "automatic",
|
|
34
|
+
define: { "process.env.NODE_ENV": '"production"' },
|
|
35
|
+
banner: { js: banner },
|
|
36
|
+
legalComments: "none",
|
|
37
|
+
minify: true,
|
|
38
|
+
write: false,
|
|
39
|
+
logLevel: "warning",
|
|
40
|
+
});
|
|
41
|
+
const code = result.outputFiles[0].text;
|
|
42
|
+
if (check) {
|
|
43
|
+
if (!existsSync(out) || readFileSync(out, "utf8") !== code) stale.push(out);
|
|
44
|
+
} else {
|
|
45
|
+
writeFileSync(out, code);
|
|
46
|
+
built.push({ file: out, bytes: code.length });
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return { built, stale };
|
|
50
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,50 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wings-plugin-sdk",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Types, React hooks and a build command for Wings plugins.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"types": "./wings.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./wings.d.ts"
|
|
11
|
+
},
|
|
12
|
+
"./react": {
|
|
13
|
+
"types": "./react.d.ts",
|
|
14
|
+
"default": "./react.js"
|
|
15
|
+
},
|
|
16
|
+
"./build": {
|
|
17
|
+
"types": "./build.d.ts",
|
|
18
|
+
"default": "./build.mjs"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"bin": {
|
|
22
|
+
"wings-plugin": "./bin.mjs"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"wings.d.ts",
|
|
26
|
+
"react.js",
|
|
27
|
+
"react.d.ts",
|
|
28
|
+
"build.mjs",
|
|
29
|
+
"build.d.ts",
|
|
30
|
+
"bin.mjs",
|
|
31
|
+
"README.md",
|
|
32
|
+
"LICENSE"
|
|
33
|
+
],
|
|
34
|
+
"repository": {
|
|
35
|
+
"type": "git",
|
|
36
|
+
"url": "git+https://github.com/josippapez/Wings.git",
|
|
37
|
+
"directory": "packages/plugin-sdk"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"esbuild": "^0.28.2"
|
|
41
|
+
},
|
|
42
|
+
"peerDependencies": {
|
|
43
|
+
"react": ">=18"
|
|
44
|
+
},
|
|
45
|
+
"peerDependenciesMeta": {
|
|
46
|
+
"react": {
|
|
47
|
+
"optional": true
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
package/react.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/// <reference path="./wings.d.ts" />
|
|
2
|
+
/**
|
|
3
|
+
* Calls `listener` with each message the plugin's other pages send with `wings.broadcast`, while the component
|
|
4
|
+
* is mounted. The latest `listener` is used, so it can read state without resubscribing.
|
|
5
|
+
*/
|
|
6
|
+
export function useBroadcast(listener: (message: unknown) => void): void;
|
package/react.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { useEffect, useRef } from "react";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Calls `listener` with each message the plugin's other pages send with `wings.broadcast`, while the component
|
|
5
|
+
* is mounted. The latest `listener` is used, so it can read state without resubscribing.
|
|
6
|
+
*/
|
|
7
|
+
export function useBroadcast(listener) {
|
|
8
|
+
const latest = useRef(listener);
|
|
9
|
+
latest.current = listener;
|
|
10
|
+
useEffect(() => wings.onBroadcast((message) => latest.current(message)), []);
|
|
11
|
+
}
|
package/wings.d.ts
ADDED
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
// Types for the Wings plugin SDK, API 1. `window.wings` exists in every plugin; this file only helps your editor.
|
|
2
|
+
// In TypeScript, add "wings-plugin-sdk" to `types` in tsconfig.json. In JavaScript, start a file with
|
|
3
|
+
// `// @ts-check` and `/// <reference types="wings-plugin-sdk" />`.
|
|
4
|
+
|
|
5
|
+
type Tone = "neutral" | "info" | "success" | "warning" | "danger" | "merged";
|
|
6
|
+
|
|
7
|
+
interface WingsPane {
|
|
8
|
+
paneId: string;
|
|
9
|
+
/** Follows `cd` in the pane. */
|
|
10
|
+
cwd: string;
|
|
11
|
+
/** What runs in the pane right now, like `zsh` or `claude`. */
|
|
12
|
+
command: string;
|
|
13
|
+
project: string;
|
|
14
|
+
/** Set while Claude Code runs in the pane. `startedAt` is when that `claude` started, in ms; a resumed session's transcript also has what came before. */
|
|
15
|
+
session: { sessionId: string; name: string | null; state: string; startedAt: number } | null;
|
|
16
|
+
/** The focused pane of the tab on screen. At most one pane has it, and none while no tab is open. */
|
|
17
|
+
focused: boolean;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface WingsTranscriptEvent {
|
|
21
|
+
sessionId: string;
|
|
22
|
+
/** The pane the session runs in. */
|
|
23
|
+
paneId: string;
|
|
24
|
+
/** The entry as Claude Code wrote it. Its `type` is one of `permissions.transcript`. */
|
|
25
|
+
entry: { type: string } & Record<string, unknown>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface WingsBadge {
|
|
29
|
+
label: string;
|
|
30
|
+
tone: Tone;
|
|
31
|
+
icon?: "pr-open" | "pr-merged" | "pr-closed" | "pr-draft";
|
|
32
|
+
/** Small icon-and-number chips after the label. Leave out zeros. */
|
|
33
|
+
counts?: { icon: "changed" | "push" | "pull"; value: number }[];
|
|
34
|
+
/** Shows a spinner in the pill. */
|
|
35
|
+
loading?: boolean;
|
|
36
|
+
title?: string;
|
|
37
|
+
subtitle?: string;
|
|
38
|
+
rows?: { label: string; value: string; tone?: Tone }[];
|
|
39
|
+
actions?: { id: string; label: string; primary?: boolean }[];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
interface WingsComment {
|
|
43
|
+
id: number;
|
|
44
|
+
replyTo: number | null;
|
|
45
|
+
/** `null` for a comment on the whole change. */
|
|
46
|
+
path: string | null;
|
|
47
|
+
/** `null` when the comment no longer matches the diff. */
|
|
48
|
+
line: number | null;
|
|
49
|
+
side: "additions" | "deletions";
|
|
50
|
+
author: string;
|
|
51
|
+
body: string;
|
|
52
|
+
createdAt: string;
|
|
53
|
+
url: string;
|
|
54
|
+
/** On the first comment of a thread: the viewer offers a reply box, and sends replies to `onReply`. */
|
|
55
|
+
canReply?: boolean;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** A reply typed in the diff viewer. `replyTo` is the id of the thread's first comment. */
|
|
59
|
+
interface WingsReply {
|
|
60
|
+
diffId: string;
|
|
61
|
+
replyTo: number;
|
|
62
|
+
body: string;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** A usage limit window. `resetsAt` is in Unix epoch seconds. */
|
|
66
|
+
interface WingsLimitWindow {
|
|
67
|
+
usedPercentage: number;
|
|
68
|
+
resetsAt: number | null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** What Claude Code last told `wings statusline`. `at` is when Wings got it, in ms. */
|
|
72
|
+
interface WingsStatusline {
|
|
73
|
+
/** Your account's 5-hour and weekly limits, from the newest session that reported them. `null` until one has. */
|
|
74
|
+
rateLimits: { fiveHour: WingsLimitWindow | null; sevenDay: WingsLimitWindow | null; at: number } | null;
|
|
75
|
+
/** By session id. Token counts are from the session's last API response. */
|
|
76
|
+
sessions: Record<
|
|
77
|
+
string,
|
|
78
|
+
{
|
|
79
|
+
/** Like `Opus 5.5 (1M context)`. */
|
|
80
|
+
model: string | null;
|
|
81
|
+
contextWindowSize: number | null;
|
|
82
|
+
usedPercentage: number | null;
|
|
83
|
+
totalInputTokens: number | null;
|
|
84
|
+
totalOutputTokens: number | null;
|
|
85
|
+
currentUsage: { inputTokens: number; outputTokens: number; cacheCreationInputTokens: number; cacheReadInputTokens: number } | null;
|
|
86
|
+
/** `expiresAt` is in Unix epoch seconds. */
|
|
87
|
+
promptCache: { warm: boolean; ttl: "5m" | "1h" | null; expiresAt: number | null } | null;
|
|
88
|
+
at: number;
|
|
89
|
+
}
|
|
90
|
+
>;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
interface WingsExecOptions {
|
|
94
|
+
cwd?: string;
|
|
95
|
+
/** 30 s by default, 5 min at most. */
|
|
96
|
+
timeoutMs?: number;
|
|
97
|
+
/** Gets each stdout and stderr line while the program runs. */
|
|
98
|
+
onOutput?: (line: string) => void;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
interface Wings {
|
|
102
|
+
// Every `on*` call returns a function that removes the listener, for cleaning up in a React effect.
|
|
103
|
+
/** Every terminal pane, on every change. */
|
|
104
|
+
onPanes(listener: (panes: WingsPane[]) => void): () => void;
|
|
105
|
+
/**
|
|
106
|
+
* A badge action was clicked. Return a promise: the button spins until it settles (up to 5 min), and a thrown
|
|
107
|
+
* error shows in the card. What it resolves to is ignored.
|
|
108
|
+
*/
|
|
109
|
+
onAction(listener: (action: { paneId: string; actionId: string }) => unknown): () => void;
|
|
110
|
+
/**
|
|
111
|
+
* Handles Claude's calls to a tool in `contributes.mcpTools`. Return a string or a JSON value; a thrown error
|
|
112
|
+
* is sent back as a failed call. `paneId` is the Wings pane Claude runs in, or `null`. Register it in `main`.
|
|
113
|
+
*/
|
|
114
|
+
onTool(name: string, handler: (input: Record<string, unknown>, context: { paneId: string | null }) => unknown): void;
|
|
115
|
+
/** Runs a command from `permissions.exec`, like `git branch --show-current`. */
|
|
116
|
+
exec(program: string, args?: string[], options?: WingsExecOptions): Promise<{ code: number | null; stdout: string; stderr: string }>;
|
|
117
|
+
/**
|
|
118
|
+
* Needs `permissions.statusline`. What Claude Code last told its status line, once `wings statusline` is your
|
|
119
|
+
* `statusLine` command: usage limits and each session's context and cache, as Claude Code reports them.
|
|
120
|
+
*/
|
|
121
|
+
statusline(): Promise<WingsStatusline>;
|
|
122
|
+
/** Claude Code transcript entries of types in `permissions.transcript`, oldest first. */
|
|
123
|
+
transcript(sessionId: string, types: string[], options?: { last?: number }): Promise<Record<string, unknown>[]>;
|
|
124
|
+
/**
|
|
125
|
+
* Each transcript entry Claude Code writes while it runs in a pane, for the types in `permissions.transcript`.
|
|
126
|
+
* Only entries written after the plugin started, or after the session started in the pane; `transcript` reads
|
|
127
|
+
* earlier ones. Entries over 256 KB are skipped.
|
|
128
|
+
*/
|
|
129
|
+
onTranscript(listener: (event: WingsTranscriptEvent) => void): () => void;
|
|
130
|
+
/** Needs `"badges"` in `contributes.ui`. `null` removes it. */
|
|
131
|
+
setBadge(paneId: string, badge: WingsBadge | null): Promise<void>;
|
|
132
|
+
/**
|
|
133
|
+
* Needs `permissions.ports`. The local TCP ports the programs in your panes listen on, that `localhost`
|
|
134
|
+
* reaches. About 20 ms a call, so poll slowly.
|
|
135
|
+
*/
|
|
136
|
+
ports(): Promise<{ paneId: string; pid: number; name: string; port: number; startedAt: number }[]>;
|
|
137
|
+
/** Opens an https URL, or an http one to localhost, 127.0.0.1 or [::1], that starts with a prefix in `permissions.openUrl`. */
|
|
138
|
+
openUrl(url: string): Promise<void>;
|
|
139
|
+
/**
|
|
140
|
+
* Needs `permissions.panes`. Opens a terminal in a new tab, or a split beside the focused pane, and resolves
|
|
141
|
+
* once the pane is in `onPanes`. `command`, like `"npm run dev"`, is typed into its shell and must start with
|
|
142
|
+
* an entry of `permissions.panes`. `cwd` must be inside one of your projects; without it the pane opens at the
|
|
143
|
+
* root of the project on screen.
|
|
144
|
+
*/
|
|
145
|
+
openPane(options?: { command?: string; cwd?: string; placement?: "tab" | "right" | "down" }): Promise<{ paneId: string }>;
|
|
146
|
+
/** Needs `permissions.panes`. Shows an open pane and moves the keyboard to it. */
|
|
147
|
+
focusPane(paneId: string): Promise<void>;
|
|
148
|
+
/**
|
|
149
|
+
* After `exec` rejected with "<program> is not installed or not on PATH": Wings asks you, installs it in a new tab
|
|
150
|
+
* and restarts the plugin once it's there. Only `az`, `gh` and `glab`, and only ones `permissions.exec` names.
|
|
151
|
+
* Resolves true once the install started (or it's already installed), false if you cancelled.
|
|
152
|
+
*/
|
|
153
|
+
installTool(program: "az" | "gh" | "glab"): Promise<boolean>;
|
|
154
|
+
/** Needs `permissions.notify`. A desktop notification, up to 3 a minute. The title is cut at 64 characters, the body at 256. */
|
|
155
|
+
notify(notification: { title: string; body?: string }): Promise<void>;
|
|
156
|
+
/** Needs `"diff"` in `contributes.ui`. Leave out `patch` to open it loading, then call `updateDiff`. */
|
|
157
|
+
openDiff(diff: { title: string; subtitle?: string; patch?: string; comments?: WingsComment[] }): Promise<{ id: string }>;
|
|
158
|
+
/** Updates a viewer opened with `openDiff`; ignored once it's closed. `{ comments }` alone keeps the patch, like after a reply. */
|
|
159
|
+
updateDiff(id: string, update: { patch: string; comments?: WingsComment[] } | { comments: WingsComment[] } | { error: string }): Promise<void>;
|
|
160
|
+
/**
|
|
161
|
+
* Needs `"diff"` in `contributes.ui`. A reply typed under a comment with `canReply` in the diff viewer. Post it,
|
|
162
|
+
* then add it with `updateDiff`. The reply box waits for the returned promise, and a thrown error shows under it
|
|
163
|
+
* with the text kept. One handler: a second call replaces the first.
|
|
164
|
+
*/
|
|
165
|
+
onReply(handler: (reply: WingsReply) => unknown): void;
|
|
166
|
+
/**
|
|
167
|
+
* POSTs `fields` (string or whole-number values, up to 64 KB) as JSON to a `permissions.post` path, as you:
|
|
168
|
+
* `gh api`, `glab api` against `host`, or `az rest` for Azure DevOps. Wings builds the command itself.
|
|
169
|
+
*/
|
|
170
|
+
post(
|
|
171
|
+
program: "gh" | "glab" | "az",
|
|
172
|
+
url: string,
|
|
173
|
+
fields: Record<string, string | number>,
|
|
174
|
+
options?: { host?: string },
|
|
175
|
+
): Promise<{ code: number | null; stdout: string; stderr: string }>;
|
|
176
|
+
/**
|
|
177
|
+
* An HTTP request to a URL under `permissions.fetch`, made by Wings. `bearer` names a secret that Wings sends
|
|
178
|
+
* as `Authorization: Bearer <secret>`. Resolves for any status; redirects aren't followed.
|
|
179
|
+
*/
|
|
180
|
+
fetch(
|
|
181
|
+
url: string,
|
|
182
|
+
init?: { method?: string; headers?: Record<string, string>; body?: string; bearer?: string },
|
|
183
|
+
): Promise<{ status: number; contentType: string | null; body: string }>;
|
|
184
|
+
/** Secrets, like API tokens, in the system keychain. `fetch` can send them, but they can't be read back. */
|
|
185
|
+
secrets: {
|
|
186
|
+
set(name: string, value: string): Promise<void>;
|
|
187
|
+
delete(name: string): Promise<void>;
|
|
188
|
+
has(name: string): Promise<boolean>;
|
|
189
|
+
};
|
|
190
|
+
/**
|
|
191
|
+
* The plugin's own storage for settings and state, since `localStorage` throws in plugin pages. Values are
|
|
192
|
+
* JSON. Keys are 1 to 128 letters, digits and `- _ . : /`, and it holds 1 MB in all. Only this plugin sees it.
|
|
193
|
+
*/
|
|
194
|
+
storage: {
|
|
195
|
+
/** The value, or `null` when the key isn't set. */
|
|
196
|
+
get(key: string): Promise<unknown>;
|
|
197
|
+
/** Refused if it would take the plugin's storage over 1 MB. */
|
|
198
|
+
set(key: string, value: unknown): Promise<void>;
|
|
199
|
+
delete(key: string): Promise<void>;
|
|
200
|
+
/** Every key, sorted. */
|
|
201
|
+
keys(): Promise<string[]>;
|
|
202
|
+
};
|
|
203
|
+
/** Short text next to a sidebar's or view's title bar button, like a running timer. `null` clears it. */
|
|
204
|
+
setSidebarLabel(sidebarId: string, label: string | null, options?: { rows?: { label: string; value: string }[]; tone?: "warning" | "danger" }): Promise<void>;
|
|
205
|
+
/**
|
|
206
|
+
* Opens one of `contributes.sidebars` or `contributes.views` by id: in the right sidebar, in a modal over Wings,
|
|
207
|
+
* or in a window of its own. `as` must be in the view's `opens`, and defaults to the first. A window that's
|
|
208
|
+
* already open comes to the front. The page gets the same `wings` object wherever it opens.
|
|
209
|
+
*/
|
|
210
|
+
openView(id: string, options?: { as?: "sidebar" | "modal" | "window" }): Promise<void>;
|
|
211
|
+
/** Closes a sidebar or view wherever it's open. */
|
|
212
|
+
closeView(id: string): Promise<void>;
|
|
213
|
+
/** Sends a JSON value, up to 64 KB, to the plugin's other pages: its main script and open sidebars, modals and windows. */
|
|
214
|
+
broadcast(message: unknown): Promise<void>;
|
|
215
|
+
/** Gets what the plugin's other pages send with `broadcast`. */
|
|
216
|
+
onBroadcast(listener: (message: unknown) => void): () => void;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
declare const wings: Wings;
|