@laststance/claude-plugin-dashboard 0.3.0 → 0.3.2

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.
Files changed (47) hide show
  1. package/README.md +1 -0
  2. package/dist/app.js +346 -211
  3. package/dist/cli.js +3 -1
  4. package/dist/components/ComponentBadges.d.ts +0 -9
  5. package/dist/components/ComponentBadges.js +0 -33
  6. package/dist/components/ComponentDetail.d.ts +32 -0
  7. package/dist/components/ComponentDetail.js +106 -0
  8. package/dist/components/ComponentList.d.ts +36 -2
  9. package/dist/components/ComponentList.js +105 -11
  10. package/dist/components/HelpOverlay.js +1 -0
  11. package/dist/components/KeyHints.d.ts +1 -0
  12. package/dist/components/KeyHints.js +8 -1
  13. package/dist/components/PluginDetail.d.ts +16 -3
  14. package/dist/components/PluginDetail.js +29 -3
  15. package/dist/services/componentService.d.ts +10 -42
  16. package/dist/services/componentService.js +19 -412
  17. package/dist/services/components/hookService.d.ts +17 -0
  18. package/dist/services/components/hookService.js +45 -0
  19. package/dist/services/components/index.d.ts +41 -0
  20. package/dist/services/components/index.js +126 -0
  21. package/dist/services/components/markdownService.d.ts +39 -0
  22. package/dist/services/components/markdownService.js +147 -0
  23. package/dist/services/components/serverService.d.ts +28 -0
  24. package/dist/services/components/serverService.js +69 -0
  25. package/dist/services/components/skillService.d.ts +48 -0
  26. package/dist/services/components/skillService.js +164 -0
  27. package/dist/services/components/utils.d.ts +23 -0
  28. package/dist/services/components/utils.js +42 -0
  29. package/dist/services/pluginActionsService.d.ts +31 -2
  30. package/dist/services/pluginActionsService.js +65 -6
  31. package/dist/store/index.d.ts +46 -0
  32. package/dist/store/index.js +47 -0
  33. package/dist/store/slices/marketplaceSlice.d.ts +344 -0
  34. package/dist/store/slices/marketplaceSlice.js +152 -0
  35. package/dist/store/slices/pluginSlice.d.ts +1544 -0
  36. package/dist/store/slices/pluginSlice.js +191 -0
  37. package/dist/store/slices/uiSlice.d.ts +147 -0
  38. package/dist/store/slices/uiSlice.js +126 -0
  39. package/dist/tabs/DiscoverTab.d.ts +8 -2
  40. package/dist/tabs/DiscoverTab.js +2 -2
  41. package/dist/tabs/EnabledTab.d.ts +8 -2
  42. package/dist/tabs/EnabledTab.js +2 -2
  43. package/dist/tabs/ErrorsTab.js +1 -1
  44. package/dist/tabs/InstalledTab.d.ts +8 -2
  45. package/dist/tabs/InstalledTab.js +2 -2
  46. package/dist/types/index.d.ts +47 -4
  47. package/package.json +7 -2
