claudeup 3.11.0 → 3.12.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/ui/screens/PluginsScreen.js +98 -18
- package/src/ui/screens/PluginsScreen.tsx +109 -19
package/package.json
CHANGED
|
@@ -16,6 +16,10 @@ import { saveInstalledPluginVersion } from "../../services/plugin-manager.js";
|
|
|
16
16
|
import { installPlugin as cliInstallPlugin, uninstallPlugin as cliUninstallPlugin, updatePlugin as cliUpdatePlugin, } from "../../services/claude-cli.js";
|
|
17
17
|
import { getPluginEnvRequirements, getPluginSourcePath, } from "../../services/plugin-mcp-config.js";
|
|
18
18
|
import { getPluginSetupFromSource, checkMissingDeps, installPluginDeps, } from "../../services/plugin-setup.js";
|
|
19
|
+
// Virtual marketplace name for the community sub-section of claude-plugins-official
|
|
20
|
+
const COMMUNITY_VIRTUAL_MARKETPLACE = "claude-plugins-official:community";
|
|
21
|
+
// The marketplace that gets split into Anthropic Official + Community sections
|
|
22
|
+
const SPLIT_MARKETPLACE = "claude-plugins-official";
|
|
19
23
|
export function PluginsScreen() {
|
|
20
24
|
const { state, dispatch } = useApp();
|
|
21
25
|
const { plugins: pluginsState } = state;
|
|
@@ -77,6 +81,64 @@ export function PluginsScreen() {
|
|
|
77
81
|
const isCollapsed = collapsed.has(marketplace.name);
|
|
78
82
|
const isEnabled = marketplacePlugins.length > 0 || marketplace.official;
|
|
79
83
|
const hasPlugins = marketplacePlugins.length > 0;
|
|
84
|
+
// Special handling: split claude-plugins-official into two sub-sections
|
|
85
|
+
if (marketplace.name === SPLIT_MARKETPLACE && hasPlugins) {
|
|
86
|
+
const anthropicPlugins = marketplacePlugins.filter((p) => p.author?.name?.toLowerCase() === "anthropic");
|
|
87
|
+
const communityPlugins = marketplacePlugins.filter((p) => p.author?.name?.toLowerCase() !== "anthropic");
|
|
88
|
+
// Sub-section 1: Anthropic Official (plugins by Anthropic)
|
|
89
|
+
const anthropicCollapsed = collapsed.has(marketplace.name);
|
|
90
|
+
const anthropicHasPlugins = anthropicPlugins.length > 0;
|
|
91
|
+
items.push({
|
|
92
|
+
id: `mp:${marketplace.name}`,
|
|
93
|
+
type: "category",
|
|
94
|
+
label: marketplace.displayName,
|
|
95
|
+
marketplace,
|
|
96
|
+
marketplaceEnabled: isEnabled,
|
|
97
|
+
pluginCount: anthropicPlugins.length,
|
|
98
|
+
isExpanded: !anthropicCollapsed && anthropicHasPlugins,
|
|
99
|
+
});
|
|
100
|
+
if (isEnabled && anthropicHasPlugins && !anthropicCollapsed) {
|
|
101
|
+
for (const plugin of anthropicPlugins) {
|
|
102
|
+
items.push({
|
|
103
|
+
id: `pl:${plugin.id}`,
|
|
104
|
+
type: "plugin",
|
|
105
|
+
label: plugin.name,
|
|
106
|
+
plugin,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// Sub-section 2: Community (third-party plugins in same marketplace)
|
|
111
|
+
if (communityPlugins.length > 0) {
|
|
112
|
+
const communityVirtualMp = {
|
|
113
|
+
name: COMMUNITY_VIRTUAL_MARKETPLACE,
|
|
114
|
+
displayName: "Community",
|
|
115
|
+
source: marketplace.source,
|
|
116
|
+
description: "Third-party plugins from the community",
|
|
117
|
+
};
|
|
118
|
+
const communityCollapsed = collapsed.has(COMMUNITY_VIRTUAL_MARKETPLACE);
|
|
119
|
+
items.push({
|
|
120
|
+
id: `mp:${COMMUNITY_VIRTUAL_MARKETPLACE}`,
|
|
121
|
+
type: "category",
|
|
122
|
+
label: "Community",
|
|
123
|
+
marketplace: communityVirtualMp,
|
|
124
|
+
marketplaceEnabled: true,
|
|
125
|
+
pluginCount: communityPlugins.length,
|
|
126
|
+
isExpanded: !communityCollapsed,
|
|
127
|
+
isCommunitySection: true,
|
|
128
|
+
});
|
|
129
|
+
if (!communityCollapsed) {
|
|
130
|
+
for (const plugin of communityPlugins) {
|
|
131
|
+
items.push({
|
|
132
|
+
id: `pl:${plugin.id}`,
|
|
133
|
+
type: "plugin",
|
|
134
|
+
label: plugin.name,
|
|
135
|
+
plugin,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
80
142
|
// Category header (marketplace)
|
|
81
143
|
items.push({
|
|
82
144
|
id: `mp:${marketplace.name}`,
|
|
@@ -113,32 +175,44 @@ export function PluginsScreen() {
|
|
|
113
175
|
// Only search plugins, not categories
|
|
114
176
|
const pluginItems = allItems.filter((item) => item.type === "plugin");
|
|
115
177
|
const fuzzyResults = fuzzyFilter(pluginItems, query, (item) => item.label);
|
|
116
|
-
//
|
|
117
|
-
const
|
|
178
|
+
// Build a set of matched plugin item ids for O(1) lookup
|
|
179
|
+
const matchedPluginIds = new Set();
|
|
118
180
|
for (const result of fuzzyResults) {
|
|
119
|
-
|
|
120
|
-
|
|
181
|
+
matchedPluginIds.add(result.item.id);
|
|
182
|
+
}
|
|
183
|
+
// Walk allItems sequentially: track the current category section.
|
|
184
|
+
// For each category, include it only if any plugin under it matched.
|
|
185
|
+
// We build a map from category item id -> whether any plugin below matched.
|
|
186
|
+
const categoryHasMatch = new Map();
|
|
187
|
+
let currentCategoryId = null;
|
|
188
|
+
for (const item of allItems) {
|
|
189
|
+
if (item.type === "category") {
|
|
190
|
+
currentCategoryId = item.id;
|
|
191
|
+
if (!categoryHasMatch.has(item.id)) {
|
|
192
|
+
categoryHasMatch.set(item.id, false);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
else if (item.type === "plugin" && currentCategoryId) {
|
|
196
|
+
if (matchedPluginIds.has(item.id)) {
|
|
197
|
+
categoryHasMatch.set(currentCategoryId, true);
|
|
198
|
+
}
|
|
121
199
|
}
|
|
122
200
|
}
|
|
123
201
|
const result = [];
|
|
124
|
-
let
|
|
202
|
+
let currentCatIncluded = false;
|
|
203
|
+
currentCategoryId = null;
|
|
125
204
|
for (const item of allItems) {
|
|
126
|
-
if (item.type === "category"
|
|
127
|
-
|
|
205
|
+
if (item.type === "category") {
|
|
206
|
+
currentCategoryId = item.id;
|
|
207
|
+
currentCatIncluded = categoryHasMatch.get(item.id) === true;
|
|
208
|
+
if (currentCatIncluded) {
|
|
128
209
|
result.push(item);
|
|
129
|
-
currentMarketplace = item.marketplace.name;
|
|
130
|
-
}
|
|
131
|
-
else {
|
|
132
|
-
currentMarketplace = null;
|
|
133
210
|
}
|
|
134
211
|
}
|
|
135
|
-
else if (item.type === "plugin" &&
|
|
136
|
-
if (
|
|
137
|
-
// Check if this plugin matched
|
|
212
|
+
else if (item.type === "plugin" && currentCatIncluded) {
|
|
213
|
+
if (matchedPluginIds.has(item.id)) {
|
|
138
214
|
const matched = fuzzyResults.find((r) => r.item.id === item.id);
|
|
139
|
-
|
|
140
|
-
result.push({ ...item, _matches: matched.matches });
|
|
141
|
-
}
|
|
215
|
+
result.push({ ...item, _matches: matched?.matches });
|
|
142
216
|
}
|
|
143
217
|
}
|
|
144
218
|
}
|
|
@@ -777,7 +851,11 @@ export function PluginsScreen() {
|
|
|
777
851
|
let statusText = "";
|
|
778
852
|
let statusColor = "green";
|
|
779
853
|
if (item.marketplaceEnabled) {
|
|
780
|
-
if (
|
|
854
|
+
if (item.isCommunitySection) {
|
|
855
|
+
statusText = "3rd Party";
|
|
856
|
+
statusColor = "gray";
|
|
857
|
+
}
|
|
858
|
+
else if (mp.name === "claude-plugins-official") {
|
|
781
859
|
statusText = "★ Official";
|
|
782
860
|
statusColor = "yellow";
|
|
783
861
|
}
|
|
@@ -857,6 +935,8 @@ export function PluginsScreen() {
|
|
|
857
935
|
const isEnabled = selectedItem.marketplaceEnabled;
|
|
858
936
|
// Get appropriate badge for marketplace type
|
|
859
937
|
const getBadge = () => {
|
|
938
|
+
if (selectedItem.isCommunitySection)
|
|
939
|
+
return " 3rd Party";
|
|
860
940
|
if (mp.name === "claude-plugins-official")
|
|
861
941
|
return " ★";
|
|
862
942
|
if (mp.name === "claude-code-plugins")
|
|
@@ -41,6 +41,11 @@ import {
|
|
|
41
41
|
} from "../../services/plugin-setup.js";
|
|
42
42
|
import type { Marketplace } from "../../types/index.js";
|
|
43
43
|
|
|
44
|
+
// Virtual marketplace name for the community sub-section of claude-plugins-official
|
|
45
|
+
const COMMUNITY_VIRTUAL_MARKETPLACE = "claude-plugins-official:community";
|
|
46
|
+
// The marketplace that gets split into Anthropic Official + Community sections
|
|
47
|
+
const SPLIT_MARKETPLACE = "claude-plugins-official";
|
|
48
|
+
|
|
44
49
|
interface ListItem {
|
|
45
50
|
id: string;
|
|
46
51
|
type: "category" | "plugin";
|
|
@@ -50,6 +55,8 @@ interface ListItem {
|
|
|
50
55
|
plugin?: PluginInfo;
|
|
51
56
|
pluginCount?: number;
|
|
52
57
|
isExpanded?: boolean;
|
|
58
|
+
/** True for the virtual Community sub-section derived from claude-plugins-official */
|
|
59
|
+
isCommunitySection?: boolean;
|
|
53
60
|
}
|
|
54
61
|
|
|
55
62
|
export function PluginsScreen() {
|
|
@@ -128,6 +135,72 @@ export function PluginsScreen() {
|
|
|
128
135
|
const isEnabled = marketplacePlugins.length > 0 || marketplace.official;
|
|
129
136
|
const hasPlugins = marketplacePlugins.length > 0;
|
|
130
137
|
|
|
138
|
+
// Special handling: split claude-plugins-official into two sub-sections
|
|
139
|
+
if (marketplace.name === SPLIT_MARKETPLACE && hasPlugins) {
|
|
140
|
+
const anthropicPlugins = marketplacePlugins.filter(
|
|
141
|
+
(p) => p.author?.name?.toLowerCase() === "anthropic",
|
|
142
|
+
);
|
|
143
|
+
const communityPlugins = marketplacePlugins.filter(
|
|
144
|
+
(p) => p.author?.name?.toLowerCase() !== "anthropic",
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
// Sub-section 1: Anthropic Official (plugins by Anthropic)
|
|
148
|
+
const anthropicCollapsed = collapsed.has(marketplace.name);
|
|
149
|
+
const anthropicHasPlugins = anthropicPlugins.length > 0;
|
|
150
|
+
items.push({
|
|
151
|
+
id: `mp:${marketplace.name}`,
|
|
152
|
+
type: "category",
|
|
153
|
+
label: marketplace.displayName,
|
|
154
|
+
marketplace,
|
|
155
|
+
marketplaceEnabled: isEnabled,
|
|
156
|
+
pluginCount: anthropicPlugins.length,
|
|
157
|
+
isExpanded: !anthropicCollapsed && anthropicHasPlugins,
|
|
158
|
+
});
|
|
159
|
+
if (isEnabled && anthropicHasPlugins && !anthropicCollapsed) {
|
|
160
|
+
for (const plugin of anthropicPlugins) {
|
|
161
|
+
items.push({
|
|
162
|
+
id: `pl:${plugin.id}`,
|
|
163
|
+
type: "plugin",
|
|
164
|
+
label: plugin.name,
|
|
165
|
+
plugin,
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Sub-section 2: Community (third-party plugins in same marketplace)
|
|
171
|
+
if (communityPlugins.length > 0) {
|
|
172
|
+
const communityVirtualMp: Marketplace = {
|
|
173
|
+
name: COMMUNITY_VIRTUAL_MARKETPLACE,
|
|
174
|
+
displayName: "Community",
|
|
175
|
+
source: marketplace.source,
|
|
176
|
+
description: "Third-party plugins from the community",
|
|
177
|
+
};
|
|
178
|
+
const communityCollapsed = collapsed.has(COMMUNITY_VIRTUAL_MARKETPLACE);
|
|
179
|
+
items.push({
|
|
180
|
+
id: `mp:${COMMUNITY_VIRTUAL_MARKETPLACE}`,
|
|
181
|
+
type: "category",
|
|
182
|
+
label: "Community",
|
|
183
|
+
marketplace: communityVirtualMp,
|
|
184
|
+
marketplaceEnabled: true,
|
|
185
|
+
pluginCount: communityPlugins.length,
|
|
186
|
+
isExpanded: !communityCollapsed,
|
|
187
|
+
isCommunitySection: true,
|
|
188
|
+
});
|
|
189
|
+
if (!communityCollapsed) {
|
|
190
|
+
for (const plugin of communityPlugins) {
|
|
191
|
+
items.push({
|
|
192
|
+
id: `pl:${plugin.id}`,
|
|
193
|
+
type: "plugin",
|
|
194
|
+
label: plugin.name,
|
|
195
|
+
plugin,
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
|
|
131
204
|
// Category header (marketplace)
|
|
132
205
|
items.push({
|
|
133
206
|
id: `mp:${marketplace.name}`,
|
|
@@ -168,34 +241,47 @@ export function PluginsScreen() {
|
|
|
168
241
|
const pluginItems = allItems.filter((item) => item.type === "plugin");
|
|
169
242
|
const fuzzyResults = fuzzyFilter(pluginItems, query, (item) => item.label);
|
|
170
243
|
|
|
171
|
-
//
|
|
172
|
-
const
|
|
244
|
+
// Build a set of matched plugin item ids for O(1) lookup
|
|
245
|
+
const matchedPluginIds = new Set<string>();
|
|
173
246
|
for (const result of fuzzyResults) {
|
|
174
|
-
|
|
175
|
-
|
|
247
|
+
matchedPluginIds.add(result.item.id);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Walk allItems sequentially: track the current category section.
|
|
251
|
+
// For each category, include it only if any plugin under it matched.
|
|
252
|
+
// We build a map from category item id -> whether any plugin below matched.
|
|
253
|
+
const categoryHasMatch = new Map<string, boolean>();
|
|
254
|
+
let currentCategoryId: string | null = null;
|
|
255
|
+
for (const item of allItems) {
|
|
256
|
+
if (item.type === "category") {
|
|
257
|
+
currentCategoryId = item.id;
|
|
258
|
+
if (!categoryHasMatch.has(item.id)) {
|
|
259
|
+
categoryHasMatch.set(item.id, false);
|
|
260
|
+
}
|
|
261
|
+
} else if (item.type === "plugin" && currentCategoryId) {
|
|
262
|
+
if (matchedPluginIds.has(item.id)) {
|
|
263
|
+
categoryHasMatch.set(currentCategoryId, true);
|
|
264
|
+
}
|
|
176
265
|
}
|
|
177
266
|
}
|
|
178
267
|
|
|
179
268
|
const result: ListItem[] = [];
|
|
180
|
-
let
|
|
269
|
+
let currentCatIncluded = false;
|
|
270
|
+
currentCategoryId = null;
|
|
181
271
|
|
|
182
272
|
for (const item of allItems) {
|
|
183
|
-
if (item.type === "category"
|
|
184
|
-
|
|
273
|
+
if (item.type === "category") {
|
|
274
|
+
currentCategoryId = item.id;
|
|
275
|
+
currentCatIncluded = categoryHasMatch.get(item.id) === true;
|
|
276
|
+
if (currentCatIncluded) {
|
|
185
277
|
result.push(item);
|
|
186
|
-
currentMarketplace = item.marketplace.name;
|
|
187
|
-
} else {
|
|
188
|
-
currentMarketplace = null;
|
|
189
278
|
}
|
|
190
|
-
} else if (item.type === "plugin" &&
|
|
191
|
-
if (
|
|
192
|
-
// Check if this plugin matched
|
|
279
|
+
} else if (item.type === "plugin" && currentCatIncluded) {
|
|
280
|
+
if (matchedPluginIds.has(item.id)) {
|
|
193
281
|
const matched = fuzzyResults.find((r) => r.item.id === item.id);
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
});
|
|
198
|
-
}
|
|
282
|
+
result.push({ ...item, _matches: matched?.matches } as ListItem & {
|
|
283
|
+
_matches?: number[];
|
|
284
|
+
});
|
|
199
285
|
}
|
|
200
286
|
}
|
|
201
287
|
}
|
|
@@ -1005,7 +1091,10 @@ export function PluginsScreen() {
|
|
|
1005
1091
|
let statusText = "";
|
|
1006
1092
|
let statusColor = "green";
|
|
1007
1093
|
if (item.marketplaceEnabled) {
|
|
1008
|
-
if (
|
|
1094
|
+
if (item.isCommunitySection) {
|
|
1095
|
+
statusText = "3rd Party";
|
|
1096
|
+
statusColor = "gray";
|
|
1097
|
+
} else if (mp.name === "claude-plugins-official") {
|
|
1009
1098
|
statusText = "★ Official";
|
|
1010
1099
|
statusColor = "yellow";
|
|
1011
1100
|
} else if (mp.name === "claude-code-plugins") {
|
|
@@ -1131,6 +1220,7 @@ export function PluginsScreen() {
|
|
|
1131
1220
|
|
|
1132
1221
|
// Get appropriate badge for marketplace type
|
|
1133
1222
|
const getBadge = () => {
|
|
1223
|
+
if (selectedItem.isCommunitySection) return " 3rd Party";
|
|
1134
1224
|
if (mp.name === "claude-plugins-official") return " ★";
|
|
1135
1225
|
if (mp.name === "claude-code-plugins") return " ⚠";
|
|
1136
1226
|
if (mp.official) return " ★";
|