claudeup 3.7.1 → 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/prerunner/index.js +2 -1
- package/src/prerunner/index.ts +2 -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 +181 -65
- package/src/ui/screens/PluginsScreen.tsx +308 -91
- 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,7 +17,12 @@ import {
|
|
|
17
17
|
import {
|
|
18
18
|
setMcpEnvVar,
|
|
19
19
|
getMcpEnvVars,
|
|
20
|
+
readSettings,
|
|
21
|
+
saveGlobalInstalledPluginVersion,
|
|
22
|
+
saveLocalInstalledPluginVersion,
|
|
20
23
|
} from "../../services/claude-settings.js";
|
|
24
|
+
import { saveProfile } from "../../services/profiles.js";
|
|
25
|
+
import { saveInstalledPluginVersion } from "../../services/plugin-manager.js";
|
|
21
26
|
import {
|
|
22
27
|
installPlugin as cliInstallPlugin,
|
|
23
28
|
uninstallPlugin as cliUninstallPlugin,
|
|
@@ -204,53 +209,87 @@ export function PluginsScreen() {
|
|
|
204
209
|
);
|
|
205
210
|
}, [filteredItems]);
|
|
206
211
|
|
|
207
|
-
// Keyboard handling
|
|
212
|
+
// Keyboard handling — inline search with live filtering
|
|
208
213
|
useKeyboard((event) => {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
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) {
|
|
213
221
|
dispatch({ type: "PLUGINS_SET_SEARCH", query: "" });
|
|
214
|
-
} else if (event.name === "enter") {
|
|
215
222
|
dispatch({ type: "SET_SEARCHING", isSearching: false });
|
|
216
|
-
|
|
217
|
-
} else if (event.name === "backspace" || event.name === "delete") {
|
|
218
|
-
dispatch({
|
|
219
|
-
type: "PLUGINS_SET_SEARCH",
|
|
220
|
-
query: pluginsState.searchQuery.slice(0, -1),
|
|
221
|
-
});
|
|
222
|
-
} else if (event.name.length === 1 && !event.ctrl && !event.meta) {
|
|
223
|
-
dispatch({
|
|
224
|
-
type: "PLUGINS_SET_SEARCH",
|
|
225
|
-
query: pluginsState.searchQuery + event.name,
|
|
226
|
-
});
|
|
223
|
+
dispatch({ type: "PLUGINS_SELECT", index: 0 });
|
|
227
224
|
}
|
|
225
|
+
// Don't return — let GlobalKeyHandler handle Escape too (for quit)
|
|
228
226
|
return;
|
|
229
227
|
}
|
|
230
228
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
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
|
+
}
|
|
236
240
|
return;
|
|
237
241
|
}
|
|
238
242
|
|
|
239
|
-
// Navigation
|
|
243
|
+
// Navigation — always works (even during search)
|
|
240
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
|
+
}
|
|
241
254
|
const newIndex = Math.max(0, pluginsState.selectedIndex - 1);
|
|
242
255
|
dispatch({ type: "PLUGINS_SELECT", index: newIndex });
|
|
243
|
-
|
|
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
|
+
}
|
|
244
268
|
const newIndex = Math.min(
|
|
245
269
|
selectableItems.length - 1,
|
|
246
270
|
pluginsState.selectedIndex + 1,
|
|
247
271
|
);
|
|
248
272
|
dispatch({ type: "PLUGINS_SELECT", index: newIndex });
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
|
|
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;
|
|
249
285
|
}
|
|
250
286
|
|
|
251
|
-
// Collapse/expand marketplace
|
|
252
|
-
|
|
253
|
-
(event.name === "left" ||
|
|
287
|
+
// Collapse/expand marketplace — always works
|
|
288
|
+
if (
|
|
289
|
+
(event.name === "left" ||
|
|
290
|
+
event.name === "right" ||
|
|
291
|
+
event.name === "<" ||
|
|
292
|
+
event.name === ">") &&
|
|
254
293
|
selectableItems[pluginsState.selectedIndex]?.marketplace
|
|
255
294
|
) {
|
|
256
295
|
const item = selectableItems[pluginsState.selectedIndex];
|
|
@@ -260,50 +299,49 @@ export function PluginsScreen() {
|
|
|
260
299
|
name: item.marketplace.name,
|
|
261
300
|
});
|
|
262
301
|
}
|
|
302
|
+
return;
|
|
263
303
|
}
|
|
264
304
|
|
|
265
|
-
//
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
else if (event.name === "t") {
|
|
277
|
-
handleShowTeamConfigHelp();
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
// Scope-specific toggle shortcuts (u/p/l)
|
|
281
|
-
else if (event.name === "u") {
|
|
282
|
-
handleScopeToggle("user");
|
|
283
|
-
} else if (event.name === "p") {
|
|
284
|
-
handleScopeToggle("project");
|
|
285
|
-
} else if (event.name === "l") {
|
|
286
|
-
handleScopeToggle("local");
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
// Update plugin (Shift+U)
|
|
290
|
-
else if (event.name === "U") {
|
|
291
|
-
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;
|
|
292
316
|
}
|
|
293
317
|
|
|
294
|
-
//
|
|
295
|
-
else if (event.name === "a") {
|
|
296
|
-
handleUpdateAll();
|
|
297
|
-
}
|
|
318
|
+
// When search query is empty: action shortcuts work normally
|
|
298
319
|
|
|
299
|
-
//
|
|
300
|
-
|
|
301
|
-
|
|
320
|
+
// Start explicit search mode with /
|
|
321
|
+
if (event.name === "/") {
|
|
322
|
+
dispatch({ type: "SET_SEARCHING", isSearching: true });
|
|
323
|
+
return;
|
|
302
324
|
}
|
|
303
325
|
|
|
304
|
-
//
|
|
305
|
-
|
|
306
|
-
|
|
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 });
|
|
307
345
|
}
|
|
308
346
|
});
|
|
309
347
|
|
|
@@ -486,9 +524,10 @@ export function PluginsScreen() {
|
|
|
486
524
|
const missing = await checkMissingDeps(setup);
|
|
487
525
|
const hasMissing =
|
|
488
526
|
(missing.pip?.length || 0) +
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
527
|
+
(missing.brew?.length || 0) +
|
|
528
|
+
(missing.npm?.length || 0) +
|
|
529
|
+
(missing.cargo?.length || 0) >
|
|
530
|
+
0;
|
|
492
531
|
|
|
493
532
|
if (!hasMissing) return;
|
|
494
533
|
|
|
@@ -497,7 +536,8 @@ export function PluginsScreen() {
|
|
|
497
536
|
if (missing.pip?.length) parts.push(`pip: ${missing.pip.join(", ")}`);
|
|
498
537
|
if (missing.brew?.length) parts.push(`brew: ${missing.brew.join(", ")}`);
|
|
499
538
|
if (missing.npm?.length) parts.push(`npm: ${missing.npm.join(", ")}`);
|
|
500
|
-
if (missing.cargo?.length)
|
|
539
|
+
if (missing.cargo?.length)
|
|
540
|
+
parts.push(`cargo: ${missing.cargo.join(", ")}`);
|
|
501
541
|
|
|
502
542
|
const wantInstall = await modal.confirm(
|
|
503
543
|
"Install Dependencies?",
|
|
@@ -531,6 +571,33 @@ export function PluginsScreen() {
|
|
|
531
571
|
}
|
|
532
572
|
};
|
|
533
573
|
|
|
574
|
+
/**
|
|
575
|
+
* Save the installed version to settings after CLI install/update.
|
|
576
|
+
* Claude CLI doesn't update installedPluginVersions in settings.json,
|
|
577
|
+
* so we do it ourselves to keep the TUI version display accurate.
|
|
578
|
+
*/
|
|
579
|
+
const saveVersionAfterInstall = async (
|
|
580
|
+
pluginId: string,
|
|
581
|
+
version: string,
|
|
582
|
+
scope: PluginScope,
|
|
583
|
+
): Promise<void> => {
|
|
584
|
+
try {
|
|
585
|
+
if (scope === "user") {
|
|
586
|
+
await saveGlobalInstalledPluginVersion(pluginId, version);
|
|
587
|
+
} else if (scope === "local") {
|
|
588
|
+
await saveLocalInstalledPluginVersion(
|
|
589
|
+
pluginId,
|
|
590
|
+
version,
|
|
591
|
+
state.projectPath,
|
|
592
|
+
);
|
|
593
|
+
} else {
|
|
594
|
+
await saveInstalledPluginVersion(pluginId, version, state.projectPath);
|
|
595
|
+
}
|
|
596
|
+
} catch {
|
|
597
|
+
// Non-fatal: version display may be stale but plugin still works
|
|
598
|
+
}
|
|
599
|
+
};
|
|
600
|
+
|
|
534
601
|
const handleSelect = async () => {
|
|
535
602
|
const item = selectableItems[pluginsState.selectedIndex];
|
|
536
603
|
if (!item) return;
|
|
@@ -663,8 +730,10 @@ export function PluginsScreen() {
|
|
|
663
730
|
await cliUninstallPlugin(plugin.id, scope, state.projectPath);
|
|
664
731
|
} else if (action === "update") {
|
|
665
732
|
await cliUpdatePlugin(plugin.id, scope);
|
|
733
|
+
await saveVersionAfterInstall(plugin.id, latestVersion, scope);
|
|
666
734
|
} else {
|
|
667
735
|
await cliInstallPlugin(plugin.id, scope);
|
|
736
|
+
await saveVersionAfterInstall(plugin.id, latestVersion, scope);
|
|
668
737
|
|
|
669
738
|
// On fresh install, configure env vars and install system deps
|
|
670
739
|
modal.hideModal();
|
|
@@ -687,11 +756,17 @@ export function PluginsScreen() {
|
|
|
687
756
|
if (!item || item.type !== "plugin" || !item.plugin?.hasUpdate) return;
|
|
688
757
|
|
|
689
758
|
const plugin = item.plugin;
|
|
690
|
-
const scope: PluginScope =
|
|
759
|
+
const scope: PluginScope =
|
|
760
|
+
pluginsState.scope === "global" ? "user" : "project";
|
|
691
761
|
|
|
692
762
|
modal.loading(`Updating ${plugin.name}...`);
|
|
693
763
|
try {
|
|
694
764
|
await cliUpdatePlugin(plugin.id, scope);
|
|
765
|
+
await saveVersionAfterInstall(
|
|
766
|
+
plugin.id,
|
|
767
|
+
plugin.version || "0.0.0",
|
|
768
|
+
scope,
|
|
769
|
+
);
|
|
695
770
|
modal.hideModal();
|
|
696
771
|
fetchData();
|
|
697
772
|
} catch (error) {
|
|
@@ -706,12 +781,18 @@ export function PluginsScreen() {
|
|
|
706
781
|
const updatable = pluginsState.plugins.data.filter((p) => p.hasUpdate);
|
|
707
782
|
if (updatable.length === 0) return;
|
|
708
783
|
|
|
709
|
-
const scope: PluginScope =
|
|
784
|
+
const scope: PluginScope =
|
|
785
|
+
pluginsState.scope === "global" ? "user" : "project";
|
|
710
786
|
modal.loading(`Updating ${updatable.length} plugin(s)...`);
|
|
711
787
|
|
|
712
788
|
try {
|
|
713
789
|
for (const plugin of updatable) {
|
|
714
790
|
await cliUpdatePlugin(plugin.id, scope);
|
|
791
|
+
await saveVersionAfterInstall(
|
|
792
|
+
plugin.id,
|
|
793
|
+
plugin.version || "0.0.0",
|
|
794
|
+
scope,
|
|
795
|
+
);
|
|
715
796
|
}
|
|
716
797
|
modal.hideModal();
|
|
717
798
|
fetchData();
|
|
@@ -771,8 +852,10 @@ export function PluginsScreen() {
|
|
|
771
852
|
await cliUninstallPlugin(plugin.id, scope, state.projectPath);
|
|
772
853
|
} else if (action === "update") {
|
|
773
854
|
await cliUpdatePlugin(plugin.id, scope);
|
|
855
|
+
await saveVersionAfterInstall(plugin.id, latestVersion, scope);
|
|
774
856
|
} else {
|
|
775
857
|
await cliInstallPlugin(plugin.id, scope);
|
|
858
|
+
await saveVersionAfterInstall(plugin.id, latestVersion, scope);
|
|
776
859
|
|
|
777
860
|
// On fresh install, configure env vars and install system deps
|
|
778
861
|
modal.hideModal();
|
|
@@ -789,6 +872,47 @@ export function PluginsScreen() {
|
|
|
789
872
|
}
|
|
790
873
|
};
|
|
791
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
|
+
|
|
792
916
|
const handleUninstall = async () => {
|
|
793
917
|
const item = selectableItems[pluginsState.selectedIndex];
|
|
794
918
|
if (!item || item.type !== "plugin" || !item.plugin) return;
|
|
@@ -836,7 +960,11 @@ export function PluginsScreen() {
|
|
|
836
960
|
modal.loading(`Uninstalling ${plugin.name}...`);
|
|
837
961
|
|
|
838
962
|
try {
|
|
839
|
-
await cliUninstallPlugin(
|
|
963
|
+
await cliUninstallPlugin(
|
|
964
|
+
plugin.id,
|
|
965
|
+
scopeValue as PluginScope,
|
|
966
|
+
state.projectPath,
|
|
967
|
+
);
|
|
840
968
|
modal.hideModal();
|
|
841
969
|
fetchData();
|
|
842
970
|
} catch (error) {
|
|
@@ -852,7 +980,9 @@ export function PluginsScreen() {
|
|
|
852
980
|
) {
|
|
853
981
|
return (
|
|
854
982
|
<box flexDirection="column" paddingLeft={1} paddingRight={1}>
|
|
855
|
-
<text fg="#7e57c2"
|
|
983
|
+
<text fg="#7e57c2">
|
|
984
|
+
<strong>claudeup Plugins</strong>
|
|
985
|
+
</text>
|
|
856
986
|
<text fg="gray">Loading...</text>
|
|
857
987
|
</box>
|
|
858
988
|
);
|
|
@@ -865,7 +995,9 @@ export function PluginsScreen() {
|
|
|
865
995
|
) {
|
|
866
996
|
return (
|
|
867
997
|
<box flexDirection="column" paddingLeft={1} paddingRight={1}>
|
|
868
|
-
<text fg="#7e57c2"
|
|
998
|
+
<text fg="#7e57c2">
|
|
999
|
+
<strong>claudeup Plugins</strong>
|
|
1000
|
+
</text>
|
|
869
1001
|
<text fg="red">Error loading data</text>
|
|
870
1002
|
</box>
|
|
871
1003
|
);
|
|
@@ -908,7 +1040,13 @@ export function PluginsScreen() {
|
|
|
908
1040
|
? ` (${item.pluginCount})`
|
|
909
1041
|
: "";
|
|
910
1042
|
return (
|
|
911
|
-
<text bg="magenta" fg="white"
|
|
1043
|
+
<text bg="magenta" fg="white">
|
|
1044
|
+
<strong>
|
|
1045
|
+
{" "}
|
|
1046
|
+
{arrow} {mp.displayName}
|
|
1047
|
+
{count}{" "}
|
|
1048
|
+
</strong>
|
|
1049
|
+
</text>
|
|
912
1050
|
);
|
|
913
1051
|
}
|
|
914
1052
|
|
|
@@ -928,7 +1066,10 @@ export function PluginsScreen() {
|
|
|
928
1066
|
let statusIcon = "○";
|
|
929
1067
|
let statusColor = "gray";
|
|
930
1068
|
|
|
931
|
-
if (plugin.
|
|
1069
|
+
if (plugin.isOrphaned) {
|
|
1070
|
+
statusIcon = "x";
|
|
1071
|
+
statusColor = "red";
|
|
1072
|
+
} else if (plugin.enabled) {
|
|
932
1073
|
statusIcon = "●";
|
|
933
1074
|
statusColor = "green";
|
|
934
1075
|
} else if (plugin.installedVersion) {
|
|
@@ -938,7 +1079,9 @@ export function PluginsScreen() {
|
|
|
938
1079
|
|
|
939
1080
|
// Build version string
|
|
940
1081
|
let versionStr = "";
|
|
941
|
-
if (plugin.
|
|
1082
|
+
if (plugin.isOrphaned) {
|
|
1083
|
+
versionStr = " deprecated";
|
|
1084
|
+
} else if (plugin.installedVersion && plugin.installedVersion !== "0.0.0") {
|
|
942
1085
|
versionStr = ` v${plugin.installedVersion}`;
|
|
943
1086
|
if (plugin.hasUpdate && plugin.version) {
|
|
944
1087
|
versionStr += ` → v${plugin.version}`;
|
|
@@ -962,9 +1105,26 @@ export function PluginsScreen() {
|
|
|
962
1105
|
const displayName = segments
|
|
963
1106
|
? segments.map((seg) => seg.text).join("")
|
|
964
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
|
+
|
|
965
1122
|
return (
|
|
966
1123
|
<text>
|
|
967
|
-
<span fg={statusColor}>
|
|
1124
|
+
<span fg={statusColor}>
|
|
1125
|
+
{" "}
|
|
1126
|
+
{statusIcon}{" "}
|
|
1127
|
+
</span>
|
|
968
1128
|
<span>{displayName}</span>
|
|
969
1129
|
<span fg={plugin.hasUpdate ? "yellow" : "gray"}>{versionStr}</span>
|
|
970
1130
|
</text>
|
|
@@ -1008,15 +1168,23 @@ export function PluginsScreen() {
|
|
|
1008
1168
|
|
|
1009
1169
|
return (
|
|
1010
1170
|
<box flexDirection="column">
|
|
1011
|
-
<text fg="cyan"
|
|
1171
|
+
<text fg="cyan">
|
|
1172
|
+
<strong>
|
|
1173
|
+
{mp.displayName}
|
|
1174
|
+
{getBadge()}
|
|
1175
|
+
</strong>
|
|
1176
|
+
</text>
|
|
1012
1177
|
<text fg="gray">{mp.description || "No description"}</text>
|
|
1013
1178
|
<text fg={isEnabled ? "green" : "gray"}>
|
|
1014
1179
|
{isEnabled ? "● Added" : "○ Not added"}
|
|
1015
1180
|
</text>
|
|
1016
|
-
<text fg="
|
|
1181
|
+
<text fg="#5c9aff">github.com/{mp.source.repo}</text>
|
|
1017
1182
|
<text>Plugins: {selectedItem.pluginCount || 0}</text>
|
|
1018
1183
|
<box marginTop={1}>
|
|
1019
|
-
<text bg={isEnabled ? "cyan" : "green"} fg="black">
|
|
1184
|
+
<text bg={isEnabled ? "cyan" : "green"} fg="black">
|
|
1185
|
+
{" "}
|
|
1186
|
+
Enter{" "}
|
|
1187
|
+
</text>
|
|
1020
1188
|
<text fg="gray"> {actionHint}</text>
|
|
1021
1189
|
</box>
|
|
1022
1190
|
{isEnabled && (
|
|
@@ -1032,6 +1200,31 @@ export function PluginsScreen() {
|
|
|
1032
1200
|
const plugin = selectedItem.plugin;
|
|
1033
1201
|
const isInstalled = plugin.enabled || plugin.installedVersion;
|
|
1034
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
|
+
|
|
1035
1228
|
// Build component counts
|
|
1036
1229
|
const components: string[] = [];
|
|
1037
1230
|
if (plugin.agents?.length)
|
|
@@ -1055,7 +1248,13 @@ export function PluginsScreen() {
|
|
|
1055
1248
|
<box flexDirection="column">
|
|
1056
1249
|
{/* Plugin name header - centered */}
|
|
1057
1250
|
<box justifyContent="center">
|
|
1058
|
-
<text bg="magenta" fg="white"
|
|
1251
|
+
<text bg="magenta" fg="white">
|
|
1252
|
+
<strong>
|
|
1253
|
+
{" "}
|
|
1254
|
+
{plugin.name}
|
|
1255
|
+
{plugin.hasUpdate ? " ⬆" : ""}{" "}
|
|
1256
|
+
</strong>
|
|
1257
|
+
</text>
|
|
1059
1258
|
</box>
|
|
1060
1259
|
|
|
1061
1260
|
{/* Status line */}
|
|
@@ -1078,7 +1277,7 @@ export function PluginsScreen() {
|
|
|
1078
1277
|
{showVersion && (
|
|
1079
1278
|
<text>
|
|
1080
1279
|
<span>Version </span>
|
|
1081
|
-
<span fg="
|
|
1280
|
+
<span fg="#5c9aff">v{plugin.version}</span>
|
|
1082
1281
|
{showInstalledVersion &&
|
|
1083
1282
|
plugin.installedVersion !== plugin.version && (
|
|
1084
1283
|
<span> (v{plugin.installedVersion} installed)</span>
|
|
@@ -1107,10 +1306,15 @@ export function PluginsScreen() {
|
|
|
1107
1306
|
{/* Scope Status with shortcuts - each scope has its own color */}
|
|
1108
1307
|
<box flexDirection="column" marginTop={1}>
|
|
1109
1308
|
<text>────────────────────────</text>
|
|
1110
|
-
<text
|
|
1309
|
+
<text>
|
|
1310
|
+
<strong>Scopes:</strong>
|
|
1311
|
+
</text>
|
|
1111
1312
|
<box marginTop={1} flexDirection="column">
|
|
1112
1313
|
<text>
|
|
1113
|
-
<span bg="cyan" fg="black">
|
|
1314
|
+
<span bg="cyan" fg="black">
|
|
1315
|
+
{" "}
|
|
1316
|
+
u{" "}
|
|
1317
|
+
</span>
|
|
1114
1318
|
<span fg={plugin.userScope?.enabled ? "cyan" : "gray"}>
|
|
1115
1319
|
{plugin.userScope?.enabled ? " ● " : " ○ "}
|
|
1116
1320
|
</span>
|
|
@@ -1121,7 +1325,10 @@ export function PluginsScreen() {
|
|
|
1121
1325
|
)}
|
|
1122
1326
|
</text>
|
|
1123
1327
|
<text>
|
|
1124
|
-
<span bg="green" fg="black">
|
|
1328
|
+
<span bg="green" fg="black">
|
|
1329
|
+
{" "}
|
|
1330
|
+
p{" "}
|
|
1331
|
+
</span>
|
|
1125
1332
|
<span fg={plugin.projectScope?.enabled ? "green" : "gray"}>
|
|
1126
1333
|
{plugin.projectScope?.enabled ? " ● " : " ○ "}
|
|
1127
1334
|
</span>
|
|
@@ -1132,7 +1339,10 @@ export function PluginsScreen() {
|
|
|
1132
1339
|
)}
|
|
1133
1340
|
</text>
|
|
1134
1341
|
<text>
|
|
1135
|
-
<span bg="yellow" fg="black">
|
|
1342
|
+
<span bg="yellow" fg="black">
|
|
1343
|
+
{" "}
|
|
1344
|
+
l{" "}
|
|
1345
|
+
</span>
|
|
1136
1346
|
<span fg={plugin.localScope?.enabled ? "yellow" : "gray"}>
|
|
1137
1347
|
{plugin.localScope?.enabled ? " ● " : " ○ "}
|
|
1138
1348
|
</span>
|
|
@@ -1150,12 +1360,18 @@ export function PluginsScreen() {
|
|
|
1150
1360
|
<box flexDirection="column" marginTop={1}>
|
|
1151
1361
|
{plugin.hasUpdate && (
|
|
1152
1362
|
<box>
|
|
1153
|
-
<text bg="magenta" fg="white">
|
|
1363
|
+
<text bg="magenta" fg="white">
|
|
1364
|
+
{" "}
|
|
1365
|
+
U{" "}
|
|
1366
|
+
</text>
|
|
1154
1367
|
<text> Update to v{plugin.version}</text>
|
|
1155
1368
|
</box>
|
|
1156
1369
|
)}
|
|
1157
1370
|
<box>
|
|
1158
|
-
<text bg="red" fg="white">
|
|
1371
|
+
<text bg="red" fg="white">
|
|
1372
|
+
{" "}
|
|
1373
|
+
d{" "}
|
|
1374
|
+
</text>
|
|
1159
1375
|
<text> Uninstall</text>
|
|
1160
1376
|
</box>
|
|
1161
1377
|
</box>
|
|
@@ -1167,9 +1383,10 @@ export function PluginsScreen() {
|
|
|
1167
1383
|
return null;
|
|
1168
1384
|
};
|
|
1169
1385
|
|
|
1170
|
-
const footerHints =
|
|
1171
|
-
|
|
1172
|
-
|
|
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";
|
|
1173
1390
|
|
|
1174
1391
|
// Calculate status for subtitle
|
|
1175
1392
|
const scopeLabel = pluginsState.scope === "global" ? "Global" : "Project";
|