@@ -0,0 +1,191 @@
1
+ /**
2
+ * Plugin state slice for plugin data and operations
3
+ * Handles plugin list, loading state, selection, and operations
4
+ */
5
+ import { createSlice } from '@reduxjs/toolkit';
6
+ import { setTab, nextTab, prevTab } from './uiSlice.js';
7
+ /**
8
+ * Initial plugin state
9
+ */
10
+ const initialState = {
11
+ plugins: [],
12
+ errors: [],
13
+ loading: true,
14
+ error: null,
15
+ selectedIndex: 0,
16
+ operation: 'idle',
17
+ operationPluginId: null,
18
+ confirmUninstall: false,
19
+ confirmUpdateAll: false,
20
+ updateProgress: null,
21
+ selectedComponentIndex: 0,
22
+ };
23
+ /**
24
+ * Plugin slice with reducers for all plugin-related state changes
25
+ */
26
+ export const pluginSlice = createSlice({
27
+ name: 'plugins',
28
+ initialState,
29
+ reducers: {
30
+ /**
31
+ * Set the plugins list
32
+ */
33
+ setPlugins: (state, action) => {
34
+ state.plugins = action.payload;
35
+ state.loading = false;
36
+ },
37
+ /**
38
+ * Set plugin errors
39
+ */
40
+ setErrors: (state, action) => {
41
+ state.errors = action.payload;
42
+ },
43
+ /**
44
+ * Set loading state
45
+ */
46
+ setLoading: (state, action) => {
47
+ state.loading = action.payload;
48
+ },
49
+ /**
50
+ * Set error message
51
+ */
52
+ setError: (state, action) => {
53
+ state.error = action.payload;
54
+ state.loading = false;
55
+ },
56
+ /**
57
+ * Set selected plugin index
58
+ */
59
+ setSelectedIndex: (state, action) => {
60
+ state.selectedIndex = action.payload;
61
+ },
62
+ /**
63
+ * Move selection up or down
64
+ */
65
+ moveSelection: (state, action) => {
66
+ const { direction, maxIndex } = action.payload;
67
+ if (direction === 'up') {
68
+ state.selectedIndex = Math.max(0, state.selectedIndex - 1);
69
+ }
70
+ else {
71
+ state.selectedIndex = Math.min(maxIndex, state.selectedIndex + 1);
72
+ }
73
+ },
74
+ /**
75
+ * Toggle plugin enabled state
76
+ */
77
+ togglePluginEnabled: (state, action) => {
78
+ const pluginId = action.payload;
79
+ const plugin = state.plugins.find((p) => p.id === pluginId);
80
+ if (plugin) {
81
+ plugin.isEnabled = !plugin.isEnabled;
82
+ }
83
+ },
84
+ /**
85
+ * Update a single plugin
86
+ */
87
+ updatePlugin: (state, action) => {
88
+ const index = state.plugins.findIndex((p) => p.id === action.payload.id);
89
+ if (index !== -1) {
90
+ state.plugins[index] = action.payload;
91
+ }
92
+ },
93
+ /**
94
+ * Start a plugin operation (install/uninstall)
95
+ */
96
+ startOperation: (state, action) => {
97
+ state.operation = action.payload.operation;
98
+ state.operationPluginId = action.payload.pluginId;
99
+ },
100
+ /**
101
+ * End the current operation
102
+ */
103
+ endOperation: (state) => {
104
+ state.operation = 'idle';
105
+ state.operationPluginId = null;
106
+ },
107
+ /**
108
+ * Show uninstall confirmation dialog
109
+ */
110
+ showConfirmUninstall: (state, action) => {
111
+ state.confirmUninstall = true;
112
+ state.operationPluginId = action.payload;
113
+ },
114
+ /**
115
+ * Hide uninstall confirmation dialog
116
+ */
117
+ hideConfirmUninstall: (state) => {
118
+ state.confirmUninstall = false;
119
+ state.operationPluginId = null;
120
+ },
121
+ /**
122
+ * Show update all confirmation dialog
123
+ */
124
+ showConfirmUpdateAll: (state) => {
125
+ state.confirmUpdateAll = true;
126
+ },
127
+ /**
128
+ * Hide update all confirmation dialog
129
+ */
130
+ hideConfirmUpdateAll: (state) => {
131
+ state.confirmUpdateAll = false;
132
+ },
133
+ /**
134
+ * Set bulk update progress
135
+ */
136
+ setUpdateProgress: (state, action) => {
137
+ state.updateProgress = action.payload;
138
+ },
139
+ /**
140
+ * Clear bulk update progress
141
+ */
142
+ clearUpdateProgress: (state) => {
143
+ state.updateProgress = null;
144
+ },
145
+ /**
146
+ * Set selected component index for component focus mode
147
+ */
148
+ setComponentIndex: (state, action) => {
149
+ state.selectedComponentIndex = action.payload;
150
+ },
151
+ /**
152
+ * Move component selection up or down
153
+ */
154
+ moveComponentSelection: (state, action) => {
155
+ const { direction, maxIndex } = action.payload;
156
+ if (direction === 'up') {
157
+ state.selectedComponentIndex = Math.max(0, state.selectedComponentIndex - 1);
158
+ }
159
+ else {
160
+ state.selectedComponentIndex = Math.min(maxIndex, state.selectedComponentIndex + 1);
161
+ }
162
+ },
163
+ /**
164
+ * Reset component index when entering component mode
165
+ */
166
+ enterComponentMode: (state) => {
167
+ state.selectedComponentIndex = 0;
168
+ },
169
+ /**
170
+ * Reset component index when exiting component mode
171
+ */
172
+ exitComponentMode: (state) => {
173
+ state.selectedComponentIndex = 0;
174
+ },
175
+ },
176
+ extraReducers: (builder) => {
177
+ // Reset selectedIndex when tab changes
178
+ builder
179
+ .addCase(setTab, (state) => {
180
+ state.selectedIndex = 0;
181
+ })
182
+ .addCase(nextTab, (state) => {
183
+ state.selectedIndex = 0;
184
+ })
185
+ .addCase(prevTab, (state) => {
186
+ state.selectedIndex = 0;
187
+ });
188
+ },
189
+ });
190
+ export const { setPlugins, setErrors, setLoading, setError, setSelectedIndex, moveSelection, togglePluginEnabled, updatePlugin, startOperation, endOperation, showConfirmUninstall, hideConfirmUninstall, showConfirmUpdateAll, hideConfirmUpdateAll, setUpdateProgress, clearUpdateProgress, setComponentIndex, moveComponentSelection, enterComponentMode, exitComponentMode, } = pluginSlice.actions;
191
+ export default pluginSlice.reducer;
@@ -0,0 +1,147 @@
1
+ /**
2
+ * UI state slice for application-wide UI state management
3
+ * Handles tabs, focus zones, dialogs, search, sort, and messages
4
+ */
5
+ import { type PayloadAction } from '@reduxjs/toolkit';
6
+ import type { AppState, FocusZone } from '../../types/index.js';
7
+ /**
8
+ * UI-specific state extracted from AppState
9
+ */
10
+ export interface UiState {
11
+ activeTab: AppState['activeTab'];
12
+ focusZone: FocusZone;
13
+ showHelp: boolean;
14
+ searchQuery: string;
15
+ sortBy: AppState['sortBy'];
16
+ sortOrder: AppState['sortOrder'];
17
+ message: string | null;
18
+ }
19
+ /**
20
+ * Get available focus zones for the current tab
21
+ * Errors tab has no search zone since it doesn't support filtering
22
+ * @param activeTab - The currently active tab
23
+ * @returns Array of available focus zones in navigation order
24
+ */
25
+ export declare function getAvailableZones(activeTab: AppState['activeTab']): FocusZone[];
26
+ /**
27
+ * UI slice with reducers for all UI-related state changes
28
+ */
29
+ export declare const uiSlice: import("@reduxjs/toolkit").Slice<UiState, {
30
+ /**
31
+ * Set the active tab and reset related state
32
+ */
33
+ setTab: (state: {
34
+ activeTab: AppState["activeTab"];
35
+ focusZone: FocusZone;
36
+ showHelp: boolean;
37
+ searchQuery: string;
38
+ sortBy: AppState["sortBy"];
39
+ sortOrder: AppState["sortOrder"];
40
+ message: string | null;
41
+ }, action: PayloadAction<AppState["activeTab"]>) => void;
42
+ /**
43
+ * Navigate to next tab
44
+ */
45
+ nextTab: (state: {
46
+ activeTab: AppState["activeTab"];
47
+ focusZone: FocusZone;
48
+ showHelp: boolean;
49
+ searchQuery: string;
50
+ sortBy: AppState["sortBy"];
51
+ sortOrder: AppState["sortOrder"];
52
+ message: string | null;
53
+ }) => void;
54
+ /**
55
+ * Navigate to previous tab
56
+ */
57
+ prevTab: (state: {
58
+ activeTab: AppState["activeTab"];
59
+ focusZone: FocusZone;
60
+ showHelp: boolean;
61
+ searchQuery: string;
62
+ sortBy: AppState["sortBy"];
63
+ sortOrder: AppState["sortOrder"];
64
+ message: string | null;
65
+ }) => void;
66
+ /**
67
+ * Set the focus zone
68
+ */
69
+ setFocusZone: (state: {
70
+ activeTab: AppState["activeTab"];
71
+ focusZone: FocusZone;
72
+ showHelp: boolean;
73
+ searchQuery: string;
74
+ sortBy: AppState["sortBy"];
75
+ sortOrder: AppState["sortOrder"];
76
+ message: string | null;
77
+ }, action: PayloadAction<FocusZone>) => void;
78
+ /**
79
+ * Toggle help overlay visibility
80
+ */
81
+ toggleHelp: (state: {
82
+ activeTab: AppState["activeTab"];
83
+ focusZone: FocusZone;
84
+ showHelp: boolean;
85
+ searchQuery: string;
86
+ sortBy: AppState["sortBy"];
87
+ sortOrder: AppState["sortOrder"];
88
+ message: string | null;
89
+ }) => void;
90
+ /**
91
+ * Set the search query
92
+ */
93
+ setSearchQuery: (state: {
94
+ activeTab: AppState["activeTab"];
95
+ focusZone: FocusZone;
96
+ showHelp: boolean;
97
+ searchQuery: string;
98
+ sortBy: AppState["sortBy"];
99
+ sortOrder: AppState["sortOrder"];
100
+ message: string | null;
101
+ }, action: PayloadAction<string>) => void;
102
+ /**
103
+ * Set sort configuration
104
+ */
105
+ setSort: (state: {
106
+ activeTab: AppState["activeTab"];
107
+ focusZone: FocusZone;
108
+ showHelp: boolean;
109
+ searchQuery: string;
110
+ sortBy: AppState["sortBy"];
111
+ sortOrder: AppState["sortOrder"];
112
+ message: string | null;
113
+ }, action: PayloadAction<{
114
+ by: AppState["sortBy"];
115
+ order: AppState["sortOrder"];
116
+ }>) => void;
117
+ /**
118
+ * Set a message to display to the user
119
+ */
120
+ setMessage: (state: {
121
+ activeTab: AppState["activeTab"];
122
+ focusZone: FocusZone;
123
+ showHelp: boolean;
124
+ searchQuery: string;
125
+ sortBy: AppState["sortBy"];
126
+ sortOrder: AppState["sortOrder"];
127
+ message: string | null;
128
+ }, action: PayloadAction<string | null>) => void;
129
+ /**
130
+ * Clear the message
131
+ */
132
+ clearMessage: (state: {
133
+ activeTab: AppState["activeTab"];
134
+ focusZone: FocusZone;
135
+ showHelp: boolean;
136
+ searchQuery: string;
137
+ sortBy: AppState["sortBy"];
138
+ sortOrder: AppState["sortOrder"];
139
+ message: string | null;
140
+ }) => void;
141
+ }, "ui", "ui", import("@reduxjs/toolkit").SliceSelectors<UiState>>;
142
+ export declare const setTab: import("@reduxjs/toolkit").ActionCreatorWithPayload<"enabled" | "installed" | "discover" | "marketplaces" | "errors", "ui/setTab">, nextTab: import("@reduxjs/toolkit").ActionCreatorWithoutPayload<"ui/nextTab">, prevTab: import("@reduxjs/toolkit").ActionCreatorWithoutPayload<"ui/prevTab">, setFocusZone: import("@reduxjs/toolkit").ActionCreatorWithPayload<FocusZone, "ui/setFocusZone">, toggleHelp: import("@reduxjs/toolkit").ActionCreatorWithoutPayload<"ui/toggleHelp">, setSearchQuery: import("@reduxjs/toolkit").ActionCreatorWithPayload<string, "ui/setSearchQuery">, setSort: import("@reduxjs/toolkit").ActionCreatorWithPayload<{
143
+ by: AppState["sortBy"];
144
+ order: AppState["sortOrder"];
145
+ }, "ui/setSort">, setMessage: import("@reduxjs/toolkit").ActionCreatorWithPayload<string | null, "ui/setMessage">, clearMessage: import("@reduxjs/toolkit").ActionCreatorWithoutPayload<"ui/clearMessage">;
146
+ declare const _default: import("@reduxjs/toolkit").Reducer<UiState>;
147
+ export default _default;
@@ -0,0 +1,126 @@
1
+ /**
2
+ * UI state slice for application-wide UI state management
3
+ * Handles tabs, focus zones, dialogs, search, sort, and messages
4
+ */
5
+ import { createSlice } from '@reduxjs/toolkit';
6
+ /**
7
+ * Initial UI state
8
+ */
9
+ const initialState = {
10
+ activeTab: 'enabled',
11
+ focusZone: 'list',
12
+ showHelp: false,
13
+ searchQuery: '',
14
+ sortBy: 'installs',
15
+ sortOrder: 'desc',
16
+ message: null,
17
+ };
18
+ /**
19
+ * Get available focus zones for the current tab
20
+ * Errors tab has no search zone since it doesn't support filtering
21
+ * @param activeTab - The currently active tab
22
+ * @returns Array of available focus zones in navigation order
23
+ */
24
+ export function getAvailableZones(activeTab) {
25
+ if (activeTab === 'errors') {
26
+ return ['tabbar', 'list'];
27
+ }
28
+ return ['tabbar', 'search', 'list'];
29
+ }
30
+ /**
31
+ * UI slice with reducers for all UI-related state changes
32
+ */
33
+ export const uiSlice = createSlice({
34
+ name: 'ui',
35
+ initialState,
36
+ reducers: {
37
+ /**
38
+ * Set the active tab and reset related state
39
+ */
40
+ setTab: (state, action) => {
41
+ state.activeTab = action.payload;
42
+ state.focusZone = 'list';
43
+ state.searchQuery = '';
44
+ state.message = null;
45
+ },
46
+ /**
47
+ * Navigate to next tab
48
+ */
49
+ nextTab: (state) => {
50
+ const tabs = [
51
+ 'enabled',
52
+ 'installed',
53
+ 'discover',
54
+ 'marketplaces',
55
+ 'errors',
56
+ ];
57
+ const currentIndex = tabs.indexOf(state.activeTab);
58
+ const nextTab = tabs[(currentIndex + 1) % tabs.length];
59
+ if (nextTab) {
60
+ state.activeTab = nextTab;
61
+ }
62
+ state.focusZone = 'list';
63
+ state.searchQuery = '';
64
+ state.message = null;
65
+ },
66
+ /**
67
+ * Navigate to previous tab
68
+ */
69
+ prevTab: (state) => {
70
+ const tabs = [
71
+ 'enabled',
72
+ 'installed',
73
+ 'discover',
74
+ 'marketplaces',
75
+ 'errors',
76
+ ];
77
+ const currentIndex = tabs.indexOf(state.activeTab);
78
+ const prevTab = tabs[(currentIndex - 1 + tabs.length) % tabs.length];
79
+ if (prevTab) {
80
+ state.activeTab = prevTab;
81
+ }
82
+ state.focusZone = 'list';
83
+ state.searchQuery = '';
84
+ state.message = null;
85
+ },
86
+ /**
87
+ * Set the focus zone
88
+ */
89
+ setFocusZone: (state, action) => {
90
+ state.focusZone = action.payload;
91
+ },
92
+ /**
93
+ * Toggle help overlay visibility
94
+ */
95
+ toggleHelp: (state) => {
96
+ state.showHelp = !state.showHelp;
97
+ },
98
+ /**
99
+ * Set the search query
100
+ */
101
+ setSearchQuery: (state, action) => {
102
+ state.searchQuery = action.payload;
103
+ },
104
+ /**
105
+ * Set sort configuration
106
+ */
107
+ setSort: (state, action) => {
108
+ state.sortBy = action.payload.by;
109
+ state.sortOrder = action.payload.order;
110
+ },
111
+ /**
112
+ * Set a message to display to the user
113
+ */
114
+ setMessage: (state, action) => {
115
+ state.message = action.payload;
116
+ },
117
+ /**
118
+ * Clear the message
119
+ */
120
+ clearMessage: (state) => {
121
+ state.message = null;
122
+ },
123
+ },
124
+ });
125
+ export const { setTab, nextTab, prevTab, setFocusZone, toggleHelp, setSearchQuery, setSort, setMessage, clearMessage, } = uiSlice.actions;
126
+ export default uiSlice.reducer;
@@ -2,7 +2,7 @@
2
2
  * DiscoverTab component
