claudeup 3.13.0 → 3.15.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/adapters/skillsAdapter.js +105 -0
- package/src/ui/adapters/skillsAdapter.ts +159 -0
- package/src/ui/components/primitives/ActionHints.js +13 -0
- package/src/ui/components/primitives/ActionHints.tsx +41 -0
- package/src/ui/components/primitives/DetailSection.js +7 -0
- package/src/ui/components/primitives/DetailSection.tsx +22 -0
- package/src/ui/components/primitives/KeyValueLine.js +8 -0
- package/src/ui/components/primitives/KeyValueLine.tsx +19 -0
- package/src/ui/components/primitives/ListCategoryRow.js +8 -0
- package/src/ui/components/primitives/ListCategoryRow.tsx +38 -0
- package/src/ui/components/primitives/MetaText.js +8 -0
- package/src/ui/components/primitives/MetaText.tsx +14 -0
- package/src/ui/components/primitives/ScopeDetail.js +32 -0
- package/src/ui/components/primitives/ScopeDetail.tsx +67 -0
- package/src/ui/components/primitives/ScopeSquares.js +11 -0
- package/src/ui/components/primitives/ScopeSquares.tsx +33 -0
- package/src/ui/components/primitives/SelectableRow.js +5 -0
- package/src/ui/components/primitives/SelectableRow.tsx +24 -0
- package/src/ui/components/primitives/index.js +8 -0
- package/src/ui/components/primitives/index.ts +9 -0
- package/src/ui/registry.js +1 -0
- package/src/ui/registry.ts +27 -0
- package/src/ui/renderers/skillRenderers.js +82 -0
- package/src/ui/renderers/skillRenderers.tsx +209 -0
- package/src/ui/screens/PluginsScreen.js +3 -4
- package/src/ui/screens/PluginsScreen.tsx +10 -8
- package/src/ui/screens/SkillsScreen.js +123 -177
- package/src/ui/screens/SkillsScreen.tsx +439 -700
- package/src/ui/theme.js +47 -0
- package/src/ui/theme.ts +53 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type React from "react";
|
|
2
|
+
|
|
3
|
+
export interface RowRenderProps<T> {
|
|
4
|
+
item: T;
|
|
5
|
+
isSelected: boolean;
|
|
6
|
+
width?: number;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface DetailRenderProps<T> {
|
|
10
|
+
item: T;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface Hint {
|
|
14
|
+
key: string;
|
|
15
|
+
label: string;
|
|
16
|
+
tone?: "default" | "primary" | "danger";
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface ItemRenderer<T> {
|
|
20
|
+
renderRow: (props: RowRenderProps<T>) => React.ReactNode;
|
|
21
|
+
renderDetail: (props: DetailRenderProps<T>) => React.ReactNode;
|
|
22
|
+
getActionHints?: (item: T) => Hint[];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export type RendererRegistry<TItem extends { kind: string }> = {
|
|
26
|
+
[K in TItem["kind"]]: ItemRenderer<Extract<TItem, { kind: K }>>;
|
|
27
|
+
};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "@opentui/react/jsx-runtime";
|
|
2
|
+
import { SelectableRow, ListCategoryRow, ScopeSquares, ScopeDetail, ActionHints, MetaText, KeyValueLine, DetailSection, } from "../components/primitives/index.js";
|
|
3
|
+
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
|
4
|
+
function formatStars(stars) {
|
|
5
|
+
if (!stars)
|
|
6
|
+
return "";
|
|
7
|
+
if (stars >= 1000000)
|
|
8
|
+
return `★ ${(stars / 1000000).toFixed(1)}M`;
|
|
9
|
+
if (stars >= 10000)
|
|
10
|
+
return `★ ${Math.round(stars / 1000)}K`;
|
|
11
|
+
if (stars >= 1000)
|
|
12
|
+
return `★ ${(stars / 1000).toFixed(1)}K`;
|
|
13
|
+
return `★ ${stars}`;
|
|
14
|
+
}
|
|
15
|
+
// ─── Category renderer ────────────────────────────────────────────────────────
|
|
16
|
+
const categoryRenderer = {
|
|
17
|
+
renderRow: ({ item, isSelected }) => {
|
|
18
|
+
const label = `${item.star ?? ""}${item.title}`;
|
|
19
|
+
return (_jsx(ListCategoryRow, { selected: isSelected, title: label, count: item.count, badge: item.badge, tone: item.tone }));
|
|
20
|
+
},
|
|
21
|
+
renderDetail: ({ item }) => {
|
|
22
|
+
const isRec = item.categoryKey === "recommended";
|
|
23
|
+
return (_jsxs("box", { flexDirection: "column", children: [_jsx("text", { fg: isRec ? "green" : "cyan", children: _jsxs("strong", { children: [item.star ?? "", item.title] }) }), _jsx("box", { marginTop: 1, children: _jsx("text", { fg: "gray", children: isRec
|
|
24
|
+
? "Curated skills recommended for most projects"
|
|
25
|
+
: "Popular skills sorted by stars" }) })] }));
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
// ─── Skill renderer ───────────────────────────────────────────────────────────
|
|
29
|
+
const skillRenderer = {
|
|
30
|
+
renderRow: ({ item, isSelected }) => {
|
|
31
|
+
const { skill } = item;
|
|
32
|
+
const hasUser = skill.installedScope === "user";
|
|
33
|
+
const hasProject = skill.installedScope === "project";
|
|
34
|
+
const starsStr = formatStars(skill.stars);
|
|
35
|
+
return (_jsxs(SelectableRow, { selected: isSelected, indent: 1, children: [_jsx(ScopeSquares, { user: hasUser, project: hasProject, selected: isSelected }), _jsx("span", { children: " " }), _jsx("span", { fg: isSelected ? "white" : skill.installed ? "white" : "gray", children: skill.name }), skill.hasUpdate ? _jsx(MetaText, { text: " \u2B06", tone: "warning" }) : null, starsStr ? _jsx(MetaText, { text: ` ${starsStr}`, tone: "warning" }) : null] }));
|
|
36
|
+
},
|
|
37
|
+
renderDetail: ({ item }) => {
|
|
38
|
+
const { skill } = item;
|
|
39
|
+
const fm = skill.frontmatter;
|
|
40
|
+
const description = fm?.description || skill.description || "Loading...";
|
|
41
|
+
const starsStr = formatStars(skill.stars);
|
|
42
|
+
return (_jsxs("box", { flexDirection: "column", children: [_jsxs("text", { fg: "cyan", children: [_jsx("strong", { children: skill.name }), starsStr ? _jsxs("span", { fg: "yellow", children: [" ", starsStr] }) : null] }), _jsx("box", { marginTop: 1, children: _jsx("text", { fg: "white", children: description }) }), fm?.category ? (_jsx(KeyValueLine, { label: "Category", value: _jsx("span", { fg: "cyan", children: fm.category }) })) : null, fm?.author ? (_jsx(KeyValueLine, { label: "Author", value: _jsx("span", { children: fm.author }) })) : null, fm?.version ? (_jsx(KeyValueLine, { label: "Version", value: _jsx("span", { children: fm.version }) })) : null, fm?.tags && fm.tags.length > 0 ? (_jsx(KeyValueLine, { label: "Tags", value: _jsx("span", { children: fm.tags.join(", ") }) })) : null, _jsxs(DetailSection, { children: [_jsxs("text", { children: [_jsx("span", { fg: "gray", children: "Source " }), _jsx("span", { fg: "#5c9aff", children: skill.source.repo })] }), _jsxs("text", { children: [_jsx("span", { fg: "gray", children: " " }), _jsx("span", { fg: "gray", children: skill.repoPath })] })] }), skill.installed && skill.installedScope && (_jsxs(DetailSection, { children: [_jsx("text", { children: "─".repeat(24) }), _jsx(ScopeDetail, { scopes: {
|
|
43
|
+
user: skill.installedScope === "user",
|
|
44
|
+
project: skill.installedScope === "project",
|
|
45
|
+
}, paths: {
|
|
46
|
+
user: "~/.claude/skills/",
|
|
47
|
+
project: ".claude/skills/",
|
|
48
|
+
} })] })), skill.hasUpdate && (_jsx("box", { marginTop: 1, children: _jsxs("text", { bg: "yellow", fg: "black", children: [" ", "UPDATE AVAILABLE", " "] }) })), _jsx(ActionHints, { hints: skill.installed
|
|
49
|
+
? [
|
|
50
|
+
{ key: "d", label: "Uninstall", tone: "danger" },
|
|
51
|
+
{ key: "u/p", label: "Reinstall in user/project scope" },
|
|
52
|
+
{ key: "o", label: "Open in browser" },
|
|
53
|
+
]
|
|
54
|
+
: [
|
|
55
|
+
{ key: "u", label: "Install in user scope", tone: "primary" },
|
|
56
|
+
{ key: "p", label: "Install in project scope", tone: "primary" },
|
|
57
|
+
{ key: "o", label: "Open in browser" },
|
|
58
|
+
] })] }));
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
// ─── Registry ─────────────────────────────────────────────────────────────────
|
|
62
|
+
export const skillRenderers = {
|
|
63
|
+
category: categoryRenderer,
|
|
64
|
+
skill: skillRenderer,
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* Dispatch rendering by item kind.
|
|
68
|
+
*/
|
|
69
|
+
export function renderSkillRow(item, _index, isSelected) {
|
|
70
|
+
if (item.kind === "category") {
|
|
71
|
+
return skillRenderers.category.renderRow({ item, isSelected });
|
|
72
|
+
}
|
|
73
|
+
return skillRenderers.skill.renderRow({ item, isSelected });
|
|
74
|
+
}
|
|
75
|
+
export function renderSkillDetail(item) {
|
|
76
|
+
if (!item)
|
|
77
|
+
return _jsx("text", { fg: "gray", children: "Select a skill to see details" });
|
|
78
|
+
if (item.kind === "category") {
|
|
79
|
+
return skillRenderers.category.renderDetail({ item });
|
|
80
|
+
}
|
|
81
|
+
return skillRenderers.skill.renderDetail({ item });
|
|
82
|
+
}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { ItemRenderer } from "../registry.js";
|
|
3
|
+
import type { SkillBrowserItem, SkillCategoryItem, SkillSkillItem } from "../adapters/skillsAdapter.js";
|
|
4
|
+
import {
|
|
5
|
+
SelectableRow,
|
|
6
|
+
ListCategoryRow,
|
|
7
|
+
ScopeSquares,
|
|
8
|
+
ScopeDetail,
|
|
9
|
+
ActionHints,
|
|
10
|
+
MetaText,
|
|
11
|
+
KeyValueLine,
|
|
12
|
+
DetailSection,
|
|
13
|
+
} from "../components/primitives/index.js";
|
|
14
|
+
|
|
15
|
+
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
|
16
|
+
|
|
17
|
+
function formatStars(stars?: number): string {
|
|
18
|
+
if (!stars) return "";
|
|
19
|
+
if (stars >= 1000000) return `★ ${(stars / 1000000).toFixed(1)}M`;
|
|
20
|
+
if (stars >= 10000) return `★ ${Math.round(stars / 1000)}K`;
|
|
21
|
+
if (stars >= 1000) return `★ ${(stars / 1000).toFixed(1)}K`;
|
|
22
|
+
return `★ ${stars}`;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// ─── Category renderer ────────────────────────────────────────────────────────
|
|
26
|
+
|
|
27
|
+
const categoryRenderer: ItemRenderer<SkillCategoryItem> = {
|
|
28
|
+
renderRow: ({ item, isSelected }) => {
|
|
29
|
+
const label = `${item.star ?? ""}${item.title}`;
|
|
30
|
+
return (
|
|
31
|
+
<ListCategoryRow
|
|
32
|
+
selected={isSelected}
|
|
33
|
+
title={label}
|
|
34
|
+
count={item.count}
|
|
35
|
+
badge={item.badge}
|
|
36
|
+
tone={item.tone}
|
|
37
|
+
/>
|
|
38
|
+
);
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
renderDetail: ({ item }) => {
|
|
42
|
+
const isRec = item.categoryKey === "recommended";
|
|
43
|
+
return (
|
|
44
|
+
<box flexDirection="column">
|
|
45
|
+
<text fg={isRec ? "green" : "cyan"}>
|
|
46
|
+
<strong>
|
|
47
|
+
{item.star ?? ""}
|
|
48
|
+
{item.title}
|
|
49
|
+
</strong>
|
|
50
|
+
</text>
|
|
51
|
+
<box marginTop={1}>
|
|
52
|
+
<text fg="gray">
|
|
53
|
+
{isRec
|
|
54
|
+
? "Curated skills recommended for most projects"
|
|
55
|
+
: "Popular skills sorted by stars"}
|
|
56
|
+
</text>
|
|
57
|
+
</box>
|
|
58
|
+
</box>
|
|
59
|
+
);
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// ─── Skill renderer ───────────────────────────────────────────────────────────
|
|
64
|
+
|
|
65
|
+
const skillRenderer: ItemRenderer<SkillSkillItem> = {
|
|
66
|
+
renderRow: ({ item, isSelected }) => {
|
|
67
|
+
const { skill } = item;
|
|
68
|
+
const hasUser = skill.installedScope === "user";
|
|
69
|
+
const hasProject = skill.installedScope === "project";
|
|
70
|
+
const starsStr = formatStars(skill.stars);
|
|
71
|
+
|
|
72
|
+
return (
|
|
73
|
+
<SelectableRow selected={isSelected} indent={1}>
|
|
74
|
+
<ScopeSquares user={hasUser} project={hasProject} selected={isSelected} />
|
|
75
|
+
<span> </span>
|
|
76
|
+
<span fg={isSelected ? "white" : skill.installed ? "white" : "gray"}>
|
|
77
|
+
{skill.name}
|
|
78
|
+
</span>
|
|
79
|
+
{skill.hasUpdate ? <MetaText text=" ⬆" tone="warning" /> : null}
|
|
80
|
+
{starsStr ? <MetaText text={` ${starsStr}`} tone="warning" /> : null}
|
|
81
|
+
</SelectableRow>
|
|
82
|
+
);
|
|
83
|
+
},
|
|
84
|
+
|
|
85
|
+
renderDetail: ({ item }) => {
|
|
86
|
+
const { skill } = item;
|
|
87
|
+
const fm = skill.frontmatter;
|
|
88
|
+
const description = fm?.description || skill.description || "Loading...";
|
|
89
|
+
const starsStr = formatStars(skill.stars);
|
|
90
|
+
|
|
91
|
+
return (
|
|
92
|
+
<box flexDirection="column">
|
|
93
|
+
<text fg="cyan">
|
|
94
|
+
<strong>{skill.name}</strong>
|
|
95
|
+
{starsStr ? <span fg="yellow"> {starsStr}</span> : null}
|
|
96
|
+
</text>
|
|
97
|
+
|
|
98
|
+
<box marginTop={1}>
|
|
99
|
+
<text fg="white">{description}</text>
|
|
100
|
+
</box>
|
|
101
|
+
|
|
102
|
+
{fm?.category ? (
|
|
103
|
+
<KeyValueLine
|
|
104
|
+
label="Category"
|
|
105
|
+
value={<span fg="cyan">{fm.category}</span>}
|
|
106
|
+
/>
|
|
107
|
+
) : null}
|
|
108
|
+
{fm?.author ? (
|
|
109
|
+
<KeyValueLine label="Author" value={<span>{fm.author}</span>} />
|
|
110
|
+
) : null}
|
|
111
|
+
{fm?.version ? (
|
|
112
|
+
<KeyValueLine label="Version" value={<span>{fm.version}</span>} />
|
|
113
|
+
) : null}
|
|
114
|
+
{fm?.tags && fm.tags.length > 0 ? (
|
|
115
|
+
<KeyValueLine
|
|
116
|
+
label="Tags"
|
|
117
|
+
value={<span>{(fm.tags as string[]).join(", ")}</span>}
|
|
118
|
+
/>
|
|
119
|
+
) : null}
|
|
120
|
+
|
|
121
|
+
<DetailSection>
|
|
122
|
+
<text>
|
|
123
|
+
<span fg="gray">Source </span>
|
|
124
|
+
<span fg="#5c9aff">{skill.source.repo}</span>
|
|
125
|
+
</text>
|
|
126
|
+
<text>
|
|
127
|
+
<span fg="gray">{" "}</span>
|
|
128
|
+
<span fg="gray">{skill.repoPath}</span>
|
|
129
|
+
</text>
|
|
130
|
+
</DetailSection>
|
|
131
|
+
|
|
132
|
+
{skill.installed && skill.installedScope && (
|
|
133
|
+
<DetailSection>
|
|
134
|
+
<text>{"─".repeat(24)}</text>
|
|
135
|
+
<ScopeDetail
|
|
136
|
+
scopes={{
|
|
137
|
+
user: skill.installedScope === "user",
|
|
138
|
+
project: skill.installedScope === "project",
|
|
139
|
+
}}
|
|
140
|
+
paths={{
|
|
141
|
+
user: "~/.claude/skills/",
|
|
142
|
+
project: ".claude/skills/",
|
|
143
|
+
}}
|
|
144
|
+
/>
|
|
145
|
+
</DetailSection>
|
|
146
|
+
)}
|
|
147
|
+
|
|
148
|
+
{skill.hasUpdate && (
|
|
149
|
+
<box marginTop={1}>
|
|
150
|
+
<text bg="yellow" fg="black">
|
|
151
|
+
{" "}
|
|
152
|
+
UPDATE AVAILABLE{" "}
|
|
153
|
+
</text>
|
|
154
|
+
</box>
|
|
155
|
+
)}
|
|
156
|
+
|
|
157
|
+
<ActionHints
|
|
158
|
+
hints={
|
|
159
|
+
skill.installed
|
|
160
|
+
? [
|
|
161
|
+
{ key: "d", label: "Uninstall", tone: "danger" },
|
|
162
|
+
{ key: "u/p", label: "Reinstall in user/project scope" },
|
|
163
|
+
{ key: "o", label: "Open in browser" },
|
|
164
|
+
]
|
|
165
|
+
: [
|
|
166
|
+
{ key: "u", label: "Install in user scope", tone: "primary" },
|
|
167
|
+
{ key: "p", label: "Install in project scope", tone: "primary" },
|
|
168
|
+
{ key: "o", label: "Open in browser" },
|
|
169
|
+
]
|
|
170
|
+
}
|
|
171
|
+
/>
|
|
172
|
+
</box>
|
|
173
|
+
);
|
|
174
|
+
},
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
// ─── Registry ─────────────────────────────────────────────────────────────────
|
|
178
|
+
|
|
179
|
+
export const skillRenderers: {
|
|
180
|
+
category: ItemRenderer<SkillCategoryItem>;
|
|
181
|
+
skill: ItemRenderer<SkillSkillItem>;
|
|
182
|
+
} = {
|
|
183
|
+
category: categoryRenderer,
|
|
184
|
+
skill: skillRenderer,
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Dispatch rendering by item kind.
|
|
189
|
+
*/
|
|
190
|
+
export function renderSkillRow(
|
|
191
|
+
item: SkillBrowserItem,
|
|
192
|
+
_index: number,
|
|
193
|
+
isSelected: boolean,
|
|
194
|
+
): React.ReactNode {
|
|
195
|
+
if (item.kind === "category") {
|
|
196
|
+
return skillRenderers.category.renderRow({ item, isSelected });
|
|
197
|
+
}
|
|
198
|
+
return skillRenderers.skill.renderRow({ item, isSelected });
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export function renderSkillDetail(
|
|
202
|
+
item: SkillBrowserItem | undefined,
|
|
203
|
+
): React.ReactNode {
|
|
204
|
+
if (!item) return <text fg="gray">Select a skill to see details</text>;
|
|
205
|
+
if (item.kind === "category") {
|
|
206
|
+
return skillRenderers.category.renderDetail({ item });
|
|
207
|
+
}
|
|
208
|
+
return skillRenderers.skill.renderDetail({ item });
|
|
209
|
+
}
|
|
@@ -901,8 +901,7 @@ export function PluginsScreen() {
|
|
|
901
901
|
const matches = item._matches;
|
|
902
902
|
const segments = matches ? highlightMatches(plugin.name, matches) : null;
|
|
903
903
|
if (isSelected) {
|
|
904
|
-
|
|
905
|
-
return (_jsxs("text", { bg: "magenta", fg: "white", children: [" ", scopeStr, " ", plugin.name, versionStr, " "] }));
|
|
904
|
+
return (_jsxs("text", { bg: "magenta", fg: "white", children: [" ", _jsx("span", { children: hasUser ? "■" : "□" }), _jsx("span", { children: hasProject ? "■" : "□" }), _jsx("span", { children: hasLocal ? "■" : "□" }), " ", plugin.name, versionStr, " "] }));
|
|
906
905
|
}
|
|
907
906
|
const displayName = segments
|
|
908
907
|
? segments.map((seg) => seg.text).join("")
|
|
@@ -910,9 +909,9 @@ export function PluginsScreen() {
|
|
|
910
909
|
if (plugin.isOrphaned) {
|
|
911
910
|
const ver = plugin.installedVersion && plugin.installedVersion !== "0.0.0"
|
|
912
911
|
? ` v${plugin.installedVersion}` : "";
|
|
913
|
-
return (_jsxs("text", { children: [_jsx("span", { fg: "red", children: "
|
|
912
|
+
return (_jsxs("text", { children: [_jsx("span", { fg: "red", children: " \u25A0\u25A0\u25A0 " }), _jsx("span", { fg: "gray", children: displayName }), ver && _jsx("span", { fg: "yellow", children: ver }), _jsx("span", { fg: "red", children: " deprecated" })] }));
|
|
914
913
|
}
|
|
915
|
-
return (_jsxs("text", { children: [_jsx("span", {
|
|
914
|
+
return (_jsxs("text", { children: [_jsx("span", { children: " " }), _jsx("span", { fg: hasUser ? "cyan" : "#333333", children: "\u25A0" }), _jsx("span", { fg: hasProject ? "green" : "#333333", children: "\u25A0" }), _jsx("span", { fg: hasLocal ? "yellow" : "#333333", children: "\u25A0" }), _jsx("span", { children: " " }), _jsx("span", { fg: hasAnyScope ? "white" : "gray", children: displayName }), _jsx("span", { fg: plugin.hasUpdate ? "yellow" : "gray", children: versionStr })] }));
|
|
916
915
|
}
|
|
917
916
|
return _jsx("text", { fg: "gray", children: item.label });
|
|
918
917
|
};
|
|
@@ -1162,10 +1162,13 @@ export function PluginsScreen() {
|
|
|
1162
1162
|
const segments = matches ? highlightMatches(plugin.name, matches) : null;
|
|
1163
1163
|
|
|
1164
1164
|
if (isSelected) {
|
|
1165
|
-
const scopeStr = `[${hasUser ? "u" : "."}${hasProject ? "p" : "."}${hasLocal ? "l" : "."}]`;
|
|
1166
1165
|
return (
|
|
1167
1166
|
<text bg="magenta" fg="white">
|
|
1168
|
-
{" "}
|
|
1167
|
+
{" "}
|
|
1168
|
+
<span>{hasUser ? "■" : "□"}</span>
|
|
1169
|
+
<span>{hasProject ? "■" : "□"}</span>
|
|
1170
|
+
<span>{hasLocal ? "■" : "□"}</span>
|
|
1171
|
+
{" "}{plugin.name}{versionStr}{" "}
|
|
1169
1172
|
</text>
|
|
1170
1173
|
);
|
|
1171
1174
|
}
|
|
@@ -1179,7 +1182,7 @@ export function PluginsScreen() {
|
|
|
1179
1182
|
? ` v${plugin.installedVersion}` : "";
|
|
1180
1183
|
return (
|
|
1181
1184
|
<text>
|
|
1182
|
-
<span fg="red">
|
|
1185
|
+
<span fg="red"> ■■■ </span>
|
|
1183
1186
|
<span fg="gray">{displayName}</span>
|
|
1184
1187
|
{ver && <span fg="yellow">{ver}</span>}
|
|
1185
1188
|
<span fg="red"> deprecated</span>
|
|
@@ -1189,11 +1192,10 @@ export function PluginsScreen() {
|
|
|
1189
1192
|
|
|
1190
1193
|
return (
|
|
1191
1194
|
<text>
|
|
1192
|
-
<span
|
|
1193
|
-
<span fg={hasUser ? "cyan" : "#
|
|
1194
|
-
<span fg={hasProject ? "green" : "#
|
|
1195
|
-
<span fg={hasLocal ? "yellow" : "#
|
|
1196
|
-
<span fg="#555555">]</span>
|
|
1195
|
+
<span> </span>
|
|
1196
|
+
<span fg={hasUser ? "cyan" : "#333333"}>■</span>
|
|
1197
|
+
<span fg={hasProject ? "green" : "#333333"}>■</span>
|
|
1198
|
+
<span fg={hasLocal ? "yellow" : "#333333"}>■</span>
|
|
1197
1199
|
<span> </span>
|
|
1198
1200
|
<span fg={hasAnyScope ? "white" : "gray"}>{displayName}</span>
|
|
1199
1201
|
<span fg={plugin.hasUpdate ? "yellow" : "gray"}>{versionStr}</span>
|