claudeup 3.7.2 → 3.8.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/package.json +1 -1
- package/src/data/settings-catalog.js +612 -0
- package/src/data/settings-catalog.ts +689 -0
- package/src/services/plugin-manager.js +2 -0
- package/src/services/plugin-manager.ts +3 -0
- package/src/services/profiles.js +161 -0
- package/src/services/profiles.ts +225 -0
- package/src/services/settings-manager.js +108 -0
- package/src/services/settings-manager.ts +140 -0
- package/src/types/index.ts +34 -0
- package/src/ui/App.js +17 -18
- package/src/ui/App.tsx +21 -23
- package/src/ui/components/TabBar.js +8 -8
- package/src/ui/components/TabBar.tsx +14 -19
- package/src/ui/components/layout/ScreenLayout.js +8 -14
- package/src/ui/components/layout/ScreenLayout.tsx +51 -58
- package/src/ui/components/modals/ModalContainer.js +43 -11
- package/src/ui/components/modals/ModalContainer.tsx +44 -12
- package/src/ui/components/modals/SelectModal.js +4 -18
- package/src/ui/components/modals/SelectModal.tsx +10 -21
- package/src/ui/screens/CliToolsScreen.js +2 -2
- package/src/ui/screens/CliToolsScreen.tsx +8 -8
- package/src/ui/screens/EnvVarsScreen.js +248 -116
- package/src/ui/screens/EnvVarsScreen.tsx +419 -184
- package/src/ui/screens/McpRegistryScreen.tsx +18 -6
- package/src/ui/screens/McpScreen.js +1 -1
- package/src/ui/screens/McpScreen.tsx +15 -5
- package/src/ui/screens/ModelSelectorScreen.js +3 -5
- package/src/ui/screens/ModelSelectorScreen.tsx +12 -16
- package/src/ui/screens/PluginsScreen.js +154 -66
- package/src/ui/screens/PluginsScreen.tsx +280 -97
- package/src/ui/screens/ProfilesScreen.js +255 -0
- package/src/ui/screens/ProfilesScreen.tsx +487 -0
- package/src/ui/screens/StatusLineScreen.js +2 -2
- package/src/ui/screens/StatusLineScreen.tsx +10 -12
- package/src/ui/screens/index.js +2 -2
- package/src/ui/screens/index.ts +2 -2
- package/src/ui/state/AppContext.js +2 -1
- package/src/ui/state/AppContext.tsx +2 -0
- package/src/ui/state/reducer.js +63 -19
- package/src/ui/state/reducer.ts +68 -19
- package/src/ui/state/types.ts +33 -14
- package/src/utils/clipboard.js +56 -0
- package/src/utils/clipboard.ts +58 -0
|
@@ -17,12 +17,12 @@ import {
|
|
|
17
17
|
import {
|
|
18
18
|
setMcpEnvVar,
|
|
19
19
|
getMcpEnvVars,
|
|
20
|
+
readSettings,
|
|
20
21
|
saveGlobalInstalledPluginVersion,
|
|
21
22
|
saveLocalInstalledPluginVersion,
|
|
22
23
|
} from "../../services/claude-settings.js";
|
|
23
|
-
import {
|
|
24
|
-
|
|
25
|
-
} from "../../services/plugin-manager.js";
|
|
24
|
+
import { saveProfile } from "../../services/profiles.js";
|
|
25
|
+
import { saveInstalledPluginVersion } from "../../services/plugin-manager.js";
|
|
26
26
|
import {
|
|
27
27
|
installPlugin as cliInstallPlugin,
|
|
28
28
|
uninstallPlugin as cliUninstallPlugin,
|
|
@@ -209,53 +209,87 @@ export function PluginsScreen() {
|
|
|
209
209
|
);
|
|
210
210
|
}, [filteredItems]);
|
|
211
211
|
|
|
212
|
-
// Keyboard handling
|
|
212
|
+
// Keyboard handling — inline search with live filtering
|
|
213
213
|
useKeyboard((event) => {
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
214
|
+
if (state.modal) return;
|
|
215
|
+
|
|
216
|
+
const hasQuery = pluginsState.searchQuery.length > 0;
|
|
217
|
+
|
|
218
|
+
// Escape: always clear search state fully
|
|
219
|
+
if (event.name === "escape") {
|
|
220
|
+
if (hasQuery || isSearchActive) {
|
|
218
221
|
dispatch({ type: "PLUGINS_SET_SEARCH", query: "" });
|
|
219
|
-
} else if (event.name === "enter") {
|
|
220
222
|
dispatch({ type: "SET_SEARCHING", isSearching: false });
|
|
221
|
-
|
|
222
|
-
} else if (event.name === "backspace" || event.name === "delete") {
|
|
223
|
-
dispatch({
|
|
224
|
-
type: "PLUGINS_SET_SEARCH",
|
|
225
|
-
query: pluginsState.searchQuery.slice(0, -1),
|
|
226
|
-
});
|
|
227
|
-
} else if (event.name.length === 1 && !event.ctrl && !event.meta) {
|
|
228
|
-
dispatch({
|
|
229
|
-
type: "PLUGINS_SET_SEARCH",
|
|
230
|
-
query: pluginsState.searchQuery + event.name,
|
|
231
|
-
});
|
|
223
|
+
dispatch({ type: "PLUGINS_SELECT", index: 0 });
|
|
232
224
|
}
|
|
225
|
+
// Don't return — let GlobalKeyHandler handle Escape too (for quit)
|
|
233
226
|
return;
|
|
234
227
|
}
|
|
235
228
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
229
|
+
// Backspace: remove last char from search query
|
|
230
|
+
if (event.name === "backspace" || event.name === "delete") {
|
|
231
|
+
if (hasQuery) {
|
|
232
|
+
const newQuery = pluginsState.searchQuery.slice(0, -1);
|
|
233
|
+
dispatch({ type: "PLUGINS_SET_SEARCH", query: newQuery });
|
|
234
|
+
dispatch({ type: "PLUGINS_SELECT", index: 0 });
|
|
235
|
+
// If query becomes empty, exit search mode
|
|
236
|
+
if (newQuery.length === 0) {
|
|
237
|
+
dispatch({ type: "SET_SEARCHING", isSearching: false });
|
|
238
|
+
}
|
|
239
|
+
}
|
|
241
240
|
return;
|
|
242
241
|
}
|
|
243
242
|
|
|
244
|
-
// Navigation
|
|
243
|
+
// Navigation — always works (even during search)
|
|
245
244
|
if (event.name === "up" || event.name === "k") {
|
|
245
|
+
// 'k' navigates when query is empty, otherwise appends to search
|
|
246
|
+
if (event.name === "k" && (hasQuery || isSearchActive)) {
|
|
247
|
+
dispatch({
|
|
248
|
+
type: "PLUGINS_SET_SEARCH",
|
|
249
|
+
query: pluginsState.searchQuery + event.name,
|
|
250
|
+
});
|
|
251
|
+
dispatch({ type: "PLUGINS_SELECT", index: 0 });
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
246
254
|
const newIndex = Math.max(0, pluginsState.selectedIndex - 1);
|
|
247
255
|
dispatch({ type: "PLUGINS_SELECT", index: newIndex });
|
|
248
|
-
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
if (event.name === "down" || event.name === "j") {
|
|
259
|
+
// 'j' navigates when query is empty, otherwise appends to search
|
|
260
|
+
if (event.name === "j" && (hasQuery || isSearchActive)) {
|
|
261
|
+
dispatch({
|
|
262
|
+
type: "PLUGINS_SET_SEARCH",
|
|
263
|
+
query: pluginsState.searchQuery + event.name,
|
|
264
|
+
});
|
|
265
|
+
dispatch({ type: "PLUGINS_SELECT", index: 0 });
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
249
268
|
const newIndex = Math.min(
|
|
250
269
|
selectableItems.length - 1,
|
|
251
270
|
pluginsState.selectedIndex + 1,
|
|
252
271
|
);
|
|
253
272
|
dispatch({ type: "PLUGINS_SELECT", index: newIndex });
|
|
273
|
+
return;
|
|
254
274
|
}
|
|
255
275
|
|
|
256
|
-
//
|
|
257
|
-
|
|
258
|
-
(
|
|
276
|
+
// Enter — exit search mode (keep filter active) + select/install
|
|
277
|
+
if (event.name === "enter") {
|
|
278
|
+
if (isSearchActive) {
|
|
279
|
+
dispatch({ type: "SET_SEARCHING", isSearching: false });
|
|
280
|
+
// Keep the query — filter stays active, shortcuts resume
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
handleSelect();
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// Collapse/expand marketplace — always works
|
|
288
|
+
if (
|
|
289
|
+
(event.name === "left" ||
|
|
290
|
+
event.name === "right" ||
|
|
291
|
+
event.name === "<" ||
|
|
292
|
+
event.name === ">") &&
|
|
259
293
|
selectableItems[pluginsState.selectedIndex]?.marketplace
|
|
260
294
|
) {
|
|
261
295
|
const item = selectableItems[pluginsState.selectedIndex];
|
|
@@ -265,50 +299,49 @@ export function PluginsScreen() {
|
|
|
265
299
|
name: item.marketplace.name,
|
|
266
300
|
});
|
|
267
301
|
}
|
|
302
|
+
return;
|
|
268
303
|
}
|
|
269
304
|
|
|
270
|
-
//
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
else if (event.name === "t") {
|
|
282
|
-
handleShowTeamConfigHelp();
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
// Scope-specific toggle shortcuts (u/p/l)
|
|
286
|
-
else if (event.name === "u") {
|
|
287
|
-
handleScopeToggle("user");
|
|
288
|
-
} else if (event.name === "p") {
|
|
289
|
-
handleScopeToggle("project");
|
|
290
|
-
} else if (event.name === "l") {
|
|
291
|
-
handleScopeToggle("local");
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
// Update plugin (Shift+U)
|
|
295
|
-
else if (event.name === "U") {
|
|
296
|
-
handleUpdate();
|
|
305
|
+
// When search query is non-empty, printable letters go to the query
|
|
306
|
+
// (shortcuts are suspended while filtering, digits skip to let tab nav work)
|
|
307
|
+
if (hasQuery || isSearchActive) {
|
|
308
|
+
if (event.name.length === 1 && !event.ctrl && !event.meta && !/[0-9]/.test(event.name)) {
|
|
309
|
+
dispatch({
|
|
310
|
+
type: "PLUGINS_SET_SEARCH",
|
|
311
|
+
query: pluginsState.searchQuery + event.name,
|
|
312
|
+
});
|
|
313
|
+
dispatch({ type: "PLUGINS_SELECT", index: 0 });
|
|
314
|
+
}
|
|
315
|
+
return;
|
|
297
316
|
}
|
|
298
317
|
|
|
299
|
-
//
|
|
300
|
-
else if (event.name === "a") {
|
|
301
|
-
handleUpdateAll();
|
|
302
|
-
}
|
|
318
|
+
// When search query is empty: action shortcuts work normally
|
|
303
319
|
|
|
304
|
-
//
|
|
305
|
-
|
|
306
|
-
|
|
320
|
+
// Start explicit search mode with /
|
|
321
|
+
if (event.name === "/") {
|
|
322
|
+
dispatch({ type: "SET_SEARCHING", isSearching: true });
|
|
323
|
+
return;
|
|
307
324
|
}
|
|
308
325
|
|
|
309
|
-
//
|
|
310
|
-
|
|
311
|
-
|
|
326
|
+
// Action shortcuts (only when query is empty)
|
|
327
|
+
if (event.name === "r") handleRefresh();
|
|
328
|
+
else if (event.name === "n") handleShowAddMarketplaceInstructions();
|
|
329
|
+
else if (event.name === "t") handleShowTeamConfigHelp();
|
|
330
|
+
else if (event.name === "u") handleScopeToggle("user");
|
|
331
|
+
else if (event.name === "p") handleScopeToggle("project");
|
|
332
|
+
else if (event.name === "l") handleScopeToggle("local");
|
|
333
|
+
else if (event.name === "U") handleUpdate();
|
|
334
|
+
else if (event.name === "a") handleUpdateAll();
|
|
335
|
+
else if (event.name === "d") handleUninstall();
|
|
336
|
+
else if (event.name === "s") handleSaveAsProfile();
|
|
337
|
+
// Any other printable letter: start inline search (skip digits — used for tab nav)
|
|
338
|
+
else if (event.name.length === 1 && !event.ctrl && !event.meta && !/[0-9]/.test(event.name)) {
|
|
339
|
+
dispatch({ type: "SET_SEARCHING", isSearching: true });
|
|
340
|
+
dispatch({
|
|
341
|
+
type: "PLUGINS_SET_SEARCH",
|
|
342
|
+
query: event.name,
|
|
343
|
+
});
|
|
344
|
+
dispatch({ type: "PLUGINS_SELECT", index: 0 });
|
|
312
345
|
}
|
|
313
346
|
});
|
|
314
347
|
|
|
@@ -491,9 +524,10 @@ export function PluginsScreen() {
|
|
|
491
524
|
const missing = await checkMissingDeps(setup);
|
|
492
525
|
const hasMissing =
|
|
493
526
|
(missing.pip?.length || 0) +
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
527
|
+
(missing.brew?.length || 0) +
|
|
528
|
+
(missing.npm?.length || 0) +
|
|
529
|
+
(missing.cargo?.length || 0) >
|
|
530
|
+
0;
|
|
497
531
|
|
|
498
532
|
if (!hasMissing) return;
|
|
499
533
|
|
|
@@ -502,7 +536,8 @@ export function PluginsScreen() {
|
|
|
502
536
|
if (missing.pip?.length) parts.push(`pip: ${missing.pip.join(", ")}`);
|
|
503
537
|
if (missing.brew?.length) parts.push(`brew: ${missing.brew.join(", ")}`);
|
|
504
538
|
if (missing.npm?.length) parts.push(`npm: ${missing.npm.join(", ")}`);
|
|
505
|
-
if (missing.cargo?.length)
|
|
539
|
+
if (missing.cargo?.length)
|
|
540
|
+
parts.push(`cargo: ${missing.cargo.join(", ")}`);
|
|
506
541
|
|
|
507
542
|
const wantInstall = await modal.confirm(
|
|
508
543
|
"Install Dependencies?",
|
|
@@ -550,7 +585,11 @@ export function PluginsScreen() {
|
|
|
550
585
|
if (scope === "user") {
|
|
551
586
|
await saveGlobalInstalledPluginVersion(pluginId, version);
|
|
552
587
|
} else if (scope === "local") {
|
|
553
|
-
await saveLocalInstalledPluginVersion(
|
|
588
|
+
await saveLocalInstalledPluginVersion(
|
|
589
|
+
pluginId,
|
|
590
|
+
version,
|
|
591
|
+
state.projectPath,
|
|
592
|
+
);
|
|
554
593
|
} else {
|
|
555
594
|
await saveInstalledPluginVersion(pluginId, version, state.projectPath);
|
|
556
595
|
}
|
|
@@ -717,12 +756,17 @@ export function PluginsScreen() {
|
|
|
717
756
|
if (!item || item.type !== "plugin" || !item.plugin?.hasUpdate) return;
|
|
718
757
|
|
|
719
758
|
const plugin = item.plugin;
|
|
720
|
-
const scope: PluginScope =
|
|
759
|
+
const scope: PluginScope =
|
|
760
|
+
pluginsState.scope === "global" ? "user" : "project";
|
|
721
761
|
|
|
722
762
|
modal.loading(`Updating ${plugin.name}...`);
|
|
723
763
|
try {
|
|
724
764
|
await cliUpdatePlugin(plugin.id, scope);
|
|
725
|
-
await saveVersionAfterInstall(
|
|
765
|
+
await saveVersionAfterInstall(
|
|
766
|
+
plugin.id,
|
|
767
|
+
plugin.version || "0.0.0",
|
|
768
|
+
scope,
|
|
769
|
+
);
|
|
726
770
|
modal.hideModal();
|
|
727
771
|
fetchData();
|
|
728
772
|
} catch (error) {
|
|
@@ -737,13 +781,18 @@ export function PluginsScreen() {
|
|
|
737
781
|
const updatable = pluginsState.plugins.data.filter((p) => p.hasUpdate);
|
|
738
782
|
if (updatable.length === 0) return;
|
|
739
783
|
|
|
740
|
-
const scope: PluginScope =
|
|
784
|
+
const scope: PluginScope =
|
|
785
|
+
pluginsState.scope === "global" ? "user" : "project";
|
|
741
786
|
modal.loading(`Updating ${updatable.length} plugin(s)...`);
|
|
742
787
|
|
|
743
788
|
try {
|
|
744
789
|
for (const plugin of updatable) {
|
|
745
790
|
await cliUpdatePlugin(plugin.id, scope);
|
|
746
|
-
await saveVersionAfterInstall(
|
|
791
|
+
await saveVersionAfterInstall(
|
|
792
|
+
plugin.id,
|
|
793
|
+
plugin.version || "0.0.0",
|
|
794
|
+
scope,
|
|
795
|
+
);
|
|
747
796
|
}
|
|
748
797
|
modal.hideModal();
|
|
749
798
|
fetchData();
|
|
@@ -823,6 +872,47 @@ export function PluginsScreen() {
|
|
|
823
872
|
}
|
|
824
873
|
};
|
|
825
874
|
|
|
875
|
+
const handleSaveAsProfile = async () => {
|
|
876
|
+
// Read current enabledPlugins from project settings
|
|
877
|
+
const settings = await readSettings(state.projectPath);
|
|
878
|
+
const enabledPlugins = settings.enabledPlugins ?? {};
|
|
879
|
+
|
|
880
|
+
const name = await modal.input("Save Profile", "Profile name:");
|
|
881
|
+
if (name === null || !name.trim()) return;
|
|
882
|
+
|
|
883
|
+
const scopeChoice = await modal.select(
|
|
884
|
+
"Save to scope",
|
|
885
|
+
"Where should this profile be saved?",
|
|
886
|
+
[
|
|
887
|
+
{
|
|
888
|
+
label: "User — ~/.claude/profiles.json (available everywhere)",
|
|
889
|
+
value: "user",
|
|
890
|
+
},
|
|
891
|
+
{
|
|
892
|
+
label: "Project — .claude/profiles.json (shared with team via git)",
|
|
893
|
+
value: "project",
|
|
894
|
+
},
|
|
895
|
+
],
|
|
896
|
+
);
|
|
897
|
+
if (scopeChoice === null) return;
|
|
898
|
+
|
|
899
|
+
const scope = scopeChoice as "user" | "project";
|
|
900
|
+
|
|
901
|
+
modal.loading("Saving profile...");
|
|
902
|
+
try {
|
|
903
|
+
await saveProfile(name.trim(), enabledPlugins, scope, state.projectPath);
|
|
904
|
+
modal.hideModal();
|
|
905
|
+
await modal.message(
|
|
906
|
+
"Saved",
|
|
907
|
+
`Profile "${name.trim()}" saved.\nPress 6 to manage profiles.`,
|
|
908
|
+
"success",
|
|
909
|
+
);
|
|
910
|
+
} catch (error) {
|
|
911
|
+
modal.hideModal();
|
|
912
|
+
await modal.message("Error", `Failed to save profile: ${error}`, "error");
|
|
913
|
+
}
|
|
914
|
+
};
|
|
915
|
+
|
|
826
916
|
const handleUninstall = async () => {
|
|
827
917
|
const item = selectableItems[pluginsState.selectedIndex];
|
|
828
918
|
if (!item || item.type !== "plugin" || !item.plugin) return;
|
|
@@ -870,7 +960,11 @@ export function PluginsScreen() {
|
|
|
870
960
|
modal.loading(`Uninstalling ${plugin.name}...`);
|
|
871
961
|
|
|
872
962
|
try {
|
|
873
|
-
await cliUninstallPlugin(
|
|
963
|
+
await cliUninstallPlugin(
|
|
964
|
+
plugin.id,
|
|
965
|
+
scopeValue as PluginScope,
|
|
966
|
+
state.projectPath,
|
|
967
|
+
);
|
|
874
968
|
modal.hideModal();
|
|
875
969
|
fetchData();
|
|
876
970
|
} catch (error) {
|
|
@@ -886,7 +980,9 @@ export function PluginsScreen() {
|
|
|
886
980
|
) {
|
|
887
981
|
return (
|
|
888
982
|
<box flexDirection="column" paddingLeft={1} paddingRight={1}>
|
|
889
|
-
<text fg="#7e57c2"
|
|
983
|
+
<text fg="#7e57c2">
|
|
984
|
+
<strong>claudeup Plugins</strong>
|
|
985
|
+
</text>
|
|
890
986
|
<text fg="gray">Loading...</text>
|
|
891
987
|
</box>
|
|
892
988
|
);
|
|
@@ -899,7 +995,9 @@ export function PluginsScreen() {
|
|
|
899
995
|
) {
|
|
900
996
|
return (
|
|
901
997
|
<box flexDirection="column" paddingLeft={1} paddingRight={1}>
|
|
902
|
-
<text fg="#7e57c2"
|
|
998
|
+
<text fg="#7e57c2">
|
|
999
|
+
<strong>claudeup Plugins</strong>
|
|
1000
|
+
</text>
|
|
903
1001
|
<text fg="red">Error loading data</text>
|
|
904
1002
|
</box>
|
|
905
1003
|
);
|
|
@@ -942,7 +1040,13 @@ export function PluginsScreen() {
|
|
|
942
1040
|
? ` (${item.pluginCount})`
|
|
943
1041
|
: "";
|
|
944
1042
|
return (
|
|
945
|
-
<text bg="magenta" fg="white"
|
|
1043
|
+
<text bg="magenta" fg="white">
|
|
1044
|
+
<strong>
|
|
1045
|
+
{" "}
|
|
1046
|
+
{arrow} {mp.displayName}
|
|
1047
|
+
{count}{" "}
|
|
1048
|
+
</strong>
|
|
1049
|
+
</text>
|
|
946
1050
|
);
|
|
947
1051
|
}
|
|
948
1052
|
|
|
@@ -962,7 +1066,10 @@ export function PluginsScreen() {
|
|
|
962
1066
|
let statusIcon = "○";
|
|
963
1067
|
let statusColor = "gray";
|
|
964
1068
|
|
|
965
|
-
if (plugin.
|
|
1069
|
+
if (plugin.isOrphaned) {
|
|
1070
|
+
statusIcon = "x";
|
|
1071
|
+
statusColor = "red";
|
|
1072
|
+
} else if (plugin.enabled) {
|
|
966
1073
|
statusIcon = "●";
|
|
967
1074
|
statusColor = "green";
|
|
968
1075
|
} else if (plugin.installedVersion) {
|
|
@@ -972,7 +1079,9 @@ export function PluginsScreen() {
|
|
|
972
1079
|
|
|
973
1080
|
// Build version string
|
|
974
1081
|
let versionStr = "";
|
|
975
|
-
if (plugin.
|
|
1082
|
+
if (plugin.isOrphaned) {
|
|
1083
|
+
versionStr = " deprecated";
|
|
1084
|
+
} else if (plugin.installedVersion && plugin.installedVersion !== "0.0.0") {
|
|
976
1085
|
versionStr = ` v${plugin.installedVersion}`;
|
|
977
1086
|
if (plugin.hasUpdate && plugin.version) {
|
|
978
1087
|
versionStr += ` → v${plugin.version}`;
|
|
@@ -996,9 +1105,26 @@ export function PluginsScreen() {
|
|
|
996
1105
|
const displayName = segments
|
|
997
1106
|
? segments.map((seg) => seg.text).join("")
|
|
998
1107
|
: plugin.name;
|
|
1108
|
+
|
|
1109
|
+
if (plugin.isOrphaned) {
|
|
1110
|
+
const ver = plugin.installedVersion && plugin.installedVersion !== "0.0.0"
|
|
1111
|
+
? ` v${plugin.installedVersion}` : "";
|
|
1112
|
+
return (
|
|
1113
|
+
<text>
|
|
1114
|
+
<span fg="red">{" "}{statusIcon} </span>
|
|
1115
|
+
<span fg="gray">{displayName}</span>
|
|
1116
|
+
{ver && <span fg="yellow">{ver}</span>}
|
|
1117
|
+
<span fg="red"> deprecated</span>
|
|
1118
|
+
</text>
|
|
1119
|
+
);
|
|
1120
|
+
}
|
|
1121
|
+
|
|
999
1122
|
return (
|
|
1000
1123
|
<text>
|
|
1001
|
-
<span fg={statusColor}>
|
|
1124
|
+
<span fg={statusColor}>
|
|
1125
|
+
{" "}
|
|
1126
|
+
{statusIcon}{" "}
|
|
1127
|
+
</span>
|
|
1002
1128
|
<span>{displayName}</span>
|
|
1003
1129
|
<span fg={plugin.hasUpdate ? "yellow" : "gray"}>{versionStr}</span>
|
|
1004
1130
|
</text>
|
|
@@ -1042,15 +1168,23 @@ export function PluginsScreen() {
|
|
|
1042
1168
|
|
|
1043
1169
|
return (
|
|
1044
1170
|
<box flexDirection="column">
|
|
1045
|
-
<text fg="cyan"
|
|
1171
|
+
<text fg="cyan">
|
|
1172
|
+
<strong>
|
|
1173
|
+
{mp.displayName}
|
|
1174
|
+
{getBadge()}
|
|
1175
|
+
</strong>
|
|
1176
|
+
</text>
|
|
1046
1177
|
<text fg="gray">{mp.description || "No description"}</text>
|
|
1047
1178
|
<text fg={isEnabled ? "green" : "gray"}>
|
|
1048
1179
|
{isEnabled ? "● Added" : "○ Not added"}
|
|
1049
1180
|
</text>
|
|
1050
|
-
<text fg="
|
|
1181
|
+
<text fg="#5c9aff">github.com/{mp.source.repo}</text>
|
|
1051
1182
|
<text>Plugins: {selectedItem.pluginCount || 0}</text>
|
|
1052
1183
|
<box marginTop={1}>
|
|
1053
|
-
<text bg={isEnabled ? "cyan" : "green"} fg="black">
|
|
1184
|
+
<text bg={isEnabled ? "cyan" : "green"} fg="black">
|
|
1185
|
+
{" "}
|
|
1186
|
+
Enter{" "}
|
|
1187
|
+
</text>
|
|
1054
1188
|
<text fg="gray"> {actionHint}</text>
|
|
1055
1189
|
</box>
|
|
1056
1190
|
{isEnabled && (
|
|
@@ -1066,6 +1200,31 @@ export function PluginsScreen() {
|
|
|
1066
1200
|
const plugin = selectedItem.plugin;
|
|
1067
1201
|
const isInstalled = plugin.enabled || plugin.installedVersion;
|
|
1068
1202
|
|
|
1203
|
+
// Orphaned/deprecated plugin
|
|
1204
|
+
if (plugin.isOrphaned) {
|
|
1205
|
+
return (
|
|
1206
|
+
<box flexDirection="column">
|
|
1207
|
+
<box justifyContent="center">
|
|
1208
|
+
<text bg="yellow" fg="black"><strong> {plugin.name} — DEPRECATED </strong></text>
|
|
1209
|
+
</box>
|
|
1210
|
+
<box marginTop={1}>
|
|
1211
|
+
<text fg="yellow">This plugin is no longer in the marketplace.</text>
|
|
1212
|
+
</box>
|
|
1213
|
+
<box marginTop={1}>
|
|
1214
|
+
<text fg="gray">It was removed from the marketplace but still referenced in your settings. Press d to uninstall and clean up.</text>
|
|
1215
|
+
</box>
|
|
1216
|
+
{isInstalled && (
|
|
1217
|
+
<box flexDirection="column" marginTop={2}>
|
|
1218
|
+
<box>
|
|
1219
|
+
<text bg="red" fg="white"> d </text>
|
|
1220
|
+
<text> Uninstall (recommended)</text>
|
|
1221
|
+
</box>
|
|
1222
|
+
</box>
|
|
1223
|
+
)}
|
|
1224
|
+
</box>
|
|
1225
|
+
);
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1069
1228
|
// Build component counts
|
|
1070
1229
|
const components: string[] = [];
|
|
1071
1230
|
if (plugin.agents?.length)
|
|
@@ -1089,7 +1248,13 @@ export function PluginsScreen() {
|
|
|
1089
1248
|
<box flexDirection="column">
|
|
1090
1249
|
{/* Plugin name header - centered */}
|
|
1091
1250
|
<box justifyContent="center">
|
|
1092
|
-
<text bg="magenta" fg="white"
|
|
1251
|
+
<text bg="magenta" fg="white">
|
|
1252
|
+
<strong>
|
|
1253
|
+
{" "}
|
|
1254
|
+
{plugin.name}
|
|
1255
|
+
{plugin.hasUpdate ? " ⬆" : ""}{" "}
|
|
1256
|
+
</strong>
|
|
1257
|
+
</text>
|
|
1093
1258
|
</box>
|
|
1094
1259
|
|
|
1095
1260
|
{/* Status line */}
|
|
@@ -1112,7 +1277,7 @@ export function PluginsScreen() {
|
|
|
1112
1277
|
{showVersion && (
|
|
1113
1278
|
<text>
|
|
1114
1279
|
<span>Version </span>
|
|
1115
|
-
<span fg="
|
|
1280
|
+
<span fg="#5c9aff">v{plugin.version}</span>
|
|
1116
1281
|
{showInstalledVersion &&
|
|
1117
1282
|
plugin.installedVersion !== plugin.version && (
|
|
1118
1283
|
<span> (v{plugin.installedVersion} installed)</span>
|
|
@@ -1141,10 +1306,15 @@ export function PluginsScreen() {
|
|
|
1141
1306
|
{/* Scope Status with shortcuts - each scope has its own color */}
|
|
1142
1307
|
<box flexDirection="column" marginTop={1}>
|
|
1143
1308
|
<text>────────────────────────</text>
|
|
1144
|
-
<text
|
|
1309
|
+
<text>
|
|
1310
|
+
<strong>Scopes:</strong>
|
|
1311
|
+
</text>
|
|
1145
1312
|
<box marginTop={1} flexDirection="column">
|
|
1146
1313
|
<text>
|
|
1147
|
-
<span bg="cyan" fg="black">
|
|
1314
|
+
<span bg="cyan" fg="black">
|
|
1315
|
+
{" "}
|
|
1316
|
+
u{" "}
|
|
1317
|
+
</span>
|
|
1148
1318
|
<span fg={plugin.userScope?.enabled ? "cyan" : "gray"}>
|
|
1149
1319
|
{plugin.userScope?.enabled ? " ● " : " ○ "}
|
|
1150
1320
|
</span>
|
|
@@ -1155,7 +1325,10 @@ export function PluginsScreen() {
|
|
|
1155
1325
|
)}
|
|
1156
1326
|
</text>
|
|
1157
1327
|
<text>
|
|
1158
|
-
<span bg="green" fg="black">
|
|
1328
|
+
<span bg="green" fg="black">
|
|
1329
|
+
{" "}
|
|
1330
|
+
p{" "}
|
|
1331
|
+
</span>
|
|
1159
1332
|
<span fg={plugin.projectScope?.enabled ? "green" : "gray"}>
|
|
1160
1333
|
{plugin.projectScope?.enabled ? " ● " : " ○ "}
|
|
1161
1334
|
</span>
|
|
@@ -1166,7 +1339,10 @@ export function PluginsScreen() {
|
|
|
1166
1339
|
)}
|
|
1167
1340
|
</text>
|
|
1168
1341
|
<text>
|
|
1169
|
-
<span bg="yellow" fg="black">
|
|
1342
|
+
<span bg="yellow" fg="black">
|
|
1343
|
+
{" "}
|
|
1344
|
+
l{" "}
|
|
1345
|
+
</span>
|
|
1170
1346
|
<span fg={plugin.localScope?.enabled ? "yellow" : "gray"}>
|
|
1171
1347
|
{plugin.localScope?.enabled ? " ● " : " ○ "}
|
|
1172
1348
|
</span>
|
|
@@ -1184,12 +1360,18 @@ export function PluginsScreen() {
|
|
|
1184
1360
|
<box flexDirection="column" marginTop={1}>
|
|
1185
1361
|
{plugin.hasUpdate && (
|
|
1186
1362
|
<box>
|
|
1187
|
-
<text bg="magenta" fg="white">
|
|
1363
|
+
<text bg="magenta" fg="white">
|
|
1364
|
+
{" "}
|
|
1365
|
+
U{" "}
|
|
1366
|
+
</text>
|
|
1188
1367
|
<text> Update to v{plugin.version}</text>
|
|
1189
1368
|
</box>
|
|
1190
1369
|
)}
|
|
1191
1370
|
<box>
|
|
1192
|
-
<text bg="red" fg="white">
|
|
1371
|
+
<text bg="red" fg="white">
|
|
1372
|
+
{" "}
|
|
1373
|
+
d{" "}
|
|
1374
|
+
</text>
|
|
1193
1375
|
<text> Uninstall</text>
|
|
1194
1376
|
</box>
|
|
1195
1377
|
</box>
|
|
@@ -1201,9 +1383,10 @@ export function PluginsScreen() {
|
|
|
1201
1383
|
return null;
|
|
1202
1384
|
};
|
|
1203
1385
|
|
|
1204
|
-
const footerHints =
|
|
1205
|
-
|
|
1206
|
-
|
|
1386
|
+
const footerHints =
|
|
1387
|
+
isSearchActive || pluginsState.searchQuery
|
|
1388
|
+
? "↑↓:nav │ Enter:select │ Esc:clear │ type to filter"
|
|
1389
|
+
: "u/p/l:scope │ U:update │ a:all │ d:remove │ s:profile │ type to search";
|
|
1207
1390
|
|
|
1208
1391
|
// Calculate status for subtitle
|
|
1209
1392
|
const scopeLabel = pluginsState.scope === "global" ? "Global" : "Project";
|