deck-plugin-sdk 1.2.0 → 1.5.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 +96 -77
- package/bin/deck-plugin.mjs +149 -116
- package/build-plugin.mjs +48 -26
- package/package.json +6 -1
- package/packages.mjs +107 -0
- package/shim/bridge.js +13 -88
- package/shim/event.js +6 -1
- package/shim/scope.js +6 -0
- package/shim/terminals.js +6 -0
- package/shim/ui.js +2 -1
- package/template/AGENTS.md +77 -0
- package/template/CLAUDE.md +1 -0
- package/template/README.md +20 -17
- package/template/gitignore +4 -4
- package/template/plugin.json +9 -9
- package/template/src/assets.d.ts +16 -0
- package/template/src/plugin.tsx +65 -65
- package/types/components/ui.d.ts +1 -1
- package/types/core/bridge.d.ts +71 -148
- package/types/core/editorsession.d.ts +50 -0
- package/types/core/editorsettings.d.ts +42 -0
- package/types/core/fileview.d.ts +35 -0
- package/types/core/lsp.d.ts +195 -193
- package/types/core/monaco.d.ts +1 -0
- package/types/core/settingsstore.d.ts +9 -0
- package/types/core/terminals.d.ts +30 -1
- package/types/core/theme.d.ts +63 -7
- package/types/core/treehide.d.ts +5 -0
- package/types/modules/editor/DiffPeek.d.ts +19 -0
- package/types/modules/editor/MonacoHosts.d.ts +19 -6
- package/types/modules/editor/hunks.d.ts +54 -0
- package/types/modules/editor/tree.d.ts +0 -6
package/packages.mjs
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// What a plugin's npm packages need from the build. Shared by Deck's own plugin build
|
|
2
|
+
// (sdk/shim-builder.mjs, which makes the shim) and the SDK's (sdk/build-plugin.mjs), so the two cannot disagree.
|
|
3
|
+
//
|
|
4
|
+
// A plugin is installed as plugin.json and one plugin.js, so everything a package brings has to
|
|
5
|
+
// end up inside that one file: a sidecar image, font or stylesheet would never be installed.
|
|
6
|
+
//
|
|
7
|
+
// "The plugin's own code" is what is under its src/. Anything else is a package, whether it sits
|
|
8
|
+
// in node_modules or is linked in from another folder (`npm install ../my-lib`).
|
|
9
|
+
import { build } from "esbuild";
|
|
10
|
+
import { readFileSync } from "node:fs";
|
|
11
|
+
import { dirname, join, relative, sep } from "node:path";
|
|
12
|
+
|
|
13
|
+
/** Images and fonts the plugin or a package imports, inlined as data: URLs. */
|
|
14
|
+
export const assetLoaders = Object.fromEntries(
|
|
15
|
+
[".png", ".jpg", ".jpeg", ".gif", ".webp", ".avif", ".bmp", ".ico", ".svg", ".woff", ".woff2", ".ttf", ".otf", ".eot"]
|
|
16
|
+
.map((ext) => [ext, "dataurl"]),
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
const under = (path, dir) => !!path && (path + sep).toLowerCase().startsWith((dir + sep).toLowerCase());
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Whether an import of Deck's shared icons or animation library comes from a package rather than
|
|
23
|
+
* the plugin's own code. The plugin's own imports go to Deck's copies, through a shim that names
|
|
24
|
+
* only the exports its `src/` uses; a package's would find its names missing there, so a package
|
|
25
|
+
* gets the real library, bundled and tree-shaken like any other dependency.
|
|
26
|
+
*/
|
|
27
|
+
export const fromPackage = (args, dir) =>
|
|
28
|
+
(args.path === "lucide-react" || args.path === "framer-motion") && !!args.importer && !under(args.importer, join(dir, "src"));
|
|
29
|
+
|
|
30
|
+
/** `import "x.css"` with nothing bound: imported for its effect, so it is applied. */
|
|
31
|
+
function importedBare(importer, spec) {
|
|
32
|
+
let text;
|
|
33
|
+
try { text = readFileSync(importer, "utf8"); } catch { return false; }
|
|
34
|
+
const q = spec.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
35
|
+
return new RegExp(`(^|[;\\n])\\s*import\\s*["']${q}["']`).test(text);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* CSS imported for its effect is applied: CSS a package imports, and CSS any code imports with
|
|
40
|
+
* nothing bound (`import "some-lib/styles.css"`, `import "@fontsource/inter"`). It is bundled
|
|
41
|
+
* with its `@import`s and the images and fonts its `url()`s name, and added to the page as one
|
|
42
|
+
* <style> per file, replaced rather than duplicated when the plugin reloads.
|
|
43
|
+
*
|
|
44
|
+
* `import css from "./x.css"` in the plugin's own code still gives it the text, to inject where and
|
|
45
|
+
* when it wants (the canvas plugin does that with Excalidraw's stylesheet).
|
|
46
|
+
*/
|
|
47
|
+
export function packageCss(pluginId, dir) {
|
|
48
|
+
const src = join(dir, "src");
|
|
49
|
+
return {
|
|
50
|
+
name: "deck-package-css",
|
|
51
|
+
setup(b) {
|
|
52
|
+
b.onResolve({ filter: /.*/ }, async (args) => {
|
|
53
|
+
if (args.pluginData?.css || (args.kind !== "import-statement" && args.kind !== "require-call")) return undefined;
|
|
54
|
+
// The plugin's own code: a bare import, which may name a package whose entry is a
|
|
55
|
+
// stylesheet (@fontsource/inter). A package's: any import of a .css file.
|
|
56
|
+
const own = under(args.importer, src);
|
|
57
|
+
if (!(own ? importedBare(args.importer, args.path) : /\.css$/.test(args.path))) return undefined;
|
|
58
|
+
const r = await b.resolve(args.path, { kind: args.kind, resolveDir: args.resolveDir, importer: args.importer, pluginData: { css: true } });
|
|
59
|
+
if (r.errors.length) return { errors: r.errors };
|
|
60
|
+
if (!/\.css$/.test(r.path)) return undefined;
|
|
61
|
+
return { path: r.path, namespace: "deck-package-css" };
|
|
62
|
+
});
|
|
63
|
+
b.onLoad({ filter: /.*/, namespace: "deck-package-css" }, async (args) => {
|
|
64
|
+
const out = await build({ entryPoints: [args.path], outfile: "package.css", bundle: true, write: false, minify: true, loader: assetLoaders, logLevel: "silent" });
|
|
65
|
+
const css = out.outputFiles.find((f) => f.path.endsWith(".css"))?.text ?? "";
|
|
66
|
+
// Named by where it is inside the project, not on this machine: the bundle is shared.
|
|
67
|
+
const where = /[\\/]node_modules[\\/]/.test(args.path) ? args.path.replace(/^.*[\\/]node_modules[\\/]/, "") : relative(dir, args.path);
|
|
68
|
+
const key = `${pluginId}:${where.replace(/\\/g, "/")}`;
|
|
69
|
+
return {
|
|
70
|
+
resolveDir: dirname(args.path),
|
|
71
|
+
loader: "js",
|
|
72
|
+
contents: `const css = ${JSON.stringify(css)};
|
|
73
|
+
if (typeof document !== "undefined") {
|
|
74
|
+
const key = ${JSON.stringify(key)};
|
|
75
|
+
let s = [...document.head.querySelectorAll("style[data-deck-css]")].find((e) => e.dataset.deckCss === key);
|
|
76
|
+
if (!s) { s = document.createElement("style"); s.dataset.deckCss = key; document.head.appendChild(s); }
|
|
77
|
+
s.textContent = css;
|
|
78
|
+
}
|
|
79
|
+
export default css;
|
|
80
|
+
`,
|
|
81
|
+
};
|
|
82
|
+
});
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* `import text from "some/file?raw"` gives a file's contents as a string, the convention Vite
|
|
89
|
+
* uses. What a library needs as text rather than as code: PDF.js's worker, which a plugin starts
|
|
90
|
+
* from a Blob URL because only plugin.js is installed (see the Requests plugin's script worker).
|
|
91
|
+
*
|
|
92
|
+
* `?base64` gives any file's bytes as a base64 string instead: data a library asks for only
|
|
93
|
+
* sometimes (PDF.js's font files and image decoders), decoded when it does rather than every time
|
|
94
|
+
* Deck starts, as a `.wasm` import would be.
|
|
95
|
+
*/
|
|
96
|
+
export const rawImports = {
|
|
97
|
+
name: "deck-raw",
|
|
98
|
+
setup(b) {
|
|
99
|
+
b.onResolve({ filter: /\?(raw|base64)$/ }, async (args) => {
|
|
100
|
+
const as = args.path.endsWith("?raw") ? "text" : "base64";
|
|
101
|
+
const r = await b.resolve(args.path.replace(/\?(raw|base64)$/, ""), { kind: args.kind, resolveDir: args.resolveDir, importer: args.importer });
|
|
102
|
+
if (r.errors.length) return { errors: r.errors };
|
|
103
|
+
return { path: r.path, namespace: "deck-raw", pluginData: { as } };
|
|
104
|
+
});
|
|
105
|
+
b.onLoad({ filter: /.*/, namespace: "deck-raw" }, (args) => ({ contents: readFileSync(args.path), loader: args.pluginData?.as === "base64" ? "base64" : "text" }));
|
|
106
|
+
},
|
|
107
|
+
};
|
package/shim/bridge.js
CHANGED
|
@@ -3,30 +3,31 @@
|
|
|
3
3
|
// Deck's bridge. Everything resolves at USE time, never at import time: a plugin module is
|
|
4
4
|
// evaluated during loadPlugins, and a reference copied out then would capture a half-built
|
|
5
5
|
// object. Functions forward their arguments; plain values are proxied.
|
|
6
|
+
import { pluginId, scoped } from "./scope.js";
|
|
6
7
|
const b = () => {
|
|
7
8
|
const x = window.__deck?.bridge;
|
|
8
9
|
if (!x) throw new Error("window.__deck.bridge is missing — this plugin only runs inside Deck");
|
|
9
10
|
return x;
|
|
10
11
|
};
|
|
11
12
|
|
|
13
|
+
export const agentClis = (...a) => b().agentClis(...a);
|
|
14
|
+
export const agentMcpStatus = (...a) => b().agentMcpStatus(...a);
|
|
12
15
|
export const askSudoPassword = (...a) => b().askSudoPassword(...a);
|
|
13
16
|
export const backupExport = (...a) => b().backupExport(...a);
|
|
14
17
|
export const backupInspect = (...a) => b().backupInspect(...a);
|
|
15
18
|
export const backupRestore = (...a) => b().backupRestore(...a);
|
|
16
|
-
export const claudeMcpEntry = (...a) => b().claudeMcpEntry(...a);
|
|
17
|
-
export const claudeMcpStatus = (...a) => b().claudeMcpStatus(...a);
|
|
18
19
|
export const claudePlanLimits = (...a) => b().claudePlanLimits(...a);
|
|
19
20
|
export const claudeUsage = (...a) => b().claudeUsage(...a);
|
|
20
21
|
export const clearWallpaper = (...a) => b().clearWallpaper(...a);
|
|
21
|
-
export const
|
|
22
|
-
export const configRead = (...a) => b().configRead(...a);
|
|
23
|
-
export const configWrite = (...a) => b().configWrite(...a);
|
|
22
|
+
export const configDelete = (name, ...a) => b().configDelete(scoped(name), ...a);
|
|
23
|
+
export const configRead = (name, ...a) => b().configRead(scoped(name), ...a);
|
|
24
|
+
export const configWrite = (name, ...a) => b().configWrite(scoped(name), ...a);
|
|
24
25
|
export const convertFileSrc = (...a) => b().convertFileSrc(...a);
|
|
26
|
+
export const dataDir = () => b().dataDir(pluginId);
|
|
25
27
|
export const dbConnect = (...a) => b().dbConnect(...a);
|
|
26
28
|
export const dbDisconnect = (...a) => b().dbDisconnect(...a);
|
|
27
29
|
export const dbQuery = (...a) => b().dbQuery(...a);
|
|
28
30
|
export const dbSchema = (...a) => b().dbSchema(...a);
|
|
29
|
-
export const defaultDownloadDir = (...a) => b().defaultDownloadDir(...a);
|
|
30
31
|
export const deleteIconPack = (...a) => b().deleteIconPack(...a);
|
|
31
32
|
export const dirsDownload = (...a) => b().dirsDownload(...a);
|
|
32
33
|
export const exec = (...a) => b().exec(...a);
|
|
@@ -55,110 +56,58 @@ export const fsTrash = (...a) => b().fsTrash(...a);
|
|
|
55
56
|
export const fsWrite = (...a) => b().fsWrite(...a);
|
|
56
57
|
export const fsWriteBytes = (...a) => b().fsWriteBytes(...a);
|
|
57
58
|
export const getAutostart = (...a) => b().getAutostart(...a);
|
|
58
|
-
export const getVaultRoot = (...a) => b().getVaultRoot(...a);
|
|
59
59
|
export const ghPrLineComment = (...a) => b().ghPrLineComment(...a);
|
|
60
60
|
export const ghPrThreads = (...a) => b().ghPrThreads(...a);
|
|
61
61
|
export const ghRun = (...a) => b().ghRun(...a);
|
|
62
62
|
export const ghThreadReply = (...a) => b().ghThreadReply(...a);
|
|
63
63
|
export const ghThreadResolve = (...a) => b().ghThreadResolve(...a);
|
|
64
|
-
export const gitAbortOp = (...a) => b().gitAbortOp(...a);
|
|
65
64
|
export const gitAiCommitMsg = (...a) => b().gitAiCommitMsg(...a);
|
|
66
65
|
export const gitBlameLine = (...a) => b().gitBlameLine(...a);
|
|
67
|
-
export const gitBranchAt = (...a) => b().gitBranchAt(...a);
|
|
68
|
-
export const gitBranchDelete = (...a) => b().gitBranchDelete(...a);
|
|
69
|
-
export const gitBranchDeleteRemote = (...a) => b().gitBranchDeleteRemote(...a);
|
|
70
|
-
export const gitBranchRename = (...a) => b().gitBranchRename(...a);
|
|
71
|
-
export const gitBranches = (...a) => b().gitBranches(...a);
|
|
72
66
|
export const gitCheckout = (...a) => b().gitCheckout(...a);
|
|
73
67
|
export const gitCheckoutDetach = (...a) => b().gitCheckoutDetach(...a);
|
|
74
68
|
export const gitCheckoutNew = (...a) => b().gitCheckoutNew(...a);
|
|
75
69
|
export const gitCheckoutTrack = (...a) => b().gitCheckoutTrack(...a);
|
|
76
|
-
export const gitCherryPick = (...a) => b().gitCherryPick(...a);
|
|
77
|
-
export const gitCommit = (...a) => b().gitCommit(...a);
|
|
78
|
-
export const gitCommitAmend = (...a) => b().gitCommitAmend(...a);
|
|
79
70
|
export const gitCommitFiles = (...a) => b().gitCommitFiles(...a);
|
|
80
|
-
export const gitContinueOp = (...a) => b().gitContinueOp(...a);
|
|
81
71
|
export const gitDiscard = (...a) => b().gitDiscard(...a);
|
|
82
|
-
export const gitFetch = (...a) => b().gitFetch(...a);
|
|
83
|
-
export const gitFetchPr = (...a) => b().gitFetchPr(...a);
|
|
84
72
|
export const gitFileBytes = (...a) => b().gitFileBytes(...a);
|
|
85
73
|
export const gitFileDiff = (...a) => b().gitFileDiff(...a);
|
|
86
|
-
export const gitHead = (...a) => b().gitHead(...a);
|
|
87
|
-
export const gitHeadFile = (...a) => b().gitHeadFile(...a);
|
|
88
|
-
export const gitIndexFile = (...a) => b().gitIndexFile(...a);
|
|
89
74
|
export const gitInfo = (...a) => b().gitInfo(...a);
|
|
90
75
|
export const gitLogGraph = (...a) => b().gitLogGraph(...a);
|
|
91
|
-
export const gitMerge = (...a) => b().gitMerge(...a);
|
|
92
|
-
export const gitMergeAs = (...a) => b().gitMergeAs(...a);
|
|
93
|
-
export const gitMergeBase = (...a) => b().gitMergeBase(...a);
|
|
94
|
-
export const gitPendingOp = (...a) => b().gitPendingOp(...a);
|
|
95
76
|
export const gitPoll = (...a) => b().gitPoll(...a);
|
|
96
|
-
export const gitPull = (...a) => b().gitPull(...a);
|
|
97
|
-
export const gitPullRebase = (...a) => b().gitPullRebase(...a);
|
|
98
|
-
export const gitPush = (...a) => b().gitPush(...a);
|
|
99
|
-
export const gitPushBranch = (...a) => b().gitPushBranch(...a);
|
|
100
77
|
export const gitRangeFiles = (...a) => b().gitRangeFiles(...a);
|
|
101
|
-
export const gitRangeSubjects = (...a) => b().gitRangeSubjects(...a);
|
|
102
|
-
export const gitRebase = (...a) => b().gitRebase(...a);
|
|
103
78
|
export const gitRefs = (...a) => b().gitRefs(...a);
|
|
104
|
-
export const gitRemoteUrl = (...a) => b().gitRemoteUrl(...a);
|
|
105
|
-
export const gitRemotes = (...a) => b().gitRemotes(...a);
|
|
106
79
|
export const gitRepos = (...a) => b().gitRepos(...a);
|
|
107
|
-
export const
|
|
108
|
-
export const gitRevert = (...a) => b().gitRevert(...a);
|
|
109
|
-
export const gitShowFile = (...a) => b().gitShowFile(...a);
|
|
110
|
-
export const gitStage = (...a) => b().gitStage(...a);
|
|
111
|
-
export const gitStageContent = (...a) => b().gitStageContent(...a);
|
|
112
|
-
export const gitStash = (...a) => b().gitStash(...a);
|
|
113
|
-
export const gitStashApply = (...a) => b().gitStashApply(...a);
|
|
114
|
-
export const gitStashClear = (...a) => b().gitStashClear(...a);
|
|
115
|
-
export const gitStashDrop = (...a) => b().gitStashDrop(...a);
|
|
116
|
-
export const gitStashList = (...a) => b().gitStashList(...a);
|
|
117
|
-
export const gitStashPop = (...a) => b().gitStashPop(...a);
|
|
118
|
-
export const gitStashShow = (...a) => b().gitStashShow(...a);
|
|
80
|
+
export const gitRun = (...a) => b().gitRun(...a);
|
|
119
81
|
export const gitStatus = (...a) => b().gitStatus(...a);
|
|
120
|
-
export const gitTag = (...a) => b().gitTag(...a);
|
|
121
|
-
export const gitTagDelete = (...a) => b().gitTagDelete(...a);
|
|
122
|
-
export const gitTagDeleteRemote = (...a) => b().gitTagDeleteRemote(...a);
|
|
123
|
-
export const gitTagPush = (...a) => b().gitTagPush(...a);
|
|
124
|
-
export const gitUndoCommit = (...a) => b().gitUndoCommit(...a);
|
|
125
|
-
export const gitUnstage = (...a) => b().gitUnstage(...a);
|
|
126
82
|
export const glabApi = (...a) => b().glabApi(...a);
|
|
127
83
|
export const glabRun = (...a) => b().glabRun(...a);
|
|
128
84
|
export const googleAccount = (...a) => b().googleAccount(...a);
|
|
129
85
|
export const googleSignIn = (...a) => b().googleSignIn(...a);
|
|
130
86
|
export const googleSignOut = (...a) => b().googleSignOut(...a);
|
|
131
87
|
export const googleToken = (...a) => b().googleToken(...a);
|
|
132
|
-
export const historyClear = (...a) => b().historyClear(...a);
|
|
133
|
-
export const historyDelete = (...a) => b().historyDelete(...a);
|
|
134
88
|
export const httpCancel = (...a) => b().httpCancel(...a);
|
|
135
89
|
export const httpSend = (...a) => b().httpSend(...a);
|
|
136
90
|
export const httpSendAs = (...a) => b().httpSendAs(...a);
|
|
137
91
|
export const httpSendReply = (...a) => b().httpSendReply(...a);
|
|
138
92
|
export const importIconPack = (...a) => b().importIconPack(...a);
|
|
139
|
-
export const
|
|
140
|
-
export const
|
|
93
|
+
export const installAgentHooks = (...a) => b().installAgentHooks(...a);
|
|
94
|
+
export const installAgentMcp = (...a) => b().installAgentMcp(...a);
|
|
95
|
+
export const installAgentSkills = (...a) => b().installAgentSkills(...a);
|
|
141
96
|
export const installFromRegistry = (...a) => b().installFromRegistry(...a);
|
|
142
|
-
export const installPlugin = (...a) => b().installPlugin(...a);
|
|
143
97
|
export const isFullscreen = (...a) => b().isFullscreen(...a);
|
|
144
98
|
export const languagesDir = (...a) => b().languagesDir(...a);
|
|
145
99
|
export const launchFiles = (...a) => b().launchFiles(...a);
|
|
146
100
|
export const listApps = (...a) => b().listApps(...a);
|
|
147
101
|
export const listInstalled = (...a) => b().listInstalled(...a);
|
|
102
|
+
export const logLatest = (...a) => b().logLatest(...a);
|
|
148
103
|
export const mediaMeta = (...a) => b().mediaMeta(...a);
|
|
149
104
|
export const mediaSetNowPlaying = (...a) => b().mediaSetNowPlaying(...a);
|
|
150
105
|
export const mediaSetPlayback = (...a) => b().mediaSetPlayback(...a);
|
|
151
106
|
export const memUsage = (...a) => b().memUsage(...a);
|
|
152
|
-
export const notesDelete = (...a) => b().notesDelete(...a);
|
|
153
|
-
export const notesList = (...a) => b().notesList(...a);
|
|
154
|
-
export const notesRead = (...a) => b().notesRead(...a);
|
|
155
|
-
export const notesRename = (...a) => b().notesRename(...a);
|
|
156
|
-
export const notesScope = (...a) => b().notesScope(...a);
|
|
157
|
-
export const notesWrite = (...a) => b().notesWrite(...a);
|
|
158
107
|
export const notifyOs = (...a) => b().notifyOs(...a);
|
|
159
|
-
export const openApp = (...a) => b().openApp(...a);
|
|
160
108
|
export const openExternal = (...a) => b().openExternal(...a);
|
|
161
109
|
export const openFile = (...a) => b().openFile(...a);
|
|
110
|
+
export const openTestDeck = (...a) => b().openTestDeck(...a);
|
|
162
111
|
export const pickAnyFile = (...a) => b().pickAnyFile(...a);
|
|
163
112
|
export const pickExe = (...a) => b().pickExe(...a);
|
|
164
113
|
export const pickFile = (...a) => b().pickFile(...a);
|
|
@@ -167,22 +116,15 @@ export const pickImage = (...a) => b().pickImage(...a);
|
|
|
167
116
|
export const pickSavePath = (...a) => b().pickSavePath(...a);
|
|
168
117
|
export const pickScript = (...a) => b().pickScript(...a);
|
|
169
118
|
export const pluginsDir = (...a) => b().pluginsDir(...a);
|
|
170
|
-
export const powerAction = (...a) => b().powerAction(...a);
|
|
171
119
|
export const procKill = (...a) => b().procKill(...a);
|
|
172
120
|
export const procStream = (...a) => b().procStream(...a);
|
|
173
121
|
export const procWrite = (...a) => b().procWrite(...a);
|
|
174
|
-
export const readDownloads = (...a) => b().readDownloads(...a);
|
|
175
122
|
export const readIconPackTheme = (...a) => b().readIconPackTheme(...a);
|
|
176
|
-
export const readLibrary = (...a) => b().readLibrary(...a);
|
|
177
|
-
export const readMusicSuggestions = (...a) => b().readMusicSuggestions(...a);
|
|
178
|
-
export const readMusicTaste = (...a) => b().readMusicTaste(...a);
|
|
179
123
|
export const readTermLayout = (...a) => b().readTermLayout(...a);
|
|
180
124
|
export const readTrackMeta = (...a) => b().readTrackMeta(...a);
|
|
181
125
|
export const remoteHome = (...a) => b().remoteHome(...a);
|
|
182
126
|
export const remoteStat = (...a) => b().remoteStat(...a);
|
|
183
127
|
export const remoteSudoPassword = (...a) => b().remoteSudoPassword(...a);
|
|
184
|
-
export const removeClaudeHooks = (...a) => b().removeClaudeHooks(...a);
|
|
185
|
-
export const removeClaudeMcp = (...a) => b().removeClaudeMcp(...a);
|
|
186
128
|
export const removeLanguage = (...a) => b().removeLanguage(...a);
|
|
187
129
|
export const restartApp = (...a) => b().restartApp(...a);
|
|
188
130
|
export const revealInFolder = (...a) => b().revealInFolder(...a);
|
|
@@ -190,37 +132,20 @@ export const runScriptNow = (...a) => b().runScriptNow(...a);
|
|
|
190
132
|
export const sessionList = (...a) => b().sessionList(...a);
|
|
191
133
|
export const setAutostart = (...a) => b().setAutostart(...a);
|
|
192
134
|
export const setDebugTargets = (...a) => b().setDebugTargets(...a);
|
|
193
|
-
export const setVaultRoot = (...a) => b().setVaultRoot(...a);
|
|
194
135
|
export const setWallpaper = (...a) => b().setWallpaper(...a);
|
|
195
136
|
export const sshHosts = (...a) => b().sshHosts(...a);
|
|
196
137
|
export const sshTunnelClose = (...a) => b().sshTunnelClose(...a);
|
|
197
|
-
export const sshTunnelList = (...a) => b().sshTunnelList(...a);
|
|
198
138
|
export const sshTunnelOpen = (...a) => b().sshTunnelOpen(...a);
|
|
199
139
|
export const startupMarks = (...a) => b().startupMarks(...a);
|
|
200
|
-
export const startupRead = (...a) => b().startupRead(...a);
|
|
201
|
-
export const startupWrite = (...a) => b().startupWrite(...a);
|
|
202
140
|
export const toggleFullscreen = (...a) => b().toggleFullscreen(...a);
|
|
203
|
-
export const vaultDataRead = (...a) => b().vaultDataRead(...a);
|
|
204
|
-
export const vaultDataWrite = (...a) => b().vaultDataWrite(...a);
|
|
205
|
-
export const vaultRelativize = (...a) => b().vaultRelativize(...a);
|
|
206
|
-
export const vaultResolve = (...a) => b().vaultResolve(...a);
|
|
207
141
|
export const vpsRunsPull = (...a) => b().vpsRunsPull(...a);
|
|
208
142
|
export const watchFiles = (...a) => b().watchFiles(...a);
|
|
209
|
-
export const writeDownloads = (...a) => b().writeDownloads(...a);
|
|
210
|
-
export const writeLibrary = (...a) => b().writeLibrary(...a);
|
|
211
|
-
export const writeMusicTaste = (...a) => b().writeMusicTaste(...a);
|
|
212
|
-
export const writeTermLayout = (...a) => b().writeTermLayout(...a);
|
|
213
143
|
|
|
214
144
|
export const DEFAULT_SETTINGS = new Proxy({}, {
|
|
215
145
|
get: (_t, k) => b().DEFAULT_SETTINGS[k],
|
|
216
146
|
ownKeys: () => Reflect.ownKeys(b().DEFAULT_SETTINGS),
|
|
217
147
|
getOwnPropertyDescriptor: (_t, k) => ({ ...Object.getOwnPropertyDescriptor(b().DEFAULT_SETTINGS, k), configurable: true }),
|
|
218
148
|
});
|
|
219
|
-
export const EMPTY_LIBRARY = new Proxy({}, {
|
|
220
|
-
get: (_t, k) => b().EMPTY_LIBRARY[k],
|
|
221
|
-
ownKeys: () => Reflect.ownKeys(b().EMPTY_LIBRARY),
|
|
222
|
-
getOwnPropertyDescriptor: (_t, k) => ({ ...Object.getOwnPropertyDescriptor(b().EMPTY_LIBRARY, k), configurable: true }),
|
|
223
|
-
});
|
|
224
149
|
export const FOOTER_ITEMS = new Proxy({}, {
|
|
225
150
|
get: (_t, k) => b().FOOTER_ITEMS[k],
|
|
226
151
|
ownKeys: () => Reflect.ownKeys(b().FOOTER_ITEMS),
|
package/shim/event.js
CHANGED
|
@@ -2,4 +2,9 @@
|
|
|
2
2
|
//
|
|
3
3
|
// @tauri-apps/api/event. Only listen: a plugin subscribing to Deck's events is expected, one
|
|
4
4
|
// emitting into them is not.
|
|
5
|
-
|
|
5
|
+
//
|
|
6
|
+
// config-changed names the configs that changed, folders and all. A plugin's own come to it as it
|
|
7
|
+
// names them, without its data/<id>/ folder, so `payload.includes("boards")` keeps working.
|
|
8
|
+
import { unscoped } from "./scope.js";
|
|
9
|
+
export const listen = (event, handler) => window.__deck.listen(event, event !== "config-changed" ? handler
|
|
10
|
+
: (e) => handler({ ...e, payload: Array.isArray(e.payload) ? e.payload.map(unscoped) : e.payload }));
|
package/shim/scope.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// GENERATED by sdk/gen-shim.mjs — do not edit.
|
|
2
|
+
const id = (() => { try { return new URL(import.meta.url).searchParams.get("deck-id") ?? ""; } catch { return ""; } })();
|
|
3
|
+
export const pluginId = id;
|
|
4
|
+
const own = id ? `data/${id}/` : "";
|
|
5
|
+
export const scoped = (name) => (own && !String(name).includes("/") ? own + name : name);
|
|
6
|
+
export const unscoped = (name) => (own && typeof name === "string" && name.startsWith(own) ? name.slice(own.length) : name);
|
package/shim/terminals.js
CHANGED
|
@@ -19,11 +19,13 @@ export const attach = (...a) => t().attach(...a);
|
|
|
19
19
|
export const bracketPaste = (...a) => t().bracketPaste(...a);
|
|
20
20
|
export const clearPane = (...a) => t().clearPane(...a);
|
|
21
21
|
export const closeLeaf = (...a) => t().closeLeaf(...a);
|
|
22
|
+
export const copyFrom = (...a) => t().copyFrom(...a);
|
|
22
23
|
export const destroy = (...a) => t().destroy(...a);
|
|
23
24
|
export const findLeaf = (...a) => t().findLeaf(...a);
|
|
24
25
|
export const focusPane = (...a) => t().focusPane(...a);
|
|
25
26
|
export const getInstance = (...a) => t().getInstance(...a);
|
|
26
27
|
export const getLayout = (...a) => t().getLayout(...a);
|
|
28
|
+
export const hasSelection = (...a) => t().hasSelection(...a);
|
|
27
29
|
export const insertBeside = (...a) => t().insertBeside(...a);
|
|
28
30
|
export const isPinned = (...a) => t().isPinned(...a);
|
|
29
31
|
export const isWorkspaceActive = (...a) => t().isWorkspaceActive(...a);
|
|
@@ -37,6 +39,7 @@ export const moveZone = (...a) => t().moveZone(...a);
|
|
|
37
39
|
export const mutateLayout = (...a) => t().mutateLayout(...a);
|
|
38
40
|
export const nextActiveAfterClose = (...a) => t().nextActiveAfterClose(...a);
|
|
39
41
|
export const nextZoneSpot = (...a) => t().nextZoneSpot(...a);
|
|
42
|
+
export const openFile = (...a) => t().openFile(...a);
|
|
40
43
|
export const openTabInWorkspace = (...a) => t().openTabInWorkspace(...a);
|
|
41
44
|
export const paneCwd = (...a) => t().paneCwd(...a);
|
|
42
45
|
export const paneHasAgent = (...a) => t().paneHasAgent(...a);
|
|
@@ -44,11 +47,14 @@ export const paneLabel = (...a) => t().paneLabel(...a);
|
|
|
44
47
|
export const paneLines = (...a) => t().paneLines(...a);
|
|
45
48
|
export const paneLocation = (...a) => t().paneLocation(...a);
|
|
46
49
|
export const pasteInto = (...a) => t().pasteInto(...a);
|
|
50
|
+
export const pasteText = (...a) => t().pasteText(...a);
|
|
51
|
+
export const pasteTextInto = (...a) => t().pasteTextInto(...a);
|
|
47
52
|
export const publishLayout = (...a) => t().publishLayout(...a);
|
|
48
53
|
export const queueInput = (...a) => t().queueInput(...a);
|
|
49
54
|
export const refit = (...a) => t().refit(...a);
|
|
50
55
|
export const reorderTab = (...a) => t().reorderTab(...a);
|
|
51
56
|
export const restartPane = (...a) => t().restartPane(...a);
|
|
57
|
+
export const runsLines = (...a) => t().runsLines(...a);
|
|
52
58
|
export const sendInput = (...a) => t().sendInput(...a);
|
|
53
59
|
export const setLeafLabel = (...a) => t().setLeafLabel(...a);
|
|
54
60
|
export const setLeafView = (...a) => t().setLeafView(...a);
|
package/shim/ui.js
CHANGED
|
@@ -51,17 +51,18 @@ export const collectFiles = (...a) => u().collectFiles(...a);
|
|
|
51
51
|
export const conflict = (...a) => u().conflict(...a);
|
|
52
52
|
export const decodeText = (...a) => u().decodeText(...a);
|
|
53
53
|
export const dispatch = (...a) => u().dispatch(...a);
|
|
54
|
+
export const forLanguage = (...a) => u().forLanguage(...a);
|
|
54
55
|
export const formatChord = (...a) => u().formatChord(...a);
|
|
55
56
|
export const getEditorSettings = (...a) => u().getEditorSettings(...a);
|
|
56
57
|
export const getOverrides = (...a) => u().getOverrides(...a);
|
|
57
58
|
export const match = (...a) => u().match(...a);
|
|
58
59
|
export const mediaKind = (...a) => u().mediaKind(...a);
|
|
60
|
+
export const modelOptionsFor = (...a) => u().modelOptionsFor(...a);
|
|
59
61
|
export const monacoOptionsFrom = (...a) => u().monacoOptionsFrom(...a);
|
|
60
62
|
export const normalizeChord = (...a) => u().normalizeChord(...a);
|
|
61
63
|
export const onKey = (...a) => u().onKey(...a);
|
|
62
64
|
export const onKeys = (...a) => u().onKeys(...a);
|
|
63
65
|
export const parseChord = (...a) => u().parseChord(...a);
|
|
64
|
-
export const parseDirty = (...a) => u().parseDirty(...a);
|
|
65
66
|
export const pushScope = (...a) => u().pushScope(...a);
|
|
66
67
|
export const repoLabel = (...a) => u().repoLabel(...a);
|
|
67
68
|
export const resolve = (...a) => u().resolve(...a);
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# __NAME__: notes for AI agents
|
|
2
|
+
|
|
3
|
+
This is a plugin for Deck, a Windows app (Tauri, React 19). It is
|
|
4
|
+
bundled into one `plugin.js` that Deck imports at run time, inside Deck's own window. Read this
|
|
5
|
+
before changing anything.
|
|
6
|
+
|
|
7
|
+
## Commands
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
npm run dev # build on every save and install into the test Deck, which reloads it
|
|
11
|
+
npm run build # build once into plugin.js
|
|
12
|
+
npm run install-local # build and install into the user's real Deck
|
|
13
|
+
npx deck-plugin check # validate plugin.json
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
The test Deck is a second Deck with its own data, started with
|
|
17
|
+
`& "$env:LOCALAPPDATA\Deck\deck.exe" --test` (PowerShell). Develop against it, never the real
|
|
18
|
+
Deck: `npm run dev` only ever writes there. Run `npm run install-local` only when the user asks
|
|
19
|
+
for the plugin to be installed for real. Its log is `%APPDATA%\Deck-Dev\logs\deck-<date>.log`;
|
|
20
|
+
the plugin's `console.warn` and `console.error` lines land there.
|
|
21
|
+
|
|
22
|
+
## Files
|
|
23
|
+
|
|
24
|
+
- `plugin.json`: `id` (letters, digits, dashes; it is a folder name and a config key), `name`,
|
|
25
|
+
`description`, `icon` (a lucide icon name), `color`, `version`, `minDeck`.
|
|
26
|
+
- `src/plugin.tsx`: the entry. Its exports are what Deck uses (below).
|
|
27
|
+
- `src/mcp.ts`, if present: tools for Deck's MCP server, built to `mcp.js`.
|
|
28
|
+
- `plugin.js` (and `mcp.js`): the build's output, gitignored. Never edit them.
|
|
29
|
+
- **The API is `node_modules/deck-plugin-sdk/types/core/bridge.d.ts`**: every function Deck
|
|
30
|
+
exposes, with its documentation, generated from Deck's own source. Read it rather than guessing
|
|
31
|
+
a name. A plugin imports it as `deck-plugin-sdk/bridge`.
|
|
32
|
+
|
|
33
|
+
## What the plugin can export
|
|
34
|
+
|
|
35
|
+
`default` (a tab), `PaneView` (a view that can sit in a split pane, given `{ cwd, runInTerminal }`),
|
|
36
|
+
`Status` (a footer readout), `Panel` (a footer drawer tab), `commands` (palette entries),
|
|
37
|
+
`Settings` (a settings panel), `keybindings`, `Provider` (wraps the app, for state that must
|
|
38
|
+
survive the tab closing), `Bar`, and `dispose`. At least one; none is required on its own.
|
|
39
|
+
|
|
40
|
+
## Rules that are easy to break
|
|
41
|
+
|
|
42
|
+
- **Import Deck's copies.** `react`, `lucide-react` and the `deck-plugin-sdk/*` modules resolve to
|
|
43
|
+
Deck's own at run time. Never add a second React, or a UI library that bundles one: two
|
|
44
|
+
Reacts in one tree break hooks.
|
|
45
|
+
- **No native code.** A plugin can only call what Deck already exposes (`deck-plugin-sdk/bridge`).
|
|
46
|
+
To run a program, use `exec` or `procStream`; to read and write files, the `fs*` calls.
|
|
47
|
+
- **Opening files**: `export const opens = [".ext"]` with a `PaneView` makes Deck open those files
|
|
48
|
+
in the plugin, each as its own tab, given as the `file` prop. Open one yourself with `openFile`
|
|
49
|
+
from `deck-plugin-sdk/terminals`; save with `fsWriteBytes`.
|
|
50
|
+
- **npm packages are fine** (`npm install` and import): the build bundles them, their images,
|
|
51
|
+
fonts and CSS included, into `plugin.js`. `?raw` on an import gives a file's text (a worker).
|
|
52
|
+
A build step of your own (an esbuild plugin) goes in `deck-plugin.config.mjs` as
|
|
53
|
+
`export const esbuildPlugins = [...]`. Not a package that needs Node (`fs`, `child_process`),
|
|
54
|
+
one that bundles its own React, or one that starts a Web Worker from a file.
|
|
55
|
+
- **Storage goes through `configRead` / `configWrite`** with plain names (`"settings"`, or
|
|
56
|
+
`"notes.md"` for a plain file). Deck keeps them in the plugin's own folder. A name never
|
|
57
|
+
written reads as `""`. Do not write a config whose text has not changed: every write wakes
|
|
58
|
+
every config listener in Deck.
|
|
59
|
+
- **`listen("config-changed", ...)` gets a `string[]` of names**: act only when it includes one
|
|
60
|
+
of yours.
|
|
61
|
+
- **Deck reloads the plugin on every rebuild.** Anything module scope starts (a `setInterval`,
|
|
62
|
+
a `listen`, a window listener) must be stopped in an exported `dispose()`, or the old copy keeps
|
|
63
|
+
running beside the new one. Anything a component creates outside the DOM (an `Audio`, a timer,
|
|
64
|
+
a socket) is stopped in its effect's cleanup.
|
|
65
|
+
- **A poll stops when nobody can see it**: skip ticks while `document.hidden`, and do not run it
|
|
66
|
+
while the readout it feeds is switched off.
|
|
67
|
+
- **Styling uses Deck's stylesheet.** Tailwind classes Deck does not already use do not exist.
|
|
68
|
+
Use Deck's semantic tokens (`text-text-primary`, `text-text-secondary`, `text-text-muted`,
|
|
69
|
+
`bg-elev`, `border-subtle`, `var(--accent)`) and Deck's components from `deck-plugin-sdk/ui`, so
|
|
70
|
+
the plugin follows the user's theme.
|
|
71
|
+
- **No error is dropped silently.** A failed save, load or action logs
|
|
72
|
+
`console.warn("[__ID__] what failed", e)`.
|
|
73
|
+
|
|
74
|
+
## More
|
|
75
|
+
|
|
76
|
+
The full guide (exports, storage, testing, publishing) is in Deck's **Docs** tab, which also has
|
|
77
|
+
the API reference for the Deck that is installed.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@AGENTS.md
|
package/template/README.md
CHANGED
|
@@ -1,17 +1,20 @@
|
|
|
1
|
-
# __NAME__
|
|
2
|
-
|
|
3
|
-
A plugin for
|
|
4
|
-
|
|
5
|
-
## Developing
|
|
6
|
-
|
|
7
|
-
```sh
|
|
8
|
-
npm run dev #
|
|
9
|
-
npm run install-local #
|
|
10
|
-
```
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
1
|
+
# __NAME__
|
|
2
|
+
|
|
3
|
+
A plugin for Deck.
|
|
4
|
+
|
|
5
|
+
## Developing
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
npm run dev # rebuild as you edit; each save shows in your test Deck
|
|
9
|
+
npm run install-local # when it is done: install it into your real Deck
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Start a test Deck from Settings › Plugins › Open a test Deck: it keeps its own settings and data,
|
|
13
|
+
so trying the plugin never touches your real Deck.
|
|
14
|
+
|
|
15
|
+
`src/plugin.tsx` is the entry point; its default export is the tab.
|
|
16
|
+
|
|
17
|
+
## What it does
|
|
18
|
+
|
|
19
|
+
Say what this plugin is for. This file is what the registry shows on its page, so it is worth
|
|
20
|
+
writing for somebody deciding whether to install it rather than for yourself.
|
package/template/gitignore
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
node_modules/
|
|
2
|
-
#
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
node_modules/
|
|
2
|
+
# What the build writes. A release carries plugin.json and these; the repository holds the source.
|
|
3
|
+
plugin.js
|
|
4
|
+
mcp.js
|
package/template/plugin.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
{
|
|
2
|
-
"id": "__ID__",
|
|
3
|
-
"name": "__NAME__",
|
|
4
|
-
"description": "One line on what this is for, shown under its name in Settings.",
|
|
5
|
-
"icon": "Sparkles",
|
|
6
|
-
"color": "#a78bfa",
|
|
7
|
-
"version": "0.1.0",
|
|
8
|
-
"minDeck": "
|
|
9
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"id": "__ID__",
|
|
3
|
+
"name": "__NAME__",
|
|
4
|
+
"description": "One line on what this is for, shown under its name in Settings.",
|
|
5
|
+
"icon": "Sparkles",
|
|
6
|
+
"color": "#a78bfa",
|
|
7
|
+
"version": "0.1.0",
|
|
8
|
+
"minDeck": "1.5.0"
|
|
9
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// What importing a file that is not code gives you, so the editor knows. The build inlines each
|
|
2
|
+
// into plugin.js: an image or font as a data: URL, a stylesheet as its text (or, imported with
|
|
3
|
+
// nothing bound — `import "x.css"` — applied to the page), a .wasm file as its bytes.
|
|
4
|
+
declare module "*.png" { const url: string; export default url; }
|
|
5
|
+
declare module "*.jpg" { const url: string; export default url; }
|
|
6
|
+
declare module "*.jpeg" { const url: string; export default url; }
|
|
7
|
+
declare module "*.gif" { const url: string; export default url; }
|
|
8
|
+
declare module "*.webp" { const url: string; export default url; }
|
|
9
|
+
declare module "*.svg" { const url: string; export default url; }
|
|
10
|
+
declare module "*.woff2" { const url: string; export default url; }
|
|
11
|
+
declare module "*.css" { const text: string; export default text; }
|
|
12
|
+
declare module "*.wasm" { const bytes: Uint8Array; export default bytes; }
|
|
13
|
+
// `import text from "some/file?raw"`: any file, as its text.
|
|
14
|
+
declare module "*?raw" { const text: string; export default text; }
|
|
15
|
+
// `import data from "some/file?base64"`: any file's bytes, as a base64 string.
|
|
16
|
+
declare module "*?base64" { const base64: string; export default base64; }
|