claudeup 0.6.2 → 1.0.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 +1 -1
- package/dist/data/marketplaces.d.ts +2 -0
- package/dist/data/marketplaces.d.ts.map +1 -1
- package/dist/data/marketplaces.js +51 -8
- package/dist/data/marketplaces.js.map +1 -1
- package/dist/data/mcp-servers.d.ts.map +1 -1
- package/dist/data/mcp-servers.js +82 -0
- package/dist/data/mcp-servers.js.map +1 -1
- package/dist/index.js +8 -5
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts +3 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/main.js +79 -0
- package/dist/main.js.map +1 -0
- package/dist/services/claude-settings.d.ts +8 -1
- package/dist/services/claude-settings.d.ts.map +1 -1
- package/dist/services/claude-settings.js +79 -0
- package/dist/services/claude-settings.js.map +1 -1
- package/dist/services/local-marketplace.d.ts +76 -0
- package/dist/services/local-marketplace.d.ts.map +1 -0
- package/dist/services/local-marketplace.js +340 -0
- package/dist/services/local-marketplace.js.map +1 -0
- package/dist/services/plugin-manager.d.ts +39 -2
- package/dist/services/plugin-manager.d.ts.map +1 -1
- package/dist/services/plugin-manager.js +259 -9
- package/dist/services/plugin-manager.js.map +1 -1
- package/dist/types/index.d.ts +6 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/ui/InkApp.d.ts +5 -0
- package/dist/ui/InkApp.d.ts.map +1 -0
- package/dist/ui/InkApp.js +175 -0
- package/dist/ui/InkApp.js.map +1 -0
- package/dist/ui/components/CategoryHeader.d.ts +16 -0
- package/dist/ui/components/CategoryHeader.d.ts.map +1 -0
- package/dist/ui/components/CategoryHeader.js +11 -0
- package/dist/ui/components/CategoryHeader.js.map +1 -0
- package/dist/ui/components/ScrollableList.d.ts +16 -0
- package/dist/ui/components/ScrollableList.d.ts.map +1 -0
- package/dist/ui/components/ScrollableList.js +35 -0
- package/dist/ui/components/ScrollableList.js.map +1 -0
- package/dist/ui/components/SearchInput.d.ts +18 -0
- package/dist/ui/components/SearchInput.d.ts.map +1 -0
- package/dist/ui/components/SearchInput.js +30 -0
- package/dist/ui/components/SearchInput.js.map +1 -0
- package/dist/ui/components/TabBar.d.ts +8 -0
- package/dist/ui/components/TabBar.d.ts.map +1 -0
- package/dist/ui/components/TabBar.js +18 -0
- package/dist/ui/components/TabBar.js.map +1 -0
- package/dist/ui/components/layout/Footer.d.ts +14 -0
- package/dist/ui/components/layout/Footer.d.ts.map +1 -0
- package/dist/ui/components/layout/Footer.js +23 -0
- package/dist/ui/components/layout/Footer.js.map +1 -0
- package/dist/ui/components/layout/Header.d.ts +4 -0
- package/dist/ui/components/layout/Header.d.ts.map +1 -0
- package/dist/ui/components/layout/Header.js +25 -0
- package/dist/ui/components/layout/Header.js.map +1 -0
- package/dist/ui/components/layout/Panel.d.ts +22 -0
- package/dist/ui/components/layout/Panel.d.ts.map +1 -0
- package/dist/ui/components/layout/Panel.js +8 -0
- package/dist/ui/components/layout/Panel.js.map +1 -0
- package/dist/ui/components/layout/ProgressBar.d.ts +12 -0
- package/dist/ui/components/layout/ProgressBar.d.ts.map +1 -0
- package/dist/ui/components/layout/ProgressBar.js +16 -0
- package/dist/ui/components/layout/ProgressBar.js.map +1 -0
- package/dist/ui/components/layout/ScopeTabs.d.ts +12 -0
- package/dist/ui/components/layout/ScopeTabs.d.ts.map +1 -0
- package/dist/ui/components/layout/ScopeTabs.js +8 -0
- package/dist/ui/components/layout/ScopeTabs.js.map +1 -0
- package/dist/ui/components/layout/ScreenLayout.d.ts +30 -0
- package/dist/ui/components/layout/ScreenLayout.d.ts.map +1 -0
- package/dist/ui/components/layout/ScreenLayout.js +23 -0
- package/dist/ui/components/layout/ScreenLayout.js.map +1 -0
- package/dist/ui/components/layout/index.d.ts +7 -0
- package/dist/ui/components/layout/index.d.ts.map +1 -0
- package/dist/ui/components/layout/index.js +7 -0
- package/dist/ui/components/layout/index.js.map +1 -0
- package/dist/ui/components/modals/ConfirmModal.d.ts +14 -0
- package/dist/ui/components/modals/ConfirmModal.d.ts.map +1 -0
- package/dist/ui/components/modals/ConfirmModal.js +15 -0
- package/dist/ui/components/modals/ConfirmModal.js.map +1 -0
- package/dist/ui/components/modals/InputModal.d.ts +16 -0
- package/dist/ui/components/modals/InputModal.d.ts.map +1 -0
- package/dist/ui/components/modals/InputModal.js +23 -0
- package/dist/ui/components/modals/InputModal.js.map +1 -0
- package/dist/ui/components/modals/LoadingModal.d.ts +8 -0
- package/dist/ui/components/modals/LoadingModal.d.ts.map +1 -0
- package/dist/ui/components/modals/LoadingModal.js +8 -0
- package/dist/ui/components/modals/LoadingModal.js.map +1 -0
- package/dist/ui/components/modals/MessageModal.d.ts +14 -0
- package/dist/ui/components/modals/MessageModal.d.ts.map +1 -0
- package/dist/ui/components/modals/MessageModal.js +17 -0
- package/dist/ui/components/modals/MessageModal.js.map +1 -0
- package/dist/ui/components/modals/ModalContainer.d.ts +7 -0
- package/dist/ui/components/modals/ModalContainer.d.ts.map +1 -0
- package/dist/ui/components/modals/ModalContainer.js +38 -0
- package/dist/ui/components/modals/ModalContainer.js.map +1 -0
- package/dist/ui/components/modals/SelectModal.d.ts +17 -0
- package/dist/ui/components/modals/SelectModal.d.ts.map +1 -0
- package/dist/ui/components/modals/SelectModal.js +33 -0
- package/dist/ui/components/modals/SelectModal.js.map +1 -0
- package/dist/ui/components/modals/index.d.ts +7 -0
- package/dist/ui/components/modals/index.d.ts.map +1 -0
- package/dist/ui/components/modals/index.js +7 -0
- package/dist/ui/components/modals/index.js.map +1 -0
- package/dist/ui/hooks/index.d.ts +3 -0
- package/dist/ui/hooks/index.d.ts.map +1 -0
- package/dist/ui/hooks/index.js +3 -0
- package/dist/ui/hooks/index.js.map +1 -0
- package/dist/ui/hooks/useAsyncData.d.ts +40 -0
- package/dist/ui/hooks/useAsyncData.d.ts.map +1 -0
- package/dist/ui/hooks/useAsyncData.js +78 -0
- package/dist/ui/hooks/useAsyncData.js.map +1 -0
- package/dist/ui/hooks/useKeyboardNavigation.d.ts +27 -0
- package/dist/ui/hooks/useKeyboardNavigation.d.ts.map +1 -0
- package/dist/ui/hooks/useKeyboardNavigation.js +82 -0
- package/dist/ui/hooks/useKeyboardNavigation.js.map +1 -0
- package/dist/ui/screens/CliToolsScreen.d.ts +4 -0
- package/dist/ui/screens/CliToolsScreen.d.ts.map +1 -0
- package/dist/ui/screens/CliToolsScreen.js +268 -0
- package/dist/ui/screens/CliToolsScreen.js.map +1 -0
- package/dist/ui/screens/EnvVarsScreen.d.ts +4 -0
- package/dist/ui/screens/EnvVarsScreen.d.ts.map +1 -0
- package/dist/ui/screens/EnvVarsScreen.js +145 -0
- package/dist/ui/screens/EnvVarsScreen.js.map +1 -0
- package/dist/ui/screens/McpRegistryScreen.d.ts +4 -0
- package/dist/ui/screens/McpRegistryScreen.d.ts.map +1 -0
- package/dist/ui/screens/McpRegistryScreen.js +226 -0
- package/dist/ui/screens/McpRegistryScreen.js.map +1 -0
- package/dist/ui/screens/McpScreen.d.ts +4 -0
- package/dist/ui/screens/McpScreen.d.ts.map +1 -0
- package/dist/ui/screens/McpScreen.js +222 -0
- package/dist/ui/screens/McpScreen.js.map +1 -0
- package/dist/ui/screens/ModelSelectorScreen.d.ts +4 -0
- package/dist/ui/screens/ModelSelectorScreen.d.ts.map +1 -0
- package/dist/ui/screens/ModelSelectorScreen.js +143 -0
- package/dist/ui/screens/ModelSelectorScreen.js.map +1 -0
- package/dist/ui/screens/PluginsScreen.d.ts +4 -0
- package/dist/ui/screens/PluginsScreen.d.ts.map +1 -0
- package/dist/ui/screens/PluginsScreen.js +747 -0
- package/dist/ui/screens/PluginsScreen.js.map +1 -0
- package/dist/ui/screens/StatusLineScreen.d.ts +4 -0
- package/dist/ui/screens/StatusLineScreen.d.ts.map +1 -0
- package/dist/ui/screens/StatusLineScreen.js +197 -0
- package/dist/ui/screens/StatusLineScreen.js.map +1 -0
- package/dist/ui/screens/index.d.ts +8 -0
- package/dist/ui/screens/index.d.ts.map +1 -0
- package/dist/ui/screens/index.js +8 -0
- package/dist/ui/screens/index.js.map +1 -0
- package/dist/ui/state/AppContext.d.ts +40 -0
- package/dist/ui/state/AppContext.d.ts.map +1 -0
- package/dist/ui/state/AppContext.js +162 -0
- package/dist/ui/state/AppContext.js.map +1 -0
- package/dist/ui/state/DimensionsContext.d.ts +25 -0
- package/dist/ui/state/DimensionsContext.d.ts.map +1 -0
- package/dist/ui/state/DimensionsContext.js +68 -0
- package/dist/ui/state/DimensionsContext.js.map +1 -0
- package/dist/ui/state/reducer.d.ts +4 -0
- package/dist/ui/state/reducer.d.ts.map +1 -0
- package/dist/ui/state/reducer.js +412 -0
- package/dist/ui/state/reducer.js.map +1 -0
- package/dist/ui/state/types.d.ts +266 -0
- package/dist/ui/state/types.d.ts.map +1 -0
- package/dist/ui/state/types.js +2 -0
- package/dist/ui/state/types.js.map +1 -0
- package/dist/utils/fuzzy-search.d.ts +33 -0
- package/dist/utils/fuzzy-search.d.ts.map +1 -0
- package/dist/utils/fuzzy-search.js +102 -0
- package/dist/utils/fuzzy-search.js.map +1 -0
- package/dist/utils/string-utils.d.ts +24 -0
- package/dist/utils/string-utils.d.ts.map +1 -0
- package/dist/utils/string-utils.js +62 -0
- package/dist/utils/string-utils.js.map +1 -0
- package/package.json +24 -8
- package/dist/ui/app.d.ts +0 -38
- package/dist/ui/app.d.ts.map +0 -1
- package/dist/ui/app.js +0 -590
- package/dist/ui/app.js.map +0 -1
- package/dist/ui/screens/cli-tools.d.ts +0 -4
- package/dist/ui/screens/cli-tools.d.ts.map +0 -1
- package/dist/ui/screens/cli-tools.js +0 -369
- package/dist/ui/screens/cli-tools.js.map +0 -1
- package/dist/ui/screens/env-vars.d.ts +0 -3
- package/dist/ui/screens/env-vars.d.ts.map +0 -1
- package/dist/ui/screens/env-vars.js +0 -119
- package/dist/ui/screens/env-vars.js.map +0 -1
- package/dist/ui/screens/main-menu.d.ts +0 -3
- package/dist/ui/screens/main-menu.d.ts.map +0 -1
- package/dist/ui/screens/main-menu.js +0 -110
- package/dist/ui/screens/main-menu.js.map +0 -1
- package/dist/ui/screens/marketplace.d.ts +0 -3
- package/dist/ui/screens/marketplace.d.ts.map +0 -1
- package/dist/ui/screens/marketplace.js +0 -132
- package/dist/ui/screens/marketplace.js.map +0 -1
- package/dist/ui/screens/mcp-registry.d.ts +0 -10
- package/dist/ui/screens/mcp-registry.d.ts.map +0 -1
- package/dist/ui/screens/mcp-registry.js +0 -310
- package/dist/ui/screens/mcp-registry.js.map +0 -1
- package/dist/ui/screens/mcp-setup.d.ts +0 -4
- package/dist/ui/screens/mcp-setup.d.ts.map +0 -1
- package/dist/ui/screens/mcp-setup.js +0 -492
- package/dist/ui/screens/mcp-setup.js.map +0 -1
- package/dist/ui/screens/plugins.d.ts +0 -3
- package/dist/ui/screens/plugins.d.ts.map +0 -1
- package/dist/ui/screens/plugins.js +0 -443
- package/dist/ui/screens/plugins.js.map +0 -1
- package/dist/ui/screens/statusline.d.ts +0 -5
- package/dist/ui/screens/statusline.d.ts.map +0 -1
- package/dist/ui/screens/statusline.js +0 -235
- package/dist/ui/screens/statusline.js.map +0 -1
|
@@ -0,0 +1,747 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useCallback, useMemo } from 'react';
|
|
3
|
+
import { Box, Text, useInput } from 'ink';
|
|
4
|
+
import { useApp, useModal, useProgress } from '../state/AppContext.js';
|
|
5
|
+
import { useDimensions } from '../state/DimensionsContext.js';
|
|
6
|
+
import { ScreenLayout } from '../components/layout/index.js';
|
|
7
|
+
import { CategoryHeader } from '../components/CategoryHeader.js';
|
|
8
|
+
import { ScrollableList } from '../components/ScrollableList.js';
|
|
9
|
+
import { fuzzyFilter, highlightMatches } from '../../utils/fuzzy-search.js';
|
|
10
|
+
import { getAllMarketplaces } from '../../data/marketplaces.js';
|
|
11
|
+
import { getAvailablePlugins, refreshAllMarketplaces, clearMarketplaceCache, saveInstalledPluginVersion, removeInstalledPluginVersion, getLocalMarketplacesInfo, } from '../../services/plugin-manager.js';
|
|
12
|
+
import { addMarketplace, removeMarketplace, addGlobalMarketplace, removeGlobalMarketplace, enablePlugin, enableGlobalPlugin, enableLocalPlugin, saveGlobalInstalledPluginVersion, removeGlobalInstalledPluginVersion, saveLocalInstalledPluginVersion, removeLocalInstalledPluginVersion, } from '../../services/claude-settings.js';
|
|
13
|
+
import { cloneMarketplace, deleteMarketplace, addToKnownMarketplaces, removeFromKnownMarketplaces, } from '../../services/local-marketplace.js';
|
|
14
|
+
export function PluginsScreen() {
|
|
15
|
+
const { state, dispatch } = useApp();
|
|
16
|
+
const { plugins: pluginsState } = state;
|
|
17
|
+
const modal = useModal();
|
|
18
|
+
const progress = useProgress();
|
|
19
|
+
const dimensions = useDimensions();
|
|
20
|
+
const isSearchActive = state.isSearching && state.currentRoute.screen === 'plugins' && !state.modal;
|
|
21
|
+
// Fetch data (always fetches all scopes)
|
|
22
|
+
const fetchData = useCallback(async () => {
|
|
23
|
+
dispatch({ type: 'PLUGINS_DATA_LOADING' });
|
|
24
|
+
try {
|
|
25
|
+
const localMarketplaces = await getLocalMarketplacesInfo();
|
|
26
|
+
const allMarketplaces = getAllMarketplaces(localMarketplaces);
|
|
27
|
+
// Always use getAvailablePlugins which fetches all scope data
|
|
28
|
+
const pluginData = await getAvailablePlugins(state.projectPath);
|
|
29
|
+
dispatch({
|
|
30
|
+
type: 'PLUGINS_DATA_SUCCESS',
|
|
31
|
+
marketplaces: allMarketplaces,
|
|
32
|
+
plugins: pluginData,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
dispatch({
|
|
37
|
+
type: 'PLUGINS_DATA_ERROR',
|
|
38
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}, [dispatch, state.projectPath]);
|
|
42
|
+
// Load data on mount or when dataRefreshVersion changes
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
fetchData();
|
|
45
|
+
}, [fetchData, state.dataRefreshVersion]);
|
|
46
|
+
// Build list items (categories + plugins)
|
|
47
|
+
const allItems = useMemo(() => {
|
|
48
|
+
if (pluginsState.marketplaces.status !== 'success' || pluginsState.plugins.status !== 'success') {
|
|
49
|
+
return [];
|
|
50
|
+
}
|
|
51
|
+
const marketplaces = pluginsState.marketplaces.data;
|
|
52
|
+
const plugins = pluginsState.plugins.data;
|
|
53
|
+
const collapsed = pluginsState.collapsedMarketplaces;
|
|
54
|
+
const pluginsByMarketplace = new Map();
|
|
55
|
+
for (const plugin of plugins) {
|
|
56
|
+
const existing = pluginsByMarketplace.get(plugin.marketplace) || [];
|
|
57
|
+
existing.push(plugin);
|
|
58
|
+
pluginsByMarketplace.set(plugin.marketplace, existing);
|
|
59
|
+
}
|
|
60
|
+
// Sort marketplaces: deprecated ones go to the bottom
|
|
61
|
+
const sortedMarketplaces = [...marketplaces].sort((a, b) => {
|
|
62
|
+
const aDeprecated = a.name === 'claude-code-plugins' ? 1 : 0;
|
|
63
|
+
const bDeprecated = b.name === 'claude-code-plugins' ? 1 : 0;
|
|
64
|
+
return aDeprecated - bDeprecated;
|
|
65
|
+
});
|
|
66
|
+
const items = [];
|
|
67
|
+
for (const marketplace of sortedMarketplaces) {
|
|
68
|
+
const marketplacePlugins = pluginsByMarketplace.get(marketplace.name) || [];
|
|
69
|
+
const isCollapsed = collapsed.has(marketplace.name);
|
|
70
|
+
const isEnabled = marketplacePlugins.length > 0 || marketplace.official;
|
|
71
|
+
const hasPlugins = marketplacePlugins.length > 0;
|
|
72
|
+
// Category header (marketplace)
|
|
73
|
+
items.push({
|
|
74
|
+
id: `mp:${marketplace.name}`,
|
|
75
|
+
type: 'category',
|
|
76
|
+
label: marketplace.displayName,
|
|
77
|
+
marketplace,
|
|
78
|
+
marketplaceEnabled: isEnabled,
|
|
79
|
+
pluginCount: marketplacePlugins.length,
|
|
80
|
+
isExpanded: !isCollapsed && hasPlugins,
|
|
81
|
+
});
|
|
82
|
+
// Plugins under this marketplace (if expanded)
|
|
83
|
+
if (isEnabled && hasPlugins && !isCollapsed) {
|
|
84
|
+
for (const plugin of marketplacePlugins) {
|
|
85
|
+
items.push({
|
|
86
|
+
id: `pl:${plugin.id}`,
|
|
87
|
+
type: 'plugin',
|
|
88
|
+
label: plugin.name,
|
|
89
|
+
plugin,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return items;
|
|
95
|
+
}, [pluginsState.marketplaces, pluginsState.plugins, pluginsState.collapsedMarketplaces]);
|
|
96
|
+
// Filter items by search query
|
|
97
|
+
const filteredItems = useMemo(() => {
|
|
98
|
+
const query = pluginsState.searchQuery.trim();
|
|
99
|
+
if (!query)
|
|
100
|
+
return allItems;
|
|
101
|
+
// Only search plugins, not categories
|
|
102
|
+
const pluginItems = allItems.filter(item => item.type === 'plugin');
|
|
103
|
+
const fuzzyResults = fuzzyFilter(pluginItems, query, item => item.label);
|
|
104
|
+
// Include parent categories for matched plugins
|
|
105
|
+
const matchedMarketplaces = new Set();
|
|
106
|
+
for (const result of fuzzyResults) {
|
|
107
|
+
if (result.item.plugin) {
|
|
108
|
+
matchedMarketplaces.add(result.item.plugin.marketplace);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
const result = [];
|
|
112
|
+
let currentMarketplace = null;
|
|
113
|
+
for (const item of allItems) {
|
|
114
|
+
if (item.type === 'category' && item.marketplace) {
|
|
115
|
+
if (matchedMarketplaces.has(item.marketplace.name)) {
|
|
116
|
+
result.push(item);
|
|
117
|
+
currentMarketplace = item.marketplace.name;
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
currentMarketplace = null;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
else if (item.type === 'plugin' && item.plugin) {
|
|
124
|
+
if (currentMarketplace === item.plugin.marketplace) {
|
|
125
|
+
// Check if this plugin matched
|
|
126
|
+
const matched = fuzzyResults.find(r => r.item.id === item.id);
|
|
127
|
+
if (matched) {
|
|
128
|
+
result.push({ ...item, _matches: matched.matches });
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return result;
|
|
134
|
+
}, [allItems, pluginsState.searchQuery]);
|
|
135
|
+
// Only selectable items (plugins, not categories)
|
|
136
|
+
const selectableItems = useMemo(() => {
|
|
137
|
+
return filteredItems.filter(item => item.type === 'plugin' || item.type === 'category');
|
|
138
|
+
}, [filteredItems]);
|
|
139
|
+
// Keyboard handling
|
|
140
|
+
useInput((input, key) => {
|
|
141
|
+
// Handle search mode
|
|
142
|
+
if (isSearchActive) {
|
|
143
|
+
if (key.escape) {
|
|
144
|
+
dispatch({ type: 'SET_SEARCHING', isSearching: false });
|
|
145
|
+
dispatch({ type: 'PLUGINS_SET_SEARCH', query: '' });
|
|
146
|
+
}
|
|
147
|
+
else if (key.return) {
|
|
148
|
+
dispatch({ type: 'SET_SEARCHING', isSearching: false });
|
|
149
|
+
// Keep the search query, just exit search mode
|
|
150
|
+
}
|
|
151
|
+
else if (key.backspace || key.delete) {
|
|
152
|
+
dispatch({ type: 'PLUGINS_SET_SEARCH', query: pluginsState.searchQuery.slice(0, -1) });
|
|
153
|
+
}
|
|
154
|
+
else if (input && !key.ctrl && !key.meta) {
|
|
155
|
+
dispatch({ type: 'PLUGINS_SET_SEARCH', query: pluginsState.searchQuery + input });
|
|
156
|
+
}
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
if (state.modal)
|
|
160
|
+
return;
|
|
161
|
+
// Start search with /
|
|
162
|
+
if (input === '/') {
|
|
163
|
+
dispatch({ type: 'SET_SEARCHING', isSearching: true });
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
// Navigation
|
|
167
|
+
if (key.upArrow || input === 'k') {
|
|
168
|
+
const newIndex = Math.max(0, pluginsState.selectedIndex - 1);
|
|
169
|
+
dispatch({ type: 'PLUGINS_SELECT', index: newIndex });
|
|
170
|
+
}
|
|
171
|
+
else if (key.downArrow || input === 'j') {
|
|
172
|
+
const newIndex = Math.min(selectableItems.length - 1, pluginsState.selectedIndex + 1);
|
|
173
|
+
dispatch({ type: 'PLUGINS_SELECT', index: newIndex });
|
|
174
|
+
}
|
|
175
|
+
// Collapse/expand marketplace
|
|
176
|
+
else if ((key.leftArrow || key.rightArrow || input === '<' || input === '>') && selectableItems[pluginsState.selectedIndex]?.marketplace) {
|
|
177
|
+
const item = selectableItems[pluginsState.selectedIndex];
|
|
178
|
+
if (item?.marketplace) {
|
|
179
|
+
dispatch({ type: 'PLUGINS_TOGGLE_MARKETPLACE', name: item.marketplace.name });
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
// Refresh
|
|
183
|
+
else if (input === 'r') {
|
|
184
|
+
handleRefresh();
|
|
185
|
+
}
|
|
186
|
+
// New marketplace
|
|
187
|
+
else if (input === 'n') {
|
|
188
|
+
handleAddMarketplace();
|
|
189
|
+
}
|
|
190
|
+
// Scope-specific toggle shortcuts (u/p/l)
|
|
191
|
+
else if (input === 'u') {
|
|
192
|
+
handleScopeToggle('user');
|
|
193
|
+
}
|
|
194
|
+
else if (input === 'p') {
|
|
195
|
+
handleScopeToggle('project');
|
|
196
|
+
}
|
|
197
|
+
else if (input === 'l') {
|
|
198
|
+
handleScopeToggle('local');
|
|
199
|
+
}
|
|
200
|
+
// Update plugin (Shift+U)
|
|
201
|
+
else if (input === 'U') {
|
|
202
|
+
handleUpdate();
|
|
203
|
+
}
|
|
204
|
+
// Update all
|
|
205
|
+
else if (input === 'a') {
|
|
206
|
+
handleUpdateAll();
|
|
207
|
+
}
|
|
208
|
+
// Delete/uninstall
|
|
209
|
+
else if (input === 'd') {
|
|
210
|
+
handleUninstall();
|
|
211
|
+
}
|
|
212
|
+
// Enter for selection
|
|
213
|
+
else if (key.return) {
|
|
214
|
+
handleSelect();
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
// Handle actions
|
|
218
|
+
const handleRefresh = async () => {
|
|
219
|
+
progress.show('Refreshing marketplaces...');
|
|
220
|
+
try {
|
|
221
|
+
await refreshAllMarketplaces((p) => {
|
|
222
|
+
progress.show(`Refreshing ${p.name}...`, p.current, p.total);
|
|
223
|
+
});
|
|
224
|
+
clearMarketplaceCache();
|
|
225
|
+
progress.hide();
|
|
226
|
+
await modal.message('Refreshed', 'Marketplaces refreshed successfully.', 'success');
|
|
227
|
+
fetchData();
|
|
228
|
+
}
|
|
229
|
+
catch (error) {
|
|
230
|
+
progress.hide();
|
|
231
|
+
await modal.message('Error', `Refresh failed: ${error}`, 'error');
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
const handleAddMarketplace = async () => {
|
|
235
|
+
const repo = await modal.input('Add Marketplace', 'GitHub repo (owner/repo):');
|
|
236
|
+
if (!repo || !repo.trim())
|
|
237
|
+
return;
|
|
238
|
+
progress.show('Cloning marketplace...');
|
|
239
|
+
try {
|
|
240
|
+
const result = await cloneMarketplace(repo.trim());
|
|
241
|
+
if (result.success) {
|
|
242
|
+
const normalizedRepo = repo.trim().replace(/^https:\/\/github\.com\//, '').replace(/\.git$/, '');
|
|
243
|
+
const marketplace = {
|
|
244
|
+
name: result.name,
|
|
245
|
+
displayName: result.name,
|
|
246
|
+
source: { source: 'github', repo: normalizedRepo },
|
|
247
|
+
description: '',
|
|
248
|
+
official: false,
|
|
249
|
+
};
|
|
250
|
+
if (pluginsState.scope === 'global') {
|
|
251
|
+
await addGlobalMarketplace(marketplace);
|
|
252
|
+
}
|
|
253
|
+
else {
|
|
254
|
+
await addMarketplace(marketplace, state.projectPath);
|
|
255
|
+
}
|
|
256
|
+
await addToKnownMarketplaces(result.name, normalizedRepo);
|
|
257
|
+
clearMarketplaceCache();
|
|
258
|
+
progress.hide();
|
|
259
|
+
await modal.message('Added', `${result.name} marketplace added.`, 'success');
|
|
260
|
+
fetchData();
|
|
261
|
+
}
|
|
262
|
+
else {
|
|
263
|
+
progress.hide();
|
|
264
|
+
await modal.message('Failed', result.error || 'Clone failed', 'error');
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
catch (error) {
|
|
268
|
+
progress.hide();
|
|
269
|
+
await modal.message('Error', `Failed to add marketplace: ${error}`, 'error');
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
const handleSelect = async () => {
|
|
273
|
+
const item = selectableItems[pluginsState.selectedIndex];
|
|
274
|
+
if (!item)
|
|
275
|
+
return;
|
|
276
|
+
if (item.type === 'category' && item.marketplace) {
|
|
277
|
+
const mp = item.marketplace;
|
|
278
|
+
const isGlobal = pluginsState.scope === 'global';
|
|
279
|
+
if (item.marketplaceEnabled) {
|
|
280
|
+
const isCollapsed = pluginsState.collapsedMarketplaces.has(mp.name);
|
|
281
|
+
// If collapsed, expand first (even if no plugins - they might load)
|
|
282
|
+
if (isCollapsed) {
|
|
283
|
+
dispatch({ type: 'PLUGINS_TOGGLE_MARKETPLACE', name: mp.name });
|
|
284
|
+
}
|
|
285
|
+
else if (item.pluginCount && item.pluginCount > 0) {
|
|
286
|
+
// If expanded with plugins, collapse
|
|
287
|
+
dispatch({ type: 'PLUGINS_TOGGLE_MARKETPLACE', name: mp.name });
|
|
288
|
+
}
|
|
289
|
+
else {
|
|
290
|
+
// If expanded with no plugins, offer to remove
|
|
291
|
+
const confirmed = await modal.confirm(`Remove ${mp.displayName}?`, 'Plugins from this marketplace will no longer be available.');
|
|
292
|
+
if (confirmed) {
|
|
293
|
+
modal.loading(`Removing ${mp.displayName}...`);
|
|
294
|
+
try {
|
|
295
|
+
if (isGlobal) {
|
|
296
|
+
await removeGlobalMarketplace(mp.name);
|
|
297
|
+
}
|
|
298
|
+
else {
|
|
299
|
+
await removeMarketplace(mp.name, state.projectPath);
|
|
300
|
+
}
|
|
301
|
+
await deleteMarketplace(mp.name);
|
|
302
|
+
await removeFromKnownMarketplaces(mp.name);
|
|
303
|
+
clearMarketplaceCache();
|
|
304
|
+
modal.hideModal();
|
|
305
|
+
await modal.message('Removed', `${mp.displayName} removed.`, 'success');
|
|
306
|
+
fetchData();
|
|
307
|
+
}
|
|
308
|
+
catch (error) {
|
|
309
|
+
modal.hideModal();
|
|
310
|
+
await modal.message('Error', `Failed to remove: ${error}`, 'error');
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
else {
|
|
316
|
+
// Add marketplace
|
|
317
|
+
modal.loading(`Adding ${mp.displayName}...`);
|
|
318
|
+
try {
|
|
319
|
+
if (isGlobal) {
|
|
320
|
+
await addGlobalMarketplace(mp);
|
|
321
|
+
}
|
|
322
|
+
else {
|
|
323
|
+
await addMarketplace(mp, state.projectPath);
|
|
324
|
+
}
|
|
325
|
+
modal.hideModal();
|
|
326
|
+
fetchData();
|
|
327
|
+
}
|
|
328
|
+
catch (error) {
|
|
329
|
+
modal.hideModal();
|
|
330
|
+
await modal.message('Error', `Failed to add: ${error}`, 'error');
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
else if (item.type === 'plugin' && item.plugin) {
|
|
335
|
+
const plugin = item.plugin;
|
|
336
|
+
const latestVersion = plugin.version || '0.0.0';
|
|
337
|
+
// Build scope options with status info
|
|
338
|
+
const buildScopeLabel = (name, scope, desc) => {
|
|
339
|
+
const installed = scope?.enabled;
|
|
340
|
+
const ver = scope?.version;
|
|
341
|
+
const hasUpdate = ver && latestVersion && ver !== latestVersion && latestVersion !== '0.0.0';
|
|
342
|
+
let label = installed ? `● ${name}` : `○ ${name}`;
|
|
343
|
+
label += ` (${desc})`;
|
|
344
|
+
if (ver)
|
|
345
|
+
label += ` v${ver}`;
|
|
346
|
+
if (hasUpdate)
|
|
347
|
+
label += ` → v${latestVersion}`;
|
|
348
|
+
return label;
|
|
349
|
+
};
|
|
350
|
+
const scopeOptions = [
|
|
351
|
+
{ label: buildScopeLabel('User', plugin.userScope, 'global'), value: 'user' },
|
|
352
|
+
{ label: buildScopeLabel('Project', plugin.projectScope, 'team'), value: 'project' },
|
|
353
|
+
{ label: buildScopeLabel('Local', plugin.localScope, 'private'), value: 'local' },
|
|
354
|
+
];
|
|
355
|
+
const scopeValue = await modal.select(plugin.name, `Select scope to toggle:`, scopeOptions);
|
|
356
|
+
if (scopeValue === null)
|
|
357
|
+
return; // Cancelled
|
|
358
|
+
// Determine action based on selected scope's current state
|
|
359
|
+
const selectedScope = scopeValue === 'user' ? plugin.userScope :
|
|
360
|
+
scopeValue === 'project' ? plugin.projectScope :
|
|
361
|
+
plugin.localScope;
|
|
362
|
+
const isInstalledInScope = selectedScope?.enabled;
|
|
363
|
+
const installedVersion = selectedScope?.version;
|
|
364
|
+
const scopeLabel = scopeValue === 'user' ? 'User' : scopeValue === 'project' ? 'Project' : 'Local';
|
|
365
|
+
// Check if this scope has an update available
|
|
366
|
+
const hasUpdateInScope = isInstalledInScope && installedVersion &&
|
|
367
|
+
latestVersion !== '0.0.0' &&
|
|
368
|
+
installedVersion !== latestVersion;
|
|
369
|
+
// Determine action: update if available, otherwise toggle
|
|
370
|
+
let action;
|
|
371
|
+
if (isInstalledInScope && hasUpdateInScope) {
|
|
372
|
+
action = 'update';
|
|
373
|
+
}
|
|
374
|
+
else if (isInstalledInScope) {
|
|
375
|
+
action = 'uninstall';
|
|
376
|
+
}
|
|
377
|
+
else {
|
|
378
|
+
action = 'install';
|
|
379
|
+
}
|
|
380
|
+
const actionLabel = action === 'update' ? `Updating ${scopeLabel}` :
|
|
381
|
+
action === 'install' ? `Installing to ${scopeLabel}` :
|
|
382
|
+
`Uninstalling from ${scopeLabel}`;
|
|
383
|
+
modal.loading(`${actionLabel}...`);
|
|
384
|
+
try {
|
|
385
|
+
if (action === 'uninstall') {
|
|
386
|
+
// Uninstall from this scope
|
|
387
|
+
if (scopeValue === 'user') {
|
|
388
|
+
await enableGlobalPlugin(plugin.id, false);
|
|
389
|
+
await removeGlobalInstalledPluginVersion(plugin.id);
|
|
390
|
+
}
|
|
391
|
+
else if (scopeValue === 'project') {
|
|
392
|
+
await enablePlugin(plugin.id, false, state.projectPath);
|
|
393
|
+
await removeInstalledPluginVersion(plugin.id, state.projectPath);
|
|
394
|
+
}
|
|
395
|
+
else {
|
|
396
|
+
await enableLocalPlugin(plugin.id, false, state.projectPath);
|
|
397
|
+
await removeLocalInstalledPluginVersion(plugin.id, state.projectPath);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
else {
|
|
401
|
+
// Install or update (both save the latest version)
|
|
402
|
+
if (scopeValue === 'user') {
|
|
403
|
+
await enableGlobalPlugin(plugin.id, true);
|
|
404
|
+
await saveGlobalInstalledPluginVersion(plugin.id, latestVersion);
|
|
405
|
+
}
|
|
406
|
+
else if (scopeValue === 'project') {
|
|
407
|
+
await enablePlugin(plugin.id, true, state.projectPath);
|
|
408
|
+
await saveInstalledPluginVersion(plugin.id, latestVersion, state.projectPath);
|
|
409
|
+
}
|
|
410
|
+
else {
|
|
411
|
+
await enableLocalPlugin(plugin.id, true, state.projectPath);
|
|
412
|
+
await saveLocalInstalledPluginVersion(plugin.id, latestVersion, state.projectPath);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
modal.hideModal();
|
|
416
|
+
fetchData();
|
|
417
|
+
}
|
|
418
|
+
catch (error) {
|
|
419
|
+
modal.hideModal();
|
|
420
|
+
await modal.message('Error', `Failed: ${error}`, 'error');
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
};
|
|
424
|
+
const handleUpdate = async () => {
|
|
425
|
+
const item = selectableItems[pluginsState.selectedIndex];
|
|
426
|
+
if (!item || item.type !== 'plugin' || !item.plugin?.hasUpdate)
|
|
427
|
+
return;
|
|
428
|
+
const plugin = item.plugin;
|
|
429
|
+
const isGlobal = pluginsState.scope === 'global';
|
|
430
|
+
modal.loading(`Updating ${plugin.name}...`);
|
|
431
|
+
try {
|
|
432
|
+
const versionToSave = plugin.version || '0.0.0';
|
|
433
|
+
if (isGlobal) {
|
|
434
|
+
await saveGlobalInstalledPluginVersion(plugin.id, versionToSave);
|
|
435
|
+
}
|
|
436
|
+
else {
|
|
437
|
+
await saveInstalledPluginVersion(plugin.id, versionToSave, state.projectPath);
|
|
438
|
+
}
|
|
439
|
+
modal.hideModal();
|
|
440
|
+
fetchData();
|
|
441
|
+
}
|
|
442
|
+
catch (error) {
|
|
443
|
+
modal.hideModal();
|
|
444
|
+
await modal.message('Error', `Failed to update: ${error}`, 'error');
|
|
445
|
+
}
|
|
446
|
+
};
|
|
447
|
+
const handleUpdateAll = async () => {
|
|
448
|
+
if (pluginsState.plugins.status !== 'success')
|
|
449
|
+
return;
|
|
450
|
+
const updatable = pluginsState.plugins.data.filter((p) => p.hasUpdate);
|
|
451
|
+
if (updatable.length === 0)
|
|
452
|
+
return;
|
|
453
|
+
const isGlobal = pluginsState.scope === 'global';
|
|
454
|
+
modal.loading(`Updating ${updatable.length} plugin(s)...`);
|
|
455
|
+
try {
|
|
456
|
+
for (const plugin of updatable) {
|
|
457
|
+
const versionToSave = plugin.version || '0.0.0';
|
|
458
|
+
if (isGlobal) {
|
|
459
|
+
await saveGlobalInstalledPluginVersion(plugin.id, versionToSave);
|
|
460
|
+
}
|
|
461
|
+
else {
|
|
462
|
+
await saveInstalledPluginVersion(plugin.id, versionToSave, state.projectPath);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
modal.hideModal();
|
|
466
|
+
fetchData();
|
|
467
|
+
}
|
|
468
|
+
catch (error) {
|
|
469
|
+
modal.hideModal();
|
|
470
|
+
await modal.message('Error', `Failed to update: ${error}`, 'error');
|
|
471
|
+
}
|
|
472
|
+
};
|
|
473
|
+
// Scope-specific toggle (install if not installed, uninstall if installed)
|
|
474
|
+
const handleScopeToggle = async (scope) => {
|
|
475
|
+
const item = selectableItems[pluginsState.selectedIndex];
|
|
476
|
+
if (!item || item.type !== 'plugin' || !item.plugin)
|
|
477
|
+
return;
|
|
478
|
+
const plugin = item.plugin;
|
|
479
|
+
const latestVersion = plugin.version || '0.0.0';
|
|
480
|
+
const scopeLabel = scope === 'user' ? 'User' : scope === 'project' ? 'Project' : 'Local';
|
|
481
|
+
// Check if installed in this scope
|
|
482
|
+
const scopeData = scope === 'user' ? plugin.userScope :
|
|
483
|
+
scope === 'project' ? plugin.projectScope :
|
|
484
|
+
plugin.localScope;
|
|
485
|
+
const isInstalledInScope = scopeData?.enabled;
|
|
486
|
+
const installedVersion = scopeData?.version;
|
|
487
|
+
// Check if this scope has an update available
|
|
488
|
+
const hasUpdateInScope = isInstalledInScope && installedVersion &&
|
|
489
|
+
latestVersion !== '0.0.0' &&
|
|
490
|
+
installedVersion !== latestVersion;
|
|
491
|
+
// Determine action: update if available, otherwise toggle install/uninstall
|
|
492
|
+
let action;
|
|
493
|
+
if (isInstalledInScope && hasUpdateInScope) {
|
|
494
|
+
action = 'update';
|
|
495
|
+
}
|
|
496
|
+
else if (isInstalledInScope) {
|
|
497
|
+
action = 'uninstall';
|
|
498
|
+
}
|
|
499
|
+
else {
|
|
500
|
+
action = 'install';
|
|
501
|
+
}
|
|
502
|
+
const actionLabel = action === 'update' ? `Updating ${scopeLabel}` :
|
|
503
|
+
action === 'install' ? `Installing to ${scopeLabel}` :
|
|
504
|
+
`Uninstalling from ${scopeLabel}`;
|
|
505
|
+
modal.loading(`${actionLabel}...`);
|
|
506
|
+
try {
|
|
507
|
+
if (action === 'uninstall') {
|
|
508
|
+
// Uninstall from this scope
|
|
509
|
+
if (scope === 'user') {
|
|
510
|
+
await enableGlobalPlugin(plugin.id, false);
|
|
511
|
+
await removeGlobalInstalledPluginVersion(plugin.id);
|
|
512
|
+
}
|
|
513
|
+
else if (scope === 'project') {
|
|
514
|
+
await enablePlugin(plugin.id, false, state.projectPath);
|
|
515
|
+
await removeInstalledPluginVersion(plugin.id, state.projectPath);
|
|
516
|
+
}
|
|
517
|
+
else {
|
|
518
|
+
await enableLocalPlugin(plugin.id, false, state.projectPath);
|
|
519
|
+
await removeLocalInstalledPluginVersion(plugin.id, state.projectPath);
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
else {
|
|
523
|
+
// Install or update to this scope (both save the latest version)
|
|
524
|
+
if (scope === 'user') {
|
|
525
|
+
await enableGlobalPlugin(plugin.id, true);
|
|
526
|
+
await saveGlobalInstalledPluginVersion(plugin.id, latestVersion);
|
|
527
|
+
}
|
|
528
|
+
else if (scope === 'project') {
|
|
529
|
+
await enablePlugin(plugin.id, true, state.projectPath);
|
|
530
|
+
await saveInstalledPluginVersion(plugin.id, latestVersion, state.projectPath);
|
|
531
|
+
}
|
|
532
|
+
else {
|
|
533
|
+
await enableLocalPlugin(plugin.id, true, state.projectPath);
|
|
534
|
+
await saveLocalInstalledPluginVersion(plugin.id, latestVersion, state.projectPath);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
modal.hideModal();
|
|
538
|
+
fetchData();
|
|
539
|
+
}
|
|
540
|
+
catch (error) {
|
|
541
|
+
modal.hideModal();
|
|
542
|
+
await modal.message('Error', `Failed: ${error}`, 'error');
|
|
543
|
+
}
|
|
544
|
+
};
|
|
545
|
+
const handleUninstall = async () => {
|
|
546
|
+
const item = selectableItems[pluginsState.selectedIndex];
|
|
547
|
+
if (!item || item.type !== 'plugin' || !item.plugin)
|
|
548
|
+
return;
|
|
549
|
+
const plugin = item.plugin;
|
|
550
|
+
// Build list of scopes where plugin is installed
|
|
551
|
+
const installedScopes = [];
|
|
552
|
+
if (plugin.userScope?.enabled) {
|
|
553
|
+
const ver = plugin.userScope.version ? ` v${plugin.userScope.version}` : '';
|
|
554
|
+
installedScopes.push({ label: `User (global)${ver}`, value: 'user' });
|
|
555
|
+
}
|
|
556
|
+
if (plugin.projectScope?.enabled) {
|
|
557
|
+
const ver = plugin.projectScope.version ? ` v${plugin.projectScope.version}` : '';
|
|
558
|
+
installedScopes.push({ label: `Project${ver}`, value: 'project' });
|
|
559
|
+
}
|
|
560
|
+
if (plugin.localScope?.enabled) {
|
|
561
|
+
const ver = plugin.localScope.version ? ` v${plugin.localScope.version}` : '';
|
|
562
|
+
installedScopes.push({ label: `Local${ver}`, value: 'local' });
|
|
563
|
+
}
|
|
564
|
+
if (installedScopes.length === 0) {
|
|
565
|
+
await modal.message('Not Installed', `${plugin.name} is not installed in any scope.`, 'info');
|
|
566
|
+
return;
|
|
567
|
+
}
|
|
568
|
+
const scopeValue = await modal.select(`Uninstall ${plugin.name}`, `Installed in ${installedScopes.length} scope(s):`, installedScopes);
|
|
569
|
+
if (scopeValue === null)
|
|
570
|
+
return; // Cancelled
|
|
571
|
+
modal.loading(`Uninstalling ${plugin.name}...`);
|
|
572
|
+
try {
|
|
573
|
+
if (scopeValue === 'user') {
|
|
574
|
+
await enableGlobalPlugin(plugin.id, false);
|
|
575
|
+
await removeGlobalInstalledPluginVersion(plugin.id);
|
|
576
|
+
}
|
|
577
|
+
else if (scopeValue === 'project') {
|
|
578
|
+
await enablePlugin(plugin.id, false, state.projectPath);
|
|
579
|
+
await removeInstalledPluginVersion(plugin.id, state.projectPath);
|
|
580
|
+
}
|
|
581
|
+
else {
|
|
582
|
+
// local scope
|
|
583
|
+
await enableLocalPlugin(plugin.id, false, state.projectPath);
|
|
584
|
+
await removeLocalInstalledPluginVersion(plugin.id, state.projectPath);
|
|
585
|
+
}
|
|
586
|
+
modal.hideModal();
|
|
587
|
+
fetchData();
|
|
588
|
+
}
|
|
589
|
+
catch (error) {
|
|
590
|
+
modal.hideModal();
|
|
591
|
+
await modal.message('Error', `Failed to uninstall: ${error}`, 'error');
|
|
592
|
+
}
|
|
593
|
+
};
|
|
594
|
+
// Render loading state
|
|
595
|
+
if (pluginsState.marketplaces.status === 'loading' || pluginsState.plugins.status === 'loading') {
|
|
596
|
+
return (_jsxs(Box, { flexDirection: "column", paddingX: 1, children: [_jsx(Text, { color: "#7e57c2", bold: true, children: "claudeup Plugins" }), _jsx(Text, { color: "gray", children: "Loading..." })] }));
|
|
597
|
+
}
|
|
598
|
+
// Render error state
|
|
599
|
+
if (pluginsState.marketplaces.status === 'error' || pluginsState.plugins.status === 'error') {
|
|
600
|
+
return (_jsxs(Box, { flexDirection: "column", paddingX: 1, children: [_jsx(Text, { color: "#7e57c2", bold: true, children: "claudeup Plugins" }), _jsx(Text, { color: "red", children: "Error loading data" })] }));
|
|
601
|
+
}
|
|
602
|
+
// Get selected item for detail panel
|
|
603
|
+
const selectedItem = selectableItems[pluginsState.selectedIndex];
|
|
604
|
+
// Render item with fuzzy highlight support
|
|
605
|
+
const renderListItem = (item, _idx, isSelected) => {
|
|
606
|
+
if (item.type === 'category' && item.marketplace) {
|
|
607
|
+
const mp = item.marketplace;
|
|
608
|
+
// Differentiate marketplace types with appropriate badges
|
|
609
|
+
let statusText = '';
|
|
610
|
+
let statusColor = 'green';
|
|
611
|
+
if (item.marketplaceEnabled) {
|
|
612
|
+
if (mp.name === 'claude-plugins-official') {
|
|
613
|
+
statusText = '★ Official';
|
|
614
|
+
statusColor = 'yellow';
|
|
615
|
+
}
|
|
616
|
+
else if (mp.name === 'claude-code-plugins') {
|
|
617
|
+
statusText = '⚠ Deprecated';
|
|
618
|
+
statusColor = 'gray';
|
|
619
|
+
}
|
|
620
|
+
else if (mp.official) {
|
|
621
|
+
statusText = '★ Official';
|
|
622
|
+
statusColor = 'yellow';
|
|
623
|
+
}
|
|
624
|
+
else {
|
|
625
|
+
statusText = '✓ Added';
|
|
626
|
+
statusColor = 'green';
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
if (isSelected) {
|
|
630
|
+
const arrow = item.isExpanded ? '▼' : '▶';
|
|
631
|
+
const count = item.pluginCount !== undefined && item.pluginCount > 0 ? ` (${item.pluginCount})` : '';
|
|
632
|
+
return (_jsx(Text, { backgroundColor: "magenta", color: "white", bold: true, children: ` ${arrow} ${mp.displayName}${count} ` }));
|
|
633
|
+
}
|
|
634
|
+
return (_jsx(CategoryHeader, { title: mp.displayName, expanded: item.isExpanded, count: item.pluginCount, status: statusText, statusColor: statusColor }));
|
|
635
|
+
}
|
|
636
|
+
if (item.type === 'plugin' && item.plugin) {
|
|
637
|
+
const plugin = item.plugin;
|
|
638
|
+
let statusIcon = '○';
|
|
639
|
+
let statusColor = 'gray';
|
|
640
|
+
if (plugin.enabled) {
|
|
641
|
+
statusIcon = '●';
|
|
642
|
+
statusColor = 'green';
|
|
643
|
+
}
|
|
644
|
+
else if (plugin.installedVersion) {
|
|
645
|
+
statusIcon = '●';
|
|
646
|
+
statusColor = 'yellow';
|
|
647
|
+
}
|
|
648
|
+
// Build version string
|
|
649
|
+
let versionStr = '';
|
|
650
|
+
if (plugin.installedVersion && plugin.installedVersion !== '0.0.0') {
|
|
651
|
+
versionStr = ` v${plugin.installedVersion}`;
|
|
652
|
+
if (plugin.hasUpdate && plugin.version) {
|
|
653
|
+
versionStr += ` → v${plugin.version}`;
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
// Get fuzzy match highlights if available
|
|
657
|
+
const matches = item._matches;
|
|
658
|
+
const segments = matches ? highlightMatches(plugin.name, matches) : null;
|
|
659
|
+
if (isSelected) {
|
|
660
|
+
const displayText = ` ${statusIcon} ${plugin.name}${versionStr} `;
|
|
661
|
+
return (_jsx(Text, { backgroundColor: "magenta", color: "white", wrap: "truncate", children: displayText }));
|
|
662
|
+
}
|
|
663
|
+
// For non-selected, render with colors
|
|
664
|
+
const displayName = segments
|
|
665
|
+
? segments.map(seg => seg.text).join('')
|
|
666
|
+
: plugin.name;
|
|
667
|
+
return (_jsxs(Text, { wrap: "truncate", children: [_jsxs(Text, { color: statusColor, children: [' ', statusIcon, " "] }), _jsx(Text, { children: displayName }), _jsx(Text, { color: plugin.hasUpdate ? 'yellow' : 'gray', children: versionStr })] }));
|
|
668
|
+
}
|
|
669
|
+
return _jsx(Text, { color: "gray", children: item.label });
|
|
670
|
+
};
|
|
671
|
+
// Render detail content - compact to fit in available space
|
|
672
|
+
const renderDetail = () => {
|
|
673
|
+
if (!selectedItem) {
|
|
674
|
+
return _jsx(Text, { color: "gray", children: "Select an item" });
|
|
675
|
+
}
|
|
676
|
+
if (selectedItem.type === 'category' && selectedItem.marketplace) {
|
|
677
|
+
const mp = selectedItem.marketplace;
|
|
678
|
+
const isEnabled = selectedItem.marketplaceEnabled;
|
|
679
|
+
// Get appropriate badge for marketplace type
|
|
680
|
+
const getBadge = () => {
|
|
681
|
+
if (mp.name === 'claude-plugins-official')
|
|
682
|
+
return ' ★';
|
|
683
|
+
if (mp.name === 'claude-code-plugins')
|
|
684
|
+
return ' ⚠';
|
|
685
|
+
if (mp.official)
|
|
686
|
+
return ' ★';
|
|
687
|
+
return '';
|
|
688
|
+
};
|
|
689
|
+
// Determine action hint based on state
|
|
690
|
+
const isCollapsed = pluginsState.collapsedMarketplaces.has(mp.name);
|
|
691
|
+
const hasPlugins = (selectedItem.pluginCount || 0) > 0;
|
|
692
|
+
let actionHint = 'Add';
|
|
693
|
+
if (isEnabled) {
|
|
694
|
+
if (isCollapsed) {
|
|
695
|
+
actionHint = 'Expand';
|
|
696
|
+
}
|
|
697
|
+
else if (hasPlugins) {
|
|
698
|
+
actionHint = 'Collapse';
|
|
699
|
+
}
|
|
700
|
+
else {
|
|
701
|
+
actionHint = 'Remove';
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { bold: true, color: "cyan", children: [mp.displayName, getBadge()] }), _jsx(Text, { color: "gray", wrap: "wrap", children: mp.description || 'No description' }), _jsx(Text, { color: isEnabled ? 'green' : 'gray', children: isEnabled ? '● Added' : '○ Not added' }), _jsxs(Text, { color: "blue", wrap: "wrap", children: ["github.com/", mp.source.repo] }), _jsxs(Text, { children: ["Plugins: ", selectedItem.pluginCount || 0] }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { backgroundColor: isEnabled ? 'cyan' : 'green', color: "black", children: " Enter " }), _jsxs(Text, { color: "gray", children: [" ", actionHint] })] }), isEnabled && (_jsx(Box, { children: _jsx(Text, { color: "gray", children: "\u2190 \u2192 to expand/collapse" }) }))] }));
|
|
705
|
+
}
|
|
706
|
+
if (selectedItem.type === 'plugin' && selectedItem.plugin) {
|
|
707
|
+
const plugin = selectedItem.plugin;
|
|
708
|
+
const isInstalled = plugin.enabled || plugin.installedVersion;
|
|
709
|
+
// Build component counts
|
|
710
|
+
const components = [];
|
|
711
|
+
if (plugin.agents?.length)
|
|
712
|
+
components.push(`${plugin.agents.length} agents`);
|
|
713
|
+
if (plugin.commands?.length)
|
|
714
|
+
components.push(`${plugin.commands.length} commands`);
|
|
715
|
+
if (plugin.skills?.length)
|
|
716
|
+
components.push(`${plugin.skills.length} skills`);
|
|
717
|
+
if (plugin.mcpServers?.length)
|
|
718
|
+
components.push(`${plugin.mcpServers.length} MCP`);
|
|
719
|
+
if (plugin.lspServers && Object.keys(plugin.lspServers).length) {
|
|
720
|
+
components.push(`${Object.keys(plugin.lspServers).length} LSP`);
|
|
721
|
+
}
|
|
722
|
+
// Show version only if valid (not null, not 0.0.0)
|
|
723
|
+
const showVersion = plugin.version && plugin.version !== '0.0.0';
|
|
724
|
+
const showInstalledVersion = plugin.installedVersion && plugin.installedVersion !== '0.0.0';
|
|
725
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { justifyContent: "center", children: _jsx(Text, { backgroundColor: "magenta", color: "white", bold: true, children: ` ${plugin.name}${plugin.hasUpdate ? ' ⬆' : ''} ` }) }), _jsx(Box, { marginTop: 1, children: isInstalled ? (_jsx(Text, { color: plugin.enabled ? 'green' : 'yellow', children: plugin.enabled ? '● Enabled' : '● Disabled' })) : (_jsx(Text, { color: "gray", children: "\u25CB Not installed" })) }), _jsx(Box, { marginTop: 1, marginBottom: 1, children: _jsx(Text, { color: "white", wrap: "wrap", children: plugin.description }) }), showVersion && (_jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: "Version " }), _jsxs(Text, { color: "blue", children: ["v", plugin.version] }), showInstalledVersion && plugin.installedVersion !== plugin.version && _jsxs(Text, { dimColor: true, children: [" (v", plugin.installedVersion, " installed)"] })] })), plugin.category && (_jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: "Category " }), _jsx(Text, { color: "magenta", children: plugin.category })] })), plugin.author && (_jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: "Author " }), _jsx(Text, { children: plugin.author.name })] })), components.length > 0 && (_jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: "Contains " }), _jsx(Text, { color: "yellow", children: components.join(' · ') })] })), _jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { dimColor: true, children: "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" }), _jsx(Text, { bold: true, children: "Scopes:" }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsxs(Box, { children: [_jsx(Text, { backgroundColor: "cyan", color: "black", children: " u " }), _jsx(Text, { color: plugin.userScope?.enabled ? 'cyan' : 'gray', children: plugin.userScope?.enabled ? ' ● ' : ' ○ ' }), _jsx(Text, { color: "cyan", children: "User" }), _jsx(Text, { dimColor: true, children: " global" }), plugin.userScope?.version && _jsxs(Text, { color: "cyan", children: [" v", plugin.userScope.version] })] }), _jsxs(Box, { children: [_jsx(Text, { backgroundColor: "green", color: "black", children: " p " }), _jsx(Text, { color: plugin.projectScope?.enabled ? 'green' : 'gray', children: plugin.projectScope?.enabled ? ' ● ' : ' ○ ' }), _jsx(Text, { color: "green", children: "Project" }), _jsx(Text, { dimColor: true, children: " team" }), plugin.projectScope?.version && _jsxs(Text, { color: "green", children: [" v", plugin.projectScope.version] })] }), _jsxs(Box, { children: [_jsx(Text, { backgroundColor: "yellow", color: "black", children: " l " }), _jsx(Text, { color: plugin.localScope?.enabled ? 'yellow' : 'gray', children: plugin.localScope?.enabled ? ' ● ' : ' ○ ' }), _jsx(Text, { color: "yellow", children: "Local" }), _jsx(Text, { dimColor: true, children: " private" }), plugin.localScope?.version && _jsxs(Text, { color: "yellow", children: [" v", plugin.localScope.version] })] })] })] }), isInstalled && (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [plugin.hasUpdate && (_jsxs(Box, { children: [_jsx(Text, { backgroundColor: "magenta", color: "white", children: " U " }), _jsxs(Text, { children: [" Update to v", plugin.version] })] })), _jsxs(Box, { children: [_jsx(Text, { backgroundColor: "red", color: "white", children: " d " }), _jsx(Text, { children: " Uninstall" })] })] }))] }));
|
|
726
|
+
}
|
|
727
|
+
return null;
|
|
728
|
+
};
|
|
729
|
+
const footerHints = isSearchActive
|
|
730
|
+
? 'Type to search │ Enter Confirm │ Esc Cancel'
|
|
731
|
+
: 'u/p/l:scope │ U:update │ a:all │ d:remove │ /:search';
|
|
732
|
+
// Calculate status for subtitle
|
|
733
|
+
const scopeLabel = pluginsState.scope === 'global' ? 'Global' : 'Project';
|
|
734
|
+
const plugins = pluginsState.plugins.status === 'success' ? pluginsState.plugins.data : [];
|
|
735
|
+
const installedCount = plugins.filter(p => p.enabled).length;
|
|
736
|
+
const updateCount = plugins.filter(p => p.hasUpdate).length;
|
|
737
|
+
const subtitle = `${scopeLabel} │ ${installedCount} installed${updateCount > 0 ? ` │ ${updateCount} updates` : ''}`;
|
|
738
|
+
// Search placeholder shows status when not searching
|
|
739
|
+
const searchPlaceholder = `${scopeLabel} │ ${installedCount} installed${updateCount > 0 ? ` │ ${updateCount} ⬆` : ''} │ / to search`;
|
|
740
|
+
return (_jsx(ScreenLayout, { title: "claudeup Plugins", subtitle: subtitle, currentScreen: "plugins", search: {
|
|
741
|
+
isActive: isSearchActive,
|
|
742
|
+
query: pluginsState.searchQuery,
|
|
743
|
+
placeholder: searchPlaceholder,
|
|
744
|
+
}, footerHints: footerHints, listPanel: _jsx(ScrollableList, { items: selectableItems, selectedIndex: pluginsState.selectedIndex, renderItem: renderListItem, maxHeight: dimensions.listPanelHeight }), detailPanel: renderDetail() }));
|
|
745
|
+
}
|
|
746
|
+
export default PluginsScreen;
|
|
747
|
+
//# sourceMappingURL=PluginsScreen.js.map
|