deck-plugin-sdk 1.13.0 → 1.15.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/README.md +16 -2
- package/bin/deck-plugin.mjs +16 -3
- package/build-plugin.mjs +9 -5
- package/package.json +1 -1
- package/packages.mjs +44 -0
- package/permissions.json +5 -0
- package/publish.mjs +2 -1
- package/shim/bridge.js +5 -0
- package/shim/p2p.js +4 -4
- package/template/plugin.json +1 -1
- package/template/src/assets.d.ts +2 -0
- package/types/core/app/p2p.d.ts +2 -116
- package/types/core/app/p2pshare.d.ts +131 -0
- package/types/core/app/platform.d.ts +14 -0
- package/types/core/app/settingsstore.d.ts +2 -0
- package/types/core/app/teams.d.ts +122 -0
- package/types/core/bridge.d.ts +9 -2
- package/types/core/browser/bookmarksources.d.ts +9 -0
- package/types/core/browser/browser.d.ts +2 -8
- package/types/core/keys/keys.d.ts +0 -6
- package/types/core/workspaces/windows.d.ts +1 -1
- package/validate.mjs +19 -0
package/README.md
CHANGED
|
@@ -62,8 +62,22 @@ actually exposes rather than a hand-written approximation that has drifted.
|
|
|
62
62
|
Any other npm package is `npm install` and import: the build bundles it into `plugin.js`, with the
|
|
63
63
|
images, fonts and CSS it brings. Writing a plugin, in the Docs, says what cannot work that way.
|
|
64
64
|
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
Two things a build often needs are settings, which the registry can still rebuild:
|
|
66
|
+
|
|
67
|
+
- **A worker of your own:** `import code from "./job.worker.ts?worker"` bundles that file and what
|
|
68
|
+
it imports into one script and gives its text, to start with
|
|
69
|
+
`new Worker(URL.createObjectURL(new Blob([code])))`.
|
|
70
|
+
- **Files a package brings that you never use** (a library's translations into every language):
|
|
71
|
+
regular expressions in `package.json`, matched against each file's path, and each match is
|
|
72
|
+
bundled empty:
|
|
73
|
+
|
|
74
|
+
```json
|
|
75
|
+
"deckPlugin": { "leaveOut": ["/locales/(?!en-)[^/]+\\.js$"] }
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
When the build needs more than that, export esbuild plugins from `deck-plugin.config.mjs` beside
|
|
79
|
+
`plugin.json`. The registry does not run your code, so it cannot rebuild a plugin that has one:
|
|
80
|
+
it is listed as build not verified, and misses those points.
|
|
67
81
|
|
|
68
82
|
```js
|
|
69
83
|
export const esbuildPlugins = [{ name: "my-step", setup(build) { /* ... */ } }];
|
package/bin/deck-plugin.mjs
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
// Run from a plugin's own directory.
|
|
14
14
|
import { watch } from "node:fs";
|
|
15
15
|
import { cpSync, existsSync, mkdirSync, readFileSync } from "node:fs";
|
|
16
|
+
import { homedir } from "node:os";
|
|
16
17
|
import { join } from "node:path";
|
|
17
18
|
import { buildPlugin } from "../build-plugin.mjs";
|
|
18
19
|
import { publish } from "../publish.mjs";
|
|
@@ -70,15 +71,27 @@ function check() {
|
|
|
70
71
|
* writes to your real Deck ("main"), for when the plugin is done.
|
|
71
72
|
*/
|
|
72
73
|
function pluginsDir(defaultTarget) {
|
|
73
|
-
const appData = process.env.APPDATA;
|
|
74
|
-
if (!appData) die("APPDATA is not set — this command is for Windows, where Deck runs");
|
|
75
74
|
const target = targetFlag ?? defaultTarget;
|
|
76
75
|
if (target !== "test" && target !== "main") die(`--target is "test" or "main", not "${target}"`);
|
|
77
|
-
return { target, dir: join(
|
|
76
|
+
return { target, dir: join(configRoot(), target === "test" ? "Deck-Dev" : "Deck", "plugins") };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Where Deck keeps its folder, as its Rust side finds it: %APPDATA% on Windows, $XDG_CONFIG_HOME
|
|
80
|
+
* or ~/.config on Linux, ~/Library/Application Support on macOS. */
|
|
81
|
+
function configRoot() {
|
|
82
|
+
if (process.platform === "win32") return process.env.APPDATA || die("APPDATA is not set");
|
|
83
|
+
if (process.platform === "darwin") return join(homedir(), "Library", "Application Support");
|
|
84
|
+
return process.env.XDG_CONFIG_HOME || join(homedir(), ".config");
|
|
78
85
|
}
|
|
79
86
|
|
|
80
87
|
/** Copy the built plugin into a Deck's plugins folder. Deck watches it and reloads the plugin. */
|
|
81
88
|
function copyInto(manifest, defaultTarget) {
|
|
89
|
+
// A plugin that lists the systems it runs on is not put into a Deck on any other, which would not
|
|
90
|
+
// load it anyway.
|
|
91
|
+
const here = { win32: "windows", darwin: "mac" }[process.platform] ?? "linux";
|
|
92
|
+
if (Array.isArray(manifest.platforms) && manifest.platforms.length && !manifest.platforms.includes(here)) {
|
|
93
|
+
die(`${manifest.id} runs on ${manifest.platforms.join(", ")} only (its plugin.json "platforms"), and this is ${here}`);
|
|
94
|
+
}
|
|
82
95
|
const { target, dir: plugins } = pluginsDir(defaultTarget);
|
|
83
96
|
const dest = join(plugins, manifest.id);
|
|
84
97
|
mkdirSync(dest, { recursive: true });
|
package/build-plugin.mjs
CHANGED
|
@@ -17,7 +17,7 @@ import { cpSync, existsSync, mkdirSync, readdirSync, readFileSync, rmSync, write
|
|
|
17
17
|
import { createRequire } from "node:module";
|
|
18
18
|
import { dirname, join } from "node:path";
|
|
19
19
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
20
|
-
import { assetLoaders, fromPackage, packageCss, rawImports } from "./packages.mjs";
|
|
20
|
+
import { assetLoaders, fromPackage, leaveOut, packageCss, rawImports, workerImports } from "./packages.mjs";
|
|
21
21
|
|
|
22
22
|
const sdk = dirname(fileURLToPath(import.meta.url));
|
|
23
23
|
const require = createRequire(import.meta.url);
|
|
@@ -58,11 +58,15 @@ export async function buildPlugin(dir, { entry = "plugin.tsx" } = {}) {
|
|
|
58
58
|
}
|
|
59
59
|
const manifest = JSON.parse(readFileSync(manifestPath, "utf8"));
|
|
60
60
|
|
|
61
|
-
//
|
|
62
|
-
//
|
|
63
|
-
// plugin
|
|
61
|
+
// What a plugin needs beyond the plain build is a setting where it can be: `?worker` imports, and
|
|
62
|
+
// `"deckPlugin": { "leaveOut": [...] }` in package.json (packages.mjs). Those run none of the
|
|
63
|
+
// plugin's code, so the registry can rebuild it. Anything more exports `esbuildPlugins` from
|
|
64
|
+
// deck-plugin.config.mjs beside plugin.json, which runs after Deck's own, so it never takes over
|
|
65
|
+
// react or the shim, and which the registry will not run.
|
|
64
66
|
const configPath = join(dir, "deck-plugin.config.mjs");
|
|
65
67
|
const config = existsSync(configPath) ? await import(pathToFileURL(configPath).href) : {};
|
|
68
|
+
const pkgPath = join(dir, "package.json");
|
|
69
|
+
const settings = (existsSync(pkgPath) ? JSON.parse(readFileSync(pkgPath, "utf8")).deckPlugin : null) ?? {};
|
|
66
70
|
|
|
67
71
|
const sources = readdirSync(join(dir, "src"), { recursive: true })
|
|
68
72
|
.filter((f) => typeof f === "string" && /\.tsx?$/.test(f))
|
|
@@ -150,7 +154,7 @@ ${names.map((n) => `export const ${n} = __src.${n};`).join("\n")}
|
|
|
150
154
|
.map((k) => k.replace(/[/\\^$*+?.()|[\]{}]/g, "\\$&")).join("|")})$`);
|
|
151
155
|
b.onResolve({ filter }, (a) => (fromPackage(a, dir) ? undefined : { path: shims[a.path] }));
|
|
152
156
|
},
|
|
153
|
-
}, rawImports, packageCss(manifest.id, dir), ...(config.esbuildPlugins ?? [])],
|
|
157
|
+
}, rawImports, workerImports, leaveOut(settings.leaveOut), packageCss(manifest.id, dir), ...(config.esbuildPlugins ?? [])],
|
|
154
158
|
logLevel: "info",
|
|
155
159
|
});
|
|
156
160
|
|
package/package.json
CHANGED
package/packages.mjs
CHANGED
|
@@ -105,3 +105,47 @@ export const rawImports = {
|
|
|
105
105
|
b.onLoad({ filter: /.*/, namespace: "deck-raw" }, (args) => ({ contents: readFileSync(args.path), loader: args.pluginData?.as === "base64" ? "base64" : "text" }));
|
|
106
106
|
},
|
|
107
107
|
};
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* `import code from "./x.worker.ts?worker"` bundles that file and what it imports into one script
|
|
111
|
+
* and gives its text, for a Worker started from a Blob URL: a plugin is one plugin.js, so the
|
|
112
|
+
* worker cannot be a file beside it. Only esbuild runs, as for the plugin itself.
|
|
113
|
+
*/
|
|
114
|
+
export const workerImports = {
|
|
115
|
+
name: "deck-worker",
|
|
116
|
+
setup(b) {
|
|
117
|
+
b.onResolve({ filter: /\?worker$/ }, async (args) => {
|
|
118
|
+
const r = await b.resolve(args.path.slice(0, -"?worker".length), { kind: args.kind, resolveDir: args.resolveDir, importer: args.importer });
|
|
119
|
+
if (r.errors.length) return { errors: r.errors };
|
|
120
|
+
return { path: r.path, namespace: "deck-worker" };
|
|
121
|
+
});
|
|
122
|
+
b.onLoad({ filter: /.*/, namespace: "deck-worker" }, async (args) => {
|
|
123
|
+
const out = await b.esbuild.build({ entryPoints: [args.path], bundle: true, write: false, format: "iife", target: "es2022", minify: true, metafile: true });
|
|
124
|
+
return { contents: out.outputFiles[0].text, loader: "text", watchFiles: Object.keys(out.metafile.inputs) };
|
|
125
|
+
});
|
|
126
|
+
},
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* `"deckPlugin": { "leaveOut": [...] }` in package.json: regular expressions, and a file whose path
|
|
131
|
+
* (with forward slashes) matches one is bundled as an empty module. For what a package bundles but
|
|
132
|
+
* the plugin never uses, such as a library's translations into every language. Matched on load
|
|
133
|
+
* rather than on import, so a file reached by a pattern import (`import(\`./locales/${l}.js\`)`) is
|
|
134
|
+
* left out too. A setting rather than code, so the registry can still rebuild the plugin.
|
|
135
|
+
*/
|
|
136
|
+
export function leaveOut(patterns = []) {
|
|
137
|
+
if (!Array.isArray(patterns) || patterns.some((p) => typeof p !== "string" || p.length > 200)) {
|
|
138
|
+
throw new Error('package.json\'s "deckPlugin.leaveOut" must be a list of regular expressions, each under 200 characters');
|
|
139
|
+
}
|
|
140
|
+
const res = patterns.map((p) => new RegExp(p));
|
|
141
|
+
return {
|
|
142
|
+
name: "deck-leave-out",
|
|
143
|
+
setup(b) {
|
|
144
|
+
if (!res.length) return;
|
|
145
|
+
b.onLoad({ filter: /.*/ }, (a) => {
|
|
146
|
+
const path = a.path.replace(/\\/g, "/");
|
|
147
|
+
return res.some((r) => r.test(path)) ? { contents: "export default {};", loader: "js" } : undefined;
|
|
148
|
+
});
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
}
|
package/permissions.json
CHANGED
|
@@ -112,6 +112,7 @@
|
|
|
112
112
|
"fsTrash": "files",
|
|
113
113
|
"fsWrite": "files",
|
|
114
114
|
"fsWriteBytes": "files",
|
|
115
|
+
"ghAvatar": "network",
|
|
115
116
|
"ghPrLineComment": "network",
|
|
116
117
|
"ghPrThreads": "network",
|
|
117
118
|
"ghRun": "processes",
|
|
@@ -230,6 +231,7 @@
|
|
|
230
231
|
"fsHomeDir",
|
|
231
232
|
"getAutostart",
|
|
232
233
|
"isFullscreen",
|
|
234
|
+
"isWindows",
|
|
233
235
|
"languagesDir",
|
|
234
236
|
"launchUrls",
|
|
235
237
|
"mediaSetNowPlaying",
|
|
@@ -237,6 +239,7 @@
|
|
|
237
239
|
"memUsage",
|
|
238
240
|
"notifyOs",
|
|
239
241
|
"openExternal",
|
|
242
|
+
"pathKey",
|
|
240
243
|
"pickAnyFile",
|
|
241
244
|
"pickExe",
|
|
242
245
|
"pickFile",
|
|
@@ -244,8 +247,10 @@
|
|
|
244
247
|
"pickImage",
|
|
245
248
|
"pickSavePath",
|
|
246
249
|
"pickScript",
|
|
250
|
+
"platform",
|
|
247
251
|
"pluginsDir",
|
|
248
252
|
"revealInFolder",
|
|
253
|
+
"samePath",
|
|
249
254
|
"startupMarks",
|
|
250
255
|
"toggleFullscreen"
|
|
251
256
|
]
|
package/publish.mjs
CHANGED
|
@@ -18,7 +18,7 @@ import { execFileSync } from "node:child_process";
|
|
|
18
18
|
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
19
19
|
import { dirname, join } from "node:path";
|
|
20
20
|
import { fileURLToPath } from "node:url";
|
|
21
|
-
import { checkAssets, checkIcon, checkId, checkLockfile, checkManifest, checkPermissions, fileFacts, ICON_FILE, points } from "./validate.mjs";
|
|
21
|
+
import { checkAssets, checkIcon, checkId, checkLockfile, checkManifest, checkPermissions, checkPortable, fileFacts, ICON_FILE, points } from "./validate.mjs";
|
|
22
22
|
|
|
23
23
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
24
24
|
const REGISTRY = "deck-desktop/deck-registry";
|
|
@@ -83,6 +83,7 @@ export async function publish(dir, manifest, { dryRun, buildPlugin }) {
|
|
|
83
83
|
const perms = checkPermissions(manifest.permissions, readFileSync(join(dir, "plugin.js"), "utf8"), table, manifest.id);
|
|
84
84
|
errors.push(...perms.errors);
|
|
85
85
|
notes.push(...perms.notes);
|
|
86
|
+
notes.push(...checkPortable(manifest, readFileSync(join(dir, "plugin.js"), "utf8")));
|
|
86
87
|
if (errors.length) {
|
|
87
88
|
for (const e of errors) console.error(` ✗ ${e}`);
|
|
88
89
|
process.exit(1);
|
package/shim/bridge.js
CHANGED
|
@@ -121,6 +121,7 @@ export const fsTrash = (...a) => b().fsTrash(...a);
|
|
|
121
121
|
export const fsWrite = (...a) => b().fsWrite(...a);
|
|
122
122
|
export const fsWriteBytes = (...a) => b().fsWriteBytes(...a);
|
|
123
123
|
export const getAutostart = (...a) => b().getAutostart(...a);
|
|
124
|
+
export const ghAvatar = (...a) => b().ghAvatar(...a);
|
|
124
125
|
export const ghPrLineComment = (...a) => b().ghPrLineComment(...a);
|
|
125
126
|
export const ghPrThreads = (...a) => b().ghPrThreads(...a);
|
|
126
127
|
export const ghRun = (...a) => b().ghRun(...a);
|
|
@@ -160,6 +161,7 @@ export const installAgentMcp = (...a) => b().installAgentMcp(...a);
|
|
|
160
161
|
export const installAgentSkills = (...a) => b().installAgentSkills(...a);
|
|
161
162
|
export const installFromRegistry = (...a) => b().installFromRegistry(...a);
|
|
162
163
|
export const isFullscreen = (...a) => b().isFullscreen(...a);
|
|
164
|
+
export const isWindows = (...a) => b().isWindows(...a);
|
|
163
165
|
export const languagesDir = (...a) => b().languagesDir(...a);
|
|
164
166
|
export const launchFiles = (...a) => b().launchFiles(...a);
|
|
165
167
|
export const launchUrls = (...a) => b().launchUrls(...a);
|
|
@@ -175,6 +177,7 @@ export const notifyOs = (...a) => b().notifyOs(...a);
|
|
|
175
177
|
export const openExternal = (...a) => b().openExternal(...a);
|
|
176
178
|
export const openFile = (...a) => b().openFile(...a);
|
|
177
179
|
export const openTestDeck = (...a) => b().openTestDeck(...a);
|
|
180
|
+
export const pathKey = (...a) => b().pathKey(...a);
|
|
178
181
|
export const pickAnyFile = (...a) => b().pickAnyFile(...a);
|
|
179
182
|
export const pickExe = (...a) => b().pickExe(...a);
|
|
180
183
|
export const pickFile = (...a) => b().pickFile(...a);
|
|
@@ -182,6 +185,7 @@ export const pickFolder = (...a) => b().pickFolder(...a);
|
|
|
182
185
|
export const pickImage = (...a) => b().pickImage(...a);
|
|
183
186
|
export const pickSavePath = (...a) => b().pickSavePath(...a);
|
|
184
187
|
export const pickScript = (...a) => b().pickScript(...a);
|
|
188
|
+
export const platform = (...a) => b().platform(...a);
|
|
185
189
|
export const pluginsDir = (...a) => b().pluginsDir(...a);
|
|
186
190
|
export const procKill = (...a) => b().procKill(...a);
|
|
187
191
|
export const procStream = (...a) => b().procStream(...a);
|
|
@@ -197,6 +201,7 @@ export const removeLanguage = (...a) => b().removeLanguage(...a);
|
|
|
197
201
|
export const restartApp = (...a) => b().restartApp(...a);
|
|
198
202
|
export const revealInFolder = (...a) => b().revealInFolder(...a);
|
|
199
203
|
export const runScriptNow = (...a) => b().runScriptNow(...a);
|
|
204
|
+
export const samePath = (...a) => b().samePath(...a);
|
|
200
205
|
export const sessionList = (...a) => b().sessionList(...a);
|
|
201
206
|
export const setAutostart = (...a) => b().setAutostart(...a);
|
|
202
207
|
export const setDebugTargets = (...a) => b().setDebugTargets(...a);
|
package/shim/p2p.js
CHANGED
|
@@ -10,7 +10,7 @@ const t = () => {
|
|
|
10
10
|
return x;
|
|
11
11
|
};
|
|
12
12
|
|
|
13
|
-
export const
|
|
14
|
-
export const
|
|
15
|
-
export const
|
|
16
|
-
export const
|
|
13
|
+
export const myTeamId = (...a) => t().myTeamId(...a);
|
|
14
|
+
export const subscribeTeams = (...a) => t().subscribeTeams(...a);
|
|
15
|
+
export const teamChannel = (...a) => t().teamChannel(...a);
|
|
16
|
+
export const teamsView = (...a) => t().teamsView(...a);
|
package/template/plugin.json
CHANGED
package/template/src/assets.d.ts
CHANGED
|
@@ -14,3 +14,5 @@ declare module "*.wasm" { const bytes: Uint8Array; export default bytes; }
|
|
|
14
14
|
declare module "*?raw" { const text: string; export default text; }
|
|
15
15
|
// `import data from "some/file?base64"`: any file's bytes, as a base64 string.
|
|
16
16
|
declare module "*?base64" { const base64: string; export default base64; }
|
|
17
|
+
// `import code from "./job.worker.ts?worker"`: that worker, bundled, as the text of a script.
|
|
18
|
+
declare module "*?worker" { const code: string; export default code; }
|
package/types/core/app/p2p.d.ts
CHANGED
|
@@ -1,117 +1,3 @@
|
|
|
1
1
|
// GENERATED by sdk/gen-types.mjs from Deck's source — do not edit.
|
|
2
|
-
|
|
3
|
-
export
|
|
4
|
-
[k: string]: Json;
|
|
5
|
-
};
|
|
6
|
-
/** Whether someone may change what is shared, or only watch. */
|
|
7
|
-
export type Mode = "edit" | "view";
|
|
8
|
-
/** Who may change things: a default for everyone, and a choice per person that wins over it. */
|
|
9
|
-
export interface Roles {
|
|
10
|
-
def: Mode;
|
|
11
|
-
per: Record<string, Mode>;
|
|
12
|
-
}
|
|
13
|
-
/** Why a share ended for a joiner: the host stopped, vanished, removed this Deck, turned it away,
|
|
14
|
-
* or speaks another version. */
|
|
15
|
-
export type Gone = "ended" | "lost" | "removed" | "denied" | "version";
|
|
16
|
-
export interface Person {
|
|
17
|
-
id: string;
|
|
18
|
-
name: string; /** Round trip in ms, once measured. */
|
|
19
|
-
ms?: number;
|
|
20
|
-
}
|
|
21
|
-
/** What a share's bar and dialogs show. Replaced, never mutated, so React sees a change. */
|
|
22
|
-
export interface ShareView {
|
|
23
|
-
/** Who is in and connected: on the host, everyone let in; on a joiner, the host and the others. */
|
|
24
|
-
peers: Person[];
|
|
25
|
-
/** On the host: who is asking to be let in. */
|
|
26
|
-
knocking: Person[];
|
|
27
|
-
/** On a joiner: the host is there but has not let this Deck in yet. */
|
|
28
|
-
waiting: boolean;
|
|
29
|
-
/** On a joiner: no host heard from for a while (wrong code, or a network that blocks it). */
|
|
30
|
-
stuck: boolean;
|
|
31
|
-
gone: Gone | null;
|
|
32
|
-
hostName: string;
|
|
33
|
-
/** A passing line: someone on another version, or whatever the plugin `say`s. */
|
|
34
|
-
note: string;
|
|
35
|
-
roles: Roles;
|
|
36
|
-
/** Whether this Deck may change things. */
|
|
37
|
-
canEdit: boolean;
|
|
38
|
-
}
|
|
39
|
-
export interface ShareOptions {
|
|
40
|
-
/** What the share is for, e.g. "canvas-share". Decks only meet others using the same one. */
|
|
41
|
-
app: string;
|
|
42
|
-
/** The share code, which names the room (see newCode). */
|
|
43
|
-
code: string;
|
|
44
|
-
/** Optional extra secret, needed with the code to connect at all. Sharing works without one. */
|
|
45
|
-
password?: string;
|
|
46
|
-
/** The plugin's message channel, at most 12 bytes (Trystero's cap). Defaults to "msg". */
|
|
47
|
-
channel?: string;
|
|
48
|
-
/** Whether this Deck is the one sharing. */
|
|
49
|
-
host: boolean;
|
|
50
|
-
/** Shown to the others: in the host's knock, on cursors, in the list of who is here. */
|
|
51
|
-
name: string;
|
|
52
|
-
/** The plugin's own protocol version. Decks on different versions do not share. */
|
|
53
|
-
version: number;
|
|
54
|
-
/** The host's default for who may change things. Joiners may until the host says otherwise. */
|
|
55
|
-
mode?: Mode;
|
|
56
|
-
}
|
|
57
|
-
export interface ShareHandlers<T> {
|
|
58
|
-
/** A plugin message, only ever from someone let in (and the host). Check `mayEdit(peer)` before
|
|
59
|
-
* applying a change: a view-only person's edits are to be dropped by every Deck. */
|
|
60
|
-
message: (msg: T, peer: string) => void;
|
|
61
|
-
/** On the host: someone was just let in. Send them what they need to start, e.g. the board. */
|
|
62
|
-
welcome?: (peer: string) => void;
|
|
63
|
-
/** Someone left or was removed: forget what was kept for them (a cursor). */
|
|
64
|
-
left?: (peer: string) => void;
|
|
65
|
-
}
|
|
66
|
-
export interface P2pShare<T> {
|
|
67
|
-
readonly host: boolean;
|
|
68
|
-
/** Send a plugin message to everyone let in, or to one of them. Sends nothing before this Deck is
|
|
69
|
-
* let in. Settles once handed to the connection, so awaiting it paces the sender. */
|
|
70
|
-
send(msg: T, to?: string): Promise<void>;
|
|
71
|
-
/** Whether a peer may change things: the host always, anyone else by the roles. */
|
|
72
|
-
mayEdit(peer: string): boolean;
|
|
73
|
-
/** The host: let someone in, or turn them away. */
|
|
74
|
-
allow(peer: string): void;
|
|
75
|
-
deny(peer: string): void;
|
|
76
|
-
/** The host: remove someone who was let in. They cannot come back with the same Deck. */
|
|
77
|
-
kick(peer: string): void;
|
|
78
|
-
/** The host: everyone's default, and one person's own choice (`null` follows the default). */
|
|
79
|
-
setDefault(mode: Mode): void;
|
|
80
|
-
setPerson(peer: string, mode: Mode | null): void;
|
|
81
|
-
/** Show a passing line in the view for a few seconds. */
|
|
82
|
-
say(note: string): void;
|
|
83
|
-
/** Leave. With `announce` (the host ending on purpose) everyone is told first, so they end at
|
|
84
|
-
* once rather than when the connection times out. Safe to call twice. */
|
|
85
|
-
close(announce?: boolean): Promise<void>;
|
|
86
|
-
/** For useSyncExternalStore. */
|
|
87
|
-
subscribe(fn: () => void): () => void;
|
|
88
|
-
getView(): ShareView;
|
|
89
|
-
}
|
|
90
|
-
/** The part of a Trystero room a share uses: a seam, so the check can run a share without a network. */
|
|
91
|
-
export interface RoomLike {
|
|
92
|
-
/** This Deck's id in the room; Trystero's own is `selfId()`. */
|
|
93
|
-
selfId?: string;
|
|
94
|
-
makeAction(name: string, config: {
|
|
95
|
-
onMessage: (data: any, ctx: {
|
|
96
|
-
peerId: string;
|
|
97
|
-
}) => void;
|
|
98
|
-
}): {
|
|
99
|
-
send(data: any, options?: {
|
|
100
|
-
target?: string | string[];
|
|
101
|
-
}): Promise<void>;
|
|
102
|
-
};
|
|
103
|
-
ping(peer: string): Promise<number>;
|
|
104
|
-
leave(): Promise<void>;
|
|
105
|
-
getPeers(): Record<string, unknown>;
|
|
106
|
-
onPeerJoin: ((peer: string) => void) | null;
|
|
107
|
-
onPeerLeave: ((peer: string) => void) | null;
|
|
108
|
-
}
|
|
109
|
-
/** This Deck's id in every share: the one the others know it by. The same for the whole session,
|
|
110
|
-
* so someone let in who rejoins after a dropped connection is let straight back in. */
|
|
111
|
-
export declare function selfId(): string;
|
|
112
|
-
/** Start sharing (`host`), or ask to join someone's share. */
|
|
113
|
-
export declare function share<T extends Json>(opts: ShareOptions, on: ShareHandlers<T>, room?: RoomLike): P2pShare<T>;
|
|
114
|
-
/** A share code: 16 random characters in four groups, easy to read out or paste. */
|
|
115
|
-
export declare function newCode(random?: (n: number) => Uint8Array): string;
|
|
116
|
-
/** A pasted code, tidied: spaces dropped, case folded. Empty when it is not a code. */
|
|
117
|
-
export declare function cleanCode(text: string): string;
|
|
2
|
+
export type { Json } from "./p2pshare";
|
|
3
|
+
export { teamChannel, teamsView, subscribeTeams, myTeamId, type Member, type TeamView, type TeamChannel, type ChannelHandlers } from "./teams";
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
// GENERATED by sdk/gen-types.mjs from Deck's source — do not edit.
|
|
2
|
+
/** What can travel: anything JSON can carry. */
|
|
3
|
+
export type Json = string | number | boolean | null | Json[] | {
|
|
4
|
+
[k: string]: Json;
|
|
5
|
+
};
|
|
6
|
+
/** Whether someone may change what is shared, or only watch. */
|
|
7
|
+
export type Mode = "edit" | "view";
|
|
8
|
+
/** Who may change things: a default for everyone, and a choice per person that wins over it. */
|
|
9
|
+
export interface Roles {
|
|
10
|
+
def: Mode;
|
|
11
|
+
per: Record<string, Mode>;
|
|
12
|
+
}
|
|
13
|
+
/** Why a share ended for a joiner: the host stopped, vanished, removed this Deck, turned it away,
|
|
14
|
+
* or speaks another version. */
|
|
15
|
+
export type Gone = "ended" | "lost" | "removed" | "denied" | "version";
|
|
16
|
+
export interface Person {
|
|
17
|
+
id: string;
|
|
18
|
+
name: string; /** Round trip in ms, once measured. */
|
|
19
|
+
ms?: number;
|
|
20
|
+
}
|
|
21
|
+
/** What a share's bar and dialogs show. Replaced, never mutated, so React sees a change. */
|
|
22
|
+
export interface ShareView {
|
|
23
|
+
/** Who is in and connected: on the host, everyone let in; on a joiner, the host and the others. */
|
|
24
|
+
peers: Person[];
|
|
25
|
+
/** On the host: who is asking to be let in. */
|
|
26
|
+
knocking: Person[];
|
|
27
|
+
/** On a joiner: the host is there but has not let this Deck in yet. */
|
|
28
|
+
waiting: boolean;
|
|
29
|
+
/** On a joiner: no host heard from for a while (wrong code, or a network that blocks it). */
|
|
30
|
+
stuck: boolean;
|
|
31
|
+
gone: Gone | null;
|
|
32
|
+
hostName: string;
|
|
33
|
+
/** A passing line: someone on another version, or whatever the plugin `say`s. */
|
|
34
|
+
note: string;
|
|
35
|
+
roles: Roles;
|
|
36
|
+
/** Whether this Deck may change things. */
|
|
37
|
+
canEdit: boolean;
|
|
38
|
+
}
|
|
39
|
+
export interface ShareOptions {
|
|
40
|
+
/** What the share is for, e.g. "canvas-share". Decks only meet others using the same one. */
|
|
41
|
+
app: string;
|
|
42
|
+
/** The share code, which names the room (see newCode). */
|
|
43
|
+
code: string;
|
|
44
|
+
/** Optional extra secret, needed with the code to connect at all. Sharing works without one. */
|
|
45
|
+
password?: string;
|
|
46
|
+
/** The plugin's message channel, at most 12 bytes (Trystero's cap). Defaults to "msg". */
|
|
47
|
+
channel?: string;
|
|
48
|
+
/** Whether this Deck is the one sharing. */
|
|
49
|
+
host: boolean;
|
|
50
|
+
/** Shown to the others: in the host's knock, on cursors, in the list of who is here. */
|
|
51
|
+
name: string;
|
|
52
|
+
/** The plugin's own protocol version. Decks on different versions do not share. */
|
|
53
|
+
version: number;
|
|
54
|
+
/** The host's default for who may change things. Joiners may until the host says otherwise. */
|
|
55
|
+
mode?: Mode;
|
|
56
|
+
/**
|
|
57
|
+
* The host: who is let in, decided here rather than by a knock. Asked for each Deck that says
|
|
58
|
+
* hello: true lets it straight in, false turns it away (no knock at all). For a team share
|
|
59
|
+
* (teams.ts): only a Deck checked as a current member of the team, so a code and password that
|
|
60
|
+
* reached anyone else, or a member removed since, let no one in.
|
|
61
|
+
*/
|
|
62
|
+
admit?: (peer: string) => boolean;
|
|
63
|
+
/**
|
|
64
|
+
* A joiner: who may be the host. Without it, the first Deck that says it hosts is believed. For a
|
|
65
|
+
* team share: only the member who sent the invite.
|
|
66
|
+
*/
|
|
67
|
+
trustHost?: (peer: string) => boolean;
|
|
68
|
+
/** The largest message taken from anyone, in bytes (12 MB unless set). */
|
|
69
|
+
maxBytes?: number;
|
|
70
|
+
}
|
|
71
|
+
export interface ShareHandlers<T> {
|
|
72
|
+
/** A plugin message, only ever from someone let in (and the host). Check `mayEdit(peer)` before
|
|
73
|
+
* applying a change: a view-only person's edits are to be dropped by every Deck. */
|
|
74
|
+
message: (msg: T, peer: string) => void;
|
|
75
|
+
/** On the host: someone was just let in. Send them what they need to start, e.g. the board. */
|
|
76
|
+
welcome?: (peer: string) => void;
|
|
77
|
+
/** Someone left or was removed: forget what was kept for them (a cursor). */
|
|
78
|
+
left?: (peer: string) => void;
|
|
79
|
+
}
|
|
80
|
+
export interface P2pShare<T> {
|
|
81
|
+
readonly host: boolean;
|
|
82
|
+
/** Send a plugin message to everyone let in, or to one of them. Sends nothing before this Deck is
|
|
83
|
+
* let in. Settles once handed to the connection, so awaiting it paces the sender. */
|
|
84
|
+
send(msg: T, to?: string): Promise<void>;
|
|
85
|
+
/** Whether a peer may change things: the host always, anyone else by the roles. */
|
|
86
|
+
mayEdit(peer: string): boolean;
|
|
87
|
+
/** The host: let someone in, or turn them away. */
|
|
88
|
+
allow(peer: string): void;
|
|
89
|
+
deny(peer: string): void;
|
|
90
|
+
/** The host: remove someone who was let in. They cannot come back with the same Deck. */
|
|
91
|
+
kick(peer: string): void;
|
|
92
|
+
/** The host: everyone's default, and one person's own choice (`null` follows the default). */
|
|
93
|
+
setDefault(mode: Mode): void;
|
|
94
|
+
setPerson(peer: string, mode: Mode | null): void;
|
|
95
|
+
/** Show a passing line in the view for a few seconds. */
|
|
96
|
+
say(note: string): void;
|
|
97
|
+
/** Leave. With `announce` (the host ending on purpose) everyone is told first, so they end at
|
|
98
|
+
* once rather than when the connection times out. Safe to call twice. */
|
|
99
|
+
close(announce?: boolean): Promise<void>;
|
|
100
|
+
/** For useSyncExternalStore. */
|
|
101
|
+
subscribe(fn: () => void): () => void;
|
|
102
|
+
getView(): ShareView;
|
|
103
|
+
}
|
|
104
|
+
/** The part of a Trystero room a share uses: a seam, so the check can run a share without a network. */
|
|
105
|
+
export interface RoomLike {
|
|
106
|
+
/** This Deck's id in the room; Trystero's own is `selfId()`. */
|
|
107
|
+
selfId?: string;
|
|
108
|
+
makeAction(name: string, config: {
|
|
109
|
+
onMessage: (data: any, ctx: {
|
|
110
|
+
peerId: string;
|
|
111
|
+
}) => void;
|
|
112
|
+
}): {
|
|
113
|
+
send(data: any, options?: {
|
|
114
|
+
target?: string | string[];
|
|
115
|
+
}): Promise<void>;
|
|
116
|
+
};
|
|
117
|
+
ping(peer: string): Promise<number>;
|
|
118
|
+
leave(): Promise<void>;
|
|
119
|
+
getPeers(): Record<string, unknown>;
|
|
120
|
+
onPeerJoin: ((peer: string) => void) | null;
|
|
121
|
+
onPeerLeave: ((peer: string) => void) | null;
|
|
122
|
+
}
|
|
123
|
+
/** This Deck's id in every share: the one the others know it by. The same for the whole session,
|
|
124
|
+
* so someone let in who rejoins after a dropped connection is let straight back in. */
|
|
125
|
+
export declare function selfId(): string;
|
|
126
|
+
/** Start sharing (`host`), or ask to join someone's share. */
|
|
127
|
+
export declare function share<T extends Json>(opts: ShareOptions, on: ShareHandlers<T>, room?: RoomLike): P2pShare<T>;
|
|
128
|
+
/** A share code: 16 random characters in four groups, easy to read out or paste. */
|
|
129
|
+
export declare function newCode(random?: (n: number) => Uint8Array): string;
|
|
130
|
+
/** A pasted code, tidied: spaces dropped, case folded. Empty when it is not a code. */
|
|
131
|
+
export declare function cleanCode(text: string): string;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// GENERATED by sdk/gen-types.mjs from Deck's source — do not edit.
|
|
2
|
+
export type Platform = "windows" | "linux" | "mac";
|
|
3
|
+
/** Which system Deck runs on. A function, as everything plugins reach through the bridge is. */
|
|
4
|
+
export declare const platform: () => Platform;
|
|
5
|
+
export declare const isWindows: () => boolean;
|
|
6
|
+
/** Whether two paths name the same file: without regard to case on Windows, whose file system
|
|
7
|
+
* ignores it, and exactly elsewhere, where `Readme.md` and `README.md` are two files. */
|
|
8
|
+
export declare function samePath(a: string, b: string): boolean;
|
|
9
|
+
/** A path as a key that `samePath` would match: lowercased on Windows only. */
|
|
10
|
+
export declare function pathKey(p: string): string;
|
|
11
|
+
/** The system's name, for words shown to the user. */
|
|
12
|
+
export declare const osName: () => string;
|
|
13
|
+
/** The menu item that shows a file in the system's file manager. */
|
|
14
|
+
export declare const revealLabel: () => string;
|
|
@@ -23,4 +23,6 @@ export declare function stepZoom(dir: -1 | 0 | 1): void;
|
|
|
23
23
|
/** Current settings for non-React callers (the palette's providers). */
|
|
24
24
|
export declare const getSettings: () => Settings;
|
|
25
25
|
export declare function userName(): Promise<string>;
|
|
26
|
+
/** Told after every change to the settings, from anywhere (Settings, a file edited elsewhere). */
|
|
27
|
+
export declare const subscribeSettings: (cb: () => void) => () => void;
|
|
26
28
|
export declare function useSettings(): Settings;
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
// GENERATED by sdk/gen-types.mjs from Deck's source — do not edit.
|
|
2
|
+
import { share, type Json, type RoomLike, type P2pShare } from "./p2pshare";
|
|
3
|
+
export type Member = {
|
|
4
|
+
id: string;
|
|
5
|
+
pub: string;
|
|
6
|
+
name: string;
|
|
7
|
+
};
|
|
8
|
+
/** A team's members, as the owner signed them. */
|
|
9
|
+
export type MemberList = {
|
|
10
|
+
team: string;
|
|
11
|
+
name: string;
|
|
12
|
+
owner: string;
|
|
13
|
+
ownerPub: string;
|
|
14
|
+
rev: number;
|
|
15
|
+
members: Member[];
|
|
16
|
+
sig: string; /** The owner ended the team. */
|
|
17
|
+
ended?: boolean;
|
|
18
|
+
};
|
|
19
|
+
/** What this Deck keeps for a team. The key is secret: it names and encrypts the room. */
|
|
20
|
+
export type Team = {
|
|
21
|
+
id: string;
|
|
22
|
+
key: string;
|
|
23
|
+
list: MemberList; /** Each member's name as last heard from them: theirs to change. */
|
|
24
|
+
seen?: Record<string, string>; /** When this Deck heard the team was ended: kept as a tombstone until ENDED_DAYS after. */
|
|
25
|
+
endedAt?: number;
|
|
26
|
+
};
|
|
27
|
+
/** A team as the Teams page shows it. */
|
|
28
|
+
export interface TeamView {
|
|
29
|
+
id: string;
|
|
30
|
+
name: string;
|
|
31
|
+
owner: boolean;
|
|
32
|
+
ownerId: string;
|
|
33
|
+
members: (Member & {
|
|
34
|
+
me: boolean;
|
|
35
|
+
online: boolean;
|
|
36
|
+
})[];
|
|
37
|
+
}
|
|
38
|
+
export interface TeamChannel<T> {
|
|
39
|
+
/** Send to every member online, or to one (`to`: a member id). Nothing goes to anyone else. */
|
|
40
|
+
send(msg: T, to?: string): Promise<void>;
|
|
41
|
+
/** The members connected and checked right now, this Deck not included. */
|
|
42
|
+
online(): Member[];
|
|
43
|
+
close(): void;
|
|
44
|
+
}
|
|
45
|
+
export interface ChannelHandlers<T> {
|
|
46
|
+
message: (msg: T, from: Member) => void;
|
|
47
|
+
/** A member connected and was checked: send them what they need to catch up. */
|
|
48
|
+
joined?: (m: Member) => void;
|
|
49
|
+
left?: (m: Member) => void;
|
|
50
|
+
}
|
|
51
|
+
/** Where teams keep things and how they reach the network: a seam, so the check runs without either. */
|
|
52
|
+
export interface TeamDeps {
|
|
53
|
+
read(name: string): Promise<string>;
|
|
54
|
+
write(name: string, text: string): Promise<void>;
|
|
55
|
+
/** This Deck's name, as the others see it. */
|
|
56
|
+
name(): Promise<string>;
|
|
57
|
+
/** A team's room: Trystero's, or a fake one. */
|
|
58
|
+
room(key: string, id: string): RoomLike;
|
|
59
|
+
/** Told when this Deck's name may have changed (the settings changed). */
|
|
60
|
+
watchName(cb: () => void): void;
|
|
61
|
+
share: typeof share;
|
|
62
|
+
}
|
|
63
|
+
/** For the check: replace the storage, the name and the network. */
|
|
64
|
+
export declare function _setTeamDeps(d: Partial<TeamDeps>): void;
|
|
65
|
+
/** A short code for an identity, to read out and compare: whoever invites shows theirs, whoever joins sees it. */
|
|
66
|
+
export declare const fingerprint: (id: string) => string;
|
|
67
|
+
/** A member's id: the first half of the hash of their public key. */
|
|
68
|
+
export declare function idOf(pub: string): Promise<string>;
|
|
69
|
+
/** Whether a list is the owner's own: signed by them, for this team, with ids that match the keys. */
|
|
70
|
+
export declare function validList(l: MemberList, ownerPub?: string): Promise<boolean>;
|
|
71
|
+
/** A plugin's line to a team: its messages, only to and from checked members. */
|
|
72
|
+
export declare function teamChannel<T extends Json>(teamId: string, app: string, on: ChannelHandlers<T>): TeamChannel<T>;
|
|
73
|
+
export declare function teamsView(): TeamView[];
|
|
74
|
+
export declare function subscribeTeams(fn: () => void): () => void;
|
|
75
|
+
/** This Deck's member id. */
|
|
76
|
+
export declare const myTeamId: () => Promise<string>;
|
|
77
|
+
/** Start a team, with this Deck as its owner and only member. */
|
|
78
|
+
export declare function createTeam(name: string): Promise<string>;
|
|
79
|
+
/** The owner takes someone off the team. */
|
|
80
|
+
export declare function removeMember(teamId: string, memberId: string): Promise<void>;
|
|
81
|
+
/**
|
|
82
|
+
* Leave a team: this Deck forgets it, and the others still list it until the owner removes it. The
|
|
83
|
+
* owner leaving ends the team for everyone: a last signed list says so, sent to those online now
|
|
84
|
+
* and, through the tombstones, to the others when they come back.
|
|
85
|
+
*/
|
|
86
|
+
export declare function leaveTeam(teamId: string): Promise<void>;
|
|
87
|
+
type InviteMsg = {
|
|
88
|
+
t: "join";
|
|
89
|
+
id: string;
|
|
90
|
+
pub: string;
|
|
91
|
+
name: string;
|
|
92
|
+
} | {
|
|
93
|
+
t: "team";
|
|
94
|
+
team: Team;
|
|
95
|
+
};
|
|
96
|
+
/**
|
|
97
|
+
* The owner invites: a code to give someone, and a share where they knock and are let in, as a
|
|
98
|
+
* live share's people are (`share.allow`). Whoever is let in is added to the team and sent it.
|
|
99
|
+
*/
|
|
100
|
+
export declare function inviteToTeam(teamId: string): Promise<{
|
|
101
|
+
code: string;
|
|
102
|
+
share: P2pShare<InviteMsg>;
|
|
103
|
+
fingerprint: string;
|
|
104
|
+
joined: Promise<string>;
|
|
105
|
+
}>;
|
|
106
|
+
/**
|
|
107
|
+
* Join a team with the code its owner gave you: knock, and once let in, say who you are and take
|
|
108
|
+
* the team the owner sends. Resolves with the team's id.
|
|
109
|
+
*/
|
|
110
|
+
/** A team offered by an invite: who owns it, to compare, and keeping it. */
|
|
111
|
+
export interface TeamOffer {
|
|
112
|
+
id: string;
|
|
113
|
+
name: string;
|
|
114
|
+
owner: string;
|
|
115
|
+
fingerprint: string;
|
|
116
|
+
keep: () => Promise<string>;
|
|
117
|
+
}
|
|
118
|
+
export declare function joinTeam(code: string): Promise<{
|
|
119
|
+
share: P2pShare<InviteMsg>;
|
|
120
|
+
done: Promise<TeamOffer>;
|
|
121
|
+
}>;
|
|
122
|
+
export {};
|
package/types/core/bridge.d.ts
CHANGED
|
@@ -672,6 +672,11 @@ export interface TrackMeta {
|
|
|
672
672
|
}
|
|
673
673
|
export declare const readTrackMeta: (paths: string[]) => Promise<TrackMeta[]>;
|
|
674
674
|
export { convertFileSrc } from "@tauri-apps/api/core";
|
|
675
|
+
export { platform } from "./app/platform";
|
|
676
|
+
export { isWindows } from "./app/platform";
|
|
677
|
+
export { samePath } from "./app/platform";
|
|
678
|
+
export { pathKey } from "./app/platform";
|
|
679
|
+
export type { Platform } from "./app/platform";
|
|
675
680
|
/** Start a sign-in. Returns the consent URL to open in the browser. */
|
|
676
681
|
export declare const googleSignIn: () => Promise<string>;
|
|
677
682
|
/** A valid access token, refreshed if needed. Rejects when not connected. */
|
|
@@ -1077,6 +1082,8 @@ export declare const gitAiCommitMsg: (dir: string, command: string, args: string
|
|
|
1077
1082
|
export declare const ghRun: (dir: string, args: string[]) => Promise<string>;
|
|
1078
1083
|
/** Pull request N's review threads, as GitHub's GraphQL JSON (parsed by prs.ts parseThreads). */
|
|
1079
1084
|
export declare const ghPrThreads: (dir: string, number: number) => Promise<string>;
|
|
1085
|
+
/** The GitHub profile picture's address of whoever commits as `email` in this repo, "" when none. */
|
|
1086
|
+
export declare const ghAvatar: (dir: string, email: string) => Promise<string>;
|
|
1080
1087
|
/** Comment on one line of a PR's diff: side "RIGHT" is the new version, "LEFT" the old. */
|
|
1081
1088
|
export declare const ghPrLineComment: (dir: string, number: number, commit: string, path: string, line: number, side: "LEFT" | "RIGHT", body: string) => Promise<string>;
|
|
1082
1089
|
export declare const ghThreadReply: (dir: string, thread: string, body: string) => Promise<string>;
|
|
@@ -1084,8 +1091,8 @@ export declare const ghThreadResolve: (dir: string, thread: string, resolve: boo
|
|
|
1084
1091
|
/** The GitLab CLI, `glab mr …` only. Rejects with "glab-missing" when glab is not installed. */
|
|
1085
1092
|
export declare const glabRun: (dir: string, args: string[]) => Promise<string>;
|
|
1086
1093
|
/**
|
|
1087
|
-
* One GitLab REST request about this repo's merge requests (`projects/:id/merge_requests
|
|
1088
|
-
* `
|
|
1094
|
+
* One GitLab REST request about this repo's merge requests (`projects/:id/merge_requests…`, `user`,
|
|
1095
|
+
* or `avatar?email=` for someone's picture). `fields` go as strings; `json` values must be objects or arrays.
|
|
1089
1096
|
*/
|
|
1090
1097
|
export declare const glabApi: (dir: string, method: "GET" | "POST" | "PUT", path: string, fields?: [string, string][], json?: Record<string, object>) => Promise<string>;
|
|
1091
1098
|
/** The files that differ between two revisions. */
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// GENERATED by sdk/gen-types.mjs from Deck's source — do not edit.
|
|
2
|
+
/** A Chromium browser's bookmarks on this machine, one per profile that has any. */
|
|
3
|
+
export interface BookmarkSource {
|
|
4
|
+
browser: string;
|
|
5
|
+
profile: string;
|
|
6
|
+
path: string;
|
|
7
|
+
}
|
|
8
|
+
/** Every profile of Chrome, Edge and Brave with a Bookmarks file. A browser not installed is no error. */
|
|
9
|
+
export declare function findBookmarkSources(): Promise<BookmarkSource[]>;
|
|
@@ -203,14 +203,8 @@ export declare function forgetDownload(key: string): void;
|
|
|
203
203
|
export declare function clearDownloads(): void;
|
|
204
204
|
export declare const browserBookmarks: () => Bookmarks;
|
|
205
205
|
export declare function setBookmarks(next: Bookmarks): void;
|
|
206
|
-
|
|
207
|
-
export
|
|
208
|
-
browser: string;
|
|
209
|
-
profile: string;
|
|
210
|
-
path: string;
|
|
211
|
-
}
|
|
212
|
-
/** Every profile of Chrome, Edge and Brave with a Bookmarks file. A browser not installed is no error. */
|
|
213
|
-
export declare function findBookmarkSources(): Promise<BookmarkSource[]>;
|
|
206
|
+
import type { BookmarkSource } from "./bookmarksources";
|
|
207
|
+
export { findBookmarkSources, type BookmarkSource } from "./bookmarksources";
|
|
214
208
|
/** Import one source's bookmarks as a "From Chrome" folder in each root. Returns how many pages. */
|
|
215
209
|
export declare function importBookmarks(src: BookmarkSource): Promise<number>;
|
|
216
210
|
/** Every bookmark to a bookmarks HTML file, which any browser imports, and Deck too. How many pages. */
|
|
@@ -89,10 +89,4 @@ export declare function toMonaco(chord: string, mods: {
|
|
|
89
89
|
Shift: number;
|
|
90
90
|
WinCtrl: number;
|
|
91
91
|
}, codes: Record<string, number>): number | null;
|
|
92
|
-
/**
|
|
93
|
-
* Deck's built-in bindings.
|
|
94
|
-
*
|
|
95
|
-
* Plugin bindings are not here — they arrive at load time through the `keybindings` export and
|
|
96
|
-
* are concatenated on, so a disabled plugin's chords leave the table with it.
|
|
97
|
-
*/
|
|
98
92
|
export declare const DEFAULT_BINDINGS: readonly KeyBinding[];
|
|
@@ -32,7 +32,7 @@ export declare function activeElement(): Element | null;
|
|
|
32
32
|
/** Run `fn` for every Deck window, now and as floats open; what it returns runs when that window goes, or on the returned unsubscribe. */
|
|
33
33
|
export declare function onEachWindow(fn: Hook): () => void;
|
|
34
34
|
/** A floating window opened (floatwin.ts), its label known: every hook runs for it. */
|
|
35
|
-
export declare function addWindow(win: Window): void;
|
|
35
|
+
export declare function addWindow(win: Window, label?: string): void;
|
|
36
36
|
/** A floating window is going: what each hook installed in it is undone. */
|
|
37
37
|
export declare function removeWindow(win: Window): void;
|
|
38
38
|
/**
|
package/validate.mjs
CHANGED
|
@@ -44,6 +44,10 @@ export function checkManifest(m) {
|
|
|
44
44
|
if (typeof m.version !== "string" || !SEMVER.test(m.version)) errors.push("`version` must be a version, such as \"1.0.0\"");
|
|
45
45
|
if (typeof m.minDeck !== "string" || !SEMVER.test(m.minDeck)) errors.push("`minDeck` must say which Deck it needs, such as \"1.8.0\"");
|
|
46
46
|
if (m.color !== undefined && !/^#[0-9a-f]{6}$/i.test(m.color)) errors.push("`color` must be #rrggbb");
|
|
47
|
+
// Where it runs: absent means every system. Deck will not load or install it on any other.
|
|
48
|
+
if (m.platforms !== undefined && (!Array.isArray(m.platforms) || !m.platforms.length || !m.platforms.every((p) => ["windows", "linux", "mac"].includes(p)))) {
|
|
49
|
+
errors.push("`platforms` lists the systems it runs on, from \"windows\", \"linux\" and \"mac\"; leave it out to run on all of them");
|
|
50
|
+
}
|
|
47
51
|
if (typeof m.icon === "string" && m.icon.endsWith(".svg") && m.icon !== ICON_FILE) errors.push(`an icon of its own must be named ${ICON_FILE}`);
|
|
48
52
|
return errors;
|
|
49
53
|
}
|
|
@@ -152,6 +156,21 @@ export function checkPermissions(declared, bundle, table, id) {
|
|
|
152
156
|
return { errors, notes, used: [...needs.keys()] };
|
|
153
157
|
}
|
|
154
158
|
|
|
159
|
+
/**
|
|
160
|
+
* Windows-only habits in a plugin that says it runs on Linux too (no "platforms", or a list naming
|
|
161
|
+
* "linux"): a path joined with a backslash, a program named with ".exe". Notes, not errors, and
|
|
162
|
+
* none for a bundle that asks isWindows(), which keeps a Windows branch beside a Linux one.
|
|
163
|
+
*/
|
|
164
|
+
export function checkPortable(manifest, bundle) {
|
|
165
|
+
const runs = Array.isArray(manifest.platforms) ? manifest.platforms : ["windows", "linux"];
|
|
166
|
+
if (!runs.includes("linux") || /\(\)\.isWindows\(|\bisWindows\(/.test(bundle)) return [];
|
|
167
|
+
const notes = [];
|
|
168
|
+
const joins = (bundle.match(/\}\\\\|["'`]\\\\["'`]/g) ?? []).length;
|
|
169
|
+
if (joins) notes.push(`It joins paths with a backslash (${joins} place${joins > 1 ? "s" : ""}), which on Linux is part of a file name: ask isWindows() from the bridge for the separator, or say "platforms": ["windows"] in plugin.json.`);
|
|
170
|
+
if (/\.exe["'`]/.test(bundle)) notes.push(`It names a program with ".exe", which Linux programs do not have: ask isWindows() from the bridge, or say "platforms": ["windows"] in plugin.json.`);
|
|
171
|
+
return notes;
|
|
172
|
+
}
|
|
173
|
+
|
|
155
174
|
// ---- points ----
|
|
156
175
|
// A version's points: advice for whoever is choosing a plugin, never a gate (the checks above are
|
|
157
176
|
// that). Worked out once when it is published, by the registry for a submission and by
|