3
3
  * Browse all available plugins from all marketplaces
4
4
  */
5
- import type { Plugin, AppState, FocusZone } from '../types/index.js';
5
+ import type { Plugin, AppState, FocusZone, ComponentDetailedInfo } from '../types/index.js';
6
6
  interface DiscoverTabProps {
7
7
  plugins: Plugin[];
8
8
  selectedIndex: number;
@@ -11,6 +11,12 @@ interface DiscoverTabProps {
11
11
  sortOrder: AppState['sortOrder'];
12
12
  /** Current focus zone for keyboard navigation */
13
13
  focusZone?: FocusZone;
14
+ /** Whether component focus mode is active */
15
+ componentFocusMode?: boolean;
16
+ /** Currently selected component index */
17
+ selectedComponentIndex?: number;
18
+ /** Selected component's detailed info */
19
+ selectedComponentDetail?: ComponentDetailedInfo | null;
14
20
  }
15
21
  /**
16
22
  * Discover tab - browse all plugins
@@ -24,5 +30,5 @@ interface DiscoverTabProps {
24
30
  * focusZone="list"
25
31
  * />
26
32
  */
27
- export default function DiscoverTab({ plugins, selectedIndex, searchQuery, sortBy, sortOrder, focusZone, }: DiscoverTabProps): import("react/jsx-runtime").JSX.Element;
33
+ export default function DiscoverTab({ plugins, selectedIndex, searchQuery, sortBy, sortOrder, focusZone, componentFocusMode, selectedComponentIndex, selectedComponentDetail, }: DiscoverTabProps): import("react/jsx-runtime").JSX.Element;
28
34
  export {};
@@ -20,7 +20,7 @@ import SortDropdown from '../components/SortDropdown.js';
20
20
  * focusZone="list"
21
21
  * />
22
22
  */
23
- export default function DiscoverTab({ plugins, selectedIndex, searchQuery, sortBy, sortOrder, focusZone = 'list', }) {
23
+ export default function DiscoverTab({ plugins, selectedIndex, searchQuery, sortBy, sortOrder, focusZone = 'list', componentFocusMode = false, selectedComponentIndex = 0, selectedComponentDetail = null, }) {
24
24
  const selectedPlugin = plugins[selectedIndex] ?? null;
25
- 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: focusZone === 'search', placeholder: "Type to search..." }) }), _jsxs(Box, { flexGrow: 1, overflow: "hidden", children: [_jsx(Box, { width: "50%", flexDirection: "column", overflow: "hidden", children: _jsx(PluginList, { plugins: plugins, selectedIndex: selectedIndex, visibleCount: 12, isFocused: focusZone === 'list' }) }), _jsx(Box, { width: "50%", flexDirection: "column", overflow: "hidden", children: _jsx(PluginDetail, { plugin: selectedPlugin }, selectedPlugin?.id ?? 'none') })] })] }));
25
+ 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: focusZone === 'search', placeholder: "Type to search..." }) }), _jsxs(Box, { flexGrow: 1, overflow: "hidden", children: [_jsx(Box, { width: "50%", flexDirection: "column", overflow: "hidden", children: _jsx(PluginList, { plugins: plugins, selectedIndex: selectedIndex, visibleCount: 12, isFocused: focusZone === 'list' }) }), _jsx(Box, { width: "50%", flexDirection: "column", overflow: "hidden", children: _jsx(PluginDetail, { plugin: selectedPlugin, componentFocusMode: componentFocusMode, selectedComponentIndex: selectedComponentIndex, selectedComponentDetail: selectedComponentDetail }, selectedPlugin?.id ?? 'none') })] })] }));
26
26
  }
