@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.
Files changed (2) hide show
  1. package/bin/index.js +77 -19
  2. 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
- console.log(`\n◆ ${title}`);
108
- choices.forEach((c, i) => {
109
- const bullet = i === 0 ? "●" : "○";
110
- console.log(`│ ${bullet} ${i + 1}) ${c.label}`);
111
- if (c.hint) console.log(`│ ${c.hint}`);
112
- });
113
- console.log("");
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 rl = readline.createInterface({
116
- input: process.stdin,
117
- output: process.stdout,
118
- });
119
- rl.question(`└ Enter choice [1-${choices.length}]: `, (answer) => {
120
- rl.close();
121
- const idx = parseInt(answer.trim(), 10) - 1;
122
- resolve(idx >= 0 && idx < choices.length ? idx : 0);
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 add "${pluginsDir}"`, { stdio: "pipe" });
254
+ execSync(`claude plugin marketplace remove "${MARKETPLACE_NAME}"`, { stdio: "pipe" });
199
255
  } catch {
200
- // marketplace may already be registeredcontinue
256
+ // didn't exist or not removableignore
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ztffn/presentation-generator-plugin",
3
- "version": "1.4.0",
3
+ "version": "1.4.1",
4
4
  "description": "Claude Code plugin for generating graph-based presentations",
5
5
  "bin": {
6
6
  "presentation-generator-plugin": "bin/index.js"