@step-forge/step-forge 0.0.25-alpha.1 → 0.0.25-alpha.2
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/RUNTIME.md +5 -2
- package/dist/cli.cjs +65 -7
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +65 -7
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/RUNTIME.md
CHANGED
|
@@ -166,8 +166,11 @@ population):
|
|
|
166
166
|
every subsequent file change. Pressing Enter again forces a re-run.
|
|
167
167
|
- **Editing the query** suspends auto-runs until you press Enter again — so you
|
|
168
168
|
can retarget without a half-typed selection firing.
|
|
169
|
-
- **↑ / ↓** move the selection highlight
|
|
170
|
-
pane when a run has more failures than fit on
|
|
169
|
+
- **↑ / ↓** move the selection highlight. The **mouse wheel** (or **PgUp /
|
|
170
|
+
PgDn**) scrolls the failures pane when a run has more failures than fit on
|
|
171
|
+
screen — the prompt, selection, and stats stay pinned. (While the dashboard is
|
|
172
|
+
up the wheel drives the results pane, so hold **Shift** for native terminal
|
|
173
|
+
text selection.)
|
|
171
174
|
- **Esc** disarms and returns to browsing.
|
|
172
175
|
- **Ctrl-C** quits and restores the terminal.
|
|
173
176
|
|
package/dist/cli.cjs
CHANGED
|
@@ -12,6 +12,7 @@ let node_util = require("node:util");
|
|
|
12
12
|
let node_child_process = require("node:child_process");
|
|
13
13
|
let node_readline = require("node:readline");
|
|
14
14
|
node_readline = require_gherkinParser.__toESM(node_readline, 1);
|
|
15
|
+
let node_stream = require("node:stream");
|
|
15
16
|
//#region src/runtime/config.ts
|
|
16
17
|
const DEFAULT_FEATURES = "**/*.feature";
|
|
17
18
|
const DEFAULT_STEPS = "**/*.steps.ts";
|
|
@@ -528,9 +529,14 @@ var Prompt = class {
|
|
|
528
529
|
results = freshResults();
|
|
529
530
|
failScroll = 0;
|
|
530
531
|
started = false;
|
|
532
|
+
/** Filtered key stream: raw stdin minus the mouse sequences we handle. */
|
|
533
|
+
input;
|
|
531
534
|
keyListener;
|
|
535
|
+
dataListener;
|
|
532
536
|
resizeListener;
|
|
533
537
|
signalCleanup;
|
|
538
|
+
/** Carry an incomplete mouse sequence split across stdin chunks. */
|
|
539
|
+
mousePending = "";
|
|
534
540
|
constructor(options) {
|
|
535
541
|
this.handlers = options.handlers;
|
|
536
542
|
this.prefix = options.prefix ?? "› ";
|
|
@@ -585,11 +591,14 @@ var Prompt = class {
|
|
|
585
591
|
start() {
|
|
586
592
|
if (this.started) return;
|
|
587
593
|
this.started = true;
|
|
588
|
-
node_readline.emitKeypressEvents(process.stdin);
|
|
589
594
|
if (process.stdin.isTTY) process.stdin.setRawMode(true);
|
|
590
|
-
write("\x1B[?1049h");
|
|
595
|
+
write("\x1B[?1049h\x1B[?1000h\x1B[?1006h");
|
|
596
|
+
this.input = new node_stream.PassThrough();
|
|
597
|
+
node_readline.emitKeypressEvents(this.input);
|
|
591
598
|
this.keyListener = (str, key) => this.onKey(str, key ?? {});
|
|
592
|
-
|
|
599
|
+
this.input.on("keypress", this.keyListener);
|
|
600
|
+
this.dataListener = (chunk) => this.onStdinData(chunk);
|
|
601
|
+
process.stdin.on("data", this.dataListener);
|
|
593
602
|
process.stdin.resume();
|
|
594
603
|
this.resizeListener = () => this.render();
|
|
595
604
|
process.stdout.on("resize", this.resizeListener);
|
|
@@ -600,11 +609,14 @@ var Prompt = class {
|
|
|
600
609
|
stop() {
|
|
601
610
|
if (!this.started) return;
|
|
602
611
|
this.started = false;
|
|
603
|
-
if (this.
|
|
612
|
+
if (this.dataListener) process.stdin.off("data", this.dataListener);
|
|
613
|
+
if (this.keyListener) this.input?.off("keypress", this.keyListener);
|
|
604
614
|
if (this.resizeListener) process.stdout.off("resize", this.resizeListener);
|
|
605
615
|
this.signalCleanup?.();
|
|
606
616
|
this.restoreTerminal();
|
|
607
617
|
process.stdin.pause();
|
|
618
|
+
this.input = void 0;
|
|
619
|
+
this.mousePending = "";
|
|
608
620
|
}
|
|
609
621
|
/** Force a repaint (after mutating `status`, say). No-op until started. */
|
|
610
622
|
redraw() {
|
|
@@ -617,6 +629,7 @@ var Prompt = class {
|
|
|
617
629
|
*/
|
|
618
630
|
restoreTerminal() {
|
|
619
631
|
if (process.stdin.isTTY) process.stdin.setRawMode(false);
|
|
632
|
+
write("\x1B[?1006l\x1B[?1000l");
|
|
620
633
|
write("\x1B[?25h");
|
|
621
634
|
write("\x1B[?1049l");
|
|
622
635
|
}
|
|
@@ -720,12 +733,57 @@ var Prompt = class {
|
|
|
720
733
|
this.reWindow();
|
|
721
734
|
this.render();
|
|
722
735
|
}
|
|
723
|
-
/** Scroll the failures pane by
|
|
736
|
+
/** Scroll the failures pane by whole pages (PgUp/PgDn). */
|
|
724
737
|
scrollFailures(pages) {
|
|
725
|
-
this.
|
|
726
|
-
this.render();
|
|
738
|
+
this.adjustFailScroll(pages * this.failPage);
|
|
727
739
|
}
|
|
728
740
|
failPage = 1;
|
|
741
|
+
/** Move the failures viewport by `deltaLines` (clamped in {@link render}). */
|
|
742
|
+
adjustFailScroll(deltaLines) {
|
|
743
|
+
this.failScroll = Math.max(0, this.failScroll + deltaLines);
|
|
744
|
+
this.render();
|
|
745
|
+
}
|
|
746
|
+
/**
|
|
747
|
+
* Split a raw stdin chunk: decode SGR/legacy mouse sequences here (only the
|
|
748
|
+
* wheel is acted on — it scrolls the failures pane by a few lines) and forward
|
|
749
|
+
* everything else to readline. An incomplete trailing mouse sequence is held
|
|
750
|
+
* in {@link mousePending} for the next chunk. `latin1` keeps every byte 1:1 so
|
|
751
|
+
* the forwarded bytes reassemble exactly (including multibyte input).
|
|
752
|
+
*/
|
|
753
|
+
onStdinData(chunk) {
|
|
754
|
+
const buf = this.mousePending + chunk.toString("latin1");
|
|
755
|
+
let out = "";
|
|
756
|
+
let i = 0;
|
|
757
|
+
while (i < buf.length) {
|
|
758
|
+
if (buf.startsWith("\x1B[<", i)) {
|
|
759
|
+
const rel = /[Mm]/.exec(buf.slice(i + 3));
|
|
760
|
+
if (!rel) break;
|
|
761
|
+
const end = i + 3 + rel.index + 1;
|
|
762
|
+
this.handleMouse(buf.slice(i, end));
|
|
763
|
+
i = end;
|
|
764
|
+
continue;
|
|
765
|
+
}
|
|
766
|
+
if (buf.startsWith("\x1B[M", i)) {
|
|
767
|
+
if (i + 6 > buf.length) break;
|
|
768
|
+
this.handleMouse(buf.slice(i, i + 6));
|
|
769
|
+
i += 6;
|
|
770
|
+
continue;
|
|
771
|
+
}
|
|
772
|
+
out += buf[i];
|
|
773
|
+
i++;
|
|
774
|
+
}
|
|
775
|
+
this.mousePending = buf.slice(i);
|
|
776
|
+
if (out) this.input?.write(Buffer.from(out, "latin1"));
|
|
777
|
+
}
|
|
778
|
+
/** Act on a decoded mouse sequence — wheel up/down scrolls failures; else ignore. */
|
|
779
|
+
handleMouse(seq) {
|
|
780
|
+
let button = null;
|
|
781
|
+
const sgr = /^\x1b\[<(\d+);\d+;\d+[Mm]$/.exec(seq);
|
|
782
|
+
if (sgr) button = parseInt(sgr[1], 10);
|
|
783
|
+
else if (seq.length === 6) button = seq.charCodeAt(3) - 32;
|
|
784
|
+
if (button === null || (button & 64) === 0) return;
|
|
785
|
+
this.adjustFailScroll((button & 1) === 0 ? -3 : 3);
|
|
786
|
+
}
|
|
729
787
|
refilter() {
|
|
730
788
|
const q = this.query.trim().toLowerCase();
|
|
731
789
|
this.matches = this.all.map((s, index) => ({
|