@@ -2,13 +2,19 @@
2
2
  * EnabledTab component
3
3
  * View and manage enabled plugins (installed + enabled)
4
4
  */
5
- import type { Plugin, FocusZone } from '../types/index.js';
5
+ import type { Plugin, FocusZone, ComponentDetailedInfo } from '../types/index.js';
6
6
  interface EnabledTabProps {
7
7
  plugins: Plugin[];
8
8
  selectedIndex: number;
9
9
  searchQuery?: string;
10
10
  /** Current focus zone for keyboard navigation */
11
11
  focusZone?: FocusZone;
12
+ /** Whether component focus mode is active */
13
+ componentFocusMode?: boolean;
14
+ /** Currently selected component index */
15
+ selectedComponentIndex?: number;
16
+ /** Selected component's detailed info */
17
+ selectedComponentDetail?: ComponentDetailedInfo | null;
12
18
  }
13
19
  /**
14
20
  * Enabled tab - view currently active plugins
@@ -20,5 +26,5 @@ interface EnabledTabProps {
20
26
  * @example
21
27
  * <EnabledTab plugins={enabledPlugins} selectedIndex={0} searchQuery="" focusZone="list" />
22
28
  */
23
- export default function EnabledTab({ plugins, selectedIndex, searchQuery, focusZone, }: EnabledTabProps): import("react/jsx-runtime").JSX.Element;
29
+ export default function EnabledTab({ plugins, selectedIndex, searchQuery, focusZone, componentFocusMode, selectedComponentIndex, selectedComponentDetail, }: EnabledTabProps): import("react/jsx-runtime").JSX.Element;
24
30
  export {};
