claudeup 1.7.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/src/prerunner/index.js +87 -0
- 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 +7 -8
- 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 +0 -64
- 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 -16
- 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,200 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "@opentui/react/jsx-runtime";
|
|
2
|
+
import { useEffect, useCallback, useState } from "react";
|
|
3
|
+
import { useApp, useModal } from "../state/AppContext.js";
|
|
4
|
+
import { useDimensions } from "../state/DimensionsContext.js";
|
|
5
|
+
import { useKeyboard } from "../hooks/useKeyboard.js";
|
|
6
|
+
import { ScreenLayout } from "../components/layout/index.js";
|
|
7
|
+
import { ScrollableList } from "../components/ScrollableList.js";
|
|
8
|
+
import { statusLineCategories } from "../../data/statuslines.js";
|
|
9
|
+
import { setStatusLine, getStatusLine, setGlobalStatusLine, getGlobalStatusLine, } from "../../services/claude-settings.js";
|
|
10
|
+
export function StatusLineScreen() {
|
|
11
|
+
const { state, dispatch } = useApp();
|
|
12
|
+
const { statusline: statusLine } = state;
|
|
13
|
+
const modal = useModal();
|
|
14
|
+
const dimensions = useDimensions();
|
|
15
|
+
const [projectStatusLine, setProjectStatusLineState] = useState();
|
|
16
|
+
const [globalStatusLine, setGlobalStatusLineState] = useState();
|
|
17
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
18
|
+
// Fetch data
|
|
19
|
+
const fetchData = useCallback(async () => {
|
|
20
|
+
setIsLoading(true);
|
|
21
|
+
try {
|
|
22
|
+
const [project, global] = await Promise.all([
|
|
23
|
+
getStatusLine(state.projectPath),
|
|
24
|
+
getGlobalStatusLine(),
|
|
25
|
+
]);
|
|
26
|
+
setProjectStatusLineState(project);
|
|
27
|
+
setGlobalStatusLineState(global);
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
// Silent error handling - show empty state
|
|
31
|
+
}
|
|
32
|
+
setIsLoading(false);
|
|
33
|
+
}, [state.projectPath]);
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
fetchData();
|
|
36
|
+
}, [fetchData]);
|
|
37
|
+
// Get current status line based on scope
|
|
38
|
+
const getCurrentStatusLine = () => {
|
|
39
|
+
return statusLine.scope === "project"
|
|
40
|
+
? projectStatusLine
|
|
41
|
+
: globalStatusLine;
|
|
42
|
+
};
|
|
43
|
+
// Build list items with categories
|
|
44
|
+
const buildListItems = () => {
|
|
45
|
+
const currentForScope = getCurrentStatusLine();
|
|
46
|
+
const items = [];
|
|
47
|
+
for (const category of statusLineCategories) {
|
|
48
|
+
items.push({
|
|
49
|
+
label: category.name,
|
|
50
|
+
isCategory: true,
|
|
51
|
+
});
|
|
52
|
+
for (const preset of category.presets) {
|
|
53
|
+
const isActive = currentForScope === preset.template;
|
|
54
|
+
const status = isActive ? "●" : "○";
|
|
55
|
+
items.push({
|
|
56
|
+
label: ` ${status} ${preset.name}`,
|
|
57
|
+
preset,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// Add custom option at the end
|
|
62
|
+
items.push({
|
|
63
|
+
label: "+ Custom Status Line",
|
|
64
|
+
isCustom: true,
|
|
65
|
+
});
|
|
66
|
+
return items;
|
|
67
|
+
};
|
|
68
|
+
const listItems = buildListItems();
|
|
69
|
+
// Keyboard handling
|
|
70
|
+
useKeyboard((event) => {
|
|
71
|
+
if (state.isSearching || state.modal)
|
|
72
|
+
return;
|
|
73
|
+
if (event.name === "up" || event.name === "k") {
|
|
74
|
+
const newIndex = Math.max(0, statusLine.selectedIndex - 1);
|
|
75
|
+
dispatch({ type: "STATUSLINE_SELECT", index: newIndex });
|
|
76
|
+
}
|
|
77
|
+
else if (event.name === "down" || event.name === "j") {
|
|
78
|
+
const newIndex = Math.min(listItems.length - 1, statusLine.selectedIndex + 1);
|
|
79
|
+
dispatch({ type: "STATUSLINE_SELECT", index: newIndex });
|
|
80
|
+
}
|
|
81
|
+
else if (event.name === "p") {
|
|
82
|
+
dispatch({ type: "STATUSLINE_SET_SCOPE", scope: "project" });
|
|
83
|
+
}
|
|
84
|
+
else if (event.name === "g") {
|
|
85
|
+
dispatch({ type: "STATUSLINE_SET_SCOPE", scope: "global" });
|
|
86
|
+
}
|
|
87
|
+
else if (event.name === "r") {
|
|
88
|
+
handleReset();
|
|
89
|
+
}
|
|
90
|
+
else if (event.name === "enter") {
|
|
91
|
+
handleSelect();
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
const saveStatusLine = async (template) => {
|
|
95
|
+
if (statusLine.scope === "global") {
|
|
96
|
+
await setGlobalStatusLine(template);
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
await setStatusLine(template, state.projectPath);
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
const handleSelect = async () => {
|
|
103
|
+
const item = listItems[statusLine.selectedIndex];
|
|
104
|
+
if (!item || item.isCategory)
|
|
105
|
+
return;
|
|
106
|
+
const scopeLabel = statusLine.scope === "global" ? "Global" : "Project";
|
|
107
|
+
if (item.isCustom) {
|
|
108
|
+
const template = await modal.input(`Custom Status Line (${scopeLabel})`, "Enter template (use {model}, {cost}, etc.):", getCurrentStatusLine() || "");
|
|
109
|
+
if (template !== null) {
|
|
110
|
+
modal.loading(`Saving custom status line...`);
|
|
111
|
+
try {
|
|
112
|
+
await saveStatusLine(template);
|
|
113
|
+
modal.hideModal();
|
|
114
|
+
await modal.message("Saved", `Custom status line saved to ${scopeLabel}.\n\nRestart Claude Code to apply changes.`, "success");
|
|
115
|
+
fetchData();
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
modal.hideModal();
|
|
119
|
+
await modal.message("Error", `Failed to save: ${error}`, "error");
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
else if (item.preset) {
|
|
124
|
+
modal.loading(`Applying ${item.preset.name}...`);
|
|
125
|
+
try {
|
|
126
|
+
await saveStatusLine(item.preset.template);
|
|
127
|
+
modal.hideModal();
|
|
128
|
+
await modal.message("Applied", `"${item.preset.name}" applied to ${scopeLabel}.\n\nRestart Claude Code to apply changes.`, "success");
|
|
129
|
+
fetchData();
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
modal.hideModal();
|
|
133
|
+
await modal.message("Error", `Failed to apply: ${error}`, "error");
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
const handleReset = async () => {
|
|
138
|
+
const scopeLabel = statusLine.scope === "global" ? "Global" : "Project";
|
|
139
|
+
const confirmed = await modal.confirm("Reset Status Line", `Clear the ${scopeLabel} status line configuration?`);
|
|
140
|
+
if (confirmed) {
|
|
141
|
+
modal.loading("Resetting...");
|
|
142
|
+
try {
|
|
143
|
+
await saveStatusLine("");
|
|
144
|
+
modal.hideModal();
|
|
145
|
+
await modal.message("Reset", `${scopeLabel} status line has been cleared.`, "success");
|
|
146
|
+
fetchData();
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
modal.hideModal();
|
|
150
|
+
await modal.message("Error", `Failed to reset: ${error}`, "error");
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
// Get selected item
|
|
155
|
+
const selectedItem = listItems[statusLine.selectedIndex];
|
|
156
|
+
// Build preview
|
|
157
|
+
const renderPreview = () => {
|
|
158
|
+
if (isLoading) {
|
|
159
|
+
return _jsx("text", { fg: "gray", children: "Loading status line settings..." });
|
|
160
|
+
}
|
|
161
|
+
if (!selectedItem || selectedItem.isCategory) {
|
|
162
|
+
return (_jsx("box", { flexDirection: "column", alignItems: "center", justifyContent: "center", flexGrow: 1, children: _jsx("text", { fg: "gray", children: "Select a theme to see preview" }) }));
|
|
163
|
+
}
|
|
164
|
+
if (selectedItem.isCustom) {
|
|
165
|
+
return (_jsxs("box", { flexDirection: "column", children: [_jsx("text", { fg: "cyan", children: _jsx("strong", { children: "\u2728 Custom Status Line" }) }), _jsx("text", { fg: "gray", children: "Create your own unique status line!" }), _jsxs("box", { marginTop: 1, flexDirection: "column", children: [_jsx("text", { fg: "yellow", children: _jsx("strong", { children: "Available variables:" }) }), _jsxs("box", { marginTop: 1, flexDirection: "column", children: [_jsxs("text", { children: [_jsx("span", { fg: "green", children: _jsx("strong", { children: "{model}" }) }), " ", _jsx("span", { fg: "gray", children: "\u2192" }), " Model name"] }), _jsxs("text", { children: [_jsx("span", { fg: "green", children: _jsx("strong", { children: "{model_short}" }) }), " ", _jsx("span", { fg: "gray", children: "\u2192" }), " Short name"] }), _jsxs("text", { children: [_jsx("span", { fg: "yellow", children: _jsx("strong", { children: "{cost}" }) }), " ", _jsx("span", { fg: "gray", children: "\u2192" }), " Session cost"] }), _jsxs("text", { children: [_jsx("span", { fg: "blue", children: _jsx("strong", { children: "{cwd}" }) }), " ", _jsx("span", { fg: "gray", children: "\u2192" }), " Working directory"] }), _jsxs("text", { children: [_jsx("span", { fg: "magenta", children: _jsx("strong", { children: "{git_branch}" }) }), " ", _jsx("span", { fg: "gray", children: "\u2192" }), " Git branch"] }), _jsxs("text", { children: [_jsx("span", { fg: "cyan", children: _jsx("strong", { children: "{input_tokens}" }) }), " ", _jsx("span", { fg: "gray", children: "\u2192" }), " Input tokens"] }), _jsxs("text", { children: [_jsx("span", { fg: "cyan", children: _jsx("strong", { children: "{output_tokens}" }) }), " ", _jsx("span", { fg: "gray", children: "\u2192" }), " Output tokens"] }), _jsxs("text", { children: [_jsx("span", { fg: "red", children: _jsx("strong", { children: "{session_duration}" }) }), _jsx("span", { fg: "gray", children: "\u2192" }), " Duration"] })] })] })] }));
|
|
166
|
+
}
|
|
167
|
+
if (selectedItem.preset) {
|
|
168
|
+
const example = selectedItem.preset.template
|
|
169
|
+
.replace("{model}", "claude-sonnet-4")
|
|
170
|
+
.replace("{model_short}", "sonnet")
|
|
171
|
+
.replace("{cost}", "0.42")
|
|
172
|
+
.replace("{cwd}", "~/myapp")
|
|
173
|
+
.replace("{git_branch}", "main")
|
|
174
|
+
.replace("{input_tokens}", "1.2k")
|
|
175
|
+
.replace("{output_tokens}", "850")
|
|
176
|
+
.replace("{session_duration}", "12m");
|
|
177
|
+
return (_jsxs("box", { flexDirection: "column", children: [_jsx("text", { fg: "cyan", children: _jsxs("strong", { children: ["\u25C6 ", selectedItem.preset.name] }) }), _jsx("text", { fg: "gray", children: selectedItem.preset.description }), _jsxs("box", { marginTop: 1, flexDirection: "column", children: [_jsx("text", { fg: "yellow", children: _jsx("strong", { children: "Preview:" }) }), _jsx("box", { marginTop: 1, paddingLeft: 1, paddingRight: 1, borderStyle: "rounded", borderColor: "green", children: _jsx("text", { fg: "white", children: example }) })] }), _jsxs("box", { marginTop: 1, flexDirection: "column", children: [_jsx("text", { fg: "#666666", children: "Template:" }), _jsx("text", { fg: "gray", children: selectedItem.preset.template })] })] }));
|
|
178
|
+
}
|
|
179
|
+
return null;
|
|
180
|
+
};
|
|
181
|
+
const renderListItem = (item, _idx, isSelected) => {
|
|
182
|
+
if (item.isCategory) {
|
|
183
|
+
return (_jsx("text", { fg: "magenta", children: _jsxs("strong", { children: ["\u25B8 ", item.label] }) }));
|
|
184
|
+
}
|
|
185
|
+
if (item.isCustom) {
|
|
186
|
+
return isSelected ? (_jsx("text", { bg: "cyan", fg: "black", children: _jsxs("strong", { children: [" ", "\u2795 Custom Status Line", " "] }) })) : (_jsx("text", { fg: "cyan", children: _jsxs("strong", { children: [" ", "\u2795 Custom Status Line"] }) }));
|
|
187
|
+
}
|
|
188
|
+
const currentForScope = getCurrentStatusLine();
|
|
189
|
+
const isActive = item.preset && currentForScope === item.preset.template;
|
|
190
|
+
return isSelected ? (_jsxs("text", { bg: "magenta", fg: "white", children: [" ", isActive ? "●" : "○", " ", item.preset?.name || "", " "] })) : (_jsxs("text", { fg: isActive ? "green" : "white", children: [" ", isActive ? "●" : "○", " ", item.preset?.name || ""] }));
|
|
191
|
+
};
|
|
192
|
+
// Build status line content
|
|
193
|
+
const scopeLabel = statusLine.scope === "project" ? "Project" : "Global";
|
|
194
|
+
const currentValue = getCurrentStatusLine();
|
|
195
|
+
const statusContent = (_jsxs(_Fragment, { children: [_jsx("text", { fg: "gray", children: "Scope: " }), _jsx("text", { fg: "cyan", children: scopeLabel }), _jsx("text", { fg: "gray", children: " \u2502 Current: " }), _jsx("text", { fg: "green", children: currentValue
|
|
196
|
+
? currentValue.slice(0, 35) + (currentValue.length > 35 ? "..." : "")
|
|
197
|
+
: "(not set)" })] }));
|
|
198
|
+
return (_jsx(ScreenLayout, { title: "claudeup Status Line", currentScreen: "statusline", statusLine: statusContent, footerHints: "\u2191\u2193:nav \u2502 Enter:apply \u2502 p:project \u2502 g:global \u2502 r:reset", listPanel: _jsx(ScrollableList, { items: listItems, selectedIndex: statusLine.selectedIndex, renderItem: renderListItem, maxHeight: dimensions.listPanelHeight }), detailPanel: renderPreview() }));
|
|
199
|
+
}
|
|
200
|
+
export default StatusLineScreen;
|
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
import React, { useEffect, useCallback, useState } from "react";
|
|
2
|
+
import { useApp, useModal } from "../state/AppContext.js";
|
|
3
|
+
import { useDimensions } from "../state/DimensionsContext.js";
|
|
4
|
+
import { useKeyboard } from "../hooks/useKeyboard.js";
|
|
5
|
+
import { ScreenLayout } from "../components/layout/index.js";
|
|
6
|
+
import { ScrollableList } from "../components/ScrollableList.js";
|
|
7
|
+
import { statusLineCategories } from "../../data/statuslines.js";
|
|
8
|
+
import type { StatusLineConfig } from "../../types/index.js";
|
|
9
|
+
import {
|
|
10
|
+
setStatusLine,
|
|
11
|
+
getStatusLine,
|
|
12
|
+
setGlobalStatusLine,
|
|
13
|
+
getGlobalStatusLine,
|
|
14
|
+
} from "../../services/claude-settings.js";
|
|
15
|
+
|
|
16
|
+
interface ListItem {
|
|
17
|
+
label: string;
|
|
18
|
+
preset?: StatusLineConfig;
|
|
19
|
+
isCustom?: boolean;
|
|
20
|
+
isCategory?: boolean;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function StatusLineScreen() {
|
|
24
|
+
const { state, dispatch } = useApp();
|
|
25
|
+
const { statusline: statusLine } = state;
|
|
26
|
+
const modal = useModal();
|
|
27
|
+
const dimensions = useDimensions();
|
|
28
|
+
|
|
29
|
+
const [projectStatusLine, setProjectStatusLineState] = useState<
|
|
30
|
+
string | undefined
|
|
31
|
+
>();
|
|
32
|
+
const [globalStatusLine, setGlobalStatusLineState] = useState<
|
|
33
|
+
string | undefined
|
|
34
|
+
>();
|
|
35
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
36
|
+
|
|
37
|
+
// Fetch data
|
|
38
|
+
const fetchData = useCallback(async () => {
|
|
39
|
+
setIsLoading(true);
|
|
40
|
+
try {
|
|
41
|
+
const [project, global] = await Promise.all([
|
|
42
|
+
getStatusLine(state.projectPath),
|
|
43
|
+
getGlobalStatusLine(),
|
|
44
|
+
]);
|
|
45
|
+
setProjectStatusLineState(project);
|
|
46
|
+
setGlobalStatusLineState(global);
|
|
47
|
+
} catch (error) {
|
|
48
|
+
// Silent error handling - show empty state
|
|
49
|
+
}
|
|
50
|
+
setIsLoading(false);
|
|
51
|
+
}, [state.projectPath]);
|
|
52
|
+
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
fetchData();
|
|
55
|
+
}, [fetchData]);
|
|
56
|
+
|
|
57
|
+
// Get current status line based on scope
|
|
58
|
+
const getCurrentStatusLine = (): string | undefined => {
|
|
59
|
+
return statusLine.scope === "project"
|
|
60
|
+
? projectStatusLine
|
|
61
|
+
: globalStatusLine;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
// Build list items with categories
|
|
65
|
+
const buildListItems = (): ListItem[] => {
|
|
66
|
+
const currentForScope = getCurrentStatusLine();
|
|
67
|
+
const items: ListItem[] = [];
|
|
68
|
+
|
|
69
|
+
for (const category of statusLineCategories) {
|
|
70
|
+
items.push({
|
|
71
|
+
label: category.name,
|
|
72
|
+
isCategory: true,
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
for (const preset of category.presets) {
|
|
76
|
+
const isActive = currentForScope === preset.template;
|
|
77
|
+
const status = isActive ? "●" : "○";
|
|
78
|
+
items.push({
|
|
79
|
+
label: ` ${status} ${preset.name}`,
|
|
80
|
+
preset,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Add custom option at the end
|
|
86
|
+
items.push({
|
|
87
|
+
label: "+ Custom Status Line",
|
|
88
|
+
isCustom: true,
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
return items;
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const listItems = buildListItems();
|
|
95
|
+
|
|
96
|
+
// Keyboard handling
|
|
97
|
+
useKeyboard((event) => {
|
|
98
|
+
if (state.isSearching || state.modal) return;
|
|
99
|
+
|
|
100
|
+
if (event.name === "up" || event.name === "k") {
|
|
101
|
+
const newIndex = Math.max(0, statusLine.selectedIndex - 1);
|
|
102
|
+
dispatch({ type: "STATUSLINE_SELECT", index: newIndex });
|
|
103
|
+
} else if (event.name === "down" || event.name === "j") {
|
|
104
|
+
const newIndex = Math.min(
|
|
105
|
+
listItems.length - 1,
|
|
106
|
+
statusLine.selectedIndex + 1,
|
|
107
|
+
);
|
|
108
|
+
dispatch({ type: "STATUSLINE_SELECT", index: newIndex });
|
|
109
|
+
} else if (event.name === "p") {
|
|
110
|
+
dispatch({ type: "STATUSLINE_SET_SCOPE", scope: "project" });
|
|
111
|
+
} else if (event.name === "g") {
|
|
112
|
+
dispatch({ type: "STATUSLINE_SET_SCOPE", scope: "global" });
|
|
113
|
+
} else if (event.name === "r") {
|
|
114
|
+
handleReset();
|
|
115
|
+
} else if (event.name === "enter") {
|
|
116
|
+
handleSelect();
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
const saveStatusLine = async (template: string): Promise<void> => {
|
|
121
|
+
if (statusLine.scope === "global") {
|
|
122
|
+
await setGlobalStatusLine(template);
|
|
123
|
+
} else {
|
|
124
|
+
await setStatusLine(template, state.projectPath);
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const handleSelect = async () => {
|
|
129
|
+
const item = listItems[statusLine.selectedIndex];
|
|
130
|
+
if (!item || item.isCategory) return;
|
|
131
|
+
|
|
132
|
+
const scopeLabel = statusLine.scope === "global" ? "Global" : "Project";
|
|
133
|
+
|
|
134
|
+
if (item.isCustom) {
|
|
135
|
+
const template = await modal.input(
|
|
136
|
+
`Custom Status Line (${scopeLabel})`,
|
|
137
|
+
"Enter template (use {model}, {cost}, etc.):",
|
|
138
|
+
getCurrentStatusLine() || "",
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
if (template !== null) {
|
|
142
|
+
modal.loading(`Saving custom status line...`);
|
|
143
|
+
try {
|
|
144
|
+
await saveStatusLine(template);
|
|
145
|
+
modal.hideModal();
|
|
146
|
+
await modal.message(
|
|
147
|
+
"Saved",
|
|
148
|
+
`Custom status line saved to ${scopeLabel}.\n\nRestart Claude Code to apply changes.`,
|
|
149
|
+
"success",
|
|
150
|
+
);
|
|
151
|
+
fetchData();
|
|
152
|
+
} catch (error) {
|
|
153
|
+
modal.hideModal();
|
|
154
|
+
await modal.message("Error", `Failed to save: ${error}`, "error");
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
} else if (item.preset) {
|
|
158
|
+
modal.loading(`Applying ${item.preset.name}...`);
|
|
159
|
+
try {
|
|
160
|
+
await saveStatusLine(item.preset.template);
|
|
161
|
+
modal.hideModal();
|
|
162
|
+
await modal.message(
|
|
163
|
+
"Applied",
|
|
164
|
+
`"${item.preset.name}" applied to ${scopeLabel}.\n\nRestart Claude Code to apply changes.`,
|
|
165
|
+
"success",
|
|
166
|
+
);
|
|
167
|
+
fetchData();
|
|
168
|
+
} catch (error) {
|
|
169
|
+
modal.hideModal();
|
|
170
|
+
await modal.message("Error", `Failed to apply: ${error}`, "error");
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
const handleReset = async () => {
|
|
176
|
+
const scopeLabel = statusLine.scope === "global" ? "Global" : "Project";
|
|
177
|
+
const confirmed = await modal.confirm(
|
|
178
|
+
"Reset Status Line",
|
|
179
|
+
`Clear the ${scopeLabel} status line configuration?`,
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
if (confirmed) {
|
|
183
|
+
modal.loading("Resetting...");
|
|
184
|
+
try {
|
|
185
|
+
await saveStatusLine("");
|
|
186
|
+
modal.hideModal();
|
|
187
|
+
await modal.message(
|
|
188
|
+
"Reset",
|
|
189
|
+
`${scopeLabel} status line has been cleared.`,
|
|
190
|
+
"success",
|
|
191
|
+
);
|
|
192
|
+
fetchData();
|
|
193
|
+
} catch (error) {
|
|
194
|
+
modal.hideModal();
|
|
195
|
+
await modal.message("Error", `Failed to reset: ${error}`, "error");
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
// Get selected item
|
|
201
|
+
const selectedItem = listItems[statusLine.selectedIndex];
|
|
202
|
+
|
|
203
|
+
// Build preview
|
|
204
|
+
const renderPreview = () => {
|
|
205
|
+
if (isLoading) {
|
|
206
|
+
return <text fg="gray">Loading status line settings...</text>;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (!selectedItem || selectedItem.isCategory) {
|
|
210
|
+
return (
|
|
211
|
+
<box
|
|
212
|
+
flexDirection="column"
|
|
213
|
+
alignItems="center"
|
|
214
|
+
justifyContent="center"
|
|
215
|
+
flexGrow={1}
|
|
216
|
+
>
|
|
217
|
+
<text fg="gray">Select a theme to see preview</text>
|
|
218
|
+
</box>
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (selectedItem.isCustom) {
|
|
223
|
+
return (
|
|
224
|
+
<box flexDirection="column">
|
|
225
|
+
<text fg="cyan">
|
|
226
|
+
<strong>✨ Custom Status Line</strong>
|
|
227
|
+
</text>
|
|
228
|
+
<text fg="gray">Create your own unique status line!</text>
|
|
229
|
+
|
|
230
|
+
<box marginTop={1} flexDirection="column">
|
|
231
|
+
<text fg="yellow">
|
|
232
|
+
<strong>Available variables:</strong>
|
|
233
|
+
</text>
|
|
234
|
+
<box marginTop={1} flexDirection="column">
|
|
235
|
+
<text>
|
|
236
|
+
<span fg="green">
|
|
237
|
+
<strong>{"{model}"}</strong>
|
|
238
|
+
</span>{" "}
|
|
239
|
+
<span fg="gray">→</span> Model name
|
|
240
|
+
</text>
|
|
241
|
+
<text>
|
|
242
|
+
<span fg="green">
|
|
243
|
+
<strong>{"{model_short}"}</strong>
|
|
244
|
+
</span>{" "}
|
|
245
|
+
<span fg="gray">→</span> Short name
|
|
246
|
+
</text>
|
|
247
|
+
<text>
|
|
248
|
+
<span fg="yellow">
|
|
249
|
+
<strong>{"{cost}"}</strong>
|
|
250
|
+
</span>{" "}
|
|
251
|
+
<span fg="gray">→</span> Session cost
|
|
252
|
+
</text>
|
|
253
|
+
<text>
|
|
254
|
+
<span fg="blue">
|
|
255
|
+
<strong>{"{cwd}"}</strong>
|
|
256
|
+
</span>{" "}
|
|
257
|
+
<span fg="gray">→</span> Working directory
|
|
258
|
+
</text>
|
|
259
|
+
<text>
|
|
260
|
+
<span fg="magenta">
|
|
261
|
+
<strong>{"{git_branch}"}</strong>
|
|
262
|
+
</span>{" "}
|
|
263
|
+
<span fg="gray">→</span> Git branch
|
|
264
|
+
</text>
|
|
265
|
+
<text>
|
|
266
|
+
<span fg="cyan">
|
|
267
|
+
<strong>{"{input_tokens}"}</strong>
|
|
268
|
+
</span>{" "}
|
|
269
|
+
<span fg="gray">→</span> Input tokens
|
|
270
|
+
</text>
|
|
271
|
+
<text>
|
|
272
|
+
<span fg="cyan">
|
|
273
|
+
<strong>{"{output_tokens}"}</strong>
|
|
274
|
+
</span>{" "}
|
|
275
|
+
<span fg="gray">→</span> Output tokens
|
|
276
|
+
</text>
|
|
277
|
+
<text>
|
|
278
|
+
<span fg="red">
|
|
279
|
+
<strong>{"{session_duration}"}</strong>
|
|
280
|
+
</span>
|
|
281
|
+
<span fg="gray">→</span> Duration
|
|
282
|
+
</text>
|
|
283
|
+
</box>
|
|
284
|
+
</box>
|
|
285
|
+
</box>
|
|
286
|
+
);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if (selectedItem.preset) {
|
|
290
|
+
const example = selectedItem.preset.template
|
|
291
|
+
.replace("{model}", "claude-sonnet-4")
|
|
292
|
+
.replace("{model_short}", "sonnet")
|
|
293
|
+
.replace("{cost}", "0.42")
|
|
294
|
+
.replace("{cwd}", "~/myapp")
|
|
295
|
+
.replace("{git_branch}", "main")
|
|
296
|
+
.replace("{input_tokens}", "1.2k")
|
|
297
|
+
.replace("{output_tokens}", "850")
|
|
298
|
+
.replace("{session_duration}", "12m");
|
|
299
|
+
|
|
300
|
+
return (
|
|
301
|
+
<box flexDirection="column">
|
|
302
|
+
<text fg="cyan">
|
|
303
|
+
<strong>◆ {selectedItem.preset.name}</strong>
|
|
304
|
+
</text>
|
|
305
|
+
<text fg="gray">{selectedItem.preset.description}</text>
|
|
306
|
+
|
|
307
|
+
<box marginTop={1} flexDirection="column">
|
|
308
|
+
<text fg="yellow">
|
|
309
|
+
<strong>Preview:</strong>
|
|
310
|
+
</text>
|
|
311
|
+
<box
|
|
312
|
+
marginTop={1}
|
|
313
|
+
paddingLeft={1} paddingRight={1}
|
|
314
|
+
borderStyle="rounded"
|
|
315
|
+
borderColor="green"
|
|
316
|
+
>
|
|
317
|
+
<text fg="white">{example}</text>
|
|
318
|
+
</box>
|
|
319
|
+
</box>
|
|
320
|
+
|
|
321
|
+
<box marginTop={1} flexDirection="column">
|
|
322
|
+
<text fg="#666666">Template:</text>
|
|
323
|
+
<text fg="gray">{selectedItem.preset.template}</text>
|
|
324
|
+
</box>
|
|
325
|
+
</box>
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
return null;
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
const renderListItem = (
|
|
333
|
+
item: ListItem,
|
|
334
|
+
_idx: number,
|
|
335
|
+
isSelected: boolean,
|
|
336
|
+
) => {
|
|
337
|
+
if (item.isCategory) {
|
|
338
|
+
return (
|
|
339
|
+
<text fg="magenta" >
|
|
340
|
+
<strong>▸ {item.label}</strong>
|
|
341
|
+
</text>
|
|
342
|
+
);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
if (item.isCustom) {
|
|
346
|
+
return isSelected ? (
|
|
347
|
+
<text bg="cyan" fg="black" >
|
|
348
|
+
<strong>
|
|
349
|
+
{" "}
|
|
350
|
+
➕ Custom Status Line{" "}
|
|
351
|
+
</strong>
|
|
352
|
+
</text>
|
|
353
|
+
) : (
|
|
354
|
+
<text fg="cyan" >
|
|
355
|
+
<strong>{" "}➕ Custom Status Line</strong>
|
|
356
|
+
</text>
|
|
357
|
+
);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
const currentForScope = getCurrentStatusLine();
|
|
361
|
+
const isActive = item.preset && currentForScope === item.preset.template;
|
|
362
|
+
|
|
363
|
+
return isSelected ? (
|
|
364
|
+
<text bg="magenta" fg="white" >
|
|
365
|
+
{" "}
|
|
366
|
+
{isActive ? "●" : "○"} {item.preset?.name || ""}{" "}
|
|
367
|
+
</text>
|
|
368
|
+
) : (
|
|
369
|
+
<text fg={isActive ? "green" : "white"} >
|
|
370
|
+
{" "}
|
|
371
|
+
{isActive ? "●" : "○"} {item.preset?.name || ""}
|
|
372
|
+
</text>
|
|
373
|
+
);
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
// Build status line content
|
|
377
|
+
const scopeLabel = statusLine.scope === "project" ? "Project" : "Global";
|
|
378
|
+
const currentValue = getCurrentStatusLine();
|
|
379
|
+
const statusContent = (
|
|
380
|
+
<>
|
|
381
|
+
<text fg="gray">Scope: </text>
|
|
382
|
+
<text fg="cyan">{scopeLabel}</text>
|
|
383
|
+
<text fg="gray"> │ Current: </text>
|
|
384
|
+
<text fg="green">
|
|
385
|
+
{currentValue
|
|
386
|
+
? currentValue.slice(0, 35) + (currentValue.length > 35 ? "..." : "")
|
|
387
|
+
: "(not set)"}
|
|
388
|
+
</text>
|
|
389
|
+
</>
|
|
390
|
+
);
|
|
391
|
+
|
|
392
|
+
return (
|
|
393
|
+
<ScreenLayout
|
|
394
|
+
title="claudeup Status Line"
|
|
395
|
+
currentScreen="statusline"
|
|
396
|
+
statusLine={statusContent}
|
|
397
|
+
footerHints="↑↓:nav │ Enter:apply │ p:project │ g:global │ r:reset"
|
|
398
|
+
listPanel={
|
|
399
|
+
<ScrollableList
|
|
400
|
+
items={listItems}
|
|
401
|
+
selectedIndex={statusLine.selectedIndex}
|
|
402
|
+
renderItem={renderListItem}
|
|
403
|
+
maxHeight={dimensions.listPanelHeight}
|
|
404
|
+
/>
|
|
405
|
+
}
|
|
406
|
+
detailPanel={renderPreview()}
|
|
407
|
+
/>
|
|
408
|
+
);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
export default StatusLineScreen;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { PluginsScreen } from "./PluginsScreen.js";
|
|
2
|
+
export { McpScreen } from "./McpScreen.js";
|
|
3
|
+
export { McpRegistryScreen } from "./McpRegistryScreen.js";
|
|
4
|
+
export { StatusLineScreen } from "./StatusLineScreen.js";
|
|
5
|
+
export { EnvVarsScreen } from "./EnvVarsScreen.js";
|
|
6
|
+
export { CliToolsScreen } from "./CliToolsScreen.js";
|
|
7
|
+
export { ModelSelectorScreen } from "./ModelSelectorScreen.js";
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { PluginsScreen } from "./PluginsScreen.js";
|
|
2
|
+
export { McpScreen } from "./McpScreen.js";
|
|
3
|
+
export { McpRegistryScreen } from "./McpRegistryScreen.js";
|
|
4
|
+
export { StatusLineScreen } from "./StatusLineScreen.js";
|
|
5
|
+
export { EnvVarsScreen } from "./EnvVarsScreen.js";
|
|
6
|
+
export { CliToolsScreen } from "./CliToolsScreen.js";
|
|
7
|
+
export { ModelSelectorScreen } from "./ModelSelectorScreen.js";
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { jsx as _jsx } from "@opentui/react/jsx-runtime";
|
|
2
|
+
import { createContext, useContext, useState, } from "react";
|
|
3
|
+
// ============================================================================
|
|
4
|
+
// Context
|
|
5
|
+
// ============================================================================
|
|
6
|
+
const AnimationContext = createContext(null);
|
|
7
|
+
export function AnimationProvider({ children }) {
|
|
8
|
+
const [timelines, setTimelines] = useState(new Map());
|
|
9
|
+
const registerTimeline = (id, timeline) => {
|
|
10
|
+
setTimelines((prev) => {
|
|
11
|
+
const next = new Map(prev);
|
|
12
|
+
next.set(id, timeline);
|
|
13
|
+
return next;
|
|
14
|
+
});
|
|
15
|
+
};
|
|
16
|
+
const unregisterTimeline = (id) => {
|
|
17
|
+
setTimelines((prev) => {
|
|
18
|
+
const next = new Map(prev);
|
|
19
|
+
next.delete(id);
|
|
20
|
+
return next;
|
|
21
|
+
});
|
|
22
|
+
};
|
|
23
|
+
return (_jsx(AnimationContext.Provider, { value: { timelines, registerTimeline, unregisterTimeline }, children: children }));
|
|
24
|
+
}
|
|
25
|
+
// ============================================================================
|
|
26
|
+
// Hook: useAnimation
|
|
27
|
+
// ============================================================================
|
|
28
|
+
export function useAnimation() {
|
|
29
|
+
const context = useContext(AnimationContext);
|
|
30
|
+
if (!context) {
|
|
31
|
+
throw new Error("useAnimation must be used within an AnimationProvider");
|
|
32
|
+
}
|
|
33
|
+
return context;
|
|
34
|
+
}
|