deck-plugin-sdk 1.4.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 +8 -90
- package/shim/event.js +6 -1
- package/shim/scope.js +6 -0
- package/shim/terminals.js +1 -0
- package/shim/ui.js +0 -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/core/bridge.d.ts +27 -152
- package/types/core/editorsession.d.ts +50 -0
- package/types/core/fileview.d.ts +35 -0
- package/types/core/terminals.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,6 +3,7 @@
|
|
|
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");
|
|
@@ -15,20 +16,18 @@ export const askSudoPassword = (...a) => b().askSudoPassword(...a);
|
|
|
15
16
|
export const backupExport = (...a) => b().backupExport(...a);
|
|
16
17
|
export const backupInspect = (...a) => b().backupInspect(...a);
|
|
17
18
|
export const backupRestore = (...a) => b().backupRestore(...a);
|
|
18
|
-
export const claudeMcpEntry = (...a) => b().claudeMcpEntry(...a);
|
|
19
|
-
export const claudeMcpStatus = (...a) => b().claudeMcpStatus(...a);
|
|
20
19
|
export const claudePlanLimits = (...a) => b().claudePlanLimits(...a);
|
|
21
20
|
export const claudeUsage = (...a) => b().claudeUsage(...a);
|
|
22
21
|
export const clearWallpaper = (...a) => b().clearWallpaper(...a);
|
|
23
|
-
export const
|
|
24
|
-
export const configRead = (...a) => b().configRead(...a);
|
|
25
|
-
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);
|
|
26
25
|
export const convertFileSrc = (...a) => b().convertFileSrc(...a);
|
|
26
|
+
export const dataDir = () => b().dataDir(pluginId);
|
|
27
27
|
export const dbConnect = (...a) => b().dbConnect(...a);
|
|
28
28
|
export const dbDisconnect = (...a) => b().dbDisconnect(...a);
|
|
29
29
|
export const dbQuery = (...a) => b().dbQuery(...a);
|
|
30
30
|
export const dbSchema = (...a) => b().dbSchema(...a);
|
|
31
|
-
export const defaultDownloadDir = (...a) => b().defaultDownloadDir(...a);
|
|
32
31
|
export const deleteIconPack = (...a) => b().deleteIconPack(...a);
|
|
33
32
|
export const dirsDownload = (...a) => b().dirsDownload(...a);
|
|
34
33
|
export const exec = (...a) => b().exec(...a);
|
|
@@ -57,82 +56,35 @@ export const fsTrash = (...a) => b().fsTrash(...a);
|
|
|
57
56
|
export const fsWrite = (...a) => b().fsWrite(...a);
|
|
58
57
|
export const fsWriteBytes = (...a) => b().fsWriteBytes(...a);
|
|
59
58
|
export const getAutostart = (...a) => b().getAutostart(...a);
|
|
60
|
-
export const getVaultRoot = (...a) => b().getVaultRoot(...a);
|
|
61
59
|
export const ghPrLineComment = (...a) => b().ghPrLineComment(...a);
|
|
62
60
|
export const ghPrThreads = (...a) => b().ghPrThreads(...a);
|
|
63
61
|
export const ghRun = (...a) => b().ghRun(...a);
|
|
64
62
|
export const ghThreadReply = (...a) => b().ghThreadReply(...a);
|
|
65
63
|
export const ghThreadResolve = (...a) => b().ghThreadResolve(...a);
|
|
66
|
-
export const gitAbortOp = (...a) => b().gitAbortOp(...a);
|
|
67
64
|
export const gitAiCommitMsg = (...a) => b().gitAiCommitMsg(...a);
|
|
68
65
|
export const gitBlameLine = (...a) => b().gitBlameLine(...a);
|
|
69
|
-
export const gitBranchAt = (...a) => b().gitBranchAt(...a);
|
|
70
|
-
export const gitBranchDelete = (...a) => b().gitBranchDelete(...a);
|
|
71
|
-
export const gitBranchDeleteRemote = (...a) => b().gitBranchDeleteRemote(...a);
|
|
72
|
-
export const gitBranchRename = (...a) => b().gitBranchRename(...a);
|
|
73
|
-
export const gitBranches = (...a) => b().gitBranches(...a);
|
|
74
66
|
export const gitCheckout = (...a) => b().gitCheckout(...a);
|
|
75
67
|
export const gitCheckoutDetach = (...a) => b().gitCheckoutDetach(...a);
|
|
76
68
|
export const gitCheckoutNew = (...a) => b().gitCheckoutNew(...a);
|
|
77
69
|
export const gitCheckoutTrack = (...a) => b().gitCheckoutTrack(...a);
|
|
78
|
-
export const gitCherryPick = (...a) => b().gitCherryPick(...a);
|
|
79
|
-
export const gitCommit = (...a) => b().gitCommit(...a);
|
|
80
|
-
export const gitCommitAmend = (...a) => b().gitCommitAmend(...a);
|
|
81
70
|
export const gitCommitFiles = (...a) => b().gitCommitFiles(...a);
|
|
82
|
-
export const gitContinueOp = (...a) => b().gitContinueOp(...a);
|
|
83
71
|
export const gitDiscard = (...a) => b().gitDiscard(...a);
|
|
84
|
-
export const gitFetch = (...a) => b().gitFetch(...a);
|
|
85
|
-
export const gitFetchPr = (...a) => b().gitFetchPr(...a);
|
|
86
72
|
export const gitFileBytes = (...a) => b().gitFileBytes(...a);
|
|
87
73
|
export const gitFileDiff = (...a) => b().gitFileDiff(...a);
|
|
88
|
-
export const gitHead = (...a) => b().gitHead(...a);
|
|
89
|
-
export const gitHeadFile = (...a) => b().gitHeadFile(...a);
|
|
90
|
-
export const gitIndexFile = (...a) => b().gitIndexFile(...a);
|
|
91
74
|
export const gitInfo = (...a) => b().gitInfo(...a);
|
|
92
75
|
export const gitLogGraph = (...a) => b().gitLogGraph(...a);
|
|
93
|
-
export const gitMerge = (...a) => b().gitMerge(...a);
|
|
94
|
-
export const gitMergeAs = (...a) => b().gitMergeAs(...a);
|
|
95
|
-
export const gitMergeBase = (...a) => b().gitMergeBase(...a);
|
|
96
|
-
export const gitPendingOp = (...a) => b().gitPendingOp(...a);
|
|
97
76
|
export const gitPoll = (...a) => b().gitPoll(...a);
|
|
98
|
-
export const gitPull = (...a) => b().gitPull(...a);
|
|
99
|
-
export const gitPullRebase = (...a) => b().gitPullRebase(...a);
|
|
100
|
-
export const gitPush = (...a) => b().gitPush(...a);
|
|
101
|
-
export const gitPushBranch = (...a) => b().gitPushBranch(...a);
|
|
102
77
|
export const gitRangeFiles = (...a) => b().gitRangeFiles(...a);
|
|
103
|
-
export const gitRangeSubjects = (...a) => b().gitRangeSubjects(...a);
|
|
104
|
-
export const gitRebase = (...a) => b().gitRebase(...a);
|
|
105
78
|
export const gitRefs = (...a) => b().gitRefs(...a);
|
|
106
|
-
export const gitRemoteUrl = (...a) => b().gitRemoteUrl(...a);
|
|
107
|
-
export const gitRemotes = (...a) => b().gitRemotes(...a);
|
|
108
79
|
export const gitRepos = (...a) => b().gitRepos(...a);
|
|
109
|
-
export const
|
|
110
|
-
export const gitRevert = (...a) => b().gitRevert(...a);
|
|
111
|
-
export const gitShowFile = (...a) => b().gitShowFile(...a);
|
|
112
|
-
export const gitStage = (...a) => b().gitStage(...a);
|
|
113
|
-
export const gitStageContent = (...a) => b().gitStageContent(...a);
|
|
114
|
-
export const gitStash = (...a) => b().gitStash(...a);
|
|
115
|
-
export const gitStashApply = (...a) => b().gitStashApply(...a);
|
|
116
|
-
export const gitStashClear = (...a) => b().gitStashClear(...a);
|
|
117
|
-
export const gitStashDrop = (...a) => b().gitStashDrop(...a);
|
|
118
|
-
export const gitStashList = (...a) => b().gitStashList(...a);
|
|
119
|
-
export const gitStashPop = (...a) => b().gitStashPop(...a);
|
|
120
|
-
export const gitStashShow = (...a) => b().gitStashShow(...a);
|
|
80
|
+
export const gitRun = (...a) => b().gitRun(...a);
|
|
121
81
|
export const gitStatus = (...a) => b().gitStatus(...a);
|
|
122
|
-
export const gitTag = (...a) => b().gitTag(...a);
|
|
123
|
-
export const gitTagDelete = (...a) => b().gitTagDelete(...a);
|
|
124
|
-
export const gitTagDeleteRemote = (...a) => b().gitTagDeleteRemote(...a);
|
|
125
|
-
export const gitTagPush = (...a) => b().gitTagPush(...a);
|
|
126
|
-
export const gitUndoCommit = (...a) => b().gitUndoCommit(...a);
|
|
127
|
-
export const gitUnstage = (...a) => b().gitUnstage(...a);
|
|
128
82
|
export const glabApi = (...a) => b().glabApi(...a);
|
|
129
83
|
export const glabRun = (...a) => b().glabRun(...a);
|
|
130
84
|
export const googleAccount = (...a) => b().googleAccount(...a);
|
|
131
85
|
export const googleSignIn = (...a) => b().googleSignIn(...a);
|
|
132
86
|
export const googleSignOut = (...a) => b().googleSignOut(...a);
|
|
133
87
|
export const googleToken = (...a) => b().googleToken(...a);
|
|
134
|
-
export const historyClear = (...a) => b().historyClear(...a);
|
|
135
|
-
export const historyDelete = (...a) => b().historyDelete(...a);
|
|
136
88
|
export const httpCancel = (...a) => b().httpCancel(...a);
|
|
137
89
|
export const httpSend = (...a) => b().httpSend(...a);
|
|
138
90
|
export const httpSendAs = (...a) => b().httpSendAs(...a);
|
|
@@ -141,29 +93,21 @@ export const importIconPack = (...a) => b().importIconPack(...a);
|
|
|
141
93
|
export const installAgentHooks = (...a) => b().installAgentHooks(...a);
|
|
142
94
|
export const installAgentMcp = (...a) => b().installAgentMcp(...a);
|
|
143
95
|
export const installAgentSkills = (...a) => b().installAgentSkills(...a);
|
|
144
|
-
export const installClaudeHooks = (...a) => b().installClaudeHooks(...a);
|
|
145
|
-
export const installClaudeMcp = (...a) => b().installClaudeMcp(...a);
|
|
146
96
|
export const installFromRegistry = (...a) => b().installFromRegistry(...a);
|
|
147
|
-
export const installPlugin = (...a) => b().installPlugin(...a);
|
|
148
97
|
export const isFullscreen = (...a) => b().isFullscreen(...a);
|
|
149
98
|
export const languagesDir = (...a) => b().languagesDir(...a);
|
|
150
99
|
export const launchFiles = (...a) => b().launchFiles(...a);
|
|
151
100
|
export const listApps = (...a) => b().listApps(...a);
|
|
152
101
|
export const listInstalled = (...a) => b().listInstalled(...a);
|
|
102
|
+
export const logLatest = (...a) => b().logLatest(...a);
|
|
153
103
|
export const mediaMeta = (...a) => b().mediaMeta(...a);
|
|
154
104
|
export const mediaSetNowPlaying = (...a) => b().mediaSetNowPlaying(...a);
|
|
155
105
|
export const mediaSetPlayback = (...a) => b().mediaSetPlayback(...a);
|
|
156
106
|
export const memUsage = (...a) => b().memUsage(...a);
|
|
157
|
-
export const notesDelete = (...a) => b().notesDelete(...a);
|
|
158
|
-
export const notesList = (...a) => b().notesList(...a);
|
|
159
|
-
export const notesRead = (...a) => b().notesRead(...a);
|
|
160
|
-
export const notesRename = (...a) => b().notesRename(...a);
|
|
161
|
-
export const notesScope = (...a) => b().notesScope(...a);
|
|
162
|
-
export const notesWrite = (...a) => b().notesWrite(...a);
|
|
163
107
|
export const notifyOs = (...a) => b().notifyOs(...a);
|
|
164
|
-
export const openApp = (...a) => b().openApp(...a);
|
|
165
108
|
export const openExternal = (...a) => b().openExternal(...a);
|
|
166
109
|
export const openFile = (...a) => b().openFile(...a);
|
|
110
|
+
export const openTestDeck = (...a) => b().openTestDeck(...a);
|
|
167
111
|
export const pickAnyFile = (...a) => b().pickAnyFile(...a);
|
|
168
112
|
export const pickExe = (...a) => b().pickExe(...a);
|
|
169
113
|
export const pickFile = (...a) => b().pickFile(...a);
|
|
@@ -172,24 +116,15 @@ export const pickImage = (...a) => b().pickImage(...a);
|
|
|
172
116
|
export const pickSavePath = (...a) => b().pickSavePath(...a);
|
|
173
117
|
export const pickScript = (...a) => b().pickScript(...a);
|
|
174
118
|
export const pluginsDir = (...a) => b().pluginsDir(...a);
|
|
175
|
-
export const powerAction = (...a) => b().powerAction(...a);
|
|
176
119
|
export const procKill = (...a) => b().procKill(...a);
|
|
177
120
|
export const procStream = (...a) => b().procStream(...a);
|
|
178
121
|
export const procWrite = (...a) => b().procWrite(...a);
|
|
179
|
-
export const readDownloads = (...a) => b().readDownloads(...a);
|
|
180
122
|
export const readIconPackTheme = (...a) => b().readIconPackTheme(...a);
|
|
181
|
-
export const readLibrary = (...a) => b().readLibrary(...a);
|
|
182
|
-
export const readMusicSuggestions = (...a) => b().readMusicSuggestions(...a);
|
|
183
|
-
export const readMusicTaste = (...a) => b().readMusicTaste(...a);
|
|
184
123
|
export const readTermLayout = (...a) => b().readTermLayout(...a);
|
|
185
124
|
export const readTrackMeta = (...a) => b().readTrackMeta(...a);
|
|
186
125
|
export const remoteHome = (...a) => b().remoteHome(...a);
|
|
187
126
|
export const remoteStat = (...a) => b().remoteStat(...a);
|
|
188
127
|
export const remoteSudoPassword = (...a) => b().remoteSudoPassword(...a);
|
|
189
|
-
export const removeAgentHooks = (...a) => b().removeAgentHooks(...a);
|
|
190
|
-
export const removeAgentMcp = (...a) => b().removeAgentMcp(...a);
|
|
191
|
-
export const removeClaudeHooks = (...a) => b().removeClaudeHooks(...a);
|
|
192
|
-
export const removeClaudeMcp = (...a) => b().removeClaudeMcp(...a);
|
|
193
128
|
export const removeLanguage = (...a) => b().removeLanguage(...a);
|
|
194
129
|
export const restartApp = (...a) => b().restartApp(...a);
|
|
195
130
|
export const revealInFolder = (...a) => b().revealInFolder(...a);
|
|
@@ -197,37 +132,20 @@ export const runScriptNow = (...a) => b().runScriptNow(...a);
|
|
|
197
132
|
export const sessionList = (...a) => b().sessionList(...a);
|
|
198
133
|
export const setAutostart = (...a) => b().setAutostart(...a);
|
|
199
134
|
export const setDebugTargets = (...a) => b().setDebugTargets(...a);
|
|
200
|
-
export const setVaultRoot = (...a) => b().setVaultRoot(...a);
|
|
201
135
|
export const setWallpaper = (...a) => b().setWallpaper(...a);
|
|
202
136
|
export const sshHosts = (...a) => b().sshHosts(...a);
|
|
203
137
|
export const sshTunnelClose = (...a) => b().sshTunnelClose(...a);
|
|
204
|
-
export const sshTunnelList = (...a) => b().sshTunnelList(...a);
|
|
205
138
|
export const sshTunnelOpen = (...a) => b().sshTunnelOpen(...a);
|
|
206
139
|
export const startupMarks = (...a) => b().startupMarks(...a);
|
|
207
|
-
export const startupRead = (...a) => b().startupRead(...a);
|
|
208
|
-
export const startupWrite = (...a) => b().startupWrite(...a);
|
|
209
140
|
export const toggleFullscreen = (...a) => b().toggleFullscreen(...a);
|
|
210
|
-
export const vaultDataRead = (...a) => b().vaultDataRead(...a);
|
|
211
|
-
export const vaultDataWrite = (...a) => b().vaultDataWrite(...a);
|
|
212
|
-
export const vaultRelativize = (...a) => b().vaultRelativize(...a);
|
|
213
|
-
export const vaultResolve = (...a) => b().vaultResolve(...a);
|
|
214
141
|
export const vpsRunsPull = (...a) => b().vpsRunsPull(...a);
|
|
215
142
|
export const watchFiles = (...a) => b().watchFiles(...a);
|
|
216
|
-
export const writeDownloads = (...a) => b().writeDownloads(...a);
|
|
217
|
-
export const writeLibrary = (...a) => b().writeLibrary(...a);
|
|
218
|
-
export const writeMusicTaste = (...a) => b().writeMusicTaste(...a);
|
|
219
|
-
export const writeTermLayout = (...a) => b().writeTermLayout(...a);
|
|
220
143
|
|
|
221
144
|
export const DEFAULT_SETTINGS = new Proxy({}, {
|
|
222
145
|
get: (_t, k) => b().DEFAULT_SETTINGS[k],
|
|
223
146
|
ownKeys: () => Reflect.ownKeys(b().DEFAULT_SETTINGS),
|
|
224
147
|
getOwnPropertyDescriptor: (_t, k) => ({ ...Object.getOwnPropertyDescriptor(b().DEFAULT_SETTINGS, k), configurable: true }),
|
|
225
148
|
});
|
|
226
|
-
export const EMPTY_LIBRARY = new Proxy({}, {
|
|
227
|
-
get: (_t, k) => b().EMPTY_LIBRARY[k],
|
|
228
|
-
ownKeys: () => Reflect.ownKeys(b().EMPTY_LIBRARY),
|
|
229
|
-
getOwnPropertyDescriptor: (_t, k) => ({ ...Object.getOwnPropertyDescriptor(b().EMPTY_LIBRARY, k), configurable: true }),
|
|
230
|
-
});
|
|
231
149
|
export const FOOTER_ITEMS = new Proxy({}, {
|
|
232
150
|
get: (_t, k) => b().FOOTER_ITEMS[k],
|
|
233
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
|
@@ -39,6 +39,7 @@ export const moveZone = (...a) => t().moveZone(...a);
|
|
|
39
39
|
export const mutateLayout = (...a) => t().mutateLayout(...a);
|
|
40
40
|
export const nextActiveAfterClose = (...a) => t().nextActiveAfterClose(...a);
|
|
41
41
|
export const nextZoneSpot = (...a) => t().nextZoneSpot(...a);
|
|
42
|
+
export const openFile = (...a) => t().openFile(...a);
|
|
42
43
|
export const openTabInWorkspace = (...a) => t().openTabInWorkspace(...a);
|
|
43
44
|
export const paneCwd = (...a) => t().paneCwd(...a);
|
|
44
45
|
export const paneHasAgent = (...a) => t().paneHasAgent(...a);
|
package/shim/ui.js
CHANGED
|
@@ -63,7 +63,6 @@ export const normalizeChord = (...a) => u().normalizeChord(...a);
|
|
|
63
63
|
export const onKey = (...a) => u().onKey(...a);
|
|
64
64
|
export const onKeys = (...a) => u().onKeys(...a);
|
|
65
65
|
export const parseChord = (...a) => u().parseChord(...a);
|
|
66
|
-
export const parseDirty = (...a) => u().parseDirty(...a);
|
|
67
66
|
export const pushScope = (...a) => u().pushScope(...a);
|
|
68
67
|
export const repoLabel = (...a) => u().repoLabel(...a);
|
|
69
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; }
|