brainrot-cli 0.1.0

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 (162) hide show
  1. package/README.md +372 -0
  2. package/dist/AchievementNotification.d.ts +28 -0
  3. package/dist/AchievementNotification.d.ts.map +1 -0
  4. package/dist/AchievementNotification.js +74 -0
  5. package/dist/AchievementNotification.js.map +1 -0
  6. package/dist/GameSelector.d.ts +25 -0
  7. package/dist/GameSelector.d.ts.map +1 -0
  8. package/dist/GameSelector.js +105 -0
  9. package/dist/GameSelector.js.map +1 -0
  10. package/dist/HelpOverlay.d.ts +15 -0
  11. package/dist/HelpOverlay.d.ts.map +1 -0
  12. package/dist/HelpOverlay.js +134 -0
  13. package/dist/HelpOverlay.js.map +1 -0
  14. package/dist/Layout.d.ts +49 -0
  15. package/dist/Layout.d.ts.map +1 -0
  16. package/dist/Layout.js +83 -0
  17. package/dist/Layout.js.map +1 -0
  18. package/dist/Leaderboard.d.ts +46 -0
  19. package/dist/Leaderboard.d.ts.map +1 -0
  20. package/dist/Leaderboard.js +68 -0
  21. package/dist/Leaderboard.js.map +1 -0
  22. package/dist/LogViewer.d.ts +33 -0
  23. package/dist/LogViewer.d.ts.map +1 -0
  24. package/dist/LogViewer.js +179 -0
  25. package/dist/LogViewer.js.map +1 -0
  26. package/dist/LoopAlertOverlay.d.ts +15 -0
  27. package/dist/LoopAlertOverlay.d.ts.map +1 -0
  28. package/dist/LoopAlertOverlay.js +17 -0
  29. package/dist/LoopAlertOverlay.js.map +1 -0
  30. package/dist/LoopManagementPanel.d.ts +44 -0
  31. package/dist/LoopManagementPanel.d.ts.map +1 -0
  32. package/dist/LoopManagementPanel.js +220 -0
  33. package/dist/LoopManagementPanel.js.map +1 -0
  34. package/dist/SettingsMenu.d.ts +22 -0
  35. package/dist/SettingsMenu.d.ts.map +1 -0
  36. package/dist/SettingsMenu.js +367 -0
  37. package/dist/SettingsMenu.js.map +1 -0
  38. package/dist/SplitPane.d.ts +63 -0
  39. package/dist/SplitPane.d.ts.map +1 -0
  40. package/dist/SplitPane.js +104 -0
  41. package/dist/SplitPane.js.map +1 -0
  42. package/dist/StatsMenu.d.ts +15 -0
  43. package/dist/StatsMenu.d.ts.map +1 -0
  44. package/dist/StatsMenu.js +230 -0
  45. package/dist/StatsMenu.js.map +1 -0
  46. package/dist/StatusBar.d.ts +58 -0
  47. package/dist/StatusBar.d.ts.map +1 -0
  48. package/dist/StatusBar.js +106 -0
  49. package/dist/StatusBar.js.map +1 -0
  50. package/dist/__tests__/ralph-loop-parser.test.d.ts +2 -0
  51. package/dist/__tests__/ralph-loop-parser.test.d.ts.map +1 -0
  52. package/dist/__tests__/ralph-loop-parser.test.js +143 -0
  53. package/dist/__tests__/ralph-loop-parser.test.js.map +1 -0
  54. package/dist/claude-code-process.d.ts +76 -0
  55. package/dist/claude-code-process.d.ts.map +1 -0
  56. package/dist/claude-code-process.js +221 -0
  57. package/dist/claude-code-process.js.map +1 -0
  58. package/dist/cli.d.ts +42 -0
  59. package/dist/cli.d.ts.map +1 -0
  60. package/dist/cli.js +265 -0
  61. package/dist/cli.js.map +1 -0
  62. package/dist/config.d.ts +206 -0
  63. package/dist/config.d.ts.map +1 -0
  64. package/dist/config.js +270 -0
  65. package/dist/config.js.map +1 -0
  66. package/dist/game-types.d.ts +177 -0
  67. package/dist/game-types.d.ts.map +1 -0
  68. package/dist/game-types.js +55 -0
  69. package/dist/game-types.js.map +1 -0
  70. package/dist/games/MinesweeperGame.d.ts +15 -0
  71. package/dist/games/MinesweeperGame.d.ts.map +1 -0
  72. package/dist/games/MinesweeperGame.js +555 -0
  73. package/dist/games/MinesweeperGame.js.map +1 -0
  74. package/dist/games/PongGame.d.ts +15 -0
  75. package/dist/games/PongGame.d.ts.map +1 -0
  76. package/dist/games/PongGame.js +379 -0
  77. package/dist/games/PongGame.js.map +1 -0
  78. package/dist/games/SnakeGame.d.ts +15 -0
  79. package/dist/games/SnakeGame.d.ts.map +1 -0
  80. package/dist/games/SnakeGame.js +333 -0
  81. package/dist/games/SnakeGame.js.map +1 -0
  82. package/dist/games/TetrisGame.d.ts +15 -0
  83. package/dist/games/TetrisGame.d.ts.map +1 -0
  84. package/dist/games/TetrisGame.js +654 -0
  85. package/dist/games/TetrisGame.js.map +1 -0
  86. package/dist/games/index.d.ts +23 -0
  87. package/dist/games/index.d.ts.map +1 -0
  88. package/dist/games/index.js +47 -0
  89. package/dist/games/index.js.map +1 -0
  90. package/dist/high-scores.d.ts +57 -0
  91. package/dist/high-scores.d.ts.map +1 -0
  92. package/dist/high-scores.js +230 -0
  93. package/dist/high-scores.js.map +1 -0
  94. package/dist/index.d.ts +3 -0
  95. package/dist/index.d.ts.map +1 -0
  96. package/dist/index.js +264 -0
  97. package/dist/index.js.map +1 -0
  98. package/dist/ralph-loop-parser.d.ts +58 -0
  99. package/dist/ralph-loop-parser.d.ts.map +1 -0
  100. package/dist/ralph-loop-parser.js +315 -0
  101. package/dist/ralph-loop-parser.js.map +1 -0
  102. package/dist/stats.d.ts +142 -0
  103. package/dist/stats.d.ts.map +1 -0
  104. package/dist/stats.js +521 -0
  105. package/dist/stats.js.map +1 -0
  106. package/dist/styled-components.d.ts +231 -0
  107. package/dist/styled-components.d.ts.map +1 -0
  108. package/dist/styled-components.js +192 -0
  109. package/dist/styled-components.js.map +1 -0
  110. package/dist/theme.d.ts +301 -0
  111. package/dist/theme.d.ts.map +1 -0
  112. package/dist/theme.js +372 -0
  113. package/dist/theme.js.map +1 -0
  114. package/dist/themes.d.ts +117 -0
  115. package/dist/themes.d.ts.map +1 -0
  116. package/dist/themes.js +296 -0
  117. package/dist/themes.js.map +1 -0
  118. package/dist/ui/index.d.ts +13 -0
  119. package/dist/ui/index.d.ts.map +1 -0
  120. package/dist/ui/index.js +29 -0
  121. package/dist/ui/index.js.map +1 -0
  122. package/dist/use-claude-code.d.ts +30 -0
  123. package/dist/use-claude-code.d.ts.map +1 -0
  124. package/dist/use-claude-code.js +84 -0
  125. package/dist/use-claude-code.js.map +1 -0
  126. package/dist/use-config.d.ts +58 -0
  127. package/dist/use-config.d.ts.map +1 -0
  128. package/dist/use-config.js +113 -0
  129. package/dist/use-config.js.map +1 -0
  130. package/dist/use-game-loop.d.ts +47 -0
  131. package/dist/use-game-loop.d.ts.map +1 -0
  132. package/dist/use-game-loop.js +136 -0
  133. package/dist/use-game-loop.js.map +1 -0
  134. package/dist/use-high-scores.d.ts +41 -0
  135. package/dist/use-high-scores.d.ts.map +1 -0
  136. package/dist/use-high-scores.js +94 -0
  137. package/dist/use-high-scores.js.map +1 -0
  138. package/dist/use-layout-state.d.ts +77 -0
  139. package/dist/use-layout-state.d.ts.map +1 -0
  140. package/dist/use-layout-state.js +160 -0
  141. package/dist/use-layout-state.js.map +1 -0
  142. package/dist/use-ralph-loop.d.ts +41 -0
  143. package/dist/use-ralph-loop.d.ts.map +1 -0
  144. package/dist/use-ralph-loop.js +106 -0
  145. package/dist/use-ralph-loop.js.map +1 -0
  146. package/dist/use-spinner.d.ts +46 -0
  147. package/dist/use-spinner.d.ts.map +1 -0
  148. package/dist/use-spinner.js +71 -0
  149. package/dist/use-spinner.js.map +1 -0
  150. package/dist/use-stats.d.ts +59 -0
  151. package/dist/use-stats.d.ts.map +1 -0
  152. package/dist/use-stats.js +150 -0
  153. package/dist/use-stats.js.map +1 -0
  154. package/dist/use-terminal-size.d.ts +29 -0
  155. package/dist/use-terminal-size.d.ts.map +1 -0
  156. package/dist/use-terminal-size.js +48 -0
  157. package/dist/use-terminal-size.js.map +1 -0
  158. package/dist/useTheme.d.ts +76 -0
  159. package/dist/useTheme.d.ts.map +1 -0
  160. package/dist/useTheme.js +136 -0
  161. package/dist/useTheme.js.map +1 -0
  162. package/package.json +58 -0
