@hypersocial/cli-games 0.2.2 → 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 +102 -47
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +6 -0
- package/dist/index.js +67 -40
- package/dist/index.js.map +1 -1
- package/package.json +3 -1
package/dist/index.d.ts
CHANGED
|
@@ -591,7 +591,13 @@ interface GamesMenuController {
|
|
|
591
591
|
}
|
|
592
592
|
interface GamesMenuOptions {
|
|
593
593
|
onGameSelect?: (gameId: string) => void;
|
|
594
|
+
onActionSelect?: (actionId: string) => void;
|
|
594
595
|
onQuit?: () => void;
|
|
596
|
+
extraActions?: Array<{
|
|
597
|
+
id: string;
|
|
598
|
+
name: string;
|
|
599
|
+
description: string;
|
|
600
|
+
}>;
|
|
595
601
|
}
|
|
596
602
|
/**
|
|
597
603
|
* Show interactive games menu
|
package/dist/index.js
CHANGED
|
@@ -14157,7 +14157,7 @@ function getRandomCharacter() {
|
|
|
14157
14157
|
// src/games/hyper-fighter/index.ts
|
|
14158
14158
|
var TICK_MS = 50;
|
|
14159
14159
|
var MIN_COLS = 60;
|
|
14160
|
-
var MIN_ROWS =
|
|
14160
|
+
var MIN_ROWS = 30;
|
|
14161
14161
|
var VS_COL_WIDTH = 12;
|
|
14162
14162
|
var SIDE_PANEL_WIDTH = 14;
|
|
14163
14163
|
var HEADER_HEIGHT = 2;
|
|
@@ -15567,9 +15567,13 @@ function runHyperFighterGame(terminal) {
|
|
|
15567
15567
|
// src/games/gamesMenu.ts
|
|
15568
15568
|
function showGamesMenu(terminal, optionsOrCallback) {
|
|
15569
15569
|
const options = typeof optionsOrCallback === "function" ? { onGameSelect: optionsOrCallback } : optionsOrCallback || {};
|
|
15570
|
-
const { onGameSelect, onQuit } = options;
|
|
15570
|
+
const { onGameSelect, onActionSelect, onQuit, extraActions = [] } = options;
|
|
15571
15571
|
const themeColor = getCurrentThemeColor();
|
|
15572
15572
|
const lightTheme = isLightTheme2();
|
|
15573
|
+
const menuEntries = [
|
|
15574
|
+
...games.map((game) => ({ ...game, kind: "game" })),
|
|
15575
|
+
...extraActions.map((action) => ({ ...action, kind: "action" }))
|
|
15576
|
+
];
|
|
15573
15577
|
let running = true;
|
|
15574
15578
|
let selectedIndex = 0;
|
|
15575
15579
|
let scrollOffset = 0;
|
|
@@ -15612,50 +15616,56 @@ function showGamesMenu(terminal, optionsOrCallback) {
|
|
|
15612
15616
|
function renderSingleColumn(baseOutput, cols, rows, boxWidth, listStartY, maxVisibleGames) {
|
|
15613
15617
|
let output = baseOutput;
|
|
15614
15618
|
const boxX = Math.floor((cols - boxWidth) / 2);
|
|
15615
|
-
const
|
|
15619
|
+
const visibleEntries = Math.min(maxVisibleGames, menuEntries.length);
|
|
15616
15620
|
if (selectedIndex < scrollOffset) {
|
|
15617
15621
|
scrollOffset = selectedIndex;
|
|
15618
|
-
} else if (selectedIndex >= scrollOffset +
|
|
15619
|
-
scrollOffset = selectedIndex -
|
|
15622
|
+
} else if (selectedIndex >= scrollOffset + visibleEntries) {
|
|
15623
|
+
scrollOffset = selectedIndex - visibleEntries + 1;
|
|
15620
15624
|
}
|
|
15621
|
-
scrollOffset = Math.max(0, Math.min(scrollOffset,
|
|
15625
|
+
scrollOffset = Math.max(0, Math.min(scrollOffset, menuEntries.length - visibleEntries));
|
|
15622
15626
|
const hasScrollUp = scrollOffset > 0;
|
|
15623
|
-
const hasScrollDown = scrollOffset +
|
|
15627
|
+
const hasScrollDown = scrollOffset + visibleEntries < menuEntries.length;
|
|
15624
15628
|
const topScrollIndicator = hasScrollUp ? " \u25B2 more " : "\u2550".repeat(8);
|
|
15625
15629
|
const topBorderWidth = boxWidth - 2 - topScrollIndicator.length;
|
|
15626
15630
|
const topLeftPad = Math.floor(topBorderWidth / 2);
|
|
15627
15631
|
const topRightPad = topBorderWidth - topLeftPad;
|
|
15628
15632
|
output += `\x1B[${listStartY};${boxX}H${themeColor}\u2554${"\u2550".repeat(topLeftPad)}${hasScrollUp ? "\x1B[33m" : ""}${topScrollIndicator}${hasScrollUp ? themeColor : ""}${"\u2550".repeat(topRightPad)}\u2557\x1B[0m`;
|
|
15629
|
-
for (let vi = 0; vi <
|
|
15633
|
+
for (let vi = 0; vi < visibleEntries; vi++) {
|
|
15630
15634
|
const i = scrollOffset + vi;
|
|
15631
|
-
const
|
|
15635
|
+
const entry = menuEntries[i];
|
|
15632
15636
|
const y = listStartY + 1 + vi * 2;
|
|
15633
15637
|
const isSelected = i === selectedIndex;
|
|
15634
|
-
output +=
|
|
15638
|
+
output += renderEntry(entry, i, isSelected, y, boxX, boxWidth);
|
|
15635
15639
|
}
|
|
15636
|
-
const bottomY = listStartY + 1 +
|
|
15640
|
+
const bottomY = listStartY + 1 + visibleEntries * 2;
|
|
15637
15641
|
const bottomScrollIndicator = hasScrollDown ? " \u25BC more " : "\u2550".repeat(8);
|
|
15638
15642
|
const bottomBorderWidth = boxWidth - 2 - bottomScrollIndicator.length;
|
|
15639
15643
|
const bottomLeftPad = Math.floor(bottomBorderWidth / 2);
|
|
15640
15644
|
const bottomRightPad = bottomBorderWidth - bottomLeftPad;
|
|
15641
15645
|
output += `\x1B[${bottomY};${boxX}H${themeColor}\u255A${"\u2550".repeat(bottomLeftPad)}${hasScrollDown ? "\x1B[33m" : ""}${bottomScrollIndicator}${hasScrollDown ? themeColor : ""}${"\u2550".repeat(bottomRightPad)}\u255D\x1B[0m`;
|
|
15646
|
+
if (onActionSelect) {
|
|
15647
|
+
const actionHint = "Press V to Vibe Code Your Own Game";
|
|
15648
|
+
const hintX = Math.floor((cols - actionHint.length) / 2);
|
|
15649
|
+
const ctaStyle = lightTheme ? "\x1B[1;30;106m" : "\x1B[1;30;103m";
|
|
15650
|
+
output += `\x1B[${rows};${hintX}H${ctaStyle}${actionHint}\x1B[0m`;
|
|
15651
|
+
}
|
|
15642
15652
|
const controls = `\u2191\u2193 Navigate | ENTER Select | 1-9 Quick | Q Quit`;
|
|
15643
15653
|
output += `\x1B[${rows - 1};${Math.floor((cols - controls.length) / 2)}H\x1B[2m${controls}\x1B[0m`;
|
|
15644
15654
|
terminal.write(output);
|
|
15645
15655
|
}
|
|
15646
15656
|
function renderTwoColumns(baseOutput, cols, rows, boxWidth, columnGap, listStartY, maxVisibleGames) {
|
|
15647
15657
|
let output = baseOutput;
|
|
15648
|
-
const
|
|
15658
|
+
const entriesPerColumn = Math.min(maxVisibleGames, Math.ceil(menuEntries.length / 2));
|
|
15649
15659
|
const selectedRow = Math.floor(selectedIndex / 2);
|
|
15650
15660
|
if (selectedRow < scrollOffset) {
|
|
15651
15661
|
scrollOffset = selectedRow;
|
|
15652
|
-
} else if (selectedRow >= scrollOffset +
|
|
15653
|
-
scrollOffset = selectedRow -
|
|
15662
|
+
} else if (selectedRow >= scrollOffset + entriesPerColumn) {
|
|
15663
|
+
scrollOffset = selectedRow - entriesPerColumn + 1;
|
|
15654
15664
|
}
|
|
15655
|
-
const maxScroll = Math.max(0, Math.ceil(
|
|
15665
|
+
const maxScroll = Math.max(0, Math.ceil(menuEntries.length / 2) - entriesPerColumn);
|
|
15656
15666
|
scrollOffset = Math.max(0, Math.min(scrollOffset, maxScroll));
|
|
15657
15667
|
const hasScrollUp = scrollOffset > 0;
|
|
15658
|
-
const hasScrollDown = (scrollOffset +
|
|
15668
|
+
const hasScrollDown = (scrollOffset + entriesPerColumn) * 2 < menuEntries.length;
|
|
15659
15669
|
const totalWidth = boxWidth * 2 + columnGap;
|
|
15660
15670
|
const startX = Math.floor((cols - totalWidth) / 2);
|
|
15661
15671
|
const leftBoxX = startX;
|
|
@@ -15666,35 +15676,41 @@ function showGamesMenu(terminal, optionsOrCallback) {
|
|
|
15666
15676
|
const topRight = topBorderContent - topLeft;
|
|
15667
15677
|
output += `\x1B[${listStartY};${leftBoxX}H${themeColor}\u2554${"\u2550".repeat(topLeft)}${hasScrollUp ? "\x1B[33m" : ""}${topScrollIndicator}${hasScrollUp ? themeColor : ""}${"\u2550".repeat(topRight)}\u2557\x1B[0m`;
|
|
15668
15678
|
output += `\x1B[${listStartY};${rightBoxX}H${themeColor}\u2554${"\u2550".repeat(topLeft)}${hasScrollUp ? "\x1B[33m" : ""}${topScrollIndicator}${hasScrollUp ? themeColor : ""}${"\u2550".repeat(topRight)}\u2557\x1B[0m`;
|
|
15669
|
-
for (let row = 0; row <
|
|
15679
|
+
for (let row = 0; row < entriesPerColumn; row++) {
|
|
15670
15680
|
const leftIdx = (scrollOffset + row) * 2;
|
|
15671
15681
|
const rightIdx = leftIdx + 1;
|
|
15672
15682
|
const y = listStartY + 1 + row * 2;
|
|
15673
|
-
if (leftIdx <
|
|
15674
|
-
output +=
|
|
15683
|
+
if (leftIdx < menuEntries.length) {
|
|
15684
|
+
output += renderEntry(menuEntries[leftIdx], leftIdx, leftIdx === selectedIndex, y, leftBoxX, boxWidth);
|
|
15675
15685
|
} else {
|
|
15676
15686
|
output += `\x1B[${y};${leftBoxX}H${themeColor}\u2551${" ".repeat(boxWidth - 2)}\u2551\x1B[0m`;
|
|
15677
15687
|
output += `\x1B[${y + 1};${leftBoxX}H${themeColor}\u2551${" ".repeat(boxWidth - 2)}\u2551\x1B[0m`;
|
|
15678
15688
|
}
|
|
15679
|
-
if (rightIdx <
|
|
15680
|
-
output +=
|
|
15689
|
+
if (rightIdx < menuEntries.length) {
|
|
15690
|
+
output += renderEntry(menuEntries[rightIdx], rightIdx, rightIdx === selectedIndex, y, rightBoxX, boxWidth);
|
|
15681
15691
|
} else {
|
|
15682
15692
|
output += `\x1B[${y};${rightBoxX}H${themeColor}\u2551${" ".repeat(boxWidth - 2)}\u2551\x1B[0m`;
|
|
15683
15693
|
output += `\x1B[${y + 1};${rightBoxX}H${themeColor}\u2551${" ".repeat(boxWidth - 2)}\u2551\x1B[0m`;
|
|
15684
15694
|
}
|
|
15685
15695
|
}
|
|
15686
|
-
const bottomY = listStartY + 1 +
|
|
15696
|
+
const bottomY = listStartY + 1 + entriesPerColumn * 2;
|
|
15687
15697
|
const bottomScrollIndicator = hasScrollDown ? " \u25BC " : "\u2550\u2550\u2550";
|
|
15688
15698
|
const bottomBorderContent = boxWidth - 2 - bottomScrollIndicator.length;
|
|
15689
15699
|
const bottomLeft = Math.floor(bottomBorderContent / 2);
|
|
15690
15700
|
const bottomRight = bottomBorderContent - bottomLeft;
|
|
15691
15701
|
output += `\x1B[${bottomY};${leftBoxX}H${themeColor}\u255A${"\u2550".repeat(bottomLeft)}${hasScrollDown ? "\x1B[33m" : ""}${bottomScrollIndicator}${hasScrollDown ? themeColor : ""}${"\u2550".repeat(bottomRight)}\u255D\x1B[0m`;
|
|
15692
15702
|
output += `\x1B[${bottomY};${rightBoxX}H${themeColor}\u255A${"\u2550".repeat(bottomLeft)}${hasScrollDown ? "\x1B[33m" : ""}${bottomScrollIndicator}${hasScrollDown ? themeColor : ""}${"\u2550".repeat(bottomRight)}\u255D\x1B[0m`;
|
|
15703
|
+
if (onActionSelect) {
|
|
15704
|
+
const actionHint = "Press V to Vibe Code Your Own Game";
|
|
15705
|
+
const hintX = Math.floor((cols - actionHint.length) / 2);
|
|
15706
|
+
const ctaStyle = lightTheme ? "\x1B[1;30;106m" : "\x1B[1;30;103m";
|
|
15707
|
+
output += `\x1B[${rows};${hintX}H${ctaStyle}${actionHint}\x1B[0m`;
|
|
15708
|
+
}
|
|
15693
15709
|
const controls = `\u2191\u2193\u2190\u2192 Navigate | ENTER Select | 1-9 Quick | Q Quit`;
|
|
15694
15710
|
output += `\x1B[${rows - 1};${Math.floor((cols - controls.length) / 2)}H\x1B[2m${controls}\x1B[0m`;
|
|
15695
15711
|
terminal.write(output);
|
|
15696
15712
|
}
|
|
15697
|
-
function
|
|
15713
|
+
function renderEntry(entry, index, isSelected, y, boxX, boxWidth) {
|
|
15698
15714
|
let output = "";
|
|
15699
15715
|
const contentWidth = boxWidth - 2;
|
|
15700
15716
|
const prefix = isSelected ? "\u25B6" : " ";
|
|
@@ -15702,10 +15718,10 @@ function showGamesMenu(terminal, optionsOrCallback) {
|
|
|
15702
15718
|
const keyColor = isSelected ? "" : "\x1B[33m";
|
|
15703
15719
|
const keyNum = index + 1;
|
|
15704
15720
|
const keyDisplay = keyNum <= 9 ? `${keyNum}` : " ";
|
|
15705
|
-
const lineContent = `${prefix} [${keyDisplay}] ${
|
|
15721
|
+
const lineContent = `${prefix} [${keyDisplay}] ${entry.name}`;
|
|
15706
15722
|
const padding = Math.max(0, contentWidth - lineContent.length);
|
|
15707
15723
|
output += `\x1B[${y};${boxX}H${themeColor}\u2551\x1B[0m${highlight}${keyColor}${lineContent}${" ".repeat(padding)}\x1B[0m${themeColor}\u2551\x1B[0m`;
|
|
15708
|
-
const descContent = ` ${
|
|
15724
|
+
const descContent = ` ${entry.description}`;
|
|
15709
15725
|
const descPadding = Math.max(0, contentWidth - descContent.length);
|
|
15710
15726
|
const descColor = isSelected ? lightTheme ? "\x1B[2;7;97m" : "\x1B[2;7m" : "\x1B[2m";
|
|
15711
15727
|
output += `\x1B[${y + 1};${boxX}H${themeColor}\u2551\x1B[0m${descColor}${descContent}${" ".repeat(descPadding)}\x1B[0m${themeColor}\u2551\x1B[0m`;
|
|
@@ -15753,16 +15769,16 @@ function showGamesMenu(terminal, optionsOrCallback) {
|
|
|
15753
15769
|
if (useTwoColumns) {
|
|
15754
15770
|
selectedIndex = Math.max(0, selectedIndex - 2);
|
|
15755
15771
|
} else {
|
|
15756
|
-
selectedIndex = (selectedIndex - 1 +
|
|
15772
|
+
selectedIndex = (selectedIndex - 1 + menuEntries.length) % menuEntries.length;
|
|
15757
15773
|
}
|
|
15758
15774
|
render();
|
|
15759
15775
|
return;
|
|
15760
15776
|
}
|
|
15761
15777
|
if (key === "ArrowDown") {
|
|
15762
15778
|
if (useTwoColumns) {
|
|
15763
|
-
selectedIndex = Math.min(
|
|
15779
|
+
selectedIndex = Math.min(menuEntries.length - 1, selectedIndex + 2);
|
|
15764
15780
|
} else {
|
|
15765
|
-
selectedIndex = (selectedIndex + 1) %
|
|
15781
|
+
selectedIndex = (selectedIndex + 1) % menuEntries.length;
|
|
15766
15782
|
}
|
|
15767
15783
|
render();
|
|
15768
15784
|
return;
|
|
@@ -15775,38 +15791,49 @@ function showGamesMenu(terminal, optionsOrCallback) {
|
|
|
15775
15791
|
return;
|
|
15776
15792
|
}
|
|
15777
15793
|
if (key === "ArrowRight" && useTwoColumns) {
|
|
15778
|
-
if (selectedIndex % 2 === 0 && selectedIndex + 1 <
|
|
15794
|
+
if (selectedIndex % 2 === 0 && selectedIndex + 1 < menuEntries.length) {
|
|
15779
15795
|
selectedIndex++;
|
|
15780
15796
|
}
|
|
15781
15797
|
render();
|
|
15782
15798
|
return;
|
|
15783
15799
|
}
|
|
15784
|
-
|
|
15800
|
+
if (keyLower === "v" && onActionSelect) {
|
|
15801
|
+
keyListener?.dispose();
|
|
15802
|
+
running = false;
|
|
15803
|
+
exitAlternateBuffer(terminal, "games-menu-vibe");
|
|
15804
|
+
onActionSelect("vibe");
|
|
15805
|
+
return;
|
|
15806
|
+
}
|
|
15807
|
+
const launchEntry = async (entry) => {
|
|
15785
15808
|
keyListener?.dispose();
|
|
15786
15809
|
running = false;
|
|
15787
15810
|
exitAlternateBuffer(terminal, "games-menu-launch");
|
|
15788
15811
|
try {
|
|
15789
|
-
|
|
15790
|
-
|
|
15791
|
-
onGameSelect
|
|
15812
|
+
if (entry.kind === "game") {
|
|
15813
|
+
await playSelectTransition(terminal, entry.name);
|
|
15814
|
+
if (onGameSelect) {
|
|
15815
|
+
onGameSelect(entry.id);
|
|
15816
|
+
}
|
|
15817
|
+
} else if (onActionSelect) {
|
|
15818
|
+
onActionSelect(entry.id);
|
|
15792
15819
|
}
|
|
15793
15820
|
} catch (err) {
|
|
15794
|
-
console.error("[GamesMenu] Failed to launch
|
|
15821
|
+
console.error("[GamesMenu] Failed to launch menu entry:", err);
|
|
15795
15822
|
forceExitAlternateBuffer(terminal, "games-menu-launch-error");
|
|
15796
|
-
terminal.write(`\x1B[91mError launching
|
|
15823
|
+
terminal.write(`\x1B[91mError launching menu entry: ${err}\x1B[0m\r
|
|
15797
15824
|
`);
|
|
15798
15825
|
}
|
|
15799
15826
|
};
|
|
15800
15827
|
if (key === "Enter") {
|
|
15801
|
-
|
|
15802
|
-
console.error("[GamesMenu] Unhandled launch error:", err);
|
|
15828
|
+
launchEntry(menuEntries[selectedIndex]).catch((err) => {
|
|
15829
|
+
console.error("[GamesMenu] Unhandled menu launch error:", err);
|
|
15803
15830
|
});
|
|
15804
15831
|
return;
|
|
15805
15832
|
}
|
|
15806
15833
|
const numKey = parseInt(key);
|
|
15807
|
-
if (numKey >= 1 && numKey <=
|
|
15808
|
-
|
|
15809
|
-
console.error("[GamesMenu] Unhandled launch error:", err);
|
|
15834
|
+
if (numKey >= 1 && numKey <= menuEntries.length && numKey <= 9) {
|
|
15835
|
+
launchEntry(menuEntries[numKey - 1]).catch((err) => {
|
|
15836
|
+
console.error("[GamesMenu] Unhandled menu launch error:", err);
|
|
15810
15837
|
});
|
|
15811
15838
|
return;
|
|
15812
15839
|
}
|