claudeup 1.8.0 → 3.3.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.
- package/bin/claudeup.js +20 -2
- package/package.json +10 -19
- package/src/data/cli-tools.js +123 -0
- package/src/data/cli-tools.ts +140 -0
- package/{dist → src}/data/marketplaces.js +23 -24
- package/src/data/marketplaces.ts +95 -0
- package/src/data/mcp-servers.js +509 -0
- package/src/data/mcp-servers.ts +526 -0
- package/src/data/statuslines.js +159 -0
- package/src/data/statuslines.ts +188 -0
- package/src/index.js +4 -0
- package/src/index.ts +5 -0
- package/{dist → src}/main.js +46 -47
- package/src/main.tsx +145 -0
- package/src/opentui.d.ts +191 -0
- package/{dist → src}/prerunner/index.js +31 -41
- package/src/prerunner/index.ts +124 -0
- package/{dist → src}/services/claude-runner.js +9 -10
- package/src/services/claude-runner.ts +31 -0
- package/{dist → src}/services/claude-settings.js +72 -33
- package/src/services/claude-settings.ts +934 -0
- package/src/services/local-marketplace.js +339 -0
- package/src/services/local-marketplace.ts +489 -0
- package/{dist → src}/services/mcp-registry.js +13 -14
- package/src/services/mcp-registry.ts +105 -0
- package/{dist → src}/services/plugin-manager.js +61 -13
- package/src/services/plugin-manager.ts +693 -0
- package/{dist → src}/services/plugin-mcp-config.js +19 -18
- package/src/services/plugin-mcp-config.ts +242 -0
- package/{dist → src}/services/update-cache.js +0 -1
- package/src/services/update-cache.ts +78 -0
- package/{dist → src}/services/version-check.js +15 -14
- package/src/services/version-check.ts +122 -0
- package/src/types/index.js +1 -0
- package/src/types/index.ts +141 -0
- package/src/ui/App.js +213 -0
- package/src/ui/App.tsx +359 -0
- package/src/ui/components/CategoryHeader.js +9 -0
- package/src/ui/components/CategoryHeader.tsx +41 -0
- package/{dist → src}/ui/components/ScrollableList.js +19 -6
- package/src/ui/components/ScrollableList.tsx +98 -0
- package/src/ui/components/SearchInput.js +19 -0
- package/src/ui/components/SearchInput.tsx +56 -0
- package/src/ui/components/StyledText.js +39 -0
- package/src/ui/components/StyledText.tsx +70 -0
- package/src/ui/components/TabBar.js +38 -0
- package/src/ui/components/TabBar.tsx +88 -0
- package/src/ui/components/layout/Panel.js +6 -0
- package/src/ui/components/layout/Panel.tsx +62 -0
- package/src/ui/components/layout/ProgressBar.js +14 -0
- package/src/ui/components/layout/ProgressBar.tsx +47 -0
- package/src/ui/components/layout/ScopeTabs.js +6 -0
- package/src/ui/components/layout/ScopeTabs.tsx +53 -0
- package/src/ui/components/layout/ScreenLayout.js +21 -0
- package/src/ui/components/layout/ScreenLayout.tsx +147 -0
- package/src/ui/components/layout/index.js +4 -0
- package/src/ui/components/layout/index.ts +4 -0
- package/src/ui/components/modals/ConfirmModal.js +14 -0
- package/src/ui/components/modals/ConfirmModal.tsx +59 -0
- package/src/ui/components/modals/InputModal.js +16 -0
- package/src/ui/components/modals/InputModal.tsx +68 -0
- package/src/ui/components/modals/LoadingModal.js +14 -0
- package/src/ui/components/modals/LoadingModal.tsx +40 -0
- package/src/ui/components/modals/MessageModal.js +16 -0
- package/src/ui/components/modals/MessageModal.tsx +64 -0
- package/src/ui/components/modals/ModalContainer.js +56 -0
- package/src/ui/components/modals/ModalContainer.tsx +104 -0
- package/src/ui/components/modals/SelectModal.js +26 -0
- package/src/ui/components/modals/SelectModal.tsx +82 -0
- package/src/ui/components/modals/index.js +6 -0
- package/src/ui/components/modals/index.ts +6 -0
- package/src/ui/hooks/index.js +3 -0
- package/src/ui/hooks/index.ts +3 -0
- package/{dist → src}/ui/hooks/useAsyncData.js +21 -22
- package/src/ui/hooks/useAsyncData.ts +127 -0
- package/src/ui/hooks/useKeyboard.js +13 -0
- package/src/ui/hooks/useKeyboard.ts +26 -0
- package/src/ui/hooks/useKeyboardHandler.js +39 -0
- package/src/ui/hooks/useKeyboardHandler.ts +63 -0
- package/{dist → src}/ui/screens/CliToolsScreen.js +60 -54
- package/src/ui/screens/CliToolsScreen.tsx +468 -0
- package/src/ui/screens/EnvVarsScreen.js +154 -0
- package/src/ui/screens/EnvVarsScreen.tsx +269 -0
- package/{dist → src}/ui/screens/McpRegistryScreen.js +56 -55
- package/src/ui/screens/McpRegistryScreen.tsx +331 -0
- package/{dist → src}/ui/screens/McpScreen.js +46 -47
- package/src/ui/screens/McpScreen.tsx +392 -0
- package/src/ui/screens/ModelSelectorScreen.js +292 -0
- package/src/ui/screens/ModelSelectorScreen.tsx +441 -0
- package/{dist → src}/ui/screens/PluginsScreen.js +305 -293
- package/src/ui/screens/PluginsScreen.tsx +1231 -0
- package/src/ui/screens/StatusLineScreen.js +200 -0
- package/src/ui/screens/StatusLineScreen.tsx +411 -0
- package/src/ui/screens/index.js +7 -0
- package/src/ui/screens/index.ts +7 -0
- package/src/ui/state/AnimationContext.js +34 -0
- package/src/ui/state/AnimationContext.tsx +76 -0
- package/{dist → src}/ui/state/AppContext.js +31 -32
- package/src/ui/state/AppContext.tsx +235 -0
- package/{dist → src}/ui/state/DimensionsContext.js +16 -17
- package/src/ui/state/DimensionsContext.tsx +144 -0
- package/{dist → src}/ui/state/reducer.js +89 -90
- package/src/ui/state/reducer.ts +467 -0
- package/src/ui/state/types.js +1 -0
- package/src/ui/state/types.ts +273 -0
- package/{dist → src}/utils/command-utils.js +3 -4
- package/src/utils/command-utils.ts +20 -0
- package/{dist → src}/utils/fuzzy-search.js +2 -3
- package/src/utils/fuzzy-search.ts +138 -0
- package/{dist → src}/utils/string-utils.js +6 -6
- package/src/utils/string-utils.ts +88 -0
- package/dist/data/cli-tools.d.ts +0 -13
- package/dist/data/cli-tools.d.ts.map +0 -1
- package/dist/data/cli-tools.js +0 -124
- package/dist/data/cli-tools.js.map +0 -1
- package/dist/data/marketplaces.d.ts +0 -6
- package/dist/data/marketplaces.d.ts.map +0 -1
- package/dist/data/marketplaces.js.map +0 -1
- package/dist/data/mcp-servers.d.ts +0 -8
- package/dist/data/mcp-servers.d.ts.map +0 -1
- package/dist/data/mcp-servers.js +0 -503
- package/dist/data/mcp-servers.js.map +0 -1
- package/dist/data/statuslines.d.ts +0 -10
- package/dist/data/statuslines.d.ts.map +0 -1
- package/dist/data/statuslines.js +0 -160
- package/dist/data/statuslines.js.map +0 -1
- package/dist/index.d.ts +0 -3
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -90
- package/dist/index.js.map +0 -1
- package/dist/main.d.ts +0 -3
- package/dist/main.d.ts.map +0 -1
- package/dist/main.js.map +0 -1
- package/dist/prerunner/index.d.ts +0 -7
- package/dist/prerunner/index.d.ts.map +0 -1
- package/dist/prerunner/index.js.map +0 -1
- package/dist/services/claude-runner.d.ts +0 -7
- package/dist/services/claude-runner.d.ts.map +0 -1
- package/dist/services/claude-runner.js.map +0 -1
- package/dist/services/claude-settings.d.ts +0 -73
- package/dist/services/claude-settings.d.ts.map +0 -1
- package/dist/services/claude-settings.js.map +0 -1
- package/dist/services/local-marketplace.d.ts +0 -111
- package/dist/services/local-marketplace.d.ts.map +0 -1
- package/dist/services/local-marketplace.js +0 -599
- package/dist/services/local-marketplace.js.map +0 -1
- package/dist/services/mcp-registry.d.ts +0 -10
- package/dist/services/mcp-registry.d.ts.map +0 -1
- package/dist/services/mcp-registry.js.map +0 -1
- package/dist/services/plugin-manager.d.ts +0 -65
- package/dist/services/plugin-manager.d.ts.map +0 -1
- package/dist/services/plugin-manager.js.map +0 -1
- package/dist/services/plugin-mcp-config.d.ts +0 -52
- package/dist/services/plugin-mcp-config.d.ts.map +0 -1
- package/dist/services/plugin-mcp-config.js.map +0 -1
- package/dist/services/update-cache.d.ts +0 -21
- package/dist/services/update-cache.d.ts.map +0 -1
- package/dist/services/update-cache.js.map +0 -1
- package/dist/services/version-check.d.ts +0 -20
- package/dist/services/version-check.d.ts.map +0 -1
- package/dist/services/version-check.js.map +0 -1
- package/dist/types/index.d.ts +0 -105
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/index.js +0 -2
- package/dist/types/index.js.map +0 -1
- package/dist/ui/InkApp.d.ts +0 -5
- package/dist/ui/InkApp.d.ts.map +0 -1
- package/dist/ui/InkApp.js +0 -188
- package/dist/ui/InkApp.js.map +0 -1
- package/dist/ui/components/CategoryHeader.d.ts +0 -16
- package/dist/ui/components/CategoryHeader.d.ts.map +0 -1
- package/dist/ui/components/CategoryHeader.js +0 -11
- package/dist/ui/components/CategoryHeader.js.map +0 -1
- package/dist/ui/components/ScrollableList.d.ts +0 -16
- package/dist/ui/components/ScrollableList.d.ts.map +0 -1
- package/dist/ui/components/ScrollableList.js.map +0 -1
- package/dist/ui/components/SearchInput.d.ts +0 -18
- package/dist/ui/components/SearchInput.d.ts.map +0 -1
- package/dist/ui/components/SearchInput.js +0 -30
- package/dist/ui/components/SearchInput.js.map +0 -1
- package/dist/ui/components/TabBar.d.ts +0 -8
- package/dist/ui/components/TabBar.d.ts.map +0 -1
- package/dist/ui/components/TabBar.js +0 -18
- package/dist/ui/components/TabBar.js.map +0 -1
- package/dist/ui/components/layout/Footer.d.ts +0 -14
- package/dist/ui/components/layout/Footer.d.ts.map +0 -1
- package/dist/ui/components/layout/Footer.js +0 -23
- package/dist/ui/components/layout/Footer.js.map +0 -1
- package/dist/ui/components/layout/Header.d.ts +0 -4
- package/dist/ui/components/layout/Header.d.ts.map +0 -1
- package/dist/ui/components/layout/Header.js +0 -25
- package/dist/ui/components/layout/Header.js.map +0 -1
- package/dist/ui/components/layout/Panel.d.ts +0 -22
- package/dist/ui/components/layout/Panel.d.ts.map +0 -1
- package/dist/ui/components/layout/Panel.js +0 -8
- package/dist/ui/components/layout/Panel.js.map +0 -1
- package/dist/ui/components/layout/ProgressBar.d.ts +0 -12
- package/dist/ui/components/layout/ProgressBar.d.ts.map +0 -1
- package/dist/ui/components/layout/ProgressBar.js +0 -16
- package/dist/ui/components/layout/ProgressBar.js.map +0 -1
- package/dist/ui/components/layout/ScopeTabs.d.ts +0 -12
- package/dist/ui/components/layout/ScopeTabs.d.ts.map +0 -1
- package/dist/ui/components/layout/ScopeTabs.js +0 -8
- package/dist/ui/components/layout/ScopeTabs.js.map +0 -1
- package/dist/ui/components/layout/ScreenLayout.d.ts +0 -30
- package/dist/ui/components/layout/ScreenLayout.d.ts.map +0 -1
- package/dist/ui/components/layout/ScreenLayout.js +0 -23
- package/dist/ui/components/layout/ScreenLayout.js.map +0 -1
- package/dist/ui/components/layout/index.d.ts +0 -7
- package/dist/ui/components/layout/index.d.ts.map +0 -1
- package/dist/ui/components/layout/index.js +0 -7
- package/dist/ui/components/layout/index.js.map +0 -1
- package/dist/ui/components/modals/ConfirmModal.d.ts +0 -14
- package/dist/ui/components/modals/ConfirmModal.d.ts.map +0 -1
- package/dist/ui/components/modals/ConfirmModal.js +0 -15
- package/dist/ui/components/modals/ConfirmModal.js.map +0 -1
- package/dist/ui/components/modals/InputModal.d.ts +0 -16
- package/dist/ui/components/modals/InputModal.d.ts.map +0 -1
- package/dist/ui/components/modals/InputModal.js +0 -23
- package/dist/ui/components/modals/InputModal.js.map +0 -1
- package/dist/ui/components/modals/LoadingModal.d.ts +0 -8
- package/dist/ui/components/modals/LoadingModal.d.ts.map +0 -1
- package/dist/ui/components/modals/LoadingModal.js +0 -8
- package/dist/ui/components/modals/LoadingModal.js.map +0 -1
- package/dist/ui/components/modals/MessageModal.d.ts +0 -14
- package/dist/ui/components/modals/MessageModal.d.ts.map +0 -1
- package/dist/ui/components/modals/MessageModal.js +0 -17
- package/dist/ui/components/modals/MessageModal.js.map +0 -1
- package/dist/ui/components/modals/ModalContainer.d.ts +0 -7
- package/dist/ui/components/modals/ModalContainer.d.ts.map +0 -1
- package/dist/ui/components/modals/ModalContainer.js +0 -38
- package/dist/ui/components/modals/ModalContainer.js.map +0 -1
- package/dist/ui/components/modals/SelectModal.d.ts +0 -17
- package/dist/ui/components/modals/SelectModal.d.ts.map +0 -1
- package/dist/ui/components/modals/SelectModal.js +0 -33
- package/dist/ui/components/modals/SelectModal.js.map +0 -1
- package/dist/ui/components/modals/index.d.ts +0 -7
- package/dist/ui/components/modals/index.d.ts.map +0 -1
- package/dist/ui/components/modals/index.js +0 -7
- package/dist/ui/components/modals/index.js.map +0 -1
- package/dist/ui/hooks/index.d.ts +0 -3
- package/dist/ui/hooks/index.d.ts.map +0 -1
- package/dist/ui/hooks/index.js +0 -3
- package/dist/ui/hooks/index.js.map +0 -1
- package/dist/ui/hooks/useAsyncData.d.ts +0 -40
- package/dist/ui/hooks/useAsyncData.d.ts.map +0 -1
- package/dist/ui/hooks/useAsyncData.js.map +0 -1
- package/dist/ui/hooks/useKeyboardNavigation.d.ts +0 -27
- package/dist/ui/hooks/useKeyboardNavigation.d.ts.map +0 -1
- package/dist/ui/hooks/useKeyboardNavigation.js +0 -82
- package/dist/ui/hooks/useKeyboardNavigation.js.map +0 -1
- package/dist/ui/screens/CliToolsScreen.d.ts +0 -4
- package/dist/ui/screens/CliToolsScreen.d.ts.map +0 -1
- package/dist/ui/screens/CliToolsScreen.js.map +0 -1
- package/dist/ui/screens/EnvVarsScreen.d.ts +0 -4
- package/dist/ui/screens/EnvVarsScreen.d.ts.map +0 -1
- package/dist/ui/screens/EnvVarsScreen.js +0 -145
- package/dist/ui/screens/EnvVarsScreen.js.map +0 -1
- package/dist/ui/screens/McpRegistryScreen.d.ts +0 -4
- package/dist/ui/screens/McpRegistryScreen.d.ts.map +0 -1
- package/dist/ui/screens/McpRegistryScreen.js.map +0 -1
- package/dist/ui/screens/McpScreen.d.ts +0 -4
- package/dist/ui/screens/McpScreen.d.ts.map +0 -1
- package/dist/ui/screens/McpScreen.js.map +0 -1
- package/dist/ui/screens/ModelSelectorScreen.d.ts +0 -4
- package/dist/ui/screens/ModelSelectorScreen.d.ts.map +0 -1
- package/dist/ui/screens/ModelSelectorScreen.js +0 -143
- package/dist/ui/screens/ModelSelectorScreen.js.map +0 -1
- package/dist/ui/screens/PluginsScreen.d.ts +0 -4
- package/dist/ui/screens/PluginsScreen.d.ts.map +0 -1
- package/dist/ui/screens/PluginsScreen.js.map +0 -1
- package/dist/ui/screens/StatusLineScreen.d.ts +0 -4
- package/dist/ui/screens/StatusLineScreen.d.ts.map +0 -1
- package/dist/ui/screens/StatusLineScreen.js +0 -197
- package/dist/ui/screens/StatusLineScreen.js.map +0 -1
- package/dist/ui/screens/index.d.ts +0 -8
- package/dist/ui/screens/index.d.ts.map +0 -1
- package/dist/ui/screens/index.js +0 -8
- package/dist/ui/screens/index.js.map +0 -1
- package/dist/ui/state/AppContext.d.ts +0 -40
- package/dist/ui/state/AppContext.d.ts.map +0 -1
- package/dist/ui/state/AppContext.js.map +0 -1
- package/dist/ui/state/DimensionsContext.d.ts +0 -27
- package/dist/ui/state/DimensionsContext.d.ts.map +0 -1
- package/dist/ui/state/DimensionsContext.js.map +0 -1
- package/dist/ui/state/reducer.d.ts +0 -4
- package/dist/ui/state/reducer.d.ts.map +0 -1
- package/dist/ui/state/reducer.js.map +0 -1
- package/dist/ui/state/types.d.ts +0 -266
- package/dist/ui/state/types.d.ts.map +0 -1
- package/dist/ui/state/types.js +0 -2
- package/dist/ui/state/types.js.map +0 -1
- package/dist/utils/command-utils.d.ts +0 -8
- package/dist/utils/command-utils.d.ts.map +0 -1
- package/dist/utils/command-utils.js.map +0 -1
- package/dist/utils/fuzzy-search.d.ts +0 -33
- package/dist/utils/fuzzy-search.d.ts.map +0 -1
- package/dist/utils/fuzzy-search.js.map +0 -1
- package/dist/utils/string-utils.d.ts +0 -24
- package/dist/utils/string-utils.d.ts.map +0 -1
- package/dist/utils/string-utils.js.map +0 -1
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "@opentui/react/jsx-runtime";
|
|
2
|
+
export function CategoryHeader({ title, status, statusColor = "green", expanded = true, count, }) {
|
|
3
|
+
const expandIcon = expanded ? "▼" : "▶";
|
|
4
|
+
const countBadge = count !== undefined ? ` (${count})` : "";
|
|
5
|
+
const statusText = status ? ` ${status}` : "";
|
|
6
|
+
// Simple format without dynamic line calculation
|
|
7
|
+
return (_jsxs("text", { children: [_jsx("span", { fg: "#666666", children: expandIcon }), _jsx("span", { fg: "white", children: _jsxs("strong", { children: [" ", title] }) }), _jsx("span", { fg: "#666666", children: countBadge }), _jsx("span", { fg: "#666666", children: " \u2500\u2500\u2500\u2500" }), _jsx("span", { fg: statusColor, children: statusText })] }));
|
|
8
|
+
}
|
|
9
|
+
export default CategoryHeader;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
interface CategoryHeaderProps {
|
|
4
|
+
/** Category title */
|
|
5
|
+
title: string;
|
|
6
|
+
/** Status badge (e.g., "✓ Configured", "3 plugins") */
|
|
7
|
+
status?: string;
|
|
8
|
+
/** Status badge color */
|
|
9
|
+
statusColor?: string;
|
|
10
|
+
/** Whether category is expanded */
|
|
11
|
+
expanded?: boolean;
|
|
12
|
+
/** Number of items in category */
|
|
13
|
+
count?: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function CategoryHeader({
|
|
17
|
+
title,
|
|
18
|
+
status,
|
|
19
|
+
statusColor = "green",
|
|
20
|
+
expanded = true,
|
|
21
|
+
count,
|
|
22
|
+
}: CategoryHeaderProps) {
|
|
23
|
+
const expandIcon = expanded ? "▼" : "▶";
|
|
24
|
+
const countBadge = count !== undefined ? ` (${count})` : "";
|
|
25
|
+
const statusText = status ? ` ${status}` : "";
|
|
26
|
+
|
|
27
|
+
// Simple format without dynamic line calculation
|
|
28
|
+
return (
|
|
29
|
+
<text>
|
|
30
|
+
<span fg="#666666">{expandIcon}</span>
|
|
31
|
+
<span fg="white">
|
|
32
|
+
<strong> {title}</strong>
|
|
33
|
+
</span>
|
|
34
|
+
<span fg="#666666">{countBadge}</span>
|
|
35
|
+
<span fg="#666666"> ────</span>
|
|
36
|
+
<span fg={statusColor}>{statusText}</span>
|
|
37
|
+
</text>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export default CategoryHeader;
|
|
@@ -1,12 +1,26 @@
|
|
|
1
|
-
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx } from "@opentui/react/jsx-runtime";
|
|
2
2
|
import { useState, useEffect, useMemo } from 'react';
|
|
3
|
-
import {
|
|
4
|
-
export function ScrollableList({ items, selectedIndex, renderItem, maxHeight, showScrollIndicators = true, }) {
|
|
3
|
+
import { useKeyboardHandler } from '../hooks/useKeyboardHandler';
|
|
4
|
+
export function ScrollableList({ items, selectedIndex, renderItem, maxHeight, showScrollIndicators = true, onSelect, focused = false, }) {
|
|
5
5
|
const [scrollOffset, setScrollOffset] = useState(0);
|
|
6
|
+
// Handle keyboard navigation
|
|
7
|
+
useKeyboardHandler((input, key) => {
|
|
8
|
+
if (!focused || !onSelect)
|
|
9
|
+
return;
|
|
10
|
+
if (key.upArrow || input === 'k') {
|
|
11
|
+
const newIndex = Math.max(0, selectedIndex - 1);
|
|
12
|
+
onSelect(newIndex);
|
|
13
|
+
}
|
|
14
|
+
else if (key.downArrow || input === 'j') {
|
|
15
|
+
const newIndex = Math.min(items.length - 1, selectedIndex + 1);
|
|
16
|
+
onSelect(newIndex);
|
|
17
|
+
}
|
|
18
|
+
});
|
|
6
19
|
// Account for scroll indicators in available space
|
|
7
20
|
const hasItemsAbove = scrollOffset > 0;
|
|
8
21
|
const hasItemsBelow = scrollOffset + maxHeight < items.length;
|
|
9
|
-
const indicatorLines = (showScrollIndicators && hasItemsAbove ? 1 : 0) +
|
|
22
|
+
const indicatorLines = (showScrollIndicators && hasItemsAbove ? 1 : 0) +
|
|
23
|
+
(showScrollIndicators && hasItemsBelow ? 1 : 0);
|
|
10
24
|
const effectiveMaxHeight = Math.max(1, maxHeight - indicatorLines);
|
|
11
25
|
// Adjust scroll offset to keep selected item visible
|
|
12
26
|
useEffect(() => {
|
|
@@ -29,7 +43,6 @@ export function ScrollableList({ items, selectedIndex, renderItem, maxHeight, sh
|
|
|
29
43
|
}));
|
|
30
44
|
}, [items, scrollOffset, effectiveMaxHeight]);
|
|
31
45
|
const itemsBelow = items.length - scrollOffset - effectiveMaxHeight;
|
|
32
|
-
return (_jsxs(
|
|
46
|
+
return (_jsxs("box", { flexDirection: "column", children: [showScrollIndicators && hasItemsAbove && (_jsxs("text", { fg: "cyan", children: ["\u2191 ", scrollOffset, " more"] })), visibleItems.map(({ item, originalIndex }) => (_jsx("box", { width: "100%", overflow: "hidden", children: renderItem(item, originalIndex, originalIndex === selectedIndex) }, originalIndex))), showScrollIndicators && hasItemsBelow && (_jsxs("text", { fg: "cyan", children: ["\u2193 ", itemsBelow, " more"] }))] }));
|
|
33
47
|
}
|
|
34
48
|
export default ScrollableList;
|
|
35
|
-
//# sourceMappingURL=ScrollableList.js.map
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import React, { useState, useEffect, useMemo } from 'react';
|
|
2
|
+
import { useKeyboardHandler } from '../hooks/useKeyboardHandler';
|
|
3
|
+
|
|
4
|
+
interface ScrollableListProps<T> {
|
|
5
|
+
/** Array of items to display */
|
|
6
|
+
items: T[];
|
|
7
|
+
/** Currently selected index */
|
|
8
|
+
selectedIndex: number;
|
|
9
|
+
/** Render function for each item */
|
|
10
|
+
renderItem: (item: T, index: number, isSelected: boolean) => React.ReactNode;
|
|
11
|
+
/** Maximum visible height (number of lines) - REQUIRED for proper rendering */
|
|
12
|
+
maxHeight: number;
|
|
13
|
+
/** Show scroll indicators */
|
|
14
|
+
showScrollIndicators?: boolean;
|
|
15
|
+
/** Called when selection changes (arrow keys) */
|
|
16
|
+
onSelect?: (index: number) => void;
|
|
17
|
+
/** Whether this list should receive keyboard input */
|
|
18
|
+
focused?: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function ScrollableList<T>({
|
|
22
|
+
items,
|
|
23
|
+
selectedIndex,
|
|
24
|
+
renderItem,
|
|
25
|
+
maxHeight,
|
|
26
|
+
showScrollIndicators = true,
|
|
27
|
+
onSelect,
|
|
28
|
+
focused = false,
|
|
29
|
+
}: ScrollableListProps<T>) {
|
|
30
|
+
const [scrollOffset, setScrollOffset] = useState(0);
|
|
31
|
+
|
|
32
|
+
// Handle keyboard navigation
|
|
33
|
+
useKeyboardHandler((input, key) => {
|
|
34
|
+
if (!focused || !onSelect) return;
|
|
35
|
+
|
|
36
|
+
if (key.upArrow || input === 'k') {
|
|
37
|
+
const newIndex = Math.max(0, selectedIndex - 1);
|
|
38
|
+
onSelect(newIndex);
|
|
39
|
+
} else if (key.downArrow || input === 'j') {
|
|
40
|
+
const newIndex = Math.min(items.length - 1, selectedIndex + 1);
|
|
41
|
+
onSelect(newIndex);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// Account for scroll indicators in available space
|
|
46
|
+
const hasItemsAbove = scrollOffset > 0;
|
|
47
|
+
const hasItemsBelow = scrollOffset + maxHeight < items.length;
|
|
48
|
+
const indicatorLines =
|
|
49
|
+
(showScrollIndicators && hasItemsAbove ? 1 : 0) +
|
|
50
|
+
(showScrollIndicators && hasItemsBelow ? 1 : 0);
|
|
51
|
+
const effectiveMaxHeight = Math.max(1, maxHeight - indicatorLines);
|
|
52
|
+
|
|
53
|
+
// Adjust scroll offset to keep selected item visible
|
|
54
|
+
useEffect(() => {
|
|
55
|
+
if (selectedIndex < scrollOffset) {
|
|
56
|
+
// Selected is above viewport - scroll up
|
|
57
|
+
setScrollOffset(selectedIndex);
|
|
58
|
+
} else if (selectedIndex >= scrollOffset + effectiveMaxHeight) {
|
|
59
|
+
// Selected is below viewport - scroll down
|
|
60
|
+
setScrollOffset(selectedIndex - effectiveMaxHeight + 1);
|
|
61
|
+
}
|
|
62
|
+
}, [selectedIndex, effectiveMaxHeight, scrollOffset]);
|
|
63
|
+
|
|
64
|
+
// Calculate visible items - strictly limited to effectiveMaxHeight
|
|
65
|
+
const visibleItems = useMemo(() => {
|
|
66
|
+
const start = scrollOffset;
|
|
67
|
+
const end = Math.min(scrollOffset + effectiveMaxHeight, items.length);
|
|
68
|
+
return items.slice(start, end).map((item, idx) => ({
|
|
69
|
+
item,
|
|
70
|
+
originalIndex: start + idx,
|
|
71
|
+
}));
|
|
72
|
+
}, [items, scrollOffset, effectiveMaxHeight]);
|
|
73
|
+
|
|
74
|
+
const itemsBelow = items.length - scrollOffset - effectiveMaxHeight;
|
|
75
|
+
|
|
76
|
+
return (
|
|
77
|
+
<box flexDirection="column">
|
|
78
|
+
{/* Scroll up indicator */}
|
|
79
|
+
{showScrollIndicators && hasItemsAbove && (
|
|
80
|
+
<text fg="cyan">↑ {scrollOffset} more</text>
|
|
81
|
+
)}
|
|
82
|
+
|
|
83
|
+
{/* Visible items - strictly limited */}
|
|
84
|
+
{visibleItems.map(({ item, originalIndex }) => (
|
|
85
|
+
<box key={originalIndex} width="100%" overflow="hidden">
|
|
86
|
+
{renderItem(item, originalIndex, originalIndex === selectedIndex)}
|
|
87
|
+
</box>
|
|
88
|
+
))}
|
|
89
|
+
|
|
90
|
+
{/* Scroll down indicator */}
|
|
91
|
+
{showScrollIndicators && hasItemsBelow && (
|
|
92
|
+
<text fg="cyan">↓ {itemsBelow} more</text>
|
|
93
|
+
)}
|
|
94
|
+
</box>
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export default ScrollableList;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "@opentui/react/jsx-runtime";
|
|
2
|
+
import { useKeyboardHandler } from '../hooks/useKeyboardHandler.js';
|
|
3
|
+
export function SearchInput({ value, onChange, placeholder = 'Search...', isActive, onExit, onSubmit, }) {
|
|
4
|
+
// Handle keyboard shortcuts when active
|
|
5
|
+
useKeyboardHandler((_input, key) => {
|
|
6
|
+
if (!isActive)
|
|
7
|
+
return;
|
|
8
|
+
if (key.escape) {
|
|
9
|
+
onExit?.();
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
if (key.return) {
|
|
13
|
+
onSubmit?.();
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
return (_jsxs("box", { flexDirection: "row", children: [_jsx("text", { fg: "cyan", children: "\u276F " }), _jsx("input", { value: value, onChange: onChange, placeholder: placeholder, focused: isActive, width: 40 })] }));
|
|
18
|
+
}
|
|
19
|
+
export default SearchInput;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { useKeyboardHandler } from '../hooks/useKeyboardHandler.js';
|
|
3
|
+
|
|
4
|
+
interface SearchInputProps {
|
|
5
|
+
/** Current search value */
|
|
6
|
+
value: string;
|
|
7
|
+
/** Called when value changes */
|
|
8
|
+
onChange: (value: string) => void;
|
|
9
|
+
/** Placeholder text when empty */
|
|
10
|
+
placeholder?: string;
|
|
11
|
+
/** Whether the input is focused/active */
|
|
12
|
+
isActive: boolean;
|
|
13
|
+
/** Called when user presses escape to exit search */
|
|
14
|
+
onExit?: () => void;
|
|
15
|
+
/** Called when user presses enter */
|
|
16
|
+
onSubmit?: () => void;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function SearchInput({
|
|
20
|
+
value,
|
|
21
|
+
onChange,
|
|
22
|
+
placeholder = 'Search...',
|
|
23
|
+
isActive,
|
|
24
|
+
onExit,
|
|
25
|
+
onSubmit,
|
|
26
|
+
}: SearchInputProps) {
|
|
27
|
+
// Handle keyboard shortcuts when active
|
|
28
|
+
useKeyboardHandler((_input, key) => {
|
|
29
|
+
if (!isActive) return;
|
|
30
|
+
|
|
31
|
+
if (key.escape) {
|
|
32
|
+
onExit?.();
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (key.return) {
|
|
37
|
+
onSubmit?.();
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<box flexDirection="row">
|
|
44
|
+
<text fg="cyan">❯ </text>
|
|
45
|
+
<input
|
|
46
|
+
value={value}
|
|
47
|
+
onChange={onChange}
|
|
48
|
+
placeholder={placeholder}
|
|
49
|
+
focused={isActive}
|
|
50
|
+
width={40}
|
|
51
|
+
/>
|
|
52
|
+
</box>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export default SearchInput;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { jsx as _jsx } from "@opentui/react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* StyledText - Utility component to simplify text styling with OpenTUI
|
|
4
|
+
*
|
|
5
|
+
* Converts props to nested tag pattern required by OpenTUI.
|
|
6
|
+
*
|
|
7
|
+
* Example:
|
|
8
|
+
* <StyledText bold color="green">Success</StyledText>
|
|
9
|
+
* becomes: <text fg="green"><strong>Success</strong></text>
|
|
10
|
+
*/
|
|
11
|
+
export function StyledText({ children, bold = false, color, bgColor, dim = false, italic = false, underline = false, inverse = false, }) {
|
|
12
|
+
let content = children;
|
|
13
|
+
// Apply text modifiers (innermost to outermost)
|
|
14
|
+
if (bold) {
|
|
15
|
+
content = _jsx("strong", { children: content });
|
|
16
|
+
}
|
|
17
|
+
if (italic) {
|
|
18
|
+
content = _jsx("em", { children: content });
|
|
19
|
+
}
|
|
20
|
+
if (underline) {
|
|
21
|
+
content = _jsx("u", { children: content });
|
|
22
|
+
}
|
|
23
|
+
// Note: dim and inverse use span with colors since OpenTUI doesn't have these as React elements
|
|
24
|
+
if (dim) {
|
|
25
|
+
content = _jsx("span", { fg: "#666666", children: content });
|
|
26
|
+
}
|
|
27
|
+
if (inverse) {
|
|
28
|
+
// Inverse effect approximated with contrasting colors
|
|
29
|
+
content = _jsx("span", { fg: "black", bg: "white", children: content });
|
|
30
|
+
}
|
|
31
|
+
// Apply colors (outer layer)
|
|
32
|
+
const textProps = {};
|
|
33
|
+
if (color)
|
|
34
|
+
textProps.fg = color;
|
|
35
|
+
if (bgColor)
|
|
36
|
+
textProps.bg = bgColor;
|
|
37
|
+
return _jsx("text", { ...textProps, children: content });
|
|
38
|
+
}
|
|
39
|
+
export default StyledText;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
interface StyledTextProps {
|
|
4
|
+
/** Text content */
|
|
5
|
+
children: React.ReactNode;
|
|
6
|
+
/** Bold text */
|
|
7
|
+
bold?: boolean;
|
|
8
|
+
/** Foreground color */
|
|
9
|
+
color?: string;
|
|
10
|
+
/** Background color */
|
|
11
|
+
bgColor?: string;
|
|
12
|
+
/** Dimmed text */
|
|
13
|
+
dim?: boolean;
|
|
14
|
+
/** Italic text */
|
|
15
|
+
italic?: boolean;
|
|
16
|
+
/** Underlined text */
|
|
17
|
+
underline?: boolean;
|
|
18
|
+
/** Inverse colors */
|
|
19
|
+
inverse?: boolean;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* StyledText - Utility component to simplify text styling with OpenTUI
|
|
24
|
+
*
|
|
25
|
+
* Converts props to nested tag pattern required by OpenTUI.
|
|
26
|
+
*
|
|
27
|
+
* Example:
|
|
28
|
+
* <StyledText bold color="green">Success</StyledText>
|
|
29
|
+
* becomes: <text fg="green"><strong>Success</strong></text>
|
|
30
|
+
*/
|
|
31
|
+
export function StyledText({
|
|
32
|
+
children,
|
|
33
|
+
bold = false,
|
|
34
|
+
color,
|
|
35
|
+
bgColor,
|
|
36
|
+
dim = false,
|
|
37
|
+
italic = false,
|
|
38
|
+
underline = false,
|
|
39
|
+
inverse = false,
|
|
40
|
+
}: StyledTextProps) {
|
|
41
|
+
let content: React.ReactNode = children;
|
|
42
|
+
|
|
43
|
+
// Apply text modifiers (innermost to outermost)
|
|
44
|
+
if (bold) {
|
|
45
|
+
content = <strong>{content}</strong>;
|
|
46
|
+
}
|
|
47
|
+
if (italic) {
|
|
48
|
+
content = <em>{content}</em>;
|
|
49
|
+
}
|
|
50
|
+
if (underline) {
|
|
51
|
+
content = <u>{content}</u>;
|
|
52
|
+
}
|
|
53
|
+
// Note: dim and inverse use span with colors since OpenTUI doesn't have these as React elements
|
|
54
|
+
if (dim) {
|
|
55
|
+
content = <span fg="#666666">{content}</span>;
|
|
56
|
+
}
|
|
57
|
+
if (inverse) {
|
|
58
|
+
// Inverse effect approximated with contrasting colors
|
|
59
|
+
content = <span fg="black" bg="white">{content}</span>;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Apply colors (outer layer)
|
|
63
|
+
const textProps: { fg?: string; bg?: string } = {};
|
|
64
|
+
if (color) textProps.fg = color;
|
|
65
|
+
if (bgColor) textProps.bg = bgColor;
|
|
66
|
+
|
|
67
|
+
return <text {...textProps}>{content}</text>;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export default StyledText;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx } from "@opentui/react/jsx-runtime";
|
|
2
|
+
import { useKeyboardHandler } from '../hooks/useKeyboardHandler';
|
|
3
|
+
const TABS = [
|
|
4
|
+
{ key: '1', label: 'Plugins', screen: 'plugins' },
|
|
5
|
+
{ key: '2', label: 'MCP', screen: 'mcp' },
|
|
6
|
+
{ key: '3', label: 'Status', screen: 'statusline' },
|
|
7
|
+
{ key: '4', label: 'Env', screen: 'env-vars' },
|
|
8
|
+
{ key: '5', label: 'CLI', screen: 'cli-tools' },
|
|
9
|
+
];
|
|
10
|
+
export function TabBar({ currentScreen, onTabChange, }) {
|
|
11
|
+
// Handle number key shortcuts (1-5)
|
|
12
|
+
useKeyboardHandler((input, key) => {
|
|
13
|
+
if (!onTabChange)
|
|
14
|
+
return;
|
|
15
|
+
// Number keys 1-5
|
|
16
|
+
const tabIndex = Number.parseInt(input, 10);
|
|
17
|
+
if (tabIndex >= 1 && tabIndex <= TABS.length) {
|
|
18
|
+
const tab = TABS[tabIndex - 1];
|
|
19
|
+
if (tab && tab.screen !== currentScreen) {
|
|
20
|
+
onTabChange(tab.screen);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
// Tab key to cycle through tabs
|
|
24
|
+
if (key.tab) {
|
|
25
|
+
const currentIndex = TABS.findIndex((t) => t.screen === currentScreen);
|
|
26
|
+
const nextIndex = key.shift
|
|
27
|
+
? (currentIndex - 1 + TABS.length) % TABS.length
|
|
28
|
+
: (currentIndex + 1) % TABS.length;
|
|
29
|
+
onTabChange(TABS[nextIndex].screen);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
return (_jsx("box", { flexDirection: "row", gap: 0, children: TABS.map((tab, index) => {
|
|
33
|
+
const isSelected = tab.screen === currentScreen;
|
|
34
|
+
const isLast = index === TABS.length - 1;
|
|
35
|
+
return (_jsxs("box", { flexDirection: "row", children: [isSelected ? (_jsx("box", { children: _jsx("text", { bg: "#7e57c2", fg: "white", children: _jsxs("strong", { children: [' ', tab.key, ":", tab.label, ' '] }) }) })) : (_jsx("box", { children: _jsxs("text", { fg: "gray", children: [' ', tab.key, ":", tab.label, ' '] }) })), !isLast && (_jsx("text", { fg: "#666666", children: "\u2502" }))] }, tab.key));
|
|
36
|
+
}) }));
|
|
37
|
+
}
|
|
38
|
+
export default TabBar;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { useKeyboardHandler } from '../hooks/useKeyboardHandler';
|
|
3
|
+
import type { Screen } from '../state/types.js';
|
|
4
|
+
|
|
5
|
+
interface Tab {
|
|
6
|
+
key: string;
|
|
7
|
+
label: string;
|
|
8
|
+
screen: Screen;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const TABS: Tab[] = [
|
|
12
|
+
{ key: '1', label: 'Plugins', screen: 'plugins' },
|
|
13
|
+
{ key: '2', label: 'MCP', screen: 'mcp' },
|
|
14
|
+
{ key: '3', label: 'Status', screen: 'statusline' },
|
|
15
|
+
{ key: '4', label: 'Env', screen: 'env-vars' },
|
|
16
|
+
{ key: '5', label: 'CLI', screen: 'cli-tools' },
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
interface TabBarProps {
|
|
20
|
+
currentScreen: Screen;
|
|
21
|
+
onTabChange?: (screen: Screen) => void;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function TabBar({
|
|
25
|
+
currentScreen,
|
|
26
|
+
onTabChange,
|
|
27
|
+
}: TabBarProps) {
|
|
28
|
+
// Handle number key shortcuts (1-5)
|
|
29
|
+
useKeyboardHandler((input, key) => {
|
|
30
|
+
if (!onTabChange) return;
|
|
31
|
+
|
|
32
|
+
// Number keys 1-5
|
|
33
|
+
const tabIndex = Number.parseInt(input, 10);
|
|
34
|
+
if (tabIndex >= 1 && tabIndex <= TABS.length) {
|
|
35
|
+
const tab = TABS[tabIndex - 1];
|
|
36
|
+
if (tab && tab.screen !== currentScreen) {
|
|
37
|
+
onTabChange(tab.screen);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Tab key to cycle through tabs
|
|
42
|
+
if (key.tab) {
|
|
43
|
+
const currentIndex = TABS.findIndex((t) => t.screen === currentScreen);
|
|
44
|
+
const nextIndex = key.shift
|
|
45
|
+
? (currentIndex - 1 + TABS.length) % TABS.length
|
|
46
|
+
: (currentIndex + 1) % TABS.length;
|
|
47
|
+
onTabChange(TABS[nextIndex].screen);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
return (
|
|
52
|
+
<box flexDirection="row" gap={0}>
|
|
53
|
+
{TABS.map((tab, index) => {
|
|
54
|
+
const isSelected = tab.screen === currentScreen;
|
|
55
|
+
const isLast = index === TABS.length - 1;
|
|
56
|
+
|
|
57
|
+
return (
|
|
58
|
+
<box key={tab.key} flexDirection="row">
|
|
59
|
+
{/* Tab content */}
|
|
60
|
+
{isSelected ? (
|
|
61
|
+
<box>
|
|
62
|
+
<text bg="#7e57c2" fg="white">
|
|
63
|
+
<strong>
|
|
64
|
+
{' '}
|
|
65
|
+
{tab.key}:{tab.label}{' '}
|
|
66
|
+
</strong>
|
|
67
|
+
</text>
|
|
68
|
+
</box>
|
|
69
|
+
) : (
|
|
70
|
+
<box>
|
|
71
|
+
<text fg="gray">
|
|
72
|
+
{' '}
|
|
73
|
+
{tab.key}:{tab.label}{' '}
|
|
74
|
+
</text>
|
|
75
|
+
</box>
|
|
76
|
+
)}
|
|
77
|
+
{/* Separator */}
|
|
78
|
+
{!isLast && (
|
|
79
|
+
<text fg="#666666">│</text>
|
|
80
|
+
)}
|
|
81
|
+
</box>
|
|
82
|
+
);
|
|
83
|
+
})}
|
|
84
|
+
</box>
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export default TabBar;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "@opentui/react/jsx-runtime";
|
|
2
|
+
export function Panel({ title, children, borderColor = "#7e57c2", titleColor = "#7e57c2", width, height, flexGrow = 1, focused = false, }) {
|
|
3
|
+
const activeColor = focused ? "#7e57c2" : borderColor;
|
|
4
|
+
return (_jsxs("box", { flexDirection: "column", width: width, height: height, flexGrow: flexGrow, border: true, borderStyle: "single", borderColor: activeColor, padding: 1, children: [title && (_jsx("box", { marginBottom: 0, children: _jsx("text", { fg: titleColor, children: _jsx("strong", { children: title }) }) })), _jsx("box", { flexDirection: "column", flexGrow: 1, children: children })] }));
|
|
5
|
+
}
|
|
6
|
+
export default Panel;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
interface PanelProps {
|
|
4
|
+
/** Panel title */
|
|
5
|
+
title?: string;
|
|
6
|
+
/** Panel content */
|
|
7
|
+
children: React.ReactNode;
|
|
8
|
+
/** Border color */
|
|
9
|
+
borderColor?: string;
|
|
10
|
+
/** Title color */
|
|
11
|
+
titleColor?: string;
|
|
12
|
+
/** Panel width - number or percentage like "50%" */
|
|
13
|
+
width?: number | `${number}%` | "auto";
|
|
14
|
+
/** Panel height - number or percentage like "50%" */
|
|
15
|
+
height?: number | `${number}%` | "auto";
|
|
16
|
+
/** Whether to use flexGrow */
|
|
17
|
+
flexGrow?: number;
|
|
18
|
+
/** Whether panel is focused/active */
|
|
19
|
+
focused?: boolean;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function Panel({
|
|
23
|
+
title,
|
|
24
|
+
children,
|
|
25
|
+
borderColor = "#7e57c2",
|
|
26
|
+
titleColor = "#7e57c2",
|
|
27
|
+
width,
|
|
28
|
+
height,
|
|
29
|
+
flexGrow = 1,
|
|
30
|
+
focused = false,
|
|
31
|
+
}: PanelProps) {
|
|
32
|
+
const activeColor = focused ? "#7e57c2" : borderColor;
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<box
|
|
36
|
+
flexDirection="column"
|
|
37
|
+
width={width}
|
|
38
|
+
height={height}
|
|
39
|
+
flexGrow={flexGrow}
|
|
40
|
+
border
|
|
41
|
+
borderStyle="single"
|
|
42
|
+
borderColor={activeColor}
|
|
43
|
+
padding={1}
|
|
44
|
+
>
|
|
45
|
+
{/* Title row */}
|
|
46
|
+
{title && (
|
|
47
|
+
<box marginBottom={0}>
|
|
48
|
+
<text fg={titleColor}>
|
|
49
|
+
<strong>{title}</strong>
|
|
50
|
+
</text>
|
|
51
|
+
</box>
|
|
52
|
+
)}
|
|
53
|
+
|
|
54
|
+
{/* Content */}
|
|
55
|
+
<box flexDirection="column" flexGrow={1}>
|
|
56
|
+
{children}
|
|
57
|
+
</box>
|
|
58
|
+
</box>
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export default Panel;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "@opentui/react/jsx-runtime";
|
|
2
|
+
export function ProgressBar({ message, current, total, }) {
|
|
3
|
+
const isDeterminate = current !== undefined && total !== undefined && total > 0;
|
|
4
|
+
if (isDeterminate) {
|
|
5
|
+
const barWidth = 20;
|
|
6
|
+
const filled = Math.round((current / total) * barWidth);
|
|
7
|
+
const empty = barWidth - filled;
|
|
8
|
+
const bar = "█".repeat(filled) + "░".repeat(empty);
|
|
9
|
+
return (_jsxs("box", { children: [_jsx("text", { fg: "cyan", children: "\u27F3" }), _jsxs("text", { children: [" ", message, " "] }), _jsxs("text", { fg: "cyan", children: ["[", bar, "] ", current, "/", total] })] }));
|
|
10
|
+
}
|
|
11
|
+
// Indeterminate progress
|
|
12
|
+
return (_jsxs("box", { children: [_jsx("text", { fg: "cyan", children: "\u27F3" }), _jsxs("text", { children: [" ", message] }), _jsx("text", { fg: "gray", children: " ..." })] }));
|
|
13
|
+
}
|
|
14
|
+
export default ProgressBar;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
interface ProgressBarProps {
|
|
4
|
+
/** Progress message */
|
|
5
|
+
message: string;
|
|
6
|
+
/** Current progress (if determinate) */
|
|
7
|
+
current?: number;
|
|
8
|
+
/** Total items (if determinate) */
|
|
9
|
+
total?: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function ProgressBar({
|
|
13
|
+
message,
|
|
14
|
+
current,
|
|
15
|
+
total,
|
|
16
|
+
}: ProgressBarProps) {
|
|
17
|
+
const isDeterminate =
|
|
18
|
+
current !== undefined && total !== undefined && total > 0;
|
|
19
|
+
|
|
20
|
+
if (isDeterminate) {
|
|
21
|
+
const barWidth = 20;
|
|
22
|
+
const filled = Math.round((current / total) * barWidth);
|
|
23
|
+
const empty = barWidth - filled;
|
|
24
|
+
const bar = "█".repeat(filled) + "░".repeat(empty);
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<box>
|
|
28
|
+
<text fg="cyan">⟳</text>
|
|
29
|
+
<text> {message} </text>
|
|
30
|
+
<text fg="cyan">
|
|
31
|
+
[{bar}] {current}/{total}
|
|
32
|
+
</text>
|
|
33
|
+
</box>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Indeterminate progress
|
|
38
|
+
return (
|
|
39
|
+
<box>
|
|
40
|
+
<text fg="cyan">⟳</text>
|
|
41
|
+
<text> {message}</text>
|
|
42
|
+
<text fg="gray"> ...</text>
|
|
43
|
+
</box>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export default ProgressBar;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "@opentui/react/jsx-runtime";
|
|
2
|
+
export function ScopeTabs({ scope, onToggle: _onToggle, toggleHint, }) {
|
|
3
|
+
const isProject = scope === "project";
|
|
4
|
+
return (_jsxs("box", { marginBottom: 1, flexDirection: "row", gap: 1, children: [_jsx("box", { children: isProject ? (_jsx("text", { bg: "cyan", fg: "black", children: _jsx("strong", { children: " \u25C6 Project " }) })) : (_jsx("text", { fg: "gray", children: " \u25CB Project " })) }), _jsx("box", { children: !isProject ? (_jsx("text", { bg: "magenta", fg: "white", children: _jsx("strong", { children: " \u25C6 Global " }) })) : (_jsx("text", { fg: "gray", children: " \u25CB Global " })) }), toggleHint && (_jsx("box", { marginLeft: 2, children: _jsxs("text", { fg: "#666666", children: ["(", toggleHint, ")"] }) }))] }));
|
|
5
|
+
}
|
|
6
|
+
export default ScopeTabs;
|