@@ -0,0 +1,134 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * HelpOverlay Component
4
+ *
5
+ * Modal overlay that displays comprehensive keyboard shortcuts organized by context.
6
+ * Triggered by ? or F1 key globally.
7
+ */
8
+ import { Box, Text, useInput } from "ink";
9
+ import { useState, useMemo } from "react";
10
+ import { navIcons } from "./theme.js";
11
+ import { useThemeColors } from "./useTheme.js";
12
+ // ============================================================================
13
+ // SHORTCUT DEFINITIONS
14
+ // ============================================================================
15
+ const shortcutSections = [
16
+ {
17
+ id: "global",
18
+ title: "Global Shortcuts",
19
+ shortcuts: [
20
+ { key: "Ctrl+C", description: "Exit application" },
21
+ { key: "Ctrl+S", description: "Start/Stop Claude Code loop" },
22
+ { key: "Tab", description: "Switch focus between panes" },
23
+ { key: "?", description: "Toggle this help overlay" },
24
+ { key: "Ctrl+,", description: "Open settings (from menu)" },
25
+ { key: "Escape", description: "Close overlay/modal/exit game" },
26
+ ],
27
+ },
28
+ {
29
+ id: "layout",
30
+ title: "Layout Controls",
31
+ shortcuts: [
32
+ { key: "Alt+←/→", description: "Resize panes horizontally" },
33
+ { key: "Alt+↑/↓", description: "Resize panes vertically" },
34
+ { key: "D", description: "Toggle split direction" },
35
+ { key: "H", description: "Hide/Show secondary pane" },
36
+ { key: "R", description: "Reset layout to defaults" },
37
+ ],
38
+ },
39
+ {
40
+ id: "navigation",
41
+ title: "Menu Navigation",
42
+ shortcuts: [
43
+ { key: "↑/↓ or K/J", description: "Navigate up/down" },
44
+ { key: "Enter/Space", description: "Select item" },
45
+ { key: "1-9", description: "Quick select game" },
46
+ { key: "L", description: "View logs" },
47
+ { key: "S", description: "Open stats/achievements" },
48
+ { key: "Q", description: "Back to menu" },
49
+ ],
50
+ },
51
+ {
52
+ id: "games",
53
+ title: "In-Game Controls",
54
+ shortcuts: [
55
+ { key: "↑↓←→ or WASD", description: "Movement (game-specific)" },
56
+ { key: "P", description: "Pause/Resume game" },
57
+ { key: "R", description: "Restart game" },
58
+ { key: "H", description: "Toggle leaderboard (game over)" },
59
+ { key: "Q/Escape", description: "Exit to menu" },
60
+ { key: "Enter", description: "Dismiss loop alert" },
61
+ ],
62
+ },
63
+ {
64
+ id: "menus",
65
+ title: "Settings/Menus",
66
+ shortcuts: [
67
+ { key: "Tab/Shift+Tab", description: "Switch tabs" },
68
+ { key: "↑↓ or K/J", description: "Navigate options" },
69
+ { key: "←→ or H/L", description: "Change value" },
70
+ { key: "Enter", description: "Toggle boolean" },
71
+ { key: "Ctrl+S", description: "Save settings" },
72
+ { key: "Escape/Q", description: "Close" },
73
+ ],
74
+ },
75
+ ];
76
+ function SectionTabs({ sections, activeSection }) {
77
+ const colors = useThemeColors();
78
+ return (_jsx(Box, { marginBottom: 1, flexWrap: "wrap", children: sections.map((section, index) => (_jsxs(Box, { children: [index > 0 && _jsx(Text, { dimColor: true, children: " | " }), _jsx(Text, { bold: activeSection === section.id, color: activeSection === section.id ? colors.primary : colors.textMuted, children: activeSection === section.id ? `[${section.title}]` : section.title })] }, section.id))) }));
79
+ }
80
+ function ShortcutList({ shortcuts }) {
81
+ const colors = useThemeColors();
82
+ return (_jsx(Box, { flexDirection: "column", children: shortcuts.map((shortcut) => (_jsxs(Box, { children: [_jsx(Box, { minWidth: 18, children: _jsx(Text, { bold: true, color: colors.primary, children: shortcut.key }) }), _jsx(Text, { color: colors.text, children: shortcut.description })] }, shortcut.key))) }));
83
+ }
84
+ function HelpFooter() {
85
+ const colors = useThemeColors();
86
+ return (_jsx(Box, { marginTop: 1, paddingTop: 1, borderStyle: "single", borderTop: true, borderBottom: false, borderLeft: false, borderRight: false, borderColor: colors.border, children: _jsxs(Text, { dimColor: true, children: [_jsx(Text, { color: colors.primary, children: "Tab" }), ": Switch sections |", " ", _jsx(Text, { color: colors.primary, children: "Escape/?" }), ": Close help"] }) }));
87
+ }
88
+ // ============================================================================
89
+ // MAIN COMPONENT
90
+ // ============================================================================
91
+ export function HelpOverlay({ hasFocus, onClose }) {
92
+ const [activeSection, setActiveSection] = useState("global");
93
+ const colors = useThemeColors();
94
+ const currentSection = useMemo(() => shortcutSections.find((s) => s.id === activeSection) ?? shortcutSections[0], [activeSection]);
95
+ // Handle tab switching
96
+ const handleTabChange = (direction) => {
97
+ const currentIdx = shortcutSections.findIndex((s) => s.id === activeSection);
98
+ let newIdx;
99
+ if (direction === "next") {
100
+ newIdx = (currentIdx + 1) % shortcutSections.length;
101
+ }
102
+ else {
103
+ newIdx = currentIdx <= 0 ? shortcutSections.length - 1 : currentIdx - 1;
104
+ }
105
+ setActiveSection(shortcutSections[newIdx].id);
106
+ };
107
+ // Keyboard input handling
108
+ useInput((input, key) => {
109
+ if (!hasFocus)
110
+ return;
111
+ // Close overlay
112
+ if (key.escape || input === "?" || input === "q" || input === "Q") {
113
+ onClose();
114
+ return;
115
+ }
116
+ // Switch tabs
117
+ if (key.tab) {
118
+ handleTabChange(key.shift ? "prev" : "next");
119
+ return;
120
+ }
121
+ // Arrow keys for section navigation
122
+ if (key.leftArrow) {
123
+ handleTabChange("prev");
124
+ return;
125
+ }
126
+ if (key.rightArrow) {
127
+ handleTabChange("next");
128
+ return;
129
+ }
130
+ }, { isActive: hasFocus });
131
+ return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsxs(Box, { marginBottom: 1, children: [_jsxs(Text, { bold: true, color: colors.primary, children: [navIcons.arrowRight, " Keyboard Shortcuts"] }), _jsx(Text, { dimColor: true, children: " - Press ? or Escape to close" })] }), _jsx(SectionTabs, { sections: shortcutSections, activeSection: activeSection }), _jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: colors.border, paddingX: 1, paddingY: 1, children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, color: colors.accent, children: currentSection.title }) }), _jsx(ShortcutList, { shortcuts: currentSection.shortcuts })] }), _jsx(HelpFooter, {})] }));
132
+ }
133
+ export default HelpOverlay;
134
+ //# sourceMappingURL=HelpOverlay.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HelpOverlay.js","sourceRoot":"","sources":["../src/HelpOverlay.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Main Layout component that provides the split-pane layout for the app.
3
+ * Combines terminal size tracking, layout state management, and the SplitPane component.
4
+ */
5
+ import type { ReactNode } from "react";
6
+ import { useTerminalSize } from "./use-terminal-size.js";
7
+ import { useLayoutState, type UseLayoutStateOptions } from "./use-layout-state.js";
8
+ export interface LayoutProps {
9
+ /** Content for the game area (left/top pane) */
10
+ gameArea: ReactNode;
11
+ /** Content for the management area (right/bottom pane) */
12
+ managementArea: ReactNode;
13
+ /** Title for the game pane */
14
+ gameTitle?: string;
15
+ /** Title for the management pane */
16
+ managementTitle?: string;
17
+ /** Layout configuration options */
18
+ layoutOptions?: UseLayoutStateOptions;
19
+ /** Custom header content */
20
+ header?: ReactNode;
21
+ /** Custom footer content */
22
+ footer?: ReactNode;
23
+ /** Whether the layout handles keyboard input */
24
+ handleInput?: boolean;
25
+ }
26
+ /**
27
+ * Main layout component that provides a resizable split-pane interface.
28
+ *
29
+ * Features:
30
+ * - Responsive to terminal size changes
31
+ * - Graceful handling of small terminal windows
32
+ * - Keyboard shortcuts for resizing and layout control
33
+ * - Persisted layout state during session
34
+ *
35
+ * Keyboard shortcuts:
36
+ * - Tab: Toggle focus between panes
37
+ * - Alt+Arrow: Resize panes
38
+ * - D: Toggle split direction
39
+ * - H: Hide/show secondary pane
40
+ * - R: Reset layout to defaults
41
+ */
42
+ export declare function Layout({ gameArea, managementArea, gameTitle, managementTitle, layoutOptions, header, footer, handleInput, }: LayoutProps): import("react/jsx-runtime").JSX.Element;
43
+ /**
44
+ * Export the layout state hook for external access to layout controls
45
+ */
46
+ export { useLayoutState, useTerminalSize };
47
+ export type { UseLayoutStateOptions, UseLayoutStateResult } from "./use-layout-state.js";
48
+ export type { UseTerminalSizeResult } from "./use-terminal-size.js";
49
+ //# sourceMappingURL=Layout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Layout.d.ts","sourceRoot":"","sources":["../src/Layout.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,EACL,eAAe,EAGhB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,cAAc,EAAE,KAAK,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAInF,MAAM,WAAW,WAAW;IAC1B,gDAAgD;IAChD,QAAQ,EAAE,SAAS,CAAC;IACpB,0DAA0D;IAC1D,cAAc,EAAE,SAAS,CAAC;IAC1B,8BAA8B;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oCAAoC;IACpC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,mCAAmC;IACnC,aAAa,CAAC,EAAE,qBAAqB,CAAC;IACtC,4BAA4B;IAC5B,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,4BAA4B;IAC5B,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,gDAAgD;IAChD,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AA+DD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,MAAM,CAAC,EACrB,QAAQ,EACR,cAAc,EACd,SAAkB,EAClB,eAA8B,EAC9B,aAAa,EACb,MAAM,EACN,MAAM,EACN,WAAkB,GACnB,EAAE,WAAW,2CAqIb;AAED;;GAEG;AACH,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,CAAC;AAC3C,YAAY,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AACzF,YAAY,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC"}
package/dist/Layout.js ADDED
@@ -0,0 +1,83 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * Main Layout component that provides the split-pane layout for the app.
4
+ * Combines terminal size tracking, layout state management, and the SplitPane component.
5
+ */
6
+ import { Box, Text, useInput } from "ink";
7
+ import { SplitPane } from "./SplitPane.js";
8
+ import { useTerminalSize, MIN_WIDTH, MIN_HEIGHT, } from "./use-terminal-size.js";
9
+ import { useLayoutState } from "./use-layout-state.js";
10
+ import { navIcons } from "./theme.js";
11
+ import { useThemeColors } from "./useTheme.js";
12
+ function TooSmallWarning({ width, height, minWidth, minHeight, }) {
13
+ const colors = useThemeColors();
14
+ return (_jsxs(Box, { flexDirection: "column", alignItems: "center", justifyContent: "center", padding: 1, children: [_jsx(Text, { color: colors.warning, bold: true, children: "Terminal too small" }), _jsxs(Text, { color: colors.textMuted, children: ["Current: ", width, "x", height] }), _jsxs(Text, { color: colors.textMuted, children: ["Required: ", minWidth, "x", minHeight] }), _jsx(Text, { color: colors.warning, children: "Please resize your terminal" })] }));
15
+ }
16
+ function LayoutHelp({ direction, showSecondary, isSmall }) {
17
+ if (isSmall) {
18
+ return (_jsx(Text, { dimColor: true, children: "Tab: Focus | H: Toggle pane | ?: Help" }));
19
+ }
20
+ const resizeKeys = direction === "horizontal" ? "Alt+←/→: Resize" : "Alt+↑/↓: Resize";
21
+ return (_jsxs(Text, { dimColor: true, children: ["Tab: Focus | ", resizeKeys, " | D: Direction | H: ", showSecondary ? "Hide" : "Show", " ", "pane | R: Reset | ?: Help"] }));
22
+ }
23
+ /**
24
+ * Main layout component that provides a resizable split-pane interface.
25
+ *
26
+ * Features:
27
+ * - Responsive to terminal size changes
28
+ * - Graceful handling of small terminal windows
29
+ * - Keyboard shortcuts for resizing and layout control
30
+ * - Persisted layout state during session
31
+ *
32
+ * Keyboard shortcuts:
33
+ * - Tab: Toggle focus between panes
34
+ * - Alt+Arrow: Resize panes
35
+ * - D: Toggle split direction
36
+ * - H: Hide/show secondary pane
37
+ * - R: Reset layout to defaults
38
+ */
39
+ export function Layout({ gameArea, managementArea, gameTitle = "Game", managementTitle = "Management", layoutOptions, header, footer, handleInput = true, }) {
40
+ const terminalSize = useTerminalSize();
41
+ const layout = useLayoutState(layoutOptions);
42
+ const colors = useThemeColors();
43
+ // Handle layout-specific keyboard shortcuts
44
+ useInput((input) => {
45
+ if (!handleInput)
46
+ return;
47
+ // 'd' to toggle direction
48
+ if (input === "d" || input === "D") {
49
+ layout.toggleDirection();
50
+ return;
51
+ }
52
+ // 'h' to toggle secondary pane
53
+ if (input === "h" || input === "H") {
54
+ layout.toggleSecondary();
55
+ return;
56
+ }
57
+ // 'r' to reset layout
58
+ if (input === "r" || input === "R") {
59
+ layout.resetLayout();
60
+ return;
61
+ }
62
+ }, { isActive: handleInput });
63
+ // Show warning if terminal is too small
64
+ if (terminalSize.isTooSmall) {
65
+ return (_jsx(TooSmallWarning, { width: terminalSize.width, height: terminalSize.height, minWidth: MIN_WIDTH, minHeight: MIN_HEIGHT }));
66
+ }
67
+ // Calculate available space for the split pane
68
+ // Account for header (3 rows), footer (2 rows), and help text (1 row)
69
+ const headerHeight = header ? 3 : 0;
70
+ const footerHeight = footer ? 2 : 0;
71
+ const helpHeight = 1;
72
+ const availableHeight = Math.max(1, terminalSize.height - headerHeight - footerHeight - helpHeight);
73
+ const availableWidth = terminalSize.width;
74
+ // Create pane content with titles
75
+ const firstPaneContent = (_jsxs(Box, { flexDirection: "column", width: "100%", height: "100%", children: [_jsx(Box, { children: _jsxs(Text, { color: layout.state.focusedPane === 0 ? colors.primary : colors.textMuted, bold: layout.state.focusedPane === 0, children: [layout.state.focusedPane === 0 ? `${navIcons.arrowRight} ` : " ", gameTitle] }) }), _jsx(Box, { flexGrow: 1, flexDirection: "column", overflow: "hidden", children: gameArea })] }));
76
+ const secondPaneContent = (_jsxs(Box, { flexDirection: "column", width: "100%", height: "100%", children: [_jsx(Box, { children: _jsxs(Text, { color: layout.state.focusedPane === 1 ? colors.primary : colors.textMuted, bold: layout.state.focusedPane === 1, children: [layout.state.focusedPane === 1 ? `${navIcons.arrowRight} ` : " ", managementTitle] }) }), _jsx(Box, { flexGrow: 1, flexDirection: "column", overflow: "hidden", children: managementArea })] }));
77
+ return (_jsxs(Box, { flexDirection: "column", width: terminalSize.width, height: terminalSize.height, children: [header && (_jsx(Box, { height: headerHeight, flexShrink: 0, children: header })), _jsx(Box, { flexGrow: 1, height: availableHeight, children: _jsx(SplitPane, { first: firstPaneContent, second: secondPaneContent, direction: layout.state.direction, splitRatio: layout.state.splitRatio, width: availableWidth, height: availableHeight, isResizing: layout.state.isResizing, showSecondary: layout.state.showSecondary, focusedPane: layout.state.focusedPane, onResize: layout.adjustSplitRatio, onToggleFocus: layout.toggleFocus, handleInput: handleInput }) }), footer && (_jsx(Box, { height: footerHeight, flexShrink: 0, children: footer })), _jsx(Box, { height: helpHeight, flexShrink: 0, children: _jsx(LayoutHelp, { direction: layout.state.direction, showSecondary: layout.state.showSecondary, isSmall: terminalSize.isSmall }) })] }));
78
+ }
79
+ /**
80
+ * Export the layout state hook for external access to layout controls
81
+ */
82
+ export { useLayoutState, useTerminalSize };
83
+ //# sourceMappingURL=Layout.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Layout.js","sourceRoot":"","sources":["../src/Layout.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Leaderboard Component
3
+ *
4
+ * Displays top 10 scores for a game with timestamps.
5
+ */
6
+ import { type ScoreEntry } from "./high-scores.js";
7
+ interface LeaderboardProps {
8
+ /** Game title to display */
9
+ title: string;
10
+ /** List of scores to display */
11
+ scores: ScoreEntry[];
12
+ /** Whether lower scores are better (for time-based games) */
13
+ lowerIsBetter?: boolean;
14
+ /** Format function for the score value */
15
+ formatScore?: (score: number) => string;
16
+ /** Highlight this score position (1-indexed) */
17
+ highlightPosition?: number;
18
+ /** Maximum entries to show */
19
+ maxEntries?: number;
20
+ }
21
+ /**
22
+ * Format time in mm:ss format
23
+ */
24
+ export declare function formatTime(seconds: number): string;
25
+ /**
26
+ * Leaderboard display component
27
+ */
28
+ export declare function Leaderboard({ title, scores, lowerIsBetter: _lowerIsBetter, formatScore, highlightPosition, maxEntries, }: LeaderboardProps): import("react/jsx-runtime").JSX.Element;
29
+ /**
30
+ * Compact leaderboard for displaying in game HUD
31
+ */
32
+ export declare function CompactLeaderboard({ scores, formatScore, maxEntries, }: {
33
+ scores: ScoreEntry[];
34
+ formatScore?: (score: number) => string;
35
+ maxEntries?: number;
36
+ }): import("react/jsx-runtime").JSX.Element | null;
37
+ /**
38
+ * New high score notification banner
39
+ */
40
+ export declare function NewHighScoreBanner({ position, score, formatScore, }: {
41
+ position: number;
42
+ score: number;
43
+ formatScore?: (score: number) => string;
44
+ }): import("react/jsx-runtime").JSX.Element;
45
+ export default Leaderboard;
46
+ //# sourceMappingURL=Leaderboard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Leaderboard.d.ts","sourceRoot":"","sources":["../src/Leaderboard.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,KAAK,UAAU,EAAmB,MAAM,kBAAkB,CAAC;AAGpE,UAAU,gBAAgB;IACxB,4BAA4B;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,gCAAgC;IAChC,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,6DAA6D;IAC7D,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,0CAA0C;IAC1C,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IACxC,gDAAgD;IAChD,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,8BAA8B;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AASD;;GAEG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAKlD;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EACL,MAAM,EACN,aAAa,EAAE,cAAsB,EACrC,WAAgC,EAChC,iBAAiB,EACjB,UAAe,GAChB,EAAE,gBAAgB,2CA6DlB;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,MAAM,EACN,WAAgC,EAChC,UAAc,GACf,EAAE;IACD,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IACxC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,kDAmBA;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,QAAQ,EACR,KAAK,EACL,WAAgC,GACjC,EAAE;IACD,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;CACzC,2CAiCA;AAED,eAAe,WAAW,CAAC"}
@@ -0,0 +1,68 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * Leaderboard Component
4
+ *
5
+ * Displays top 10 scores for a game with timestamps.
6
+ */
7
+ import { Box, Text } from "ink";
8
+ import { formatScoreDate } from "./high-scores.js";
9
+ import { useThemeColors, getThemedRankColor, useTheme } from "./useTheme.js";
10
+ /**
11
+ * Default score formatter
12
+ */
13
+ function defaultFormatScore(score) {
14
+ return score.toLocaleString();
15
+ }
16
+ /**
17
+ * Format time in mm:ss format
18
+ */
19
+ export function formatTime(seconds) {
20
+ if (seconds >= 999)
21
+ return "--:--";
22
+ const mins = Math.floor(seconds / 60);
23
+ const secs = seconds % 60;
24
+ return `${mins.toString().padStart(2, "0")}:${secs.toString().padStart(2, "0")}`;
25
+ }
26
+ /**
27
+ * Leaderboard display component
28
+ */
29
+ export function Leaderboard({ title, scores, lowerIsBetter: _lowerIsBetter = false, formatScore = defaultFormatScore, highlightPosition, maxEntries = 10, }) {
30
+ const colors = useThemeColors();
31
+ const { theme } = useTheme();
32
+ const displayScores = scores.slice(0, maxEntries);
33
+ const hasScores = displayScores.length > 0;
34
+ return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", paddingX: 1, children: [_jsx(Box, { justifyContent: "center", marginBottom: 1, children: _jsx(Text, { bold: true, color: colors.primary, children: title }) }), !hasScores ? (_jsx(Box, { justifyContent: "center", paddingY: 1, children: _jsx(Text, { dimColor: true, children: "No scores yet" }) })) : (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { children: _jsxs(Text, { dimColor: true, children: ["#".padStart(2), " ", "Score".padStart(8), " ", "Date".padStart(10)] }) }), displayScores.map((entry, index) => {
35
+ const position = index + 1;
36
+ const isHighlighted = highlightPosition === position;
37
+ const positionStr = position.toString().padStart(2);
38
+ const scoreStr = formatScore(entry.score).padStart(8);
39
+ const dateStr = formatScoreDate(entry.timestamp).padStart(10);
40
+ return (_jsx(Box, { children: _jsxs(Text, { color: getThemedRankColor(theme, position, isHighlighted), bold: isHighlighted || position === 1, children: [positionStr, " ", scoreStr, " ", dateStr, isHighlighted ? " NEW!" : ""] }) }, index));
41
+ }), displayScores.length < maxEntries && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { dimColor: true, children: [maxEntries - displayScores.length, " more slot", maxEntries - displayScores.length !== 1 ? "s" : "", " available"] }) }))] }))] }));
42
+ }
43
+ /**
44
+ * Compact leaderboard for displaying in game HUD
45
+ */
46
+ export function CompactLeaderboard({ scores, formatScore = defaultFormatScore, maxEntries = 5, }) {
47
+ const displayScores = scores.slice(0, maxEntries);
48
+ if (displayScores.length === 0) {
49
+ return null;
50
+ }
51
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { dimColor: true, bold: true, children: "Top Scores" }), displayScores.map((entry, index) => (_jsxs(Text, { dimColor: true, children: [(index + 1).toString(), ". ", formatScore(entry.score)] }, index)))] }));
52
+ }
53
+ /**
54
+ * New high score notification banner
55
+ */
56
+ export function NewHighScoreBanner({ position, score, formatScore = defaultFormatScore, }) {
57
+ const colors = useThemeColors();
58
+ const positionText = position === 1
59
+ ? "1st"
60
+ : position === 2
61
+ ? "2nd"
62
+ : position === 3
63
+ ? "3rd"
64
+ : `${position}th`;
65
+ return (_jsxs(Box, { flexDirection: "column", alignItems: "center", borderStyle: "double", borderColor: colors.success, paddingX: 2, paddingY: 1, children: [_jsx(Text, { bold: true, color: colors.success, children: "NEW HIGH SCORE!" }), _jsx(Text, { children: _jsx(Text, { color: colors.accent, bold: true, children: formatScore(score) }) }), _jsxs(Text, { children: ["You ranked ", _jsx(Text, { color: colors.primary, children: positionText }), " place!"] })] }));
66
+ }
67
+ export default Leaderboard;
68
+ //# sourceMappingURL=Leaderboard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Leaderboard.js","sourceRoot":"","sources":["../src/Leaderboard.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * LogViewer Component
3
+ *
4
+ * Real-time log streaming component with scrollable view,
5
+ * condensed/full view toggle, and formatted output.
6
+ */
7
+ import type { ClaudeCodeOutput } from "./use-claude-code.js";
8
+ export interface LogViewerProps {
9
+ /** Array of log outputs from Claude Code */
10
+ logs: ClaudeCodeOutput[];
11
+ /** Maximum number of lines to display in the viewport */
12
+ maxVisibleLines?: number;
13
+ /** Whether the component has focus for keyboard input */
14
+ hasFocus?: boolean;
15
+ /** Height of the log viewer area */
16
+ height?: number;
17
+ /** Initial view mode */
18
+ initialViewMode?: "condensed" | "full";
19
+ }
20
+ export interface LogLine {
21
+ id: number;
22
+ timestamp: Date;
23
+ type: "stdout" | "stderr";
24
+ content: string;
25
+ /** Original log entry index for tracking */
26
+ sourceIndex: number;
27
+ }
28
+ /**
29
+ * Main LogViewer component
30
+ */
31
+ export declare function LogViewer({ logs, maxVisibleLines, hasFocus, height, initialViewMode, }: LogViewerProps): import("react/jsx-runtime").JSX.Element;
32
+ export default LogViewer;
33
+ //# sourceMappingURL=LogViewer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LogViewer.d.ts","sourceRoot":"","sources":["../src/LogViewer.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAG7D,MAAM,WAAW,cAAc;IAC7B,4CAA4C;IAC5C,IAAI,EAAE,gBAAgB,EAAE,CAAC;IACzB,yDAAyD;IACzD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,yDAAyD;IACzD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,oCAAoC;IACpC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,wBAAwB;IACxB,eAAe,CAAC,EAAE,WAAW,GAAG,MAAM,CAAC;CACxC;AAED,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,IAAI,CAAC;IAChB,IAAI,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,4CAA4C;IAC5C,WAAW,EAAE,MAAM,CAAC;CACrB;AAsKD;;GAEG;AACH,wBAAgB,SAAS,CAAC,EACxB,IAAI,EACJ,eAAoB,EACpB,QAAgB,EAChB,MAAM,EACN,eAA6B,GAC9B,EAAE,cAAc,2CA0JhB;AAED,eAAe,SAAS,CAAC"}
@@ -0,0 +1,179 @@
1
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * LogViewer Component
4
+ *
5
+ * Real-time log streaming component with scrollable view,
6
+ * condensed/full view toggle, and formatted output.
7
+ */
8
+ import { Box, Text, useInput } from "ink";
9
+ import { useState, useEffect, useRef, useMemo } from "react";
10
+ import { useThemeColors } from "./useTheme.js";
11
+ // ANSI escape code regex for stripping colors in condensed view
12
+ const ANSI_REGEX =
13
+ // eslint-disable-next-line no-control-regex
14
+ /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g;
15
+ /**
16
+ * Strip ANSI escape codes from text
17
+ */
18
+ function stripAnsi(text) {
19
+ return text.replace(ANSI_REGEX, "");
20
+ }
21
+ /**
22
+ * Format timestamp for log display
23
+ */
24
+ function formatTimestamp(date) {
25
+ return date.toLocaleTimeString("en-US", {
26
+ hour12: false,
27
+ hour: "2-digit",
28
+ minute: "2-digit",
29
+ second: "2-digit",
30
+ });
31
+ }
32
+ /**
33
+ * Process logs into display lines
34
+ */
35
+ function processLogs(logs, viewMode) {
36
+ const lines = [];
37
+ let lineId = 0;
38
+ for (let i = 0; i < logs.length; i++) {
39
+ const log = logs[i];
40
+ const content = viewMode === "condensed" ? stripAnsi(log.content) : log.content;
41
+ // Split multi-line content into separate lines
42
+ const contentLines = content.split("\n");
43
+ for (const line of contentLines) {
44
+ // In condensed mode, skip empty lines and whitespace-only lines
45
+ if (viewMode === "condensed" && line.trim() === "") {
46
+ continue;
47
+ }
48
+ lines.push({
49
+ id: lineId++,
50
+ timestamp: log.timestamp,
51
+ type: log.type,
52
+ content: line,
53
+ sourceIndex: i,
54
+ });
55
+ }
56
+ }
57
+ return lines;
58
+ }
59
+ /**
60
+ * Single log line component
61
+ */
62
+ function LogLineDisplay({ line, showTimestamp, viewMode, }) {
63
+ const colors = useThemeColors();
64
+ const textColor = line.type === "stderr" ? colors.error : colors.text;
65
+ return (_jsxs(Box, { children: [showTimestamp && (_jsxs(Text, { dimColor: true, children: ["[", formatTimestamp(line.timestamp), "] "] })), viewMode === "full" && (_jsx(Text, { color: line.type === "stderr" ? colors.error : colors.textMuted, children: line.type === "stderr" ? "ERR " : "OUT " })), _jsx(Text, { color: textColor, wrap: "truncate", children: line.content })] }));
66
+ }
67
+ /**
68
+ * Scrollbar indicator component
69
+ */
70
+ function ScrollIndicator({ currentPosition, totalLines, visibleLines, }) {
71
+ if (totalLines <= visibleLines) {
72
+ return null;
73
+ }
74
+ const scrollPercentage = totalLines > visibleLines
75
+ ? (currentPosition / (totalLines - visibleLines)) * 100
76
+ : 0;
77
+ return (_jsxs(Box, { flexDirection: "column", marginLeft: 1, children: [_jsx(Text, { dimColor: true, children: scrollPercentage < 100 ? "^" : " " }), _jsxs(Text, { dimColor: true, children: [Math.round(scrollPercentage), "%"] }), _jsx(Text, { dimColor: true, children: scrollPercentage > 0 ? "v" : " " })] }));
78
+ }
79
+ /**
80
+ * Log viewer header with controls
81
+ */
82
+ function LogViewerHeader({ viewMode, totalLines, autoScroll, hasFocus, }) {
83
+ const colors = useThemeColors();
84
+ return (_jsxs(Box, { justifyContent: "space-between", marginBottom: 1, children: [_jsxs(Box, { children: [_jsx(Text, { bold: true, color: colors.primary, children: "Logs" }), _jsxs(Text, { dimColor: true, children: [" (", totalLines, " lines)"] }), autoScroll && _jsx(Text, { color: colors.success, children: " [AUTO]" })] }), _jsxs(Box, { children: [_jsx(Text, { dimColor: true, children: "View: " }), _jsx(Text, { color: viewMode === "full" ? colors.primary : colors.textMuted, children: viewMode.toUpperCase() }), hasFocus && (_jsx(Text, { dimColor: true, children: " | V: toggle view | J/K: scroll" }))] })] }));
85
+ }
86
+ /**
87
+ * Main LogViewer component
88
+ */
89
+ export function LogViewer({ logs, maxVisibleLines = 20, hasFocus = false, height, initialViewMode = "condensed", }) {
90
+ const [viewMode, setViewMode] = useState(initialViewMode);
91
+ const [scrollPosition, setScrollPosition] = useState(0);
92
+ const [autoScroll, setAutoScroll] = useState(true);
93
+ const lastLogCountRef = useRef(logs.length);
94
+ // Process logs into display lines
95
+ const displayLines = useMemo(() => processLogs(logs, viewMode), [logs, viewMode]);
96
+ // Calculate visible area
97
+ const effectiveHeight = height ? Math.max(height - 4, 5) : maxVisibleLines;
98
+ const totalLines = displayLines.length;
99
+ const maxScroll = Math.max(0, totalLines - effectiveHeight);
100
+ // Auto-scroll when new logs arrive
101
+ useEffect(() => {
102
+ if (logs.length > lastLogCountRef.current && autoScroll) {
103
+ setScrollPosition(maxScroll);
104
+ }
105
+ lastLogCountRef.current = logs.length;
106
+ }, [logs.length, maxScroll, autoScroll]);
107
+ // Handle keyboard input when focused
108
+ useInput((input, key) => {
109
+ if (!hasFocus)
110
+ return;
111
+ // Toggle view mode with 'v'
112
+ if (input === "v" || input === "V") {
113
+ setViewMode((current) => (current === "condensed" ? "full" : "condensed"));
114
+ return;
115
+ }
116
+ // Scroll up with 'k' or arrow up
117
+ if (input === "k" || key.upArrow) {
118
+ setScrollPosition((pos) => Math.max(0, pos - 1));
119
+ setAutoScroll(false);
120
+ return;
121
+ }
122
+ // Scroll down with 'j' or arrow down
123
+ if (input === "j" || key.downArrow) {
124
+ setScrollPosition((pos) => {
125
+ const newPos = Math.min(maxScroll, pos + 1);
126
+ // Re-enable auto-scroll if we're at the bottom
127
+ if (newPos === maxScroll) {
128
+ setAutoScroll(true);
129
+ }
130
+ return newPos;
131
+ });
132
+ return;
133
+ }
134
+ // Page up with 'u' or page up
135
+ if (input === "u" || key.pageUp) {
136
+ setScrollPosition((pos) => Math.max(0, pos - effectiveHeight));
137
+ setAutoScroll(false);
138
+ return;
139
+ }
140
+ // Page down with 'd' or page down
141
+ if (input === "d" || key.pageDown) {
142
+ setScrollPosition((pos) => {
143
+ const newPos = Math.min(maxScroll, pos + effectiveHeight);
144
+ if (newPos === maxScroll) {
145
+ setAutoScroll(true);
146
+ }
147
+ return newPos;
148
+ });
149
+ return;
150
+ }
151
+ // Jump to top with 'g'
152
+ if (input === "g") {
153
+ setScrollPosition(0);
154
+ setAutoScroll(false);
155
+ return;
156
+ }
157
+ // Jump to bottom with 'G'
158
+ if (input === "G") {
159
+ setScrollPosition(maxScroll);
160
+ setAutoScroll(true);
161
+ return;
162
+ }
163
+ // Toggle auto-scroll with 'a'
164
+ if (input === "a" || input === "A") {
165
+ setAutoScroll((current) => !current);
166
+ if (!autoScroll) {
167
+ setScrollPosition(maxScroll);
168
+ }
169
+ return;
170
+ }
171
+ }, { isActive: hasFocus });
172
+ // Get visible lines based on scroll position
173
+ const visibleLines = displayLines.slice(scrollPosition, scrollPosition + effectiveHeight);
174
+ // Show timestamps only in full mode or every few lines in condensed
175
+ const showTimestamps = viewMode === "full";
176
+ return (_jsxs(Box, { flexDirection: "column", height: height, padding: 1, children: [_jsx(LogViewerHeader, { viewMode: viewMode, totalLines: totalLines, autoScroll: autoScroll, hasFocus: hasFocus }), _jsxs(Box, { flexDirection: "row", flexGrow: 1, children: [_jsx(Box, { flexDirection: "column", flexGrow: 1, children: visibleLines.length === 0 ? (_jsx(Text, { dimColor: true, children: "No logs yet. Start Claude Code to see output." })) : (visibleLines.map((line) => (_jsx(LogLineDisplay, { line: line, showTimestamp: showTimestamps, viewMode: viewMode }, line.id)))) }), _jsx(ScrollIndicator, { currentPosition: scrollPosition, totalLines: totalLines, visibleLines: effectiveHeight })] }), totalLines > effectiveHeight && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { dimColor: true, children: ["Lines ", scrollPosition + 1, "-", Math.min(scrollPosition + effectiveHeight, totalLines), " of", " ", totalLines] }) }))] }));
177
+ }
178
+ export default LogViewer;
179
+ //# sourceMappingURL=LogViewer.js.map