@@ -17,10 +17,10 @@ import SearchInput from '../components/SearchInput.js';
17
17
  * @example
18
18
  * <EnabledTab plugins={enabledPlugins} selectedIndex={0} searchQuery="" focusZone="list" />
19
19
  */
20
- export default function EnabledTab({ plugins, selectedIndex, searchQuery = '', focusZone = 'list', }) {
20
+ export default function EnabledTab({ plugins, selectedIndex, searchQuery = '', focusZone = 'list', componentFocusMode = false, selectedComponentIndex = 0, selectedComponentDetail = null, }) {
21
21
  // Plugins are already filtered by parent, use directly
22
22
  const selectedPlugin = plugins[selectedIndex] ?? null;
23
23
  return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsxs(Box, { marginBottom: 1, gap: 2, children: [_jsxs(Text, { bold: true, children: ["Enabled plugins (", plugins.length > 0 ? `${selectedIndex + 1}/${plugins.length}` : '0', ")"] }), _jsx(Box, { flexGrow: 1 }), _jsx(Text, { dimColor: true, children: "Currently active in Claude Code" })] }), _jsx(Box, { marginBottom: 1, children: _jsx(SearchInput, { query: searchQuery, isActive: focusZone === 'search', placeholder: "Type to search enabled plugins..." }) }), _jsxs(Box, { flexGrow: 1, overflow: "hidden", children: [_jsx(Box, { width: "50%", flexDirection: "column", overflow: "hidden", children: plugins.length === 0 ? (_jsxs(Box, { padding: 1, flexDirection: "column", children: [_jsx(Text, { color: "gray", children: searchQuery ? 'No matching plugins' : 'No enabled plugins' }), _jsx(Text, { dimColor: true, children: searchQuery
24
24
  ? 'Try a different search term'
25
- : 'Enable plugins in the Installed tab or use /plugin enable' })] })) : (_jsx(PluginList, { plugins: plugins, selectedIndex: selectedIndex, visibleCount: 12, isFocused: focusZone === 'list' })) }), _jsx(Box, { width: "50%", flexDirection: "column", overflow: "hidden", children: _jsx(PluginDetail, { plugin: selectedPlugin }, selectedPlugin?.id ?? 'none') })] })] }));
25
+ : 'Enable plugins in the Installed tab or use /plugin enable' })] })) : (_jsx(PluginList, { plugins: plugins, selectedIndex: selectedIndex, visibleCount: 12, isFocused: focusZone === 'list' })) }), _jsx(Box, { width: "50%", flexDirection: "column", overflow: "hidden", children: _jsx(PluginDetail, { plugin: selectedPlugin, componentFocusMode: componentFocusMode, selectedComponentIndex: selectedComponentIndex, selectedComponentDetail: selectedComponentDetail }, selectedPlugin?.id ?? 'none') })] })] }));
26
26
  }
