rxcc
Version:
To install dependencies:
879 lines (865 loc) • 26.8 kB
JavaScript
// src/App.tsx
import { useEffect as useEffect3 } from "react";
// src/components/FileExplorer.tsx
import { Box as Box2, Text as Text2, useInput, useStdout } from "ink";
import { useCallback as useCallback2, useState as useState3 } from "react";
// src/hooks/useFileExplorer.ts
import { useState, useEffect, useCallback } from "react";
// src/utils/fileSystem.ts
import fs from "fs";
import path2 from "path";
// src/utils/tokenCounting.ts
import { runDefaultAction, setLogLevel } from "repomix";
import path from "path";
var tokenCountsCache = null;
async function getTokenCounts(cwd = ".") {
if (tokenCountsCache) {
return tokenCountsCache;
}
try {
setLogLevel(-1);
const result = await runDefaultAction(["."], cwd, {
tokenCountTree: true,
quiet: true
});
if (!result || !result.packResult || !result.packResult.fileTokenCounts) {
throw new Error("No token counts from repomix");
}
tokenCountsCache = result.packResult.fileTokenCounts;
return tokenCountsCache;
} catch (error) {
console.error("Failed to get token counts:", error);
tokenCountsCache = {};
return tokenCountsCache;
}
}
function getFileTokenCount(filePath, tokenCounts) {
const relativePath = path.relative(".", filePath);
const normalizedPath = relativePath.replace(/\\/g, "/");
const result = tokenCounts[normalizedPath] || tokenCounts[relativePath] || tokenCounts[filePath] || tokenCounts[`./${normalizedPath}`] || 0;
return result;
}
function calculateDirectoryTokensFromTokenCounts(dirPath, tokenCounts) {
const relativeDirPath = path.relative(".", dirPath);
const normalizedDirPath = relativeDirPath.replace(/\\/g, "/");
let total = 0;
for (const [filePath, tokens] of Object.entries(tokenCounts)) {
if (filePath.startsWith(normalizedDirPath + "/") || filePath.startsWith(relativeDirPath + "/") || filePath.startsWith(dirPath + "/")) {
total += tokens;
}
}
return total;
}
function calculateSelectedTokenCount(item, tokenCounts) {
if (!item.isSelected) {
return 0;
}
if (!item.isDirectory) {
return item.tokenCount;
}
if (item.children && item.children.length > 0) {
let total = 0;
for (const child of item.children) {
total += calculateSelectedTokenCount(child, tokenCounts);
}
return total;
}
if (tokenCounts) {
return calculateDirectoryTokensFromTokenCounts(item.path, tokenCounts);
}
return 0;
}
function updateTokenCountsRecursively(items, tokenCounts) {
return items.map((item) => {
const updatedItem = { ...item };
if (item.isDirectory) {
updatedItem.tokenCount = calculateDirectoryTokensFromTokenCounts(item.path, tokenCounts);
if (item.children) {
updatedItem.children = updateTokenCountsRecursively(
item.children,
tokenCounts
);
}
} else {
updatedItem.tokenCount = getFileTokenCount(item.path, tokenCounts);
}
updatedItem.selectedTokenCount = calculateSelectedTokenCount(updatedItem, tokenCounts);
return updatedItem;
});
}
function propagateSelectionTokenCounts(items, tokenCounts = {}) {
const updateParentTokenCounts = (item) => {
if (!item.isDirectory) {
return item.isSelected ? item.tokenCount : 0;
}
let selectedTokens = 0;
if (item.children && item.children.length > 0) {
for (const child of item.children) {
selectedTokens += updateParentTokenCounts(child);
}
} else if (item.isSelected) {
selectedTokens = calculateDirectoryTokensFromTokenCounts(item.path, tokenCounts);
}
item.selectedTokenCount = selectedTokens;
return selectedTokens;
};
const updatedItems = [...items];
for (const item of updatedItems) {
updateParentTokenCounts(item);
}
return updatedItems;
}
function getTotalSelectedTokens(items, tokenCounts = {}) {
let total = 0;
const traverse = (itemList) => {
for (const item of itemList) {
if (item.isSelected && !item.isDirectory) {
total += item.tokenCount;
} else if (item.isSelected && item.isDirectory && (!item.children || item.children.length === 0)) {
total += calculateDirectoryTokensFromTokenCounts(item.path, tokenCounts);
}
if (item.children && item.children.length > 0) {
traverse(item.children);
}
}
};
traverse(items);
return total;
}
// src/utils/ignorePatterns.ts
var defaultIgnoreList = [
// Version control
".git/**",
".hg/**",
".hgignore",
".svn/**",
// Dependency directories
"**/node_modules/**",
"**/bower_components/**",
"**/jspm_packages/**",
"vendor/**",
"**/.bundle/**",
"**/.gradle/**",
"target/**",
// Logs
"logs/**",
"**/*.log",
"**/npm-debug.log*",
"**/yarn-debug.log*",
"**/yarn-error.log*",
// Runtime data
"pids/**",
"*.pid",
"*.seed",
"*.pid.lock",
// Directory for instrumented libs generated by jscoverage/JSCover
"lib-cov/**",
// Coverage directory used by tools like istanbul
"coverage/**",
// nyc test coverage
".nyc_output/**",
// Grunt intermediate storage
".grunt/**",
// node-waf configuration
".lock-wscript",
// Compiled binary addons
"build/Release/**",
// TypeScript v1 declaration files
"typings/**",
// Optional npm cache directory
"**/.npm/**",
// Cache directories
".eslintcache",
".rollup.cache/**",
".webpack.cache/**",
".parcel-cache/**",
".sass-cache/**",
"*.cache",
// Optional REPL history
".node_repl_history",
// Output of 'npm pack'
"*.tgz",
// Yarn files
"**/.yarn/**",
// Yarn Integrity file
"**/.yarn-integrity",
// dotenv environment variables file
".env",
// next.js build output
".next/**",
// nuxt.js build output
".nuxt/**",
// vuepress build output
".vuepress/dist/**",
// Serverless directories
".serverless/**",
// FuseBox cache
".fusebox/**",
// DynamoDB Local files
".dynamodb/**",
// TypeScript output
"dist/**",
// OS generated files
"**/.DS_Store",
"**/Thumbs.db",
// Editor directories and files
".idea/**",
".vscode/**",
"**/*.swp",
"**/*.swo",
"**/*.swn",
"**/*.bak",
// Build outputs
"build/**",
"out/**",
// Temporary files
"tmp/**",
"temp/**",
// repomix output
"**/repomix-output.*",
"**/repopack-output.*",
// Legacy
// Essential Node.js-related entries
"**/package-lock.json",
"**/yarn-error.log",
"**/yarn.lock",
"**/pnpm-lock.yaml",
"**/bun.lockb",
"**/bun.lock",
// Essential Python-related entries
"**/__pycache__/**",
"**/*.py[cod]",
"**/venv/**",
"**/.venv/**",
"**/.pytest_cache/**",
"**/.mypy_cache/**",
"**/.ipynb_checkpoints/**",
"**/Pipfile.lock",
"**/poetry.lock",
"**/uv.lock",
// Essential Rust-related entries
"**/Cargo.lock",
"**/Cargo.toml.orig",
"**/target/**",
"**/*.rs.bk",
// Essential PHP-related entries
"**/composer.lock",
// Essential Ruby-related entries
"**/Gemfile.lock",
// Essential Go-related entries
"**/go.sum",
// Essential Elixir-related entries
"**/mix.lock",
// Essential Haskell-related entries
"**/stack.yaml.lock",
"**/cabal.project.freeze"
];
function matchesPattern(filePath, pattern, _depth = 0) {
if (_depth > 10) return false;
const normalizedPath = filePath.replace(/^\.\//, "").replace(/\\/g, "/");
let normalizedPattern = pattern.replace(/^\.\//, "").replace(/\\/g, "/");
if (normalizedPattern.startsWith("**/")) {
const subPattern = normalizedPattern.slice(3);
const pathParts = normalizedPath.split("/");
for (let i = 0; i < pathParts.length; i++) {
const subPath = pathParts.slice(i).join("/");
if (matchesPattern(subPath, subPattern, _depth + 1)) {
return true;
}
}
return matchesPattern(normalizedPath, subPattern, _depth + 1);
}
if (normalizedPattern.endsWith("/**")) {
const directoryPattern = normalizedPattern.slice(0, -3);
return matchesPattern(normalizedPath, directoryPattern, _depth + 1) || normalizedPath.startsWith(directoryPattern + "/");
}
let regexPattern = normalizedPattern.replace(/\*\*/g, "\xA7DOUBLESTAR\xA7").replace(/\*/g, "[^/]*").replace(/§DOUBLESTAR§/g, ".*").replace(/\./g, "\\.").replace(/\?/g, ".");
regexPattern = `^${regexPattern}$`;
const regex = new RegExp(regexPattern);
return regex.test(normalizedPath);
}
function shouldIgnore(filePath, ignorePatterns = defaultIgnoreList) {
const normalizedPath = filePath.replace(/^\.\//, "").replace(/\\/g, "/");
for (const pattern of ignorePatterns) {
if (matchesPattern(normalizedPath, pattern)) {
return true;
}
}
return false;
}
// src/utils/fileSystem.ts
function readDirectory(dirPath, depth = 0, parent, tokenCounts = {}) {
try {
const entries = fs.readdirSync(dirPath, { withFileTypes: true });
return entries.filter((entry) => {
const itemPath = path2.join(dirPath, entry.name);
const relativePath = path2.relative(".", itemPath);
return !shouldIgnore(relativePath);
}).sort((a, b) => {
if (a.isDirectory() && !b.isDirectory()) return -1;
if (!a.isDirectory() && b.isDirectory()) return 1;
return a.name.localeCompare(b.name);
}).map((entry) => {
const itemPath = path2.join(dirPath, entry.name);
const tokenCount = entry.isDirectory() ? 0 : getFileTokenCount(itemPath, tokenCounts);
return {
name: entry.name,
path: itemPath,
isDirectory: entry.isDirectory(),
isExpanded: false,
isSelected: false,
children: entry.isDirectory() ? [] : void 0,
parent,
depth,
tokenCount,
selectedTokenCount: 0
};
});
} catch {
return [];
}
}
function flattenItems(items) {
const result = [];
const traverse = (itemList) => {
for (const item of itemList) {
result.push(item);
if (item.isDirectory && item.isExpanded && item.children) {
traverse(item.children);
}
}
};
traverse(items);
return result;
}
// src/utils/itemOperations.ts
function selectAllChildren(item, isSelected) {
const updatedItem = { ...item, isSelected };
if (updatedItem.children) {
updatedItem.children = updatedItem.children.map(
(child) => selectAllChildren(child, isSelected)
);
}
return updatedItem;
}
function updateParentSelectionState(item) {
if (!item.children || item.children.length === 0) {
return { ...item, isPartiallySelected: false };
}
const updatedChildren = item.children.map(updateParentSelectionState);
const allChildrenSelected = updatedChildren.every((child) => child.isSelected);
const anyChildrenSelected = updatedChildren.some(
(child) => child.isSelected || child.isPartiallySelected
);
return {
...item,
children: updatedChildren,
isSelected: allChildrenSelected,
// Parent is selected only if ALL children are selected
isPartiallySelected: !allChildrenSelected && anyChildrenSelected
// Partially selected if some but not all children are selected
};
}
function expandFolder(item, items, tokenCounts = {}) {
if (!item.isDirectory || item.isExpanded) return items;
const children = readDirectory(item.path, item.depth + 1, item, tokenCounts);
const inheritedChildren = item.isSelected ? children.map((child) => selectAllChildren(child, true)) : children;
const updateItems = (itemList) => {
return itemList.map((currentItem) => {
if (currentItem === item) {
return {
...currentItem,
isExpanded: true,
children: inheritedChildren
};
}
if (currentItem.children) {
return {
...currentItem,
children: updateItems(currentItem.children)
};
}
return currentItem;
});
};
return updateItems(items);
}
function collapseFolder(item, items) {
if (!item.isDirectory || !item.isExpanded) return items;
const updateItems = (itemList) => {
return itemList.map((currentItem) => {
if (currentItem === item) {
return {
...currentItem,
isExpanded: false
};
}
if (currentItem.children) {
return {
...currentItem,
children: updateItems(currentItem.children)
};
}
return currentItem;
});
};
return updateItems(items);
}
function toggleSelection(item, items, tokenCounts = {}) {
const updateItems = (itemList) => {
return itemList.map((currentItem) => {
if (currentItem === item) {
const newSelectionState = !currentItem.isSelected;
return selectAllChildren(currentItem, newSelectionState);
}
if (currentItem.children) {
const updatedItem = {
...currentItem,
children: updateItems(currentItem.children)
};
return updateParentSelectionState(updatedItem);
}
return currentItem;
});
};
let updatedItems = updateItems(items);
updatedItems = updatedItems.map(updateParentSelectionState);
const updatedItemsWithTokens = propagateSelectionTokenCounts(updatedItems, tokenCounts);
const totalSelectedTokens = getTotalSelectedTokens(updatedItemsWithTokens, tokenCounts);
const flatItems = flattenItems(updatedItemsWithTokens);
const selectedCount = flatItems.filter((item2) => item2.isSelected).length;
return [updatedItemsWithTokens, selectedCount, totalSelectedTokens];
}
function toggleAll(items, tokenCounts = {}) {
const flatItems = flattenItems(items);
const allSelected = flatItems.every((item) => item.isSelected);
let selectedCount = 0;
const updateItems = (itemList) => {
return itemList.map((item) => {
const newItem = { ...item, isSelected: !allSelected };
if (newItem.isSelected) selectedCount++;
if (item.children) {
return {
...newItem,
children: updateItems(item.children)
};
}
return newItem;
});
};
const updatedItems = updateItems(items);
const updatedItemsWithTokens = propagateSelectionTokenCounts(updatedItems, tokenCounts);
const totalSelectedTokens = getTotalSelectedTokens(updatedItemsWithTokens, tokenCounts);
return [updatedItemsWithTokens, selectedCount, totalSelectedTokens];
}
// src/hooks/useFileExplorer.ts
function useFileExplorer() {
const [state, setState] = useState({
items: [],
currentIndex: 0,
selectedCount: 0,
totalSelectedTokens: 0
});
const [tokenCounts, setTokenCounts] = useState({});
const [isLoading, setIsLoading] = useState(true);
const currentDir = process.cwd();
useEffect(() => {
const initializeData = async () => {
try {
const tokenData = await getTokenCounts(currentDir);
setTokenCounts(tokenData);
const initialItems = readDirectory(currentDir, 0, void 0, tokenData);
const itemsWithTokens = updateTokenCountsRecursively(initialItems, tokenData);
setState({
items: itemsWithTokens,
currentIndex: 0,
selectedCount: 0,
totalSelectedTokens: 0
});
} finally {
setIsLoading(false);
}
};
initializeData();
}, [currentDir]);
const handleExpandFolder = useCallback(
(item) => {
const updatedItems = expandFolder(item, state.items, tokenCounts);
const itemsWithTokens = updateTokenCountsRecursively(updatedItems, tokenCounts);
setState((prev) => ({
...prev,
items: itemsWithTokens
}));
},
[state.items, tokenCounts]
);
const handleCollapseFolder = useCallback(
(item) => {
const updatedItems = collapseFolder(item, state.items);
setState((prev) => ({
...prev,
items: updatedItems
}));
},
[state.items]
);
const handleToggleSelection = useCallback(
(item) => {
const [updatedItems, newSelectedCount, newTotalSelectedTokens] = toggleSelection(
item,
state.items,
tokenCounts
);
setState((prev) => ({
...prev,
items: updatedItems,
selectedCount: newSelectedCount,
totalSelectedTokens: newTotalSelectedTokens
}));
},
[state.items, tokenCounts]
);
const handleToggleAll = useCallback(() => {
const [updatedItems, newSelectedCount, newTotalSelectedTokens] = toggleAll(state.items, tokenCounts);
setState((prev) => ({
...prev,
items: updatedItems,
selectedCount: newSelectedCount,
totalSelectedTokens: newTotalSelectedTokens
}));
}, [state.items, tokenCounts]);
const moveUp = useCallback(() => {
setState((prev) => ({
...prev,
currentIndex: Math.max(0, prev.currentIndex - 1)
}));
}, []);
const moveDown = useCallback(() => {
const flatItems = flattenItems(state.items);
setState((prev) => ({
...prev,
currentIndex: Math.min(flatItems.length - 1, prev.currentIndex + 1)
}));
}, [state.items]);
const navigateToIndex = useCallback((index) => {
const flatItems = flattenItems(state.items);
const clampedIndex = Math.max(0, Math.min(flatItems.length - 1, index));
setState((prev) => ({
...prev,
currentIndex: clampedIndex
}));
}, [state.items]);
return {
state,
currentDir,
flattenItems,
handleExpandFolder,
handleCollapseFolder,
handleToggleSelection,
handleToggleAll,
moveUp,
moveDown,
navigateToIndex,
isLoading
};
}
// src/utils/repomixRunner.ts
import { runCli, setLogLevel as setLogLevel2 } from "repomix";
function getSelectedFilePaths(items) {
const selectedPaths = [];
function traverse(items2) {
for (const item of items2) {
if (item.isSelected) {
selectedPaths.push(item.path);
}
if (item.children) {
traverse(item.children);
}
}
}
traverse(items);
return selectedPaths;
}
async function runRepomixWithSelection({
selectedFiles,
cwd
}) {
if (selectedFiles.length === 0) {
throw new Error("No files selected");
}
const options = {
include: selectedFiles.join(","),
copy: true,
quiet: true
};
const originalCwd = process.cwd();
try {
setLogLevel2(-1);
process.chdir(cwd);
await runCli([cwd], cwd, options);
} finally {
process.chdir(originalCwd);
}
}
// src/components/LoadingScreen.tsx
import { Box, Text } from "ink";
import { useState as useState2, useEffect as useEffect2 } from "react";
import { jsx, jsxs } from "react/jsx-runtime";
function LoadingScreen() {
const [dots, setDots] = useState2(0);
useEffect2(() => {
const interval = setInterval(() => {
setDots((prev) => (prev + 1) % 4);
}, 400);
return () => {
clearInterval(interval);
};
}, []);
const dotString = ".".repeat(dots);
return /* @__PURE__ */ jsxs(
Box,
{
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
height: 24,
children: [
/* @__PURE__ */ jsx(Text, { children: " " }),
/* @__PURE__ */ jsx(Text, { children: " " }),
/* @__PURE__ */ jsx(Text, { color: "cyan", bold: true, children: "Crimson Phoenix" }),
/* @__PURE__ */ jsx(Text, { children: " " }),
/* @__PURE__ */ jsxs(Text, { color: "gray", children: [
"Loading",
dotString
] })
]
}
);
}
// src/components/FileExplorer.tsx
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
function FileExplorer() {
const {
state,
currentDir,
flattenItems: flattenItems2,
handleExpandFolder,
handleCollapseFolder,
handleToggleSelection,
handleToggleAll,
moveUp,
moveDown,
navigateToIndex,
isLoading
} = useFileExplorer();
const { stdout } = useStdout();
const [isExecuting, setIsExecuting] = useState3(false);
const [executionResult, setExecutionResult] = useState3(null);
const navigateToParent = useCallback2(() => {
const flatItems2 = flattenItems2(state.items);
const currentItem = flatItems2[state.currentIndex];
if (currentItem && currentItem.depth > 0) {
for (let i = state.currentIndex - 1; i >= 0; i--) {
const item = flatItems2[i];
if (!item) {
continue;
}
if (item.depth < currentItem.depth) {
navigateToIndex(i);
return true;
}
}
}
return false;
}, [state.currentIndex, state.items, navigateToIndex]);
const executeRepomix = useCallback2(async () => {
try {
setIsExecuting(true);
setExecutionResult(null);
const selectedFiles = getSelectedFilePaths(state.items);
if (selectedFiles.length === 0) {
setExecutionResult("\u274C No files selected");
return;
}
await runRepomixWithSelection({
selectedFiles,
cwd: currentDir
});
setExecutionResult("\u2705 Complete");
} catch (error) {
setExecutionResult(
`\u274C ${error instanceof Error ? error.message : "Error"}`
);
} finally {
setIsExecuting(false);
}
}, [state.items, currentDir]);
useInput((input, key) => {
if (executionResult) {
setExecutionResult(null);
return;
}
if (state.items.length === 0 || isExecuting) return;
const flatItems2 = flattenItems2(state.items);
const currentItem = flatItems2[state.currentIndex];
if (key.return) {
executeRepomix();
} else if (key.upArrow) {
moveUp();
} else if (key.downArrow) {
moveDown();
} else if (key.rightArrow) {
if (currentItem?.isDirectory && !currentItem.isExpanded) {
handleExpandFolder(currentItem);
} else {
moveDown();
}
} else if (key.leftArrow) {
if (currentItem?.isDirectory && currentItem.isExpanded) {
handleCollapseFolder(currentItem);
} else if (currentItem && currentItem.depth > 0) {
navigateToParent();
}
} else if (input === " " && currentItem) {
handleToggleSelection(currentItem);
} else if (input === "a") {
handleToggleAll();
}
});
const formatTokenCount = (count) => {
if (count === 0) return "";
if (count < 1e3) return `${count}`;
if (count < 1e6) return `${(count / 1e3).toFixed(1)}k`;
return `${(count / 1e6).toFixed(1)}m`;
};
const renderItem = (item, index, isActive) => {
const indent = " ".repeat(item.depth);
let checkbox = "[ ]";
let checkboxColor = void 0;
if (item.isSelected) {
checkbox = "[\u2713]";
checkboxColor = "green";
} else if (item.isPartiallySelected) {
checkbox = "[\u25D0]";
checkboxColor = "yellow";
}
const icon = item.isDirectory ? item.isExpanded ? "\u25BE" : "\u25B8" : "\xB7";
const cursor = isActive ? "\u258D " : " ";
let tokenDisplay = "";
if (item.isDirectory && item.isPartiallySelected) {
const selectedTokens2 = formatTokenCount(item.selectedTokenCount);
const totalTokens = formatTokenCount(item.tokenCount);
tokenDisplay = `${selectedTokens2}/${totalTokens}`;
} else if (item.isDirectory && item.isSelected) {
tokenDisplay = formatTokenCount(item.selectedTokenCount);
} else {
tokenDisplay = formatTokenCount(item.tokenCount);
}
const tokenColor = item.isSelected || item.isPartiallySelected ? "cyan" : "gray";
const nameColor = item.isSelected ? "white" : void 0;
return /* @__PURE__ */ jsxs2(
Text2,
{
children: [
cursor,
/* @__PURE__ */ jsx2(Text2, { color: checkboxColor, children: checkbox }),
" ",
indent,
/* @__PURE__ */ jsxs2(Text2, { color: nameColor, children: [
icon,
" ",
item.name
] }),
tokenDisplay && /* @__PURE__ */ jsxs2(Text2, { color: tokenColor, children: [
" (",
tokenDisplay,
")"
] })
]
},
`${item.path}-${index}-${item.isSelected}-${item.isPartiallySelected}`
);
};
const flatItems = flattenItems2(state.items);
const visibleHeight = (stdout?.rows || 24) - 3;
const startIndex = Math.max(
0,
Math.min(
state.currentIndex - Math.floor(visibleHeight / 2),
flatItems.length - visibleHeight
)
);
const endIndex = Math.min(flatItems.length, startIndex + visibleHeight);
const visibleItems = flatItems.slice(startIndex, endIndex);
const getTotalAvailableTokens = () => {
return flatItems.reduce((total, item) => {
if (item.isDirectory && item.isExpanded) {
return total;
}
return total + item.tokenCount;
}, 0);
};
const totalAvailable = getTotalAvailableTokens();
const selectedTokens = state.totalSelectedTokens;
const statusLine = `${state.selectedCount} selected \u2022 ${formatTokenCount(
selectedTokens
)}/${formatTokenCount(totalAvailable)} tokens \u2022 ${state.currentIndex + 1}/${flatItems.length} \u2022 ${currentDir}`;
if (isLoading) {
return /* @__PURE__ */ jsx2(LoadingScreen, {});
}
if (isExecuting) {
return /* @__PURE__ */ jsx2(
Box2,
{
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
height: stdout?.rows || 24,
children: /* @__PURE__ */ jsx2(Text2, { color: "yellow", children: "Processing..." })
}
);
}
if (executionResult) {
return /* @__PURE__ */ jsxs2(
Box2,
{
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
height: stdout?.rows || 24,
children: [
/* @__PURE__ */ jsx2(Text2, { children: executionResult }),
/* @__PURE__ */ jsx2(Text2, { children: " " }),
/* @__PURE__ */ jsx2(Text2, { color: "gray", children: "Press any key to continue..." })
]
}
);
}
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", height: stdout?.rows || 24, children: [
/* @__PURE__ */ jsx2(Text2, { color: "gray", children: "\u2191/\u2193 move \u2022 \u2192 expand \u2022 \u2190 collapse/parent \u2022 Space select \u2022 a toggle all \u2022 Enter execute" }),
/* @__PURE__ */ jsx2(Text2, { children: " " }),
/* @__PURE__ */ jsx2(Box2, { flexDirection: "column", flexGrow: 1, children: visibleItems.map((item, index) => {
const actualIndex = startIndex + index;
return renderItem(
item,
actualIndex,
actualIndex === state.currentIndex
);
}) }),
/* @__PURE__ */ jsx2(Text2, { color: "gray", children: statusLine })
] });
}
// src/App.tsx
import { render } from "ink";
import { jsx as jsx3 } from "react/jsx-runtime";
function App() {
useEffect3(() => {
process.stdout.write("\x1B[2J\x1B[3J\x1B[H");
}, []);
return /* @__PURE__ */ jsx3(FileExplorer, {});
}
render(/* @__PURE__ */ jsx3(App, {}));
export {
App
};