@hypersocial/cli-games 0.2.3 → 0.2.4
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/dist/cli.js +101 -46
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +6 -0
- package/dist/index.js +66 -39
- package/dist/index.js.map +1 -1
- package/package.json +3 -1
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
// src/cli.ts
|
|
4
|
+
import { spawn } from "child_process";
|
|
5
|
+
import { fileURLToPath } from "url";
|
|
6
|
+
|
|
3
7
|
// src/themes/index.ts
|
|
4
8
|
var themes = {
|
|
5
9
|
cyan: {
|
|
@@ -2891,7 +2895,7 @@ function runCourierGame(terminal) {
|
|
|
2891
2895
|
flapAnimation = 4;
|
|
2892
2896
|
spawnParticles(particles, rigX, rigY + 2, 2, "\x1B[93m", ["\xB7", "\u2218"]);
|
|
2893
2897
|
}
|
|
2894
|
-
const
|
|
2898
|
+
const spawn2 = (x, y, count, color, chars) => spawnParticles(particles, x, y, count, color, chars);
|
|
2895
2899
|
const firework = (x, y, intensity) => spawnFirework(particles, x, y, intensity);
|
|
2896
2900
|
const popup = (x, y, text, color) => addPopup(popups, x, y, text, color);
|
|
2897
2901
|
const sparkleTrail = (x, y, count, color) => spawnSparkleTrail(particles, x, y, count, color);
|
|
@@ -2972,7 +2976,7 @@ function runCourierGame(terminal) {
|
|
|
2972
2976
|
if (levelCompleteAnimation % 8 === 0) {
|
|
2973
2977
|
const burstX = 5 + Math.random() * (screenWidth - 10);
|
|
2974
2978
|
const burstY = 2 + Math.random() * (screenHeight * 0.4);
|
|
2975
|
-
|
|
2979
|
+
spawn2(
|
|
2976
2980
|
burstX,
|
|
2977
2981
|
burstY,
|
|
2978
2982
|
5,
|
|
@@ -3184,7 +3188,7 @@ function runCourierGame(terminal) {
|
|
|
3184
3188
|
rigVY = -rigVY * 0.3;
|
|
3185
3189
|
if (impactVelocity > 0.3) {
|
|
3186
3190
|
screenShake = Math.min(5, Math.floor(impactVelocity * 4));
|
|
3187
|
-
|
|
3191
|
+
spawn2(rigX, rigY + 2, Math.floor(impactVelocity * 5), "\x1B[93m", ["\xB7", "\u2022"]);
|
|
3188
3192
|
}
|
|
3189
3193
|
if (rigPlatformIndex === pickupPlatformIndex && !hasPassenger && tutorialCountdown === 0) {
|
|
3190
3194
|
hasPassenger = true;
|
|
@@ -3193,14 +3197,14 @@ function runCourierGame(terminal) {
|
|
|
3193
3197
|
passengerVX = rigVX * 0.5;
|
|
3194
3198
|
passengerVY = rigVY * 0.5;
|
|
3195
3199
|
passengerGraceFrames = 20;
|
|
3196
|
-
|
|
3200
|
+
spawn2(rigX, rigY + 2, 6, themeColor, ["\u25C7", "\u2726"]);
|
|
3197
3201
|
popup(rigX - 3, rigY - 1, "GOT IT!", "\x1B[1;93m");
|
|
3198
3202
|
doorAnimation = 30;
|
|
3199
3203
|
const dropoffPlat = level.platforms[dropoffPlatformIndex];
|
|
3200
3204
|
const dropX = dropoffPlat.x + Math.floor(dropoffPlat.width / 2);
|
|
3201
3205
|
const dropY = dropoffPlat.y - 5;
|
|
3202
3206
|
popup(dropX - 4, dropY, "DROP HERE!", "\x1B[1;93m");
|
|
3203
|
-
|
|
3207
|
+
spawn2(dropX, dropY + 2, 4, "\x1B[93m", ["\u25BD", "\u25C7", "\u2726"]);
|
|
3204
3208
|
}
|
|
3205
3209
|
}
|
|
3206
3210
|
if (checkWaterCollision(rigY + 2)) {
|
|
@@ -3213,7 +3217,7 @@ function runCourierGame(terminal) {
|
|
|
3213
3217
|
crashAnimation = 35;
|
|
3214
3218
|
screenShake = 15;
|
|
3215
3219
|
splash(rigX, level.waterLevel, 1.25);
|
|
3216
|
-
|
|
3220
|
+
spawn2(rigX, rigY, 12, "\x1B[91m", ["\u2717", "\xD7", "\u2593", "\u2591", "!", "*"]);
|
|
3217
3221
|
popup(rigX - 4, rigY - 4, "\u{1F480} SPLASH! \u{1F480}", "\x1B[1;91m");
|
|
3218
3222
|
return;
|
|
3219
3223
|
}
|
|
@@ -3248,7 +3252,7 @@ function runCourierGame(terminal) {
|
|
|
3248
3252
|
passengerVX *= 0.8;
|
|
3249
3253
|
if (passPlatformIndex === dropoffPlatformIndex && Math.abs(passengerVY) < 0.3 && deliveringAnimation === 0) {
|
|
3250
3254
|
deliveringAnimation = 40;
|
|
3251
|
-
|
|
3255
|
+
spawn2(passengerX, passengerY - 1, 8, "\x1B[1;93m", ["\u2605", "\u2726", "\u25C6"]);
|
|
3252
3256
|
popup(passengerX - 4, passengerY - 3, "DROPPING!", "\x1B[1;93m");
|
|
3253
3257
|
}
|
|
3254
3258
|
}
|
|
@@ -15636,9 +15640,13 @@ function runHyperFighterGame(terminal) {
|
|
|
15636
15640
|
// src/games/gamesMenu.ts
|
|
15637
15641
|
function showGamesMenu(terminal, optionsOrCallback) {
|
|
15638
15642
|
const options = typeof optionsOrCallback === "function" ? { onGameSelect: optionsOrCallback } : optionsOrCallback || {};
|
|
15639
|
-
const { onGameSelect, onQuit } = options;
|
|
15643
|
+
const { onGameSelect, onActionSelect, onQuit, extraActions = [] } = options;
|
|
15640
15644
|
const themeColor = getCurrentThemeColor();
|
|
15641
15645
|
const lightTheme = isLightTheme2();
|
|
15646
|
+
const menuEntries = [
|
|
15647
|
+
...games.map((game) => ({ ...game, kind: "game" })),
|
|
15648
|
+
...extraActions.map((action) => ({ ...action, kind: "action" }))
|
|
15649
|
+
];
|
|
15642
15650
|
let running = true;
|
|
15643
15651
|
let selectedIndex = 0;
|
|
15644
15652
|
let scrollOffset = 0;
|
|
@@ -15681,50 +15689,56 @@ function showGamesMenu(terminal, optionsOrCallback) {
|
|
|
15681
15689
|
function renderSingleColumn(baseOutput, cols, rows, boxWidth, listStartY, maxVisibleGames) {
|
|
15682
15690
|
let output = baseOutput;
|
|
15683
15691
|
const boxX = Math.floor((cols - boxWidth) / 2);
|
|
15684
|
-
const
|
|
15692
|
+
const visibleEntries = Math.min(maxVisibleGames, menuEntries.length);
|
|
15685
15693
|
if (selectedIndex < scrollOffset) {
|
|
15686
15694
|
scrollOffset = selectedIndex;
|
|
15687
|
-
} else if (selectedIndex >= scrollOffset +
|
|
15688
|
-
scrollOffset = selectedIndex -
|
|
15695
|
+
} else if (selectedIndex >= scrollOffset + visibleEntries) {
|
|
15696
|
+
scrollOffset = selectedIndex - visibleEntries + 1;
|
|
15689
15697
|
}
|
|
15690
|
-
scrollOffset = Math.max(0, Math.min(scrollOffset,
|
|
15698
|
+
scrollOffset = Math.max(0, Math.min(scrollOffset, menuEntries.length - visibleEntries));
|
|
15691
15699
|
const hasScrollUp = scrollOffset > 0;
|
|
15692
|
-
const hasScrollDown = scrollOffset +
|
|
15700
|
+
const hasScrollDown = scrollOffset + visibleEntries < menuEntries.length;
|
|
15693
15701
|
const topScrollIndicator = hasScrollUp ? " \u25B2 more " : "\u2550".repeat(8);
|
|
15694
15702
|
const topBorderWidth = boxWidth - 2 - topScrollIndicator.length;
|
|
15695
15703
|
const topLeftPad = Math.floor(topBorderWidth / 2);
|
|
15696
15704
|
const topRightPad = topBorderWidth - topLeftPad;
|
|
15697
15705
|
output += `\x1B[${listStartY};${boxX}H${themeColor}\u2554${"\u2550".repeat(topLeftPad)}${hasScrollUp ? "\x1B[33m" : ""}${topScrollIndicator}${hasScrollUp ? themeColor : ""}${"\u2550".repeat(topRightPad)}\u2557\x1B[0m`;
|
|
15698
|
-
for (let vi = 0; vi <
|
|
15706
|
+
for (let vi = 0; vi < visibleEntries; vi++) {
|
|
15699
15707
|
const i = scrollOffset + vi;
|
|
15700
|
-
const
|
|
15708
|
+
const entry = menuEntries[i];
|
|
15701
15709
|
const y = listStartY + 1 + vi * 2;
|
|
15702
15710
|
const isSelected = i === selectedIndex;
|
|
15703
|
-
output +=
|
|
15711
|
+
output += renderEntry(entry, i, isSelected, y, boxX, boxWidth);
|
|
15704
15712
|
}
|
|
15705
|
-
const bottomY = listStartY + 1 +
|
|
15713
|
+
const bottomY = listStartY + 1 + visibleEntries * 2;
|
|
15706
15714
|
const bottomScrollIndicator = hasScrollDown ? " \u25BC more " : "\u2550".repeat(8);
|
|
15707
15715
|
const bottomBorderWidth = boxWidth - 2 - bottomScrollIndicator.length;
|
|
15708
15716
|
const bottomLeftPad = Math.floor(bottomBorderWidth / 2);
|
|
15709
15717
|
const bottomRightPad = bottomBorderWidth - bottomLeftPad;
|
|
15710
15718
|
output += `\x1B[${bottomY};${boxX}H${themeColor}\u255A${"\u2550".repeat(bottomLeftPad)}${hasScrollDown ? "\x1B[33m" : ""}${bottomScrollIndicator}${hasScrollDown ? themeColor : ""}${"\u2550".repeat(bottomRightPad)}\u255D\x1B[0m`;
|
|
15719
|
+
if (onActionSelect) {
|
|
15720
|
+
const actionHint = "Press V to Vibe Code Your Own Game";
|
|
15721
|
+
const hintX = Math.floor((cols - actionHint.length) / 2);
|
|
15722
|
+
const ctaStyle = lightTheme ? "\x1B[1;30;106m" : "\x1B[1;30;103m";
|
|
15723
|
+
output += `\x1B[${rows};${hintX}H${ctaStyle}${actionHint}\x1B[0m`;
|
|
15724
|
+
}
|
|
15711
15725
|
const controls = `\u2191\u2193 Navigate | ENTER Select | 1-9 Quick | Q Quit`;
|
|
15712
15726
|
output += `\x1B[${rows - 1};${Math.floor((cols - controls.length) / 2)}H\x1B[2m${controls}\x1B[0m`;
|
|
15713
15727
|
terminal.write(output);
|
|
15714
15728
|
}
|
|
15715
15729
|
function renderTwoColumns(baseOutput, cols, rows, boxWidth, columnGap, listStartY, maxVisibleGames) {
|
|
15716
15730
|
let output = baseOutput;
|
|
15717
|
-
const
|
|
15731
|
+
const entriesPerColumn = Math.min(maxVisibleGames, Math.ceil(menuEntries.length / 2));
|
|
15718
15732
|
const selectedRow = Math.floor(selectedIndex / 2);
|
|
15719
15733
|
if (selectedRow < scrollOffset) {
|
|
15720
15734
|
scrollOffset = selectedRow;
|
|
15721
|
-
} else if (selectedRow >= scrollOffset +
|
|
15722
|
-
scrollOffset = selectedRow -
|
|
15735
|
+
} else if (selectedRow >= scrollOffset + entriesPerColumn) {
|
|
15736
|
+
scrollOffset = selectedRow - entriesPerColumn + 1;
|
|
15723
15737
|
}
|
|
15724
|
-
const maxScroll = Math.max(0, Math.ceil(
|
|
15738
|
+
const maxScroll = Math.max(0, Math.ceil(menuEntries.length / 2) - entriesPerColumn);
|
|
15725
15739
|
scrollOffset = Math.max(0, Math.min(scrollOffset, maxScroll));
|
|
15726
15740
|
const hasScrollUp = scrollOffset > 0;
|
|
15727
|
-
const hasScrollDown = (scrollOffset +
|
|
15741
|
+
const hasScrollDown = (scrollOffset + entriesPerColumn) * 2 < menuEntries.length;
|
|
15728
15742
|
const totalWidth = boxWidth * 2 + columnGap;
|
|
15729
15743
|
const startX = Math.floor((cols - totalWidth) / 2);
|
|
15730
15744
|
const leftBoxX = startX;
|
|
@@ -15735,35 +15749,41 @@ function showGamesMenu(terminal, optionsOrCallback) {
|
|
|
15735
15749
|
const topRight = topBorderContent - topLeft;
|
|
15736
15750
|
output += `\x1B[${listStartY};${leftBoxX}H${themeColor}\u2554${"\u2550".repeat(topLeft)}${hasScrollUp ? "\x1B[33m" : ""}${topScrollIndicator}${hasScrollUp ? themeColor : ""}${"\u2550".repeat(topRight)}\u2557\x1B[0m`;
|
|
15737
15751
|
output += `\x1B[${listStartY};${rightBoxX}H${themeColor}\u2554${"\u2550".repeat(topLeft)}${hasScrollUp ? "\x1B[33m" : ""}${topScrollIndicator}${hasScrollUp ? themeColor : ""}${"\u2550".repeat(topRight)}\u2557\x1B[0m`;
|
|
15738
|
-
for (let row = 0; row <
|
|
15752
|
+
for (let row = 0; row < entriesPerColumn; row++) {
|
|
15739
15753
|
const leftIdx = (scrollOffset + row) * 2;
|
|
15740
15754
|
const rightIdx = leftIdx + 1;
|
|
15741
15755
|
const y = listStartY + 1 + row * 2;
|
|
15742
|
-
if (leftIdx <
|
|
15743
|
-
output +=
|
|
15756
|
+
if (leftIdx < menuEntries.length) {
|
|
15757
|
+
output += renderEntry(menuEntries[leftIdx], leftIdx, leftIdx === selectedIndex, y, leftBoxX, boxWidth);
|
|
15744
15758
|
} else {
|
|
15745
15759
|
output += `\x1B[${y};${leftBoxX}H${themeColor}\u2551${" ".repeat(boxWidth - 2)}\u2551\x1B[0m`;
|
|
15746
15760
|
output += `\x1B[${y + 1};${leftBoxX}H${themeColor}\u2551${" ".repeat(boxWidth - 2)}\u2551\x1B[0m`;
|
|
15747
15761
|
}
|
|
15748
|
-
if (rightIdx <
|
|
15749
|
-
output +=
|
|
15762
|
+
if (rightIdx < menuEntries.length) {
|
|
15763
|
+
output += renderEntry(menuEntries[rightIdx], rightIdx, rightIdx === selectedIndex, y, rightBoxX, boxWidth);
|
|
15750
15764
|
} else {
|
|
15751
15765
|
output += `\x1B[${y};${rightBoxX}H${themeColor}\u2551${" ".repeat(boxWidth - 2)}\u2551\x1B[0m`;
|
|
15752
15766
|
output += `\x1B[${y + 1};${rightBoxX}H${themeColor}\u2551${" ".repeat(boxWidth - 2)}\u2551\x1B[0m`;
|
|
15753
15767
|
}
|
|
15754
15768
|
}
|
|
15755
|
-
const bottomY = listStartY + 1 +
|
|
15769
|
+
const bottomY = listStartY + 1 + entriesPerColumn * 2;
|
|
15756
15770
|
const bottomScrollIndicator = hasScrollDown ? " \u25BC " : "\u2550\u2550\u2550";
|
|
15757
15771
|
const bottomBorderContent = boxWidth - 2 - bottomScrollIndicator.length;
|
|
15758
15772
|
const bottomLeft = Math.floor(bottomBorderContent / 2);
|
|
15759
15773
|
const bottomRight = bottomBorderContent - bottomLeft;
|
|
15760
15774
|
output += `\x1B[${bottomY};${leftBoxX}H${themeColor}\u255A${"\u2550".repeat(bottomLeft)}${hasScrollDown ? "\x1B[33m" : ""}${bottomScrollIndicator}${hasScrollDown ? themeColor : ""}${"\u2550".repeat(bottomRight)}\u255D\x1B[0m`;
|
|
15761
15775
|
output += `\x1B[${bottomY};${rightBoxX}H${themeColor}\u255A${"\u2550".repeat(bottomLeft)}${hasScrollDown ? "\x1B[33m" : ""}${bottomScrollIndicator}${hasScrollDown ? themeColor : ""}${"\u2550".repeat(bottomRight)}\u255D\x1B[0m`;
|
|
15776
|
+
if (onActionSelect) {
|
|
15777
|
+
const actionHint = "Press V to Vibe Code Your Own Game";
|
|
15778
|
+
const hintX = Math.floor((cols - actionHint.length) / 2);
|
|
15779
|
+
const ctaStyle = lightTheme ? "\x1B[1;30;106m" : "\x1B[1;30;103m";
|
|
15780
|
+
output += `\x1B[${rows};${hintX}H${ctaStyle}${actionHint}\x1B[0m`;
|
|
15781
|
+
}
|
|
15762
15782
|
const controls = `\u2191\u2193\u2190\u2192 Navigate | ENTER Select | 1-9 Quick | Q Quit`;
|
|
15763
15783
|
output += `\x1B[${rows - 1};${Math.floor((cols - controls.length) / 2)}H\x1B[2m${controls}\x1B[0m`;
|
|
15764
15784
|
terminal.write(output);
|
|
15765
15785
|
}
|
|
15766
|
-
function
|
|
15786
|
+
function renderEntry(entry, index, isSelected, y, boxX, boxWidth) {
|
|
15767
15787
|
let output = "";
|
|
15768
15788
|
const contentWidth = boxWidth - 2;
|
|
15769
15789
|
const prefix = isSelected ? "\u25B6" : " ";
|
|
@@ -15771,10 +15791,10 @@ function showGamesMenu(terminal, optionsOrCallback) {
|
|
|
15771
15791
|
const keyColor = isSelected ? "" : "\x1B[33m";
|
|
15772
15792
|
const keyNum = index + 1;
|
|
15773
15793
|
const keyDisplay = keyNum <= 9 ? `${keyNum}` : " ";
|
|
15774
|
-
const lineContent = `${prefix} [${keyDisplay}] ${
|
|
15794
|
+
const lineContent = `${prefix} [${keyDisplay}] ${entry.name}`;
|
|
15775
15795
|
const padding = Math.max(0, contentWidth - lineContent.length);
|
|
15776
15796
|
output += `\x1B[${y};${boxX}H${themeColor}\u2551\x1B[0m${highlight}${keyColor}${lineContent}${" ".repeat(padding)}\x1B[0m${themeColor}\u2551\x1B[0m`;
|
|
15777
|
-
const descContent = ` ${
|
|
15797
|
+
const descContent = ` ${entry.description}`;
|
|
15778
15798
|
const descPadding = Math.max(0, contentWidth - descContent.length);
|
|
15779
15799
|
const descColor = isSelected ? lightTheme ? "\x1B[2;7;97m" : "\x1B[2;7m" : "\x1B[2m";
|
|
15780
15800
|
output += `\x1B[${y + 1};${boxX}H${themeColor}\u2551\x1B[0m${descColor}${descContent}${" ".repeat(descPadding)}\x1B[0m${themeColor}\u2551\x1B[0m`;
|
|
@@ -15822,16 +15842,16 @@ function showGamesMenu(terminal, optionsOrCallback) {
|
|
|
15822
15842
|
if (useTwoColumns) {
|
|
15823
15843
|
selectedIndex = Math.max(0, selectedIndex - 2);
|
|
15824
15844
|
} else {
|
|
15825
|
-
selectedIndex = (selectedIndex - 1 +
|
|
15845
|
+
selectedIndex = (selectedIndex - 1 + menuEntries.length) % menuEntries.length;
|
|
15826
15846
|
}
|
|
15827
15847
|
render();
|
|
15828
15848
|
return;
|
|
15829
15849
|
}
|
|
15830
15850
|
if (key === "ArrowDown") {
|
|
15831
15851
|
if (useTwoColumns) {
|
|
15832
|
-
selectedIndex = Math.min(
|
|
15852
|
+
selectedIndex = Math.min(menuEntries.length - 1, selectedIndex + 2);
|
|
15833
15853
|
} else {
|
|
15834
|
-
selectedIndex = (selectedIndex + 1) %
|
|
15854
|
+
selectedIndex = (selectedIndex + 1) % menuEntries.length;
|
|
15835
15855
|
}
|
|
15836
15856
|
render();
|
|
15837
15857
|
return;
|
|
@@ -15844,38 +15864,49 @@ function showGamesMenu(terminal, optionsOrCallback) {
|
|
|
15844
15864
|
return;
|
|
15845
15865
|
}
|
|
15846
15866
|
if (key === "ArrowRight" && useTwoColumns) {
|
|
15847
|
-
if (selectedIndex % 2 === 0 && selectedIndex + 1 <
|
|
15867
|
+
if (selectedIndex % 2 === 0 && selectedIndex + 1 < menuEntries.length) {
|
|
15848
15868
|
selectedIndex++;
|
|
15849
15869
|
}
|
|
15850
15870
|
render();
|
|
15851
15871
|
return;
|
|
15852
15872
|
}
|
|
15853
|
-
|
|
15873
|
+
if (keyLower === "v" && onActionSelect) {
|
|
15874
|
+
keyListener?.dispose();
|
|
15875
|
+
running = false;
|
|
15876
|
+
exitAlternateBuffer(terminal, "games-menu-vibe");
|
|
15877
|
+
onActionSelect("vibe");
|
|
15878
|
+
return;
|
|
15879
|
+
}
|
|
15880
|
+
const launchEntry = async (entry) => {
|
|
15854
15881
|
keyListener?.dispose();
|
|
15855
15882
|
running = false;
|
|
15856
15883
|
exitAlternateBuffer(terminal, "games-menu-launch");
|
|
15857
15884
|
try {
|
|
15858
|
-
|
|
15859
|
-
|
|
15860
|
-
onGameSelect
|
|
15885
|
+
if (entry.kind === "game") {
|
|
15886
|
+
await playSelectTransition(terminal, entry.name);
|
|
15887
|
+
if (onGameSelect) {
|
|
15888
|
+
onGameSelect(entry.id);
|
|
15889
|
+
}
|
|
15890
|
+
} else if (onActionSelect) {
|
|
15891
|
+
onActionSelect(entry.id);
|
|
15861
15892
|
}
|
|
15862
15893
|
} catch (err) {
|
|
15863
|
-
console.error("[GamesMenu] Failed to launch
|
|
15894
|
+
console.error("[GamesMenu] Failed to launch menu entry:", err);
|
|
15864
15895
|
forceExitAlternateBuffer(terminal, "games-menu-launch-error");
|
|
15865
|
-
terminal.write(`\x1B[91mError launching
|
|
15896
|
+
terminal.write(`\x1B[91mError launching menu entry: ${err}\x1B[0m\r
|
|
15866
15897
|
`);
|
|
15867
15898
|
}
|
|
15868
15899
|
};
|
|
15869
15900
|
if (key === "Enter") {
|
|
15870
|
-
|
|
15871
|
-
console.error("[GamesMenu] Unhandled launch error:", err);
|
|
15901
|
+
launchEntry(menuEntries[selectedIndex]).catch((err) => {
|
|
15902
|
+
console.error("[GamesMenu] Unhandled menu launch error:", err);
|
|
15872
15903
|
});
|
|
15873
15904
|
return;
|
|
15874
15905
|
}
|
|
15875
15906
|
const numKey = parseInt(key);
|
|
15876
|
-
if (numKey >= 1 && numKey <=
|
|
15877
|
-
|
|
15878
|
-
console.error("[GamesMenu] Unhandled launch error:", err);
|
|
15907
|
+
if (numKey >= 1 && numKey <= menuEntries.length && numKey <= 9) {
|
|
15908
|
+
launchEntry(menuEntries[numKey - 1]).catch((err) => {
|
|
15909
|
+
console.error("[GamesMenu] Unhandled menu launch error:", err);
|
|
15879
15910
|
});
|
|
15880
15911
|
return;
|
|
15881
15912
|
}
|
|
@@ -16157,11 +16188,35 @@ function openMenu(terminal) {
|
|
|
16157
16188
|
const game = games.find((g) => g.id === gameId);
|
|
16158
16189
|
if (game) launchGame(terminal, game);
|
|
16159
16190
|
},
|
|
16191
|
+
onActionSelect: (actionId) => {
|
|
16192
|
+
if (actionId !== "vibe") return;
|
|
16193
|
+
launchVibeFromMenu();
|
|
16194
|
+
},
|
|
16160
16195
|
onQuit: () => {
|
|
16161
16196
|
process.exit(0);
|
|
16162
16197
|
}
|
|
16163
16198
|
});
|
|
16164
16199
|
}
|
|
16200
|
+
function launchVibeFromMenu() {
|
|
16201
|
+
if (process.stdin.isTTY) {
|
|
16202
|
+
process.stdin.setRawMode(false);
|
|
16203
|
+
}
|
|
16204
|
+
process.stdin.pause();
|
|
16205
|
+
process.stdout.write("\x1B[?25h");
|
|
16206
|
+
process.stdout.write("\x1B[0m");
|
|
16207
|
+
const cliPath = fileURLToPath(import.meta.url);
|
|
16208
|
+
const child = spawn(process.execPath, [cliPath, "vibe"], {
|
|
16209
|
+
stdio: "inherit",
|
|
16210
|
+
env: process.env
|
|
16211
|
+
});
|
|
16212
|
+
child.on("close", (code) => {
|
|
16213
|
+
process.exit(code ?? 0);
|
|
16214
|
+
});
|
|
16215
|
+
child.on("error", () => {
|
|
16216
|
+
console.error("Failed to launch vibe command");
|
|
16217
|
+
process.exit(1);
|
|
16218
|
+
});
|
|
16219
|
+
}
|
|
16165
16220
|
function launchGame(terminal, game) {
|
|
16166
16221
|
game.run(terminal);
|
|
16167
16222
|
}
|