@@ -16,7 +16,7 @@ export default function ErrorsTab({ errors, selectedIndex }) {
16
16
  const selectedError = errors[selectedIndex] ?? null;
17
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
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}`));
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}-${error.type}-${error.timestamp}-${index}`));
20
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
21
  }
22
22
  /**
@@ -2,13 +2,19 @@
2
2
  * InstalledTab component
3
3
  * View and manage installed plugins
4
4
  */
5
- import type { Plugin, FocusZone } from '../types/index.js';
5
+ import type { Plugin, FocusZone, ComponentDetailedInfo } from '../types/index.js';
6
6
  interface InstalledTabProps {
7
7
  plugins: Plugin[];
8
8
  selectedIndex: number;
9
9
  searchQuery?: string;
10
10
  /** Current focus zone for keyboard navigation */
11
11
  focusZone?: FocusZone;
12
+ /** Whether component focus mode is active */
13
+ componentFocusMode?: boolean;
14
+ /** Currently selected component index */
15
+ selectedComponentIndex?: number;
16
+ /** Selected component's detailed info */
17
+ selectedComponentDetail?: ComponentDetailedInfo | null;
12
18
  }
13
19
  /**
14
20
  * Installed tab - manage installed plugins
@@ -19,5 +25,5 @@ interface InstalledTabProps {
19
25
  * @example
20
26
  * <InstalledTab plugins={installedPlugins} selectedIndex={0} searchQuery="" focusZone="list" />
21
27
  */
