@laststance/claude-plugin-dashboard 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +235 -0
- package/dist/app.d.ts +8 -0
- package/dist/app.js +481 -0
- package/dist/cli.d.ts +16 -0
- package/dist/cli.js +316 -0
- package/dist/components/ConfirmDialog.d.ts +14 -0
- package/dist/components/ConfirmDialog.js +14 -0
- package/dist/components/KeyHints.d.ts +19 -0
- package/dist/components/KeyHints.js +23 -0
- package/dist/components/MarketplaceDetail.d.ts +15 -0
- package/dist/components/MarketplaceDetail.js +39 -0
- package/dist/components/MarketplaceList.d.ts +16 -0
- package/dist/components/MarketplaceList.js +32 -0
- package/dist/components/PluginDetail.d.ts +15 -0
- package/dist/components/PluginDetail.js +52 -0
- package/dist/components/PluginList.d.ts +19 -0
- package/dist/components/PluginList.js +54 -0
- package/dist/components/SearchInput.d.ts +16 -0
- package/dist/components/SearchInput.js +14 -0
- package/dist/components/SortDropdown.d.ts +21 -0
- package/dist/components/SortDropdown.js +29 -0
- package/dist/components/StatusIcon.d.ts +20 -0
- package/dist/components/StatusIcon.js +25 -0
- package/dist/components/TabBar.d.ts +24 -0
- package/dist/components/TabBar.js +38 -0
- package/dist/services/fileService.d.ts +41 -0
- package/dist/services/fileService.js +104 -0
- package/dist/services/pluginActionsService.d.ts +21 -0
- package/dist/services/pluginActionsService.js +65 -0
- package/dist/services/pluginService.d.ts +66 -0
- package/dist/services/pluginService.js +188 -0
- package/dist/services/settingsService.d.ts +82 -0
- package/dist/services/settingsService.js +117 -0
- package/dist/tabs/DiscoverTab.d.ts +26 -0
- package/dist/tabs/DiscoverTab.js +25 -0
- package/dist/tabs/ErrorsTab.d.ts +16 -0
- package/dist/tabs/ErrorsTab.js +39 -0
- package/dist/tabs/InstalledTab.d.ts +16 -0
- package/dist/tabs/InstalledTab.js +24 -0
- package/dist/tabs/MarketplacesTab.d.ts +16 -0
- package/dist/tabs/MarketplacesTab.js +21 -0
- package/dist/types/index.d.ts +250 -0
- package/dist/types/index.js +5 -0
- package/dist/utils/paths.d.ts +40 -0
- package/dist/utils/paths.js +50 -0
- package/package.json +60 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Settings service for managing plugin enable/disable state
|
|
3
|
+
* Reads and writes to ~/.claude/settings.json
|
|
4
|
+
*/
|
|
5
|
+
import type { Settings } from '../types/index.js';
|
|
6
|
+
/**
|
|
7
|
+
* Read current settings from settings.json
|
|
8
|
+
* @returns Settings object
|
|
9
|
+
* @throws Error if settings.json doesn't exist or is invalid
|
|
10
|
+
* @example
|
|
11
|
+
* const settings = readSettings();
|
|
12
|
+
* console.log(settings.enabledPlugins);
|
|
13
|
+
*/
|
|
14
|
+
export declare function readSettings(): Settings;
|
|
15
|
+
/**
|
|
16
|
+
* Write settings to settings.json
|
|
17
|
+
* @param settings - Settings object to write
|
|
18
|
+
* @example
|
|
19
|
+
* const settings = readSettings();
|
|
20
|
+
* settings.enabledPlugins['my-plugin@marketplace'] = true;
|
|
21
|
+
* writeSettings(settings);
|
|
22
|
+
*/
|
|
23
|
+
export declare function writeSettings(settings: Settings): void;
|
|
24
|
+
/**
|
|
25
|
+
* Get enabled plugins map from settings
|
|
26
|
+
* @returns Record of plugin IDs to enabled state
|
|
27
|
+
* @example
|
|
28
|
+
* const enabled = getEnabledPlugins();
|
|
29
|
+
* // { "context7@claude-plugins-official": true, ... }
|
|
30
|
+
*/
|
|
31
|
+
export declare function getEnabledPlugins(): Record<string, boolean>;
|
|
32
|
+
/**
|
|
33
|
+
* Check if a plugin is enabled
|
|
34
|
+
* @param pluginId - Plugin identifier (e.g., "context7@claude-plugins-official")
|
|
35
|
+
* @returns true if enabled, false if disabled or not found
|
|
36
|
+
* @example
|
|
37
|
+
* isPluginEnabled("context7@claude-plugins-official"); // true/false
|
|
38
|
+
*/
|
|
39
|
+
export declare function isPluginEnabled(pluginId: string): boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Set plugin enabled state
|
|
42
|
+
* @param pluginId - Plugin identifier
|
|
43
|
+
* @param enabled - Whether to enable or disable
|
|
44
|
+
* @example
|
|
45
|
+
* setPluginEnabled("context7@claude-plugins-official", true);
|
|
46
|
+
*/
|
|
47
|
+
export declare function setPluginEnabled(pluginId: string, enabled: boolean): void;
|
|
48
|
+
/**
|
|
49
|
+
* Enable a plugin
|
|
50
|
+
* @param pluginId - Plugin identifier
|
|
51
|
+
* @example
|
|
52
|
+
* enablePlugin("context7@claude-plugins-official");
|
|
53
|
+
*/
|
|
54
|
+
export declare function enablePlugin(pluginId: string): void;
|
|
55
|
+
/**
|
|
56
|
+
* Disable a plugin
|
|
57
|
+
* @param pluginId - Plugin identifier
|
|
58
|
+
* @example
|
|
59
|
+
* disablePlugin("context7@claude-plugins-official");
|
|
60
|
+
*/
|
|
61
|
+
export declare function disablePlugin(pluginId: string): void;
|
|
62
|
+
/**
|
|
63
|
+
* Toggle plugin enabled state
|
|
64
|
+
* @param pluginId - Plugin identifier
|
|
65
|
+
* @returns New enabled state
|
|
66
|
+
* @example
|
|
67
|
+
* const newState = togglePlugin("context7@claude-plugins-official");
|
|
68
|
+
* console.log(`Plugin is now ${newState ? 'enabled' : 'disabled'}`);
|
|
69
|
+
*/
|
|
70
|
+
export declare function togglePlugin(pluginId: string): boolean;
|
|
71
|
+
/**
|
|
72
|
+
* Get plugin statistics from settings
|
|
73
|
+
* @returns Object with counts of enabled/disabled plugins
|
|
74
|
+
* @example
|
|
75
|
+
* const stats = getPluginStats();
|
|
76
|
+
* // { total: 21, enabled: 13, disabled: 8 }
|
|
77
|
+
*/
|
|
78
|
+
export declare function getPluginStats(): {
|
|
79
|
+
total: number;
|
|
80
|
+
enabled: number;
|
|
81
|
+
disabled: number;
|
|
82
|
+
};
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Settings service for managing plugin enable/disable state
|
|
3
|
+
* Reads and writes to ~/.claude/settings.json
|
|
4
|
+
*/
|
|
5
|
+
import { readJsonFile, writeJsonFile } from './fileService.js';
|
|
6
|
+
import { PATHS } from '../utils/paths.js';
|
|
7
|
+
/**
|
|
8
|
+
* Read current settings from settings.json
|
|
9
|
+
* @returns Settings object
|
|
10
|
+
* @throws Error if settings.json doesn't exist or is invalid
|
|
11
|
+
* @example
|
|
12
|
+
* const settings = readSettings();
|
|
13
|
+
* console.log(settings.enabledPlugins);
|
|
14
|
+
*/
|
|
15
|
+
export function readSettings() {
|
|
16
|
+
const settings = readJsonFile(PATHS.settings);
|
|
17
|
+
if (!settings) {
|
|
18
|
+
throw new Error(`settings.json not found at ${PATHS.settings}. Is Claude Code installed?`);
|
|
19
|
+
}
|
|
20
|
+
return settings;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Write settings to settings.json
|
|
24
|
+
* @param settings - Settings object to write
|
|
25
|
+
* @example
|
|
26
|
+
* const settings = readSettings();
|
|
27
|
+
* settings.enabledPlugins['my-plugin@marketplace'] = true;
|
|
28
|
+
* writeSettings(settings);
|
|
29
|
+
*/
|
|
30
|
+
export function writeSettings(settings) {
|
|
31
|
+
writeJsonFile(PATHS.settings, settings);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Get enabled plugins map from settings
|
|
35
|
+
* @returns Record of plugin IDs to enabled state
|
|
36
|
+
* @example
|
|
37
|
+
* const enabled = getEnabledPlugins();
|
|
38
|
+
* // { "context7@claude-plugins-official": true, ... }
|
|
39
|
+
*/
|
|
40
|
+
export function getEnabledPlugins() {
|
|
41
|
+
const settings = readSettings();
|
|
42
|
+
return settings.enabledPlugins ?? {};
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Check if a plugin is enabled
|
|
46
|
+
* @param pluginId - Plugin identifier (e.g., "context7@claude-plugins-official")
|
|
47
|
+
* @returns true if enabled, false if disabled or not found
|
|
48
|
+
* @example
|
|
49
|
+
* isPluginEnabled("context7@claude-plugins-official"); // true/false
|
|
50
|
+
*/
|
|
51
|
+
export function isPluginEnabled(pluginId) {
|
|
52
|
+
const enabled = getEnabledPlugins();
|
|
53
|
+
return enabled[pluginId] ?? false;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Set plugin enabled state
|
|
57
|
+
* @param pluginId - Plugin identifier
|
|
58
|
+
* @param enabled - Whether to enable or disable
|
|
59
|
+
* @example
|
|
60
|
+
* setPluginEnabled("context7@claude-plugins-official", true);
|
|
61
|
+
*/
|
|
62
|
+
export function setPluginEnabled(pluginId, enabled) {
|
|
63
|
+
const settings = readSettings();
|
|
64
|
+
if (!settings.enabledPlugins) {
|
|
65
|
+
settings.enabledPlugins = {};
|
|
66
|
+
}
|
|
67
|
+
settings.enabledPlugins[pluginId] = enabled;
|
|
68
|
+
writeSettings(settings);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Enable a plugin
|
|
72
|
+
* @param pluginId - Plugin identifier
|
|
73
|
+
* @example
|
|
74
|
+
* enablePlugin("context7@claude-plugins-official");
|
|
75
|
+
*/
|
|
76
|
+
export function enablePlugin(pluginId) {
|
|
77
|
+
setPluginEnabled(pluginId, true);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Disable a plugin
|
|
81
|
+
* @param pluginId - Plugin identifier
|
|
82
|
+
* @example
|
|
83
|
+
* disablePlugin("context7@claude-plugins-official");
|
|
84
|
+
*/
|
|
85
|
+
export function disablePlugin(pluginId) {
|
|
86
|
+
setPluginEnabled(pluginId, false);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Toggle plugin enabled state
|
|
90
|
+
* @param pluginId - Plugin identifier
|
|
91
|
+
* @returns New enabled state
|
|
92
|
+
* @example
|
|
93
|
+
* const newState = togglePlugin("context7@claude-plugins-official");
|
|
94
|
+
* console.log(`Plugin is now ${newState ? 'enabled' : 'disabled'}`);
|
|
95
|
+
*/
|
|
96
|
+
export function togglePlugin(pluginId) {
|
|
97
|
+
const currentState = isPluginEnabled(pluginId);
|
|
98
|
+
setPluginEnabled(pluginId, !currentState);
|
|
99
|
+
return !currentState;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Get plugin statistics from settings
|
|
103
|
+
* @returns Object with counts of enabled/disabled plugins
|
|
104
|
+
* @example
|
|
105
|
+
* const stats = getPluginStats();
|
|
106
|
+
* // { total: 21, enabled: 13, disabled: 8 }
|
|
107
|
+
*/
|
|
108
|
+
export function getPluginStats() {
|
|
109
|
+
const enabled = getEnabledPlugins();
|
|
110
|
+
const entries = Object.entries(enabled);
|
|
111
|
+
const enabledCount = entries.filter(([, v]) => v === true).length;
|
|
112
|
+
return {
|
|
113
|
+
total: entries.length,
|
|
114
|
+
enabled: enabledCount,
|
|
115
|
+
disabled: entries.length - enabledCount,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DiscoverTab component
|
|
3
|
+
* Browse all available plugins from all marketplaces
|
|
4
|
+
*/
|
|
5
|
+
import type { Plugin, AppState } from '../types/index.js';
|
|
6
|
+
interface DiscoverTabProps {
|
|
7
|
+
plugins: Plugin[];
|
|
8
|
+
selectedIndex: number;
|
|
9
|
+
searchQuery: string;
|
|
10
|
+
sortBy: AppState['sortBy'];
|
|
11
|
+
sortOrder: AppState['sortOrder'];
|
|
12
|
+
isSearchMode?: boolean;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Discover tab - browse all plugins
|
|
16
|
+
* @example
|
|
17
|
+
* <DiscoverTab
|
|
18
|
+
* plugins={filteredPlugins}
|
|
19
|
+
* selectedIndex={state.selectedIndex}
|
|
20
|
+
* searchQuery={state.searchQuery}
|
|
21
|
+
* sortBy={state.sortBy}
|
|
22
|
+
* sortOrder={state.sortOrder}
|
|
23
|
+
* />
|
|
24
|
+
*/
|
|
25
|
+
export default function DiscoverTab({ plugins, selectedIndex, searchQuery, sortBy, sortOrder, isSearchMode, }: DiscoverTabProps): import("react/jsx-runtime").JSX.Element;
|
|
26
|
+
export {};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* DiscoverTab component
|
|
4
|
+
* Browse all available plugins from all marketplaces
|
|
5
|
+
*/
|
|
6
|
+
import { Box, Text } from 'ink';
|
|
7
|
+
import PluginList from '../components/PluginList.js';
|
|
8
|
+
import PluginDetail from '../components/PluginDetail.js';
|
|
9
|
+
import SearchInput from '../components/SearchInput.js';
|
|
10
|
+
import SortDropdown from '../components/SortDropdown.js';
|
|
11
|
+
/**
|
|
12
|
+
* Discover tab - browse all plugins
|
|
13
|
+
* @example
|
|
14
|
+
* <DiscoverTab
|
|
15
|
+
* plugins={filteredPlugins}
|
|
16
|
+
* selectedIndex={state.selectedIndex}
|
|
17
|
+
* searchQuery={state.searchQuery}
|
|
18
|
+
* sortBy={state.sortBy}
|
|
19
|
+
* sortOrder={state.sortOrder}
|
|
20
|
+
* />
|
|
21
|
+
*/
|
|
22
|
+
export default function DiscoverTab({ plugins, selectedIndex, searchQuery, sortBy, sortOrder, isSearchMode = false, }) {
|
|
23
|
+
const selectedPlugin = plugins[selectedIndex] ?? null;
|
|
24
|
+
return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsxs(Box, { marginBottom: 1, gap: 2, children: [_jsxs(Text, { bold: true, children: ["Discover plugins (", plugins.length > 0 ? `${selectedIndex + 1}/${plugins.length}` : '0', ")"] }), _jsx(Box, { flexGrow: 1 }), _jsx(SortDropdown, { sortBy: sortBy, sortOrder: sortOrder })] }), _jsx(Box, { marginBottom: 1, children: _jsx(SearchInput, { query: searchQuery, isActive: isSearchMode, placeholder: "Type to search..." }) }), _jsxs(Box, { flexGrow: 1, children: [_jsx(Box, { width: "50%", flexDirection: "column", children: _jsx(PluginList, { plugins: plugins, selectedIndex: selectedIndex, visibleCount: 12 }) }), _jsx(Box, { width: "50%", flexDirection: "column", children: _jsx(PluginDetail, { plugin: selectedPlugin }) })] })] }));
|
|
25
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ErrorsTab component
|
|
3
|
+
* Display plugin-related errors and issues
|
|
4
|
+
*/
|
|
5
|
+
import type { PluginError } from '../types/index.js';
|
|
6
|
+
interface ErrorsTabProps {
|
|
7
|
+
errors: PluginError[];
|
|
8
|
+
selectedIndex: number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Errors tab - display plugin errors
|
|
12
|
+
* @example
|
|
13
|
+
* <ErrorsTab errors={errors} selectedIndex={0} />
|
|
14
|
+
*/
|
|
15
|
+
export default function ErrorsTab({ errors, selectedIndex }: ErrorsTabProps): import("react/jsx-runtime").JSX.Element;
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* ErrorsTab component
|
|
4
|
+
* Display plugin-related errors and issues
|
|
5
|
+
*/
|
|
6
|
+
import { Box, Text } from 'ink';
|
|
7
|
+
/**
|
|
8
|
+
* Errors tab - display plugin errors
|
|
9
|
+
* @example
|
|
10
|
+
* <ErrorsTab errors={errors} selectedIndex={0} />
|
|
11
|
+
*/
|
|
12
|
+
export default function ErrorsTab({ errors, selectedIndex }) {
|
|
13
|
+
if (errors.length === 0) {
|
|
14
|
+
return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, children: "Errors (0)" }) }), _jsxs(Box, { flexDirection: "column", padding: 2, borderStyle: "round", borderColor: "green", children: [_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: "green", children: "\u2713" }), _jsx(Text, { bold: true, color: "green", children: "No errors to display" })] }), _jsx(Text, { dimColor: true, children: "All plugins are working correctly" })] })] }));
|
|
15
|
+
}
|
|
16
|
+
const selectedError = errors[selectedIndex] ?? null;
|
|
17
|
+
return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsx(Box, { marginBottom: 1, gap: 2, children: _jsxs(Text, { bold: true, color: "red", children: ["Errors (", errors.length > 0 ? `${selectedIndex + 1}/${errors.length}` : '0', ")"] }) }), _jsxs(Box, { flexGrow: 1, children: [_jsx(Box, { width: "50%", flexDirection: "column", children: errors.map((error, index) => {
|
|
18
|
+
const isSelected = index === selectedIndex;
|
|
19
|
+
return (_jsxs(Box, { paddingX: 1, children: [_jsx(Box, { width: 2, children: isSelected ? (_jsx(Text, { color: "cyan", children: '>' })) : (_jsx(Text, { children: " " })) }), _jsx(Box, { width: 2, children: _jsx(Text, { color: "red", children: "\u2717" }) }), _jsxs(Box, { flexGrow: 1, flexDirection: "column", children: [_jsx(Text, { bold: true, color: isSelected ? 'cyan' : 'white', children: error.pluginId }), _jsx(Text, { dimColor: true, wrap: "truncate", children: error.message })] })] }, `${error.pluginId}-${index}`));
|
|
20
|
+
}) }), _jsx(Box, { width: "50%", flexDirection: "column", children: selectedError ? (_jsxs(Box, { flexDirection: "column", padding: 1, borderStyle: "round", borderColor: "red", children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, color: "red", children: selectedError.pluginId }) }), _jsxs(Box, { flexDirection: "column", gap: 0, children: [_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: "gray", children: "Type:" }), _jsx(Text, { children: selectedError.type })] }), _jsxs(Box, { gap: 1, children: [_jsx(Text, { color: "gray", children: "Time:" }), _jsx(Text, { children: formatDate(selectedError.timestamp) })] })] }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { color: "gray", children: "Message:" }), _jsx(Text, { wrap: "wrap", children: selectedError.message })] }), selectedError.details && (_jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { color: "gray", children: "Details:" }), _jsx(Text, { dimColor: true, wrap: "wrap", children: selectedError.details })] }))] })) : (_jsx(Box, { flexDirection: "column", padding: 1, borderStyle: "round", borderColor: "gray", children: _jsx(Text, { dimColor: true, children: "Select an error to view details" }) })) })] })] }));
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Format ISO date string to readable format
|
|
24
|
+
*/
|
|
25
|
+
function formatDate(isoString) {
|
|
26
|
+
try {
|
|
27
|
+
const date = new Date(isoString);
|
|
28
|
+
return date.toLocaleString('en-US', {
|
|
29
|
+
year: 'numeric',
|
|
30
|
+
month: 'short',
|
|
31
|
+
day: 'numeric',
|
|
32
|
+
hour: '2-digit',
|
|
33
|
+
minute: '2-digit',
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return isoString;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* InstalledTab component
|
|
3
|
+
* View and manage installed plugins
|
|
4
|
+
*/
|
|
5
|
+
import type { Plugin } from '../types/index.js';
|
|
6
|
+
interface InstalledTabProps {
|
|
7
|
+
plugins: Plugin[];
|
|
8
|
+
selectedIndex: number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Installed tab - manage installed plugins
|
|
12
|
+
* @example
|
|
13
|
+
* <InstalledTab plugins={installedPlugins} selectedIndex={0} />
|
|
14
|
+
*/
|
|
15
|
+
export default function InstalledTab({ plugins, selectedIndex, }: InstalledTabProps): import("react/jsx-runtime").JSX.Element;
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* InstalledTab component
|
|
4
|
+
* View and manage installed plugins
|
|
5
|
+
*/
|
|
6
|
+
import { Box, Text } from 'ink';
|
|
7
|
+
import PluginList from '../components/PluginList.js';
|
|
8
|
+
import PluginDetail from '../components/PluginDetail.js';
|
|
9
|
+
/**
|
|
10
|
+
* Installed tab - manage installed plugins
|
|
11
|
+
* @example
|
|
12
|
+
* <InstalledTab plugins={installedPlugins} selectedIndex={0} />
|
|
13
|
+
*/
|
|
14
|
+
export default function InstalledTab({ plugins, selectedIndex, }) {
|
|
15
|
+
// Filter to installed plugins only
|
|
16
|
+
const installedPlugins = plugins.filter((p) => p.isInstalled);
|
|
17
|
+
const selectedPlugin = installedPlugins[selectedIndex] ?? null;
|
|
18
|
+
// Count enabled/disabled
|
|
19
|
+
const enabledCount = installedPlugins.filter((p) => p.isEnabled).length;
|
|
20
|
+
const disabledCount = installedPlugins.length - enabledCount;
|
|
21
|
+
return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsxs(Box, { marginBottom: 1, gap: 2, children: [_jsxs(Text, { bold: true, children: ["Installed plugins (", installedPlugins.length > 0
|
|
22
|
+
? `${selectedIndex + 1}/${installedPlugins.length}`
|
|
23
|
+
: '0', ")"] }), _jsx(Box, { flexGrow: 1 }), _jsxs(Box, { gap: 2, children: [_jsxs(Text, { color: "green", children: ["\u25CF ", enabledCount, " enabled"] }), _jsxs(Text, { color: "yellow", children: ["\u25D0 ", disabledCount, " disabled"] })] })] }), _jsxs(Box, { flexGrow: 1, children: [_jsx(Box, { width: "50%", flexDirection: "column", children: installedPlugins.length === 0 ? (_jsxs(Box, { padding: 1, flexDirection: "column", children: [_jsx(Text, { color: "gray", children: "No plugins installed" }), _jsxs(Text, { dimColor: true, children: ["Use the Discover tab or", ' ', _jsx(Text, { color: "white", children: "/plugin install" }), " in Claude Code"] })] })) : (_jsx(PluginList, { plugins: installedPlugins, selectedIndex: selectedIndex, visibleCount: 12 })) }), _jsx(Box, { width: "50%", flexDirection: "column", children: _jsx(PluginDetail, { plugin: selectedPlugin }) })] })] }));
|
|
24
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MarketplacesTab component
|
|
3
|
+
* View and manage marketplace sources
|
|
4
|
+
*/
|
|
5
|
+
import type { Marketplace } from '../types/index.js';
|
|
6
|
+
interface MarketplacesTabProps {
|
|
7
|
+
marketplaces: Marketplace[];
|
|
8
|
+
selectedIndex: number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Marketplaces tab - manage plugin sources
|
|
12
|
+
* @example
|
|
13
|
+
* <MarketplacesTab marketplaces={marketplaces} selectedIndex={0} />
|
|
14
|
+
*/
|
|
15
|
+
export default function MarketplacesTab({ marketplaces, selectedIndex, }: MarketplacesTabProps): import("react/jsx-runtime").JSX.Element;
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* MarketplacesTab component
|
|
4
|
+
* View and manage marketplace sources
|
|
5
|
+
*/
|
|
6
|
+
import { Box, Text } from 'ink';
|
|
7
|
+
import MarketplaceList from '../components/MarketplaceList.js';
|
|
8
|
+
import MarketplaceDetail from '../components/MarketplaceDetail.js';
|
|
9
|
+
/**
|
|
10
|
+
* Marketplaces tab - manage plugin sources
|
|
11
|
+
* @example
|
|
12
|
+
* <MarketplacesTab marketplaces={marketplaces} selectedIndex={0} />
|
|
13
|
+
*/
|
|
14
|
+
export default function MarketplacesTab({ marketplaces, selectedIndex, }) {
|
|
15
|
+
const selectedMarketplace = marketplaces[selectedIndex] ?? null;
|
|
16
|
+
// Count total plugins across all marketplaces
|
|
17
|
+
const totalPlugins = marketplaces.reduce((sum, m) => sum + (m.pluginCount || 0), 0);
|
|
18
|
+
return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsxs(Box, { marginBottom: 1, gap: 2, children: [_jsxs(Text, { bold: true, children: ["Marketplaces (", marketplaces.length > 0
|
|
19
|
+
? `${selectedIndex + 1}/${marketplaces.length}`
|
|
20
|
+
: '0', ")"] }), _jsx(Box, { flexGrow: 1 }), _jsxs(Text, { color: "gray", children: [totalPlugins, " total plugins"] })] }), _jsxs(Box, { flexGrow: 1, children: [_jsx(Box, { width: "50%", flexDirection: "column", children: marketplaces.length === 0 ? (_jsxs(Box, { padding: 1, flexDirection: "column", children: [_jsx(Text, { color: "gray", children: "No marketplaces found" }), _jsxs(Text, { dimColor: true, children: ["Add marketplaces with", ' ', _jsx(Text, { color: "white", children: "/plugin add-marketplace" })] })] })) : (_jsx(MarketplaceList, { marketplaces: marketplaces, selectedIndex: selectedIndex })) }), _jsx(Box, { width: "50%", flexDirection: "column", children: _jsx(MarketplaceDetail, { marketplace: selectedMarketplace }) })] })] }));
|
|
21
|
+
}
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TypeScript interfaces for Claude Code Plugin Dashboard
|
|
3
|
+
* These types aggregate data from multiple Claude Code configuration files
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Aggregated plugin data from multiple sources
|
|
7
|
+
* Combines data from settings.json, installed_plugins.json, marketplace.json, etc.
|
|
8
|
+
*/
|
|
9
|
+
export interface Plugin {
|
|
10
|
+
/** Unique identifier: "name@marketplace" */
|
|
11
|
+
id: string;
|
|
12
|
+
/** Plugin name */
|
|
13
|
+
name: string;
|
|
14
|
+
/** Marketplace identifier */
|
|
15
|
+
marketplace: string;
|
|
16
|
+
/** Short description */
|
|
17
|
+
description: string;
|
|
18
|
+
/** Semantic version or commit hash */
|
|
19
|
+
version: string;
|
|
20
|
+
/** Global installation count from install-counts-cache.json */
|
|
21
|
+
installCount: number;
|
|
22
|
+
/** Whether plugin is installed locally */
|
|
23
|
+
isInstalled: boolean;
|
|
24
|
+
/** Whether plugin is enabled in settings.json */
|
|
25
|
+
isEnabled: boolean;
|
|
26
|
+
/** Installation timestamp (if installed) */
|
|
27
|
+
installedAt?: string;
|
|
28
|
+
/** Last update timestamp (if installed) */
|
|
29
|
+
lastUpdated?: string;
|
|
30
|
+
/** Plugin category */
|
|
31
|
+
category?: string;
|
|
32
|
+
/** Searchable tags */
|
|
33
|
+
tags?: string[];
|
|
34
|
+
/** Author information */
|
|
35
|
+
author?: {
|
|
36
|
+
name: string;
|
|
37
|
+
email?: string;
|
|
38
|
+
};
|
|
39
|
+
/** Homepage URL */
|
|
40
|
+
homepage?: string;
|
|
41
|
+
/** Whether this is a local development plugin */
|
|
42
|
+
isLocal?: boolean;
|
|
43
|
+
/** Git commit SHA (if installed) */
|
|
44
|
+
gitCommitSha?: string;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Raw installed plugin data from installed_plugins.json
|
|
48
|
+
*/
|
|
49
|
+
export interface InstalledPluginEntry {
|
|
50
|
+
scope: 'user' | 'project';
|
|
51
|
+
installPath: string;
|
|
52
|
+
version: string;
|
|
53
|
+
installedAt: string;
|
|
54
|
+
lastUpdated: string;
|
|
55
|
+
gitCommitSha: string;
|
|
56
|
+
isLocal: boolean;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Structure of installed_plugins.json file
|
|
60
|
+
*/
|
|
61
|
+
export interface InstalledPluginsFile {
|
|
62
|
+
version: number;
|
|
63
|
+
plugins: Record<string, InstalledPluginEntry[]>;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Marketplace configuration from known_marketplaces.json
|
|
67
|
+
*/
|
|
68
|
+
export interface Marketplace {
|
|
69
|
+
id: string;
|
|
70
|
+
name: string;
|
|
71
|
+
source: {
|
|
72
|
+
source: 'git' | 'github';
|
|
73
|
+
url?: string;
|
|
74
|
+
repo?: string;
|
|
75
|
+
};
|
|
76
|
+
installLocation: string;
|
|
77
|
+
lastUpdated: string;
|
|
78
|
+
pluginCount?: number;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Structure of known_marketplaces.json file
|
|
82
|
+
*/
|
|
83
|
+
export interface KnownMarketplacesFile {
|
|
84
|
+
[marketplaceId: string]: {
|
|
85
|
+
source: {
|
|
86
|
+
source: 'git' | 'github';
|
|
87
|
+
url?: string;
|
|
88
|
+
repo?: string;
|
|
89
|
+
};
|
|
90
|
+
installLocation: string;
|
|
91
|
+
lastUpdated: string;
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Plugin entry in marketplace.json
|
|
96
|
+
*/
|
|
97
|
+
export interface MarketplacePluginEntry {
|
|
98
|
+
name: string;
|
|
99
|
+
description: string;
|
|
100
|
+
version?: string;
|
|
101
|
+
author?: {
|
|
102
|
+
name: string;
|
|
103
|
+
email?: string;
|
|
104
|
+
};
|
|
105
|
+
category?: string;
|
|
106
|
+
homepage?: string;
|
|
107
|
+
tags?: string[];
|
|
108
|
+
keywords?: string[];
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Structure of marketplace.json file
|
|
112
|
+
*/
|
|
113
|
+
export interface MarketplaceFile {
|
|
114
|
+
$schema?: string;
|
|
115
|
+
name: string;
|
|
116
|
+
description?: string;
|
|
117
|
+
owner?: {
|
|
118
|
+
name: string;
|
|
119
|
+
email?: string;
|
|
120
|
+
};
|
|
121
|
+
plugins: MarketplacePluginEntry[];
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Install count entry from install-counts-cache.json
|
|
125
|
+
*/
|
|
126
|
+
export interface InstallCount {
|
|
127
|
+
plugin: string;
|
|
128
|
+
unique_installs: number;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Structure of install-counts-cache.json file
|
|
132
|
+
*/
|
|
133
|
+
export interface InstallCountsFile {
|
|
134
|
+
version: number;
|
|
135
|
+
fetchedAt: string;
|
|
136
|
+
counts: InstallCount[];
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Plugin error entry
|
|
140
|
+
*/
|
|
141
|
+
export interface PluginError {
|
|
142
|
+
pluginId: string;
|
|
143
|
+
type: 'installation' | 'runtime' | 'config';
|
|
144
|
+
message: string;
|
|
145
|
+
timestamp: string;
|
|
146
|
+
details?: string;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Settings.json structure (partial - only plugin-related fields)
|
|
150
|
+
*/
|
|
151
|
+
export interface Settings {
|
|
152
|
+
enabledPlugins?: Record<string, boolean>;
|
|
153
|
+
[key: string]: unknown;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Application state for useReducer
|
|
157
|
+
*/
|
|
158
|
+
export interface AppState {
|
|
159
|
+
/** Current active tab */
|
|
160
|
+
activeTab: 'discover' | 'installed' | 'marketplaces' | 'errors';
|
|
161
|
+
/** All plugins from all marketplaces */
|
|
162
|
+
plugins: Plugin[];
|
|
163
|
+
/** All marketplaces */
|
|
164
|
+
marketplaces: Marketplace[];
|
|
165
|
+
/** Plugin errors */
|
|
166
|
+
errors: PluginError[];
|
|
167
|
+
/** Currently selected item index in the list */
|
|
168
|
+
selectedIndex: number;
|
|
169
|
+
/** Search/filter query */
|
|
170
|
+
searchQuery: string;
|
|
171
|
+
/** Sort field */
|
|
172
|
+
sortBy: 'installs' | 'name' | 'date';
|
|
173
|
+
/** Sort direction */
|
|
174
|
+
sortOrder: 'asc' | 'desc';
|
|
175
|
+
/** Loading state */
|
|
176
|
+
loading: boolean;
|
|
177
|
+
/** Error message */
|
|
178
|
+
error: string | null;
|
|
179
|
+
/** Status message */
|
|
180
|
+
message: string | null;
|
|
181
|
+
/** Current async operation (install/uninstall) */
|
|
182
|
+
operation: 'idle' | 'installing' | 'uninstalling';
|
|
183
|
+
/** Plugin ID being operated on */
|
|
184
|
+
operationPluginId: string | null;
|
|
185
|
+
/** Whether confirmation dialog is showing */
|
|
186
|
+
confirmUninstall: boolean;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Action types for useReducer
|
|
190
|
+
*/
|
|
191
|
+
export type Action = {
|
|
192
|
+
type: 'SET_TAB';
|
|
193
|
+
payload: AppState['activeTab'];
|
|
194
|
+
} | {
|
|
195
|
+
type: 'SET_PLUGINS';
|
|
196
|
+
payload: Plugin[];
|
|
197
|
+
} | {
|
|
198
|
+
type: 'SET_MARKETPLACES';
|
|
199
|
+
payload: Marketplace[];
|
|
200
|
+
} | {
|
|
201
|
+
type: 'SET_ERRORS';
|
|
202
|
+
payload: PluginError[];
|
|
203
|
+
} | {
|
|
204
|
+
type: 'SET_SELECTED_INDEX';
|
|
205
|
+
payload: number;
|
|
206
|
+
} | {
|
|
207
|
+
type: 'MOVE_SELECTION';
|
|
208
|
+
payload: 'up' | 'down';
|
|
209
|
+
} | {
|
|
210
|
+
type: 'SET_SEARCH_QUERY';
|
|
211
|
+
payload: string;
|
|
212
|
+
} | {
|
|
213
|
+
type: 'SET_SORT';
|
|
214
|
+
payload: {
|
|
215
|
+
by: AppState['sortBy'];
|
|
216
|
+
order: AppState['sortOrder'];
|
|
217
|
+
};
|
|
218
|
+
} | {
|
|
219
|
+
type: 'TOGGLE_PLUGIN_ENABLED';
|
|
220
|
+
payload: string;
|
|
221
|
+
} | {
|
|
222
|
+
type: 'UPDATE_PLUGIN';
|
|
223
|
+
payload: Plugin;
|
|
224
|
+
} | {
|
|
225
|
+
type: 'SET_LOADING';
|
|
226
|
+
payload: boolean;
|
|
227
|
+
} | {
|
|
228
|
+
type: 'SET_ERROR';
|
|
229
|
+
payload: string | null;
|
|
230
|
+
} | {
|
|
231
|
+
type: 'SET_MESSAGE';
|
|
232
|
+
payload: string | null;
|
|
233
|
+
} | {
|
|
234
|
+
type: 'NEXT_TAB';
|
|
235
|
+
} | {
|
|
236
|
+
type: 'PREV_TAB';
|
|
237
|
+
} | {
|
|
238
|
+
type: 'START_OPERATION';
|
|
239
|
+
payload: {
|
|
240
|
+
operation: 'installing' | 'uninstalling';
|
|
241
|
+
pluginId: string;
|
|
242
|
+
};
|
|
243
|
+
} | {
|
|
244
|
+
type: 'END_OPERATION';
|
|
245
|
+
} | {
|
|
246
|
+
type: 'SHOW_CONFIRM_UNINSTALL';
|
|
247
|
+
payload: string;
|
|
248
|
+
} | {
|
|
249
|
+
type: 'HIDE_CONFIRM_UNINSTALL';
|
|
250
|
+
};
|