@ztffn/presentation-generator-plugin 1.4.0 → 1.4.1
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/bin/index.js +77 -19
- package/package.json +1 -1
package/bin/index.js
CHANGED
|
@@ -5,7 +5,6 @@ const { execSync } = require("child_process");
|
|
|
5
5
|
const fs = require("fs");
|
|
6
6
|
const path = require("path");
|
|
7
7
|
const os = require("os");
|
|
8
|
-
const readline = require("readline");
|
|
9
8
|
|
|
10
9
|
const NPM_PACKAGE = "@ztffn/presentation-generator-plugin";
|
|
11
10
|
const PLUGIN_NAME = "presentation-generator";
|
|
@@ -104,23 +103,78 @@ async function downloadAndExtract(version, tarballUrl, installDir) {
|
|
|
104
103
|
|
|
105
104
|
function promptChoice(title, choices) {
|
|
106
105
|
return new Promise((resolve) => {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
106
|
+
let selected = 0;
|
|
107
|
+
|
|
108
|
+
// Lines rendered per call to draw() — used to reposition cursor on redraw
|
|
109
|
+
const lineCount = () => {
|
|
110
|
+
let n = 2; // blank line + title line
|
|
111
|
+
choices.forEach((c) => { n += c.hint ? 2 : 1; });
|
|
112
|
+
n += 1; // trailing │
|
|
113
|
+
n += 1; // └ line (no trailing newline — cursor stays here)
|
|
114
|
+
return n;
|
|
115
|
+
};
|
|
114
116
|
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
117
|
+
const C = {
|
|
118
|
+
cyan: (s) => `\x1B[36m${s}\x1B[0m`,
|
|
119
|
+
dim: (s) => `\x1B[2m${s}\x1B[0m`,
|
|
120
|
+
up: (n) => `\x1B[${n}A`,
|
|
121
|
+
clear: () => "\x1B[0J",
|
|
122
|
+
cursorHide: "\x1B[?25l",
|
|
123
|
+
cursorShow: "\x1B[?25h",
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const draw = (first = false) => {
|
|
127
|
+
if (!first) {
|
|
128
|
+
// Reposition to top of the widget and clear downward
|
|
129
|
+
process.stdout.write(C.up(lineCount() - 1) + C.clear());
|
|
130
|
+
}
|
|
131
|
+
process.stdout.write(`\n◆ ${title}\n`);
|
|
132
|
+
choices.forEach((c, i) => {
|
|
133
|
+
const active = i === selected;
|
|
134
|
+
const bullet = active ? C.cyan("●") : "○";
|
|
135
|
+
const label = active ? C.cyan(c.label) : c.label;
|
|
136
|
+
process.stdout.write(`│ ${bullet} ${label}\n`);
|
|
137
|
+
if (c.hint) process.stdout.write(`│ ${C.dim(c.hint)}\n`);
|
|
138
|
+
});
|
|
139
|
+
process.stdout.write("│\n");
|
|
140
|
+
process.stdout.write(C.dim("└ ↑↓ to move · Enter to confirm"));
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
process.stdout.write(C.cursorHide);
|
|
144
|
+
draw(true);
|
|
145
|
+
|
|
146
|
+
process.stdin.setRawMode(true);
|
|
147
|
+
process.stdin.resume();
|
|
148
|
+
process.stdin.setEncoding("utf8");
|
|
149
|
+
|
|
150
|
+
const onKey = (key) => {
|
|
151
|
+
if (key === "\x1B[A") { // ↑
|
|
152
|
+
selected = (selected - 1 + choices.length) % choices.length;
|
|
153
|
+
draw();
|
|
154
|
+
} else if (key === "\x1B[B") { // ↓
|
|
155
|
+
selected = (selected + 1) % choices.length;
|
|
156
|
+
draw();
|
|
157
|
+
} else if (key === "\r" || key === " ") { // Enter or Space
|
|
158
|
+
process.stdin.setRawMode(false);
|
|
159
|
+
process.stdin.pause();
|
|
160
|
+
process.stdin.removeListener("data", onKey);
|
|
161
|
+
process.stdout.write(C.cursorShow);
|
|
162
|
+
|
|
163
|
+
// Redraw with confirmed selection, then leave a clean line
|
|
164
|
+
process.stdout.write(C.up(lineCount() - 1) + C.clear());
|
|
165
|
+
process.stdout.write(`\n◆ ${title}\n`);
|
|
166
|
+
process.stdout.write(`│ ${C.cyan("●")} ${C.cyan(choices[selected].label)}\n`);
|
|
167
|
+
process.stdout.write("└\n\n");
|
|
168
|
+
|
|
169
|
+
resolve(selected);
|
|
170
|
+
} else if (key === "\x03") { // Ctrl-C
|
|
171
|
+
process.stdin.setRawMode(false);
|
|
172
|
+
process.stdout.write(C.cursorShow + "\n");
|
|
173
|
+
process.exit(0);
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
process.stdin.on("data", onKey);
|
|
124
178
|
});
|
|
125
179
|
}
|
|
126
180
|
|
|
@@ -194,12 +248,16 @@ function registerWithClaude(installDir, scope) {
|
|
|
194
248
|
const pluginsDir = path.dirname(installDir);
|
|
195
249
|
ensureMarketplaceJson(pluginsDir);
|
|
196
250
|
|
|
251
|
+
// Remove any stale registration (e.g. from a previous install at a different path)
|
|
252
|
+
// before adding the current one. Errors are ignored — it may not exist yet.
|
|
197
253
|
try {
|
|
198
|
-
execSync(`claude plugin marketplace
|
|
254
|
+
execSync(`claude plugin marketplace remove "${MARKETPLACE_NAME}"`, { stdio: "pipe" });
|
|
199
255
|
} catch {
|
|
200
|
-
//
|
|
256
|
+
// didn't exist or not removable — ignore
|
|
201
257
|
}
|
|
202
258
|
|
|
259
|
+
execSync(`claude plugin marketplace add "${pluginsDir}"`, { stdio: "pipe" });
|
|
260
|
+
|
|
203
261
|
const scopeFlag = scope === "project" ? "--scope project" : "--scope user";
|
|
204
262
|
execSync(`claude plugin install ${PLUGIN_NAME}@${MARKETPLACE_NAME} ${scopeFlag}`, { stdio: "pipe" });
|
|
205
263
|
}
|