22
- export default function InstalledTab({ plugins, selectedIndex, searchQuery, focusZone, }: InstalledTabProps): import("react/jsx-runtime").JSX.Element;
28
+ export default function InstalledTab({ plugins, selectedIndex, searchQuery, focusZone, componentFocusMode, selectedComponentIndex, selectedComponentDetail, }: InstalledTabProps): import("react/jsx-runtime").JSX.Element;
23
29
  export {};
@@ -16,7 +16,7 @@ import SearchInput from '../components/SearchInput.js';
16
16
  * @example
17
17
  * <InstalledTab plugins={installedPlugins} selectedIndex={0} searchQuery="" focusZone="list" />
18
18
  */
19
- export default function InstalledTab({ plugins, selectedIndex, searchQuery = '', focusZone = 'list', }) {
19
+ export default function InstalledTab({ plugins, selectedIndex, searchQuery = '', focusZone = 'list', componentFocusMode = false, selectedComponentIndex = 0, selectedComponentDetail = null, }) {
20
20
  // Plugins are already filtered by parent, use directly
21
21
  const selectedPlugin = plugins[selectedIndex] ?? null;
22
22
  // Count enabled/disabled
@@ -24,5 +24,5 @@ export default function InstalledTab({ plugins, selectedIndex, searchQuery = '',
24
24
  const disabledCount = plugins.length - enabledCount;
25
25
  return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsxs(Box, { marginBottom: 1, gap: 2, children: [_jsxs(Text, { bold: true, children: ["Installed plugins (", plugins.length > 0 ? `${selectedIndex + 1}/${plugins.length}` : '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"] })] })] }), _jsx(Box, { marginBottom: 1, children: _jsx(SearchInput, { query: searchQuery, isActive: focusZone === 'search', placeholder: "Type to search installed plugins..." }) }), _jsxs(Box, { flexGrow: 1, overflow: "hidden", children: [_jsx(Box, { width: "50%", flexDirection: "column", overflow: "hidden", children: plugins.length === 0 ? (_jsxs(Box, { padding: 1, flexDirection: "column", children: [_jsx(Text, { color: "gray", children: searchQuery ? 'No matching plugins' : 'No plugins installed' }), _jsx(Text, { dimColor: true, children: searchQuery
26
26
  ? 'Try a different search term'
27
- : 'Use the Discover tab or /plugin install in Claude Code' })] })) : (_jsx(PluginList, { plugins: plugins, selectedIndex: selectedIndex, visibleCount: 12, isFocused: focusZone === 'list' })) }), _jsx(Box, { width: "50%", flexDirection: "column", overflow: "hidden", children: _jsx(PluginDetail, { plugin: selectedPlugin }, selectedPlugin?.id ?? 'none') })] })] }));
27
+ : 'Use the Discover tab or /plugin install in Claude Code' })] })) : (_jsx(PluginList, { plugins: plugins, selectedIndex: selectedIndex, visibleCount: 12, isFocused: focusZone === 'list' })) }), _jsx(Box, { width: "50%", flexDirection: "column", overflow: "hidden", children: _jsx(PluginDetail, { plugin: selectedPlugin, componentFocusMode: componentFocusMode, selectedComponentIndex: selectedComponentIndex, selectedComponentDetail: selectedComponentDetail }, selectedPlugin?.id ?? 'none') })] })] }));
28
28
  }