oh-aicoding-tool 0.1.7 → 0.1.8

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/cli.js +55 -33
  2. package/package.json +1 -1
package/bin/cli.js CHANGED
@@ -108,26 +108,38 @@ function rawKeySeq(raw) {
108
108
  return String(raw ?? "");
109
109
  }
110
110
 
111
- function parseRawKey(raw) {
112
- const seq = rawKeySeq(raw);
113
- if (seq === "\x03") return { name: "ctrl-c", sequence: seq };
114
- if (seq === "\x1b[A" || seq === "\x1bOA" || seq === "\x00H" || seq === "\xe0H") return { name: "up", sequence: seq };
115
- if (seq === "\x1b[B" || seq === "\x1bOB" || seq === "\x00P" || seq === "\xe0P") return { name: "down", sequence: seq };
116
- if (seq === "\r" || seq === "\n" || seq === "\r\n") return { name: "enter", sequence: seq };
117
- if (seq === " ") return { name: "space", sequence: seq };
118
- if (seq === "\x1b") return { name: "escape", sequence: seq };
119
- if (/^[1-9]$/.test(seq)) return { name: "number", number: Number.parseInt(seq, 10), sequence: seq };
120
- if (seq.length === 1) return { name: seq.toLowerCase(), sequence: seq };
121
- return { name: "", sequence: seq };
111
+ function readKeyFromBuffer(buffer) {
112
+ if (!buffer) return { pending: true, rest: "" };
113
+ if (buffer === "\x1b" || buffer === "\x1b[" || buffer === "\x1bO" || buffer === "\x00" || buffer === "\xe0") {
114
+ return { pending: true, rest: buffer };
115
+ }
116
+
117
+ const three = buffer.slice(0, 3);
118
+ if (three === "\x1b[A" || three === "\x1bOA") return { key: { name: "up", sequence: three }, rest: buffer.slice(3) };
119
+ if (three === "\x1b[B" || three === "\x1bOB") return { key: { name: "down", sequence: three }, rest: buffer.slice(3) };
120
+
121
+ const two = buffer.slice(0, 2);
122
+ if (two === "\x00H" || two === "\xe0H") return { key: { name: "up", sequence: two }, rest: buffer.slice(2) };
123
+ if (two === "\x00P" || two === "\xe0P") return { key: { name: "down", sequence: two }, rest: buffer.slice(2) };
124
+ if (two === "\r\n") return { key: { name: "enter", sequence: two }, rest: buffer.slice(2) };
125
+
126
+ const seq = buffer[0];
127
+ if (seq === "\x03") return { key: { name: "ctrl-c", sequence: seq }, rest: buffer.slice(1) };
128
+ if (seq === "\r" || seq === "\n") return { key: { name: "enter", sequence: seq }, rest: buffer.slice(1) };
129
+ if (seq === " ") return { key: { name: "space", sequence: seq }, rest: buffer.slice(1) };
130
+ if (seq === "\x1b") return { key: { name: "escape", sequence: seq }, rest: buffer.slice(1) };
131
+ if (/^[1-9]$/.test(seq)) return { key: { name: "number", number: Number.parseInt(seq, 10), sequence: seq }, rest: buffer.slice(1) };
132
+ return { key: { name: seq.toLowerCase(), sequence: seq }, rest: buffer.slice(1) };
122
133
  }
123
134
 
124
135
  async function askMultiChoice(rl, title, choices, subtitle) {
125
136
  if (process.stdin.isTTY && process.stdout.isTTY) {
126
137
  rl.pause();
127
138
  return await new Promise((resolve) => {
128
- let index = 0;
129
- const selected = new Set(choices.filter((choice) => choice.selected).map((choice) => choice.value));
130
- const stdin = process.stdin;
139
+ let index = 0;
140
+ let keyBuffer = "";
141
+ const selected = new Set(choices.filter((choice) => choice.selected).map((choice) => choice.value));
142
+ const stdin = process.stdin;
131
143
 
132
144
  function cleanup(value) {
133
145
  stdin.off("data", onData);
@@ -146,25 +158,35 @@ async function askMultiChoice(rl, title, choices, subtitle) {
146
158
  }
147
159
 
148
160
  function onData(raw) {
149
- const key = parseRawKey(raw);
150
- if (key.name === "ctrl-c") return cleanup([]);
151
- if (key.name === "up") {
152
- index = (index - 1 + choices.length) % choices.length;
153
- renderMultiChoice(title, choices, index, selected, subtitle);
154
- return;
155
- }
156
- if (key.name === "down") {
157
- index = (index + 1) % choices.length;
158
- renderMultiChoice(title, choices, index, selected, subtitle);
159
- return;
160
- }
161
- if (key.name === "q" || key.name === "escape") return cleanup([]);
162
- if (key.name === "space") return toggle();
163
- if (key.name === "enter") return cleanup([...selected]);
164
- const number = key.name === "number" ? key.number : Number.NaN;
165
- if (Number.isInteger(number) && choices[number - 1]) {
166
- index = number - 1;
167
- toggle();
161
+ keyBuffer += rawKeySeq(raw);
162
+ while (keyBuffer) {
163
+ const result = readKeyFromBuffer(keyBuffer);
164
+ if (result.pending) {
165
+ keyBuffer = result.rest;
166
+ return;
167
+ }
168
+ keyBuffer = result.rest;
169
+ const key = result.key;
170
+
171
+ if (key.name === "ctrl-c") return cleanup([]);
172
+ if (key.name === "up") {
173
+ index = (index - 1 + choices.length) % choices.length;
174
+ renderMultiChoice(title, choices, index, selected, subtitle);
175
+ return;
176
+ }
177
+ if (key.name === "down") {
178
+ index = (index + 1) % choices.length;
179
+ renderMultiChoice(title, choices, index, selected, subtitle);
180
+ return;
181
+ }
182
+ if (key.name === "q" || key.name === "escape") return cleanup([]);
183
+ if (key.name === "space") return toggle();
184
+ if (key.name === "enter") return cleanup([...selected]);
185
+ const number = key.name === "number" ? key.number : Number.NaN;
186
+ if (Number.isInteger(number) && choices[number - 1]) {
187
+ index = number - 1;
188
+ toggle();
189
+ }
168
190
  }
169
191
  }
170
192
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oh-aicoding-tool",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Interactive installer for AI coding tools: Langfuse tracing and oh-ai-report.",