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/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 closeApp = (...a) => b().closeApp(...a);
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 gitResetTo = (...a) => b().gitResetTo(...a);
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 installClaudeHooks = (...a) => b().installClaudeHooks(...a);
140
- export const installClaudeMcp = (...a) => b().installClaudeMcp(...a);
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
- export const listen = (event, handler) => window.__deck.listen(event, handler);
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
@@ -1,17 +1,20 @@
1
- # __NAME__
2
-
3
- A plugin for [Deck](https://deck.hasan-zarif.com).
4
-
5
- ## Developing
6
-
7
- ```sh
8
- npm run dev # build, and rebuild as you edit
9
- npm run install-local # copy it into Deck, which loads it without a restart
10
- ```
11
-
12
- `src/plugin.tsx` is the entry point; its default export is the tab.
13
-
14
- ## What it does
15
-
16
- Say what this plugin is for. This file is what the registry shows on its page, so it is worth
17
- writing for somebody deciding whether to install it rather than for yourself.
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.
@@ -1,4 +1,4 @@
1
- node_modules/
2
- # Copied in by the build from the SDK — not yours to edit, and not worth committing.
3
- shim/
4
- types/
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
@@ -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": "0.17.0"
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; }