claudeup 4.18.0 → 4.22.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/bin/claudeup.js +43 -19
- package/package.json +14 -19
- package/scripts/build-binaries.ts +77 -0
- package/scripts/check-optional-deps.ts +49 -0
- package/scripts/test-isolated.ts +43 -0
- package/src/__tests__/alias-adopt.test.ts +364 -0
- package/src/__tests__/alias-parser.test.ts +92 -0
- package/src/__tests__/alias-shell-writer.test.ts +7 -0
- package/src/__tests__/alias-store.test.ts +77 -0
- package/src/__tests__/cli-router.test.ts +65 -0
- package/src/__tests__/conventions-integration.test.ts +745 -0
- package/src/__tests__/conventions-manager.test.ts +1172 -0
- package/src/__tests__/doctor-bins.test.ts +35 -0
- package/src/__tests__/doctor.test.ts +450 -0
- package/src/__tests__/dual-write-prevention.test.ts +14 -4
- package/src/__tests__/file-locking.test.ts +208 -0
- package/src/__tests__/gitignore-prerun.test.ts +1 -0
- package/src/__tests__/install-command.test.ts +81 -0
- package/src/__tests__/install-plan.test.ts +98 -0
- package/src/__tests__/manifest.test.ts +136 -0
- package/src/__tests__/marketplace-refresh.test.ts +267 -0
- package/src/__tests__/plugin-requires.test.ts +133 -0
- package/src/__tests__/plugin-setup.test.ts +118 -0
- package/src/__tests__/profile-command.test.ts +116 -0
- package/src/__tests__/profile-materializer.test.ts +82 -0
- package/src/__tests__/profile-sync.test.ts +136 -0
- package/src/__tests__/registry-version-resolution.test.ts +74 -0
- package/src/__tests__/resolve-executable.test.ts +42 -0
- package/src/__tests__/resolver.test.ts +218 -0
- package/src/__tests__/symlink-manager.test.ts +99 -0
- package/src/__tests__/toolchain.test.ts +56 -0
- package/src/cli/claude.ts +21 -0
- package/src/cli/doctor.ts +132 -0
- package/src/cli/install.ts +360 -0
- package/src/cli/profile.ts +166 -0
- package/src/cli/router.ts +107 -0
- package/src/cli/update.ts +85 -0
- package/src/data/alias-flags.ts +11 -1
- package/src/data/cli-tools.ts +7 -7
- package/src/data/gitignore-defaults.ts +4 -0
- package/src/data/marketplaces.ts +12 -0
- package/src/data/predefined-profiles.ts +3 -4
- package/src/main.tsx +11 -154
- package/src/prerunner/index.ts +62 -27
- package/src/services/alias-shell-writer.ts +382 -8
- package/src/services/alias-store.ts +60 -0
- package/src/services/claude-cli.ts +19 -22
- package/src/services/claude-settings.ts +99 -26
- package/src/services/conventions-manager.ts +865 -0
- package/src/services/doctor-bins.ts +49 -0
- package/src/services/doctor.ts +509 -0
- package/src/services/install-plan.ts +107 -0
- package/src/services/manifest.ts +166 -0
- package/src/services/marketplace-refresh.ts +173 -0
- package/src/services/plugin-manager.ts +7 -1
- package/src/services/plugin-requires.ts +215 -0
- package/src/services/plugin-setup.ts +61 -4
- package/src/services/plugin-version-check.ts +9 -4
- package/src/services/profile-materializer.ts +61 -0
- package/src/services/profile-sync.ts +150 -0
- package/src/services/resolver.ts +293 -0
- package/src/services/symlink-manager.ts +146 -0
- package/src/services/toolchain.ts +97 -0
- package/src/services/version-check.ts +10 -11
- package/src/types/index.ts +138 -31
- package/src/ui/App.tsx +16 -11
- package/src/ui/components/FlagDetailEditor.tsx +0 -0
- package/src/ui/components/modals/ConfirmModal.tsx +1 -1
- package/src/ui/screens/AliasScreen.tsx +491 -359
- package/src/ui/screens/CliToolsScreen.tsx +11 -3
- package/src/ui/screens/PluginsScreen.tsx +3 -1
- package/src/ui/screens/index.ts +0 -1
- package/src/ui/state/reducer.ts +6 -102
- package/src/ui/state/types.ts +8 -35
- package/src/utils/command-utils.ts +23 -0
- package/src/utils/file-locking.ts +144 -0
- package/src/data/alias-flags.js +0 -196
- package/src/data/cli-tools.js +0 -123
- package/src/data/gitignore-defaults.js +0 -24
- package/src/data/gitignore-reasons.js +0 -97
- package/src/data/gitignore-templates.js +0 -21
- package/src/data/marketplaces.js +0 -138
- package/src/data/mcp-servers.js +0 -509
- package/src/data/predefined-profiles.js +0 -248
- package/src/data/settings-catalog.js +0 -625
- package/src/data/skill-repos.js +0 -160
- package/src/data/statuslines.js +0 -159
- package/src/data/statuslines.ts +0 -188
- package/src/index.js +0 -4
- package/src/index.ts +0 -5
- package/src/main.js +0 -170
- package/src/prerunner/index.js +0 -252
- package/src/services/alias-settings.js +0 -51
- package/src/services/alias-shell-writer.js +0 -764
- package/src/services/alias-store.js +0 -77
- package/src/services/claude-cli.js +0 -189
- package/src/services/claude-runner.js +0 -28
- package/src/services/claude-settings.js +0 -1223
- package/src/services/gitignore-detector.js +0 -155
- package/src/services/gitignore-fixer.js +0 -231
- package/src/services/gitignore-prerun.js +0 -46
- package/src/services/gitignore-resolver.js +0 -143
- package/src/services/gitignore-service.js +0 -117
- package/src/services/local-marketplace.js +0 -339
- package/src/services/marketplace-fetcher.js +0 -96
- package/src/services/marketplace-sync.js +0 -94
- package/src/services/mcp-registry.js +0 -87
- package/src/services/plugin-manager.js +0 -473
- package/src/services/plugin-mcp-config.js +0 -177
- package/src/services/plugin-setup.js +0 -444
- package/src/services/plugin-version-check.js +0 -262
- package/src/services/profiles.js +0 -161
- package/src/services/settings-manager.js +0 -243
- package/src/services/skills-manager.js +0 -393
- package/src/services/skillsmp-client.js +0 -87
- package/src/services/update-cache.js +0 -52
- package/src/services/version-check.js +0 -99
- package/src/types/gitignore.js +0 -6
- package/src/types/index.js +0 -1
- package/src/ui/App.js +0 -346
- package/src/ui/adapters/pluginsAdapter.js +0 -139
- package/src/ui/adapters/settingsAdapter.js +0 -111
- package/src/ui/adapters/skillsAdapter.js +0 -154
- package/src/ui/components/CategoryHeader.js +0 -9
- package/src/ui/components/EmptyFilterState.js +0 -4
- package/src/ui/components/ScopeIndicator.js +0 -30
- package/src/ui/components/ScrollableList.js +0 -36
- package/src/ui/components/SearchInput.js +0 -19
- package/src/ui/components/StyledText.js +0 -39
- package/src/ui/components/TabBar.js +0 -19
- package/src/ui/components/layout/FooterHints.js +0 -29
- package/src/ui/components/layout/Panel.js +0 -6
- package/src/ui/components/layout/ProgressBar.js +0 -14
- package/src/ui/components/layout/ScopeTabs.js +0 -6
- package/src/ui/components/layout/ScreenLayout.js +0 -16
- package/src/ui/components/layout/index.js +0 -5
- package/src/ui/components/modals/ConfirmModal.js +0 -14
- package/src/ui/components/modals/InputModal.js +0 -5
- package/src/ui/components/modals/LoadingModal.js +0 -17
- package/src/ui/components/modals/MessageModal.js +0 -16
- package/src/ui/components/modals/ModalContainer.js +0 -137
- package/src/ui/components/modals/SelectModal.js +0 -18
- package/src/ui/components/modals/VersionMismatchModal.js +0 -36
- package/src/ui/components/modals/index.js +0 -6
- package/src/ui/components/primitives/ActionHints.js +0 -13
- package/src/ui/components/primitives/DetailSection.js +0 -7
- package/src/ui/components/primitives/KeyValueLine.js +0 -8
- package/src/ui/components/primitives/ListCategoryRow.js +0 -8
- package/src/ui/components/primitives/MetaText.js +0 -8
- package/src/ui/components/primitives/ScopeDetail.js +0 -32
- package/src/ui/components/primitives/ScopeSquares.js +0 -11
- package/src/ui/components/primitives/SelectableRow.js +0 -5
- package/src/ui/components/primitives/index.js +0 -8
- package/src/ui/hooks/index.js +0 -4
- package/src/ui/hooks/useAsyncData.js +0 -77
- package/src/ui/hooks/useGitignoreModal.js +0 -74
- package/src/ui/hooks/useKeyboard.js +0 -13
- package/src/ui/hooks/useKeyboardHandler.js +0 -39
- package/src/ui/hooks/useMismatchModal.js +0 -77
- package/src/ui/registry.js +0 -1
- package/src/ui/renderers/cliToolRenderers.js +0 -54
- package/src/ui/renderers/gitignoreRenderers.js +0 -46
- package/src/ui/renderers/mcpRenderers.js +0 -26
- package/src/ui/renderers/pluginRenderers.js +0 -124
- package/src/ui/renderers/profileRenderers.js +0 -177
- package/src/ui/renderers/settingsRenderers.js +0 -73
- package/src/ui/renderers/skillRenderers.js +0 -138
- package/src/ui/screens/AliasScreen.js +0 -1008
- package/src/ui/screens/CliToolsScreen.js +0 -338
- package/src/ui/screens/EnvVarsScreen.js +0 -152
- package/src/ui/screens/GitignoreScreen.js +0 -328
- package/src/ui/screens/McpRegistryScreen.js +0 -238
- package/src/ui/screens/McpScreen.js +0 -176
- package/src/ui/screens/ModelSelectorScreen.js +0 -296
- package/src/ui/screens/ModelSelectorScreen.tsx +0 -444
- package/src/ui/screens/PluginsScreen.js +0 -766
- package/src/ui/screens/ProfilesScreen.js +0 -298
- package/src/ui/screens/SkillsScreen.js +0 -549
- package/src/ui/screens/StatusLineScreen.js +0 -206
- package/src/ui/screens/StatusLineScreen.tsx +0 -416
- package/src/ui/screens/index.js +0 -10
- package/src/ui/state/AnimationContext.js +0 -34
- package/src/ui/state/AppContext.js +0 -162
- package/src/ui/state/DimensionsContext.js +0 -71
- package/src/ui/state/reducer.js +0 -543
- package/src/ui/state/types.js +0 -1
- package/src/ui/theme.js +0 -47
- package/src/utils/clipboard.js +0 -56
- package/src/utils/command-utils.js +0 -19
- package/src/utils/fuzzy-search.js +0 -101
- package/src/utils/string-utils.js +0 -62
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "@opentui/react/jsx-runtime";
|
|
2
|
-
const COLOR_BG = "#1C1C1E";
|
|
3
|
-
const COLOR_BORDER = "#525252";
|
|
4
|
-
const COLOR_TITLE = "#EDEDED";
|
|
5
|
-
const COLOR_MUTED = "#A1A1AA";
|
|
6
|
-
const COLOR_DIM = "#71717A";
|
|
7
|
-
const COLOR_WARN = "#FBBF24";
|
|
8
|
-
const COLOR_BAD = "#F87171";
|
|
9
|
-
const COLOR_GOOD = "#4ADE80";
|
|
10
|
-
const COLOR_ACCENT = "#F4F4F5";
|
|
11
|
-
/** Truncate a plugin name to fit the column width */
|
|
12
|
-
function shortName(pluginId, max) {
|
|
13
|
-
const name = pluginId.split("@")[0];
|
|
14
|
-
if (name.length <= max)
|
|
15
|
-
return name;
|
|
16
|
-
return `${name.slice(0, max - 1)}…`;
|
|
17
|
-
}
|
|
18
|
-
/** Truncate a version string to fit the column width */
|
|
19
|
-
function shortVersion(v, max) {
|
|
20
|
-
if (v.length <= max)
|
|
21
|
-
return v;
|
|
22
|
-
return `${v.slice(0, max - 1)}…`;
|
|
23
|
-
}
|
|
24
|
-
export function VersionMismatchModal({ mismatches, defaultIndex, }) {
|
|
25
|
-
const selectedIndex = defaultIndex ?? 0;
|
|
26
|
-
// Column widths chosen to fit a 64-wide modal with padding
|
|
27
|
-
const NAME_W = 22;
|
|
28
|
-
const VER_W = 12;
|
|
29
|
-
return (_jsxs("box", { flexDirection: "column", border: true, borderStyle: "rounded", borderColor: COLOR_BORDER, backgroundColor: COLOR_BG, paddingLeft: 3, paddingRight: 3, paddingTop: 1, paddingBottom: 1, width: 64, children: [_jsx("box", { marginBottom: 1, children: _jsxs("text", { fg: COLOR_TITLE, children: [_jsx("span", { fg: COLOR_WARN, children: "\u26A0 " }), _jsx("strong", { children: "Plugin Version Mismatch" })] }) }), _jsxs("box", { flexDirection: "column", marginBottom: 1, children: [_jsxs("text", { fg: COLOR_MUTED, children: ["Plugins in this project will load ", _jsx("strong", { children: "wrong versions" })] }), _jsx("text", { fg: COLOR_MUTED, children: "due to a Claude Code bug (#45997)." })] }), _jsxs("box", { flexDirection: "column", marginBottom: 1, children: [_jsx("text", { fg: COLOR_DIM, children: _jsxs("strong", { children: ["Plugin".padEnd(NAME_W), "Loaded".padEnd(VER_W), "Expected".padEnd(VER_W)] }) }), _jsxs("text", { fg: COLOR_BORDER, children: ["─".repeat(NAME_W - 1), " ", "─".repeat(VER_W - 1), " ", "─".repeat(VER_W - 1)] }), mismatches.map((m) => {
|
|
30
|
-
const name = shortName(m.pluginId, NAME_W - 1);
|
|
31
|
-
const loaded = `v${shortVersion(m.firstEntryVersion, VER_W - 2)}`;
|
|
32
|
-
const expected = `v${shortVersion(m.currentProjectVersion, VER_W - 2)}`;
|
|
33
|
-
return (_jsxs("text", { fg: COLOR_MUTED, children: [_jsx("span", { fg: COLOR_ACCENT, children: name.padEnd(NAME_W) }), _jsx("span", { fg: COLOR_BAD, children: loaded.padEnd(VER_W) }), _jsx("span", { fg: COLOR_GOOD, children: expected.padEnd(VER_W) })] }, m.pluginId));
|
|
34
|
-
})] }), _jsx("box", { marginBottom: 1, children: _jsx("text", { fg: COLOR_DIM, children: "Details: github.com/anthropics/claude-code/issues/45997" }) }), _jsxs("box", { flexDirection: "column", paddingLeft: 1, children: [_jsxs("text", { fg: selectedIndex === 0 ? COLOR_ACCENT : COLOR_MUTED, children: [_jsx("span", { fg: selectedIndex === 0 ? COLOR_ACCENT : COLOR_DIM, children: selectedIndex === 0 ? "❯ " : " " }), selectedIndex === 0 ? (_jsx("strong", { children: "Fix all projects" })) : ("Fix all projects")] }), _jsxs("text", { fg: selectedIndex === 1 ? COLOR_ACCENT : COLOR_MUTED, children: [_jsx("span", { fg: selectedIndex === 1 ? COLOR_ACCENT : COLOR_DIM, children: selectedIndex === 1 ? "❯ " : " " }), selectedIndex === 1 ? _jsx("strong", { children: "Dismiss" }) : "Dismiss"] })] }), _jsx("box", { marginTop: 1, children: _jsx("text", { fg: COLOR_DIM, children: "\u2191\u2193 Select \u2022 \u21B5 Confirm \u2022 Esc Cancel" }) })] }));
|
|
35
|
-
}
|
|
36
|
-
export default VersionMismatchModal;
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
export { ConfirmModal } from "./ConfirmModal.js";
|
|
2
|
-
export { InputModal } from "./InputModal.js";
|
|
3
|
-
export { SelectModal } from "./SelectModal.js";
|
|
4
|
-
export { MessageModal } from "./MessageModal.js";
|
|
5
|
-
export { LoadingModal } from "./LoadingModal.js";
|
|
6
|
-
export { ModalContainer } from "./ModalContainer.js";
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { jsxs as _jsxs, jsx as _jsx } from "@opentui/react/jsx-runtime";
|
|
2
|
-
import { theme } from "../../theme.js";
|
|
3
|
-
/**
|
|
4
|
-
* Keyboard shortcut badges for detail panels.
|
|
5
|
-
* Renders: [bg] key [/bg] label
|
|
6
|
-
*/
|
|
7
|
-
export function ActionHints({ hints }) {
|
|
8
|
-
return (_jsx("box", { flexDirection: "column", marginTop: 1, children: hints.map((hint) => (_jsxs("box", { children: [_jsxs("text", { bg: hint.tone === "danger"
|
|
9
|
-
? theme.hints.dangerBg
|
|
10
|
-
: hint.tone === "primary"
|
|
11
|
-
? theme.hints.primaryBg
|
|
12
|
-
: theme.hints.defaultBg, fg: "black", children: [" ", hint.key, " "] }), _jsxs("text", { fg: "gray", children: [" ", hint.label] })] }, `${hint.key}:${hint.label}`))) }));
|
|
13
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "@opentui/react/jsx-runtime";
|
|
2
|
-
/**
|
|
3
|
-
* A labeled section in a detail panel with optional title header.
|
|
4
|
-
*/
|
|
5
|
-
export function DetailSection({ title, children }) {
|
|
6
|
-
return (_jsxs("box", { flexDirection: "column", marginTop: 1, children: [title ? (_jsx("text", { fg: "cyan", children: _jsx("strong", { children: title }) })) : null, children] }));
|
|
7
|
-
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "@opentui/react/jsx-runtime";
|
|
2
|
-
/**
|
|
3
|
-
* Aligned label-value pair for detail panels.
|
|
4
|
-
* Label is padded to 10 chars for consistent alignment.
|
|
5
|
-
*/
|
|
6
|
-
export function KeyValueLine({ label, value }) {
|
|
7
|
-
return (_jsxs("text", { children: [_jsx("span", { fg: "gray", children: label.padEnd(10) }), value] }));
|
|
8
|
-
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { jsxs as _jsxs, jsx as _jsx } from "@opentui/react/jsx-runtime";
|
|
2
|
-
import { theme } from "../../theme.js";
|
|
3
|
-
import { SelectableRow } from "./SelectableRow.js";
|
|
4
|
-
export function ListCategoryRow({ title, expanded = false, count, badge, tone = "gray", selected, }) {
|
|
5
|
-
const colors = theme.category[tone];
|
|
6
|
-
const label = `${expanded ? "▼" : "▶"} ${title}${count !== undefined ? ` (${count})` : ""}`;
|
|
7
|
-
return (_jsxs(SelectableRow, { selected: selected, children: [!selected && (_jsx("span", { bg: colors.bg, fg: colors.fg, children: _jsxs("strong", { children: [" ", label, " "] }) })), selected && _jsxs("strong", { children: [" ", label, " "] }), !selected && badge ? (_jsxs("span", { fg: colors.badgeFg, children: [" ", badge] })) : null] }));
|
|
8
|
-
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "@opentui/react/jsx-runtime";
|
|
2
|
-
import { theme } from "../../theme.js";
|
|
3
|
-
/**
|
|
4
|
-
* Subdued text for versions, stars, status indicators.
|
|
5
|
-
*/
|
|
6
|
-
export function MetaText({ text, tone = "muted" }) {
|
|
7
|
-
return _jsx("span", { fg: theme.meta[tone], children: text });
|
|
8
|
-
}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { jsxs as _jsxs, jsx as _jsx } from "@opentui/react/jsx-runtime";
|
|
2
|
-
import { theme } from "../../theme.js";
|
|
3
|
-
/**
|
|
4
|
-
* The u/p/l badges used in detail panels.
|
|
5
|
-
* Renders each scope as: [bg=color] key [/bg] ● or ○ Label path
|
|
6
|
-
*/
|
|
7
|
-
export function ScopeDetail({ scopes, paths }) {
|
|
8
|
-
const items = [
|
|
9
|
-
{
|
|
10
|
-
key: "u",
|
|
11
|
-
label: "User",
|
|
12
|
-
color: theme.scopes.user,
|
|
13
|
-
active: scopes.user,
|
|
14
|
-
path: paths?.user,
|
|
15
|
-
},
|
|
16
|
-
{
|
|
17
|
-
key: "p",
|
|
18
|
-
label: "Project",
|
|
19
|
-
color: theme.scopes.project,
|
|
20
|
-
active: scopes.project,
|
|
21
|
-
path: paths?.project,
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
key: "l",
|
|
25
|
-
label: "Local",
|
|
26
|
-
color: theme.scopes.local,
|
|
27
|
-
active: scopes.local,
|
|
28
|
-
path: paths?.local,
|
|
29
|
-
},
|
|
30
|
-
].filter((i) => i.path !== undefined || i.active !== undefined);
|
|
31
|
-
return (_jsx("box", { flexDirection: "column", children: items.map((item) => (_jsxs("text", { children: [_jsxs("span", { bg: item.color, fg: "black", children: [" ", item.key, " "] }), _jsx("span", { fg: item.active ? item.color : "gray", children: item.active ? " ● " : " ○ " }), _jsx("span", { fg: item.color, children: item.label }), item.path && _jsxs("span", { fg: "gray", children: [" ", item.path] })] }, item.key))) }));
|
|
32
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "@opentui/react/jsx-runtime";
|
|
2
|
-
import { theme } from "../../theme.js";
|
|
3
|
-
/**
|
|
4
|
-
* Colored filled/empty squares for scope display in list items.
|
|
5
|
-
* ■ = installed in that scope, □ = not installed.
|
|
6
|
-
*/
|
|
7
|
-
export function ScopeSquares({ user, project, local, selected = false, }) {
|
|
8
|
-
const inactive = selected ? "white" : theme.colors.dim;
|
|
9
|
-
// Returns fragments of <span> — safe to embed inside a <text> parent
|
|
10
|
-
return (_jsxs(_Fragment, { children: [_jsx("span", { fg: user ? theme.scopes.user : inactive, children: "\u25A0" }), _jsx("span", { fg: project ? theme.scopes.project : inactive, children: "\u25A0" }), local !== undefined && (_jsx("span", { fg: local ? theme.scopes.local : inactive, children: "\u25A0" }))] }));
|
|
11
|
-
}
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import { jsxs as _jsxs } from "@opentui/react/jsx-runtime";
|
|
2
|
-
import { theme } from "../../theme.js";
|
|
3
|
-
export function SelectableRow({ selected, indent = 0, children, }) {
|
|
4
|
-
return (_jsxs("text", { bg: selected ? theme.selection.bg : undefined, fg: selected ? theme.selection.fg : undefined, children: [" ".repeat(indent), children] }));
|
|
5
|
-
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
export { SelectableRow } from "./SelectableRow.js";
|
|
2
|
-
export { ListCategoryRow } from "./ListCategoryRow.js";
|
|
3
|
-
export { ScopeSquares } from "./ScopeSquares.js";
|
|
4
|
-
export { ScopeDetail } from "./ScopeDetail.js";
|
|
5
|
-
export { ActionHints } from "./ActionHints.js";
|
|
6
|
-
export { MetaText } from "./MetaText.js";
|
|
7
|
-
export { KeyValueLine } from "./KeyValueLine.js";
|
|
8
|
-
export { DetailSection } from "./DetailSection.js";
|
package/src/ui/hooks/index.js
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import { useState, useEffect, useCallback } from "react";
|
|
2
|
-
/**
|
|
3
|
-
* Hook for fetching async data with loading/error states
|
|
4
|
-
*/
|
|
5
|
-
export function useAsyncData({ fetcher, deps = [], immediate = true, }) {
|
|
6
|
-
const [data, setData] = useState({ status: "idle" });
|
|
7
|
-
const refetch = useCallback(async () => {
|
|
8
|
-
setData({ status: "loading" });
|
|
9
|
-
try {
|
|
10
|
-
const result = await fetcher();
|
|
11
|
-
setData({ status: "success", data: result });
|
|
12
|
-
}
|
|
13
|
-
catch (err) {
|
|
14
|
-
setData({
|
|
15
|
-
status: "error",
|
|
16
|
-
error: err instanceof Error ? err : new Error(String(err)),
|
|
17
|
-
});
|
|
18
|
-
}
|
|
19
|
-
}, [fetcher]);
|
|
20
|
-
useEffect(() => {
|
|
21
|
-
if (immediate) {
|
|
22
|
-
refetch();
|
|
23
|
-
}
|
|
24
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
25
|
-
}, deps);
|
|
26
|
-
return {
|
|
27
|
-
data,
|
|
28
|
-
isLoading: data.status === "loading",
|
|
29
|
-
isSuccess: data.status === "success",
|
|
30
|
-
isError: data.status === "error",
|
|
31
|
-
error: data.status === "error" ? data.error : null,
|
|
32
|
-
refetch,
|
|
33
|
-
value: data.status === "success" ? data.data : null,
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
/**
|
|
37
|
-
* Hook for fetching async data with debounced query
|
|
38
|
-
*/
|
|
39
|
-
export function useDebouncedAsyncData({ fetcher, query, debounceMs = 300, minQueryLength = 1, }) {
|
|
40
|
-
const [data, setData] = useState({ status: "idle" });
|
|
41
|
-
const refetch = useCallback(async () => {
|
|
42
|
-
if (query.length < minQueryLength) {
|
|
43
|
-
setData({ status: "idle" });
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
setData({ status: "loading" });
|
|
47
|
-
try {
|
|
48
|
-
const result = await fetcher(query);
|
|
49
|
-
setData({ status: "success", data: result });
|
|
50
|
-
}
|
|
51
|
-
catch (err) {
|
|
52
|
-
setData({
|
|
53
|
-
status: "error",
|
|
54
|
-
error: err instanceof Error ? err : new Error(String(err)),
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
}, [fetcher, query, minQueryLength]);
|
|
58
|
-
useEffect(() => {
|
|
59
|
-
if (query.length < minQueryLength) {
|
|
60
|
-
setData({ status: "idle" });
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
const timeoutId = setTimeout(() => {
|
|
64
|
-
refetch();
|
|
65
|
-
}, debounceMs);
|
|
66
|
-
return () => clearTimeout(timeoutId);
|
|
67
|
-
}, [query, debounceMs, minQueryLength, refetch]);
|
|
68
|
-
return {
|
|
69
|
-
data,
|
|
70
|
-
isLoading: data.status === "loading",
|
|
71
|
-
isSuccess: data.status === "success",
|
|
72
|
-
isError: data.status === "error",
|
|
73
|
-
error: data.status === "error" ? data.error : null,
|
|
74
|
-
refetch,
|
|
75
|
-
value: data.status === "success" ? data.data : null,
|
|
76
|
-
};
|
|
77
|
-
}
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import { useCallback } from "react";
|
|
2
|
-
import { useApp, useNavigation } from "../state/AppContext.js";
|
|
3
|
-
export function buildGitignoreModal(args) {
|
|
4
|
-
const { violationCount, safeCount, onFixSafe, onOpenTab, onDismiss } = args;
|
|
5
|
-
const options = [
|
|
6
|
-
{
|
|
7
|
-
label: safeCount > 0
|
|
8
|
-
? `Add ${safeCount} missing .gitignore entr${safeCount === 1 ? "y" : "ies"}`
|
|
9
|
-
: "(no missing .gitignore entries)",
|
|
10
|
-
value: "fix-safe",
|
|
11
|
-
},
|
|
12
|
-
{
|
|
13
|
-
label: "Open Git State tab to review",
|
|
14
|
-
value: "open-tab",
|
|
15
|
-
},
|
|
16
|
-
{
|
|
17
|
-
label: "Dismiss",
|
|
18
|
-
value: "dismiss",
|
|
19
|
-
},
|
|
20
|
-
];
|
|
21
|
-
return {
|
|
22
|
-
type: "select",
|
|
23
|
-
title: "Gitignore violations detected",
|
|
24
|
-
message: `Found ${violationCount} managed gitignore item(s) that do not match ` +
|
|
25
|
-
`the current repo state.\n\n` +
|
|
26
|
-
`Missing .gitignore entries can be added now. Items that change git ` +
|
|
27
|
-
`tracking are reviewed and fixed from the Gitignore tab.`,
|
|
28
|
-
options,
|
|
29
|
-
onSelect: (value) => {
|
|
30
|
-
if (value === "fix-safe")
|
|
31
|
-
onFixSafe();
|
|
32
|
-
else if (value === "open-tab")
|
|
33
|
-
onOpenTab();
|
|
34
|
-
else
|
|
35
|
-
onDismiss();
|
|
36
|
-
},
|
|
37
|
-
onCancel: onDismiss,
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
export function useGitignoreModal() {
|
|
41
|
-
const { dispatch } = useApp();
|
|
42
|
-
const { navigateToScreen } = useNavigation();
|
|
43
|
-
const show = useCallback((args) => {
|
|
44
|
-
if (args.violationCount === 0)
|
|
45
|
-
return;
|
|
46
|
-
const dismiss = () => dispatch({ type: "HIDE_MODAL" });
|
|
47
|
-
const modal = buildGitignoreModal({
|
|
48
|
-
violationCount: args.violationCount,
|
|
49
|
-
safeCount: args.safeCount,
|
|
50
|
-
onFixSafe: () => {
|
|
51
|
-
dispatch({ type: "HIDE_MODAL" });
|
|
52
|
-
void Promise.resolve(args.onFixSafe()).catch((err) => {
|
|
53
|
-
dispatch({
|
|
54
|
-
type: "SHOW_MODAL",
|
|
55
|
-
modal: {
|
|
56
|
-
type: "message",
|
|
57
|
-
title: "Safe fixes failed",
|
|
58
|
-
message: err instanceof Error ? err.message : String(err),
|
|
59
|
-
variant: "error",
|
|
60
|
-
onDismiss: dismiss,
|
|
61
|
-
},
|
|
62
|
-
});
|
|
63
|
-
});
|
|
64
|
-
},
|
|
65
|
-
onOpenTab: () => {
|
|
66
|
-
dispatch({ type: "HIDE_MODAL" });
|
|
67
|
-
navigateToScreen("gitignore");
|
|
68
|
-
},
|
|
69
|
-
onDismiss: dismiss,
|
|
70
|
-
});
|
|
71
|
-
dispatch({ type: "SHOW_MODAL", modal });
|
|
72
|
-
}, [dispatch, navigateToScreen]);
|
|
73
|
-
return { show };
|
|
74
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { useKeyboard as useOpenTUIKeyboard } from "@opentui/react";
|
|
2
|
-
/**
|
|
3
|
-
* Simple re-export of OpenTUI's useKeyboard hook
|
|
4
|
-
* Used by modals for direct keyboard event handling
|
|
5
|
-
*
|
|
6
|
-
* Key names in OpenTUI:
|
|
7
|
-
* - Single characters: "a", "1", etc.
|
|
8
|
-
* - Arrow keys: "up", "down", "left", "right"
|
|
9
|
-
* - Special: "enter", "escape", "tab", "backspace", "delete"
|
|
10
|
-
*/
|
|
11
|
-
export function useKeyboard(handler) {
|
|
12
|
-
useOpenTUIKeyboard(handler);
|
|
13
|
-
}
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import { useKeyboard as useOpenTUIKeyboard } from "@opentui/react";
|
|
2
|
-
/**
|
|
3
|
-
* useKeyboardHandler hook
|
|
4
|
-
* Provides Ink-like API for keyboard events
|
|
5
|
-
*
|
|
6
|
-
* @param handler - Callback function with Ink-compatible signature
|
|
7
|
-
*
|
|
8
|
-
* @example
|
|
9
|
-
* ```tsx
|
|
10
|
-
* useKeyboardHandler((input, key) => {
|
|
11
|
-
* if (key.return) handleEnter();
|
|
12
|
-
* if (key.upArrow) handleUp();
|
|
13
|
-
* if (key.escape) handleEscape();
|
|
14
|
-
* if (input === 'q') handleQuit();
|
|
15
|
-
* });
|
|
16
|
-
* ```
|
|
17
|
-
*/
|
|
18
|
-
export function useKeyboardHandler(handler) {
|
|
19
|
-
useOpenTUIKeyboard((event) => {
|
|
20
|
-
// Map OpenTUI key names to Ink-like key names
|
|
21
|
-
const key = {
|
|
22
|
-
name: event.name,
|
|
23
|
-
ctrl: event.ctrl || false,
|
|
24
|
-
shift: event.shift || false,
|
|
25
|
-
meta: event.meta || false,
|
|
26
|
-
return: event.name === "enter" || event.name === "return",
|
|
27
|
-
upArrow: event.name === "up",
|
|
28
|
-
downArrow: event.name === "down",
|
|
29
|
-
leftArrow: event.name === "left",
|
|
30
|
-
rightArrow: event.name === "right",
|
|
31
|
-
escape: event.name === "escape",
|
|
32
|
-
tab: event.name === "tab",
|
|
33
|
-
};
|
|
34
|
-
// For printable characters, pass the character as input
|
|
35
|
-
// For special keys, pass empty string
|
|
36
|
-
const input = event.name.length === 1 ? event.name : "";
|
|
37
|
-
handler(input, key);
|
|
38
|
-
});
|
|
39
|
-
}
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import { useCallback } from "react";
|
|
2
|
-
import { useApp } from "../state/AppContext.js";
|
|
3
|
-
import { fixAllPluginVersionMismatches, } from "../../services/plugin-version-check.js";
|
|
4
|
-
/**
|
|
5
|
-
* Shared hook for showing the plugin version mismatch modal.
|
|
6
|
-
*
|
|
7
|
-
* Used in two places:
|
|
8
|
-
* 1. App.tsx — startup check shows mismatches found across all enabled plugins
|
|
9
|
-
* 2. PluginsScreen.tsx — post-install/update check shows a single plugin mismatch
|
|
10
|
-
*
|
|
11
|
-
* Both flows now share the same interactive modal with a "Fix all projects"
|
|
12
|
-
* button, so users never have to restart claudeup to access the fix.
|
|
13
|
-
*
|
|
14
|
-
* The fix uses fixAllPluginVersionMismatches() which rewrites
|
|
15
|
-
* installed_plugins.json in-place to align all entries to the current
|
|
16
|
-
* project's expected versions. After success, shows a hint to restart
|
|
17
|
-
* Claude Code so the new versions actually load (the running session
|
|
18
|
-
* has the old plugin code in memory).
|
|
19
|
-
*/
|
|
20
|
-
export function useMismatchModal() {
|
|
21
|
-
const { dispatch } = useApp();
|
|
22
|
-
const show = useCallback((mismatches) => {
|
|
23
|
-
if (mismatches.length === 0)
|
|
24
|
-
return;
|
|
25
|
-
const dismiss = () => dispatch({ type: "HIDE_MODAL" });
|
|
26
|
-
const fix = async () => {
|
|
27
|
-
dispatch({ type: "HIDE_MODAL" });
|
|
28
|
-
dispatch({
|
|
29
|
-
type: "SHOW_PROGRESS",
|
|
30
|
-
state: { message: "Aligning plugin versions across projects..." },
|
|
31
|
-
});
|
|
32
|
-
try {
|
|
33
|
-
const results = await fixAllPluginVersionMismatches(mismatches);
|
|
34
|
-
dispatch({ type: "HIDE_PROGRESS" });
|
|
35
|
-
// Count how many projects were updated across all plugins
|
|
36
|
-
const totalUpdated = Array.from(results.values()).reduce((sum, r) => sum + r.updated, 0);
|
|
37
|
-
dispatch({
|
|
38
|
-
type: "SHOW_MODAL",
|
|
39
|
-
modal: {
|
|
40
|
-
type: "message",
|
|
41
|
-
title: "Plugin Versions Aligned",
|
|
42
|
-
message: `Updated ${totalUpdated} project entr${totalUpdated === 1 ? "y" : "ies"} in ` +
|
|
43
|
-
`installed_plugins.json.\n\n` +
|
|
44
|
-
`Restart Claude Code for the new versions to load. ` +
|
|
45
|
-
`The running session still has the old plugin code in memory.`,
|
|
46
|
-
variant: "success",
|
|
47
|
-
onDismiss: dismiss,
|
|
48
|
-
},
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
catch (error) {
|
|
52
|
-
dispatch({ type: "HIDE_PROGRESS" });
|
|
53
|
-
dispatch({
|
|
54
|
-
type: "SHOW_MODAL",
|
|
55
|
-
modal: {
|
|
56
|
-
type: "message",
|
|
57
|
-
title: "Fix Failed",
|
|
58
|
-
message: `Could not align plugin versions: ${error instanceof Error ? error.message : String(error)}\n\n` +
|
|
59
|
-
`Try running claudeup again, or manually update the plugin in each project.`,
|
|
60
|
-
variant: "error",
|
|
61
|
-
onDismiss: dismiss,
|
|
62
|
-
},
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
};
|
|
66
|
-
dispatch({
|
|
67
|
-
type: "SHOW_MODAL",
|
|
68
|
-
modal: {
|
|
69
|
-
type: "version-mismatch",
|
|
70
|
-
mismatches,
|
|
71
|
-
onFix: fix,
|
|
72
|
-
onDismiss: dismiss,
|
|
73
|
-
},
|
|
74
|
-
});
|
|
75
|
-
}, [dispatch]);
|
|
76
|
-
return { show };
|
|
77
|
-
}
|
package/src/ui/registry.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { jsxs as _jsxs, jsx as _jsx } from "@opentui/react/jsx-runtime";
|
|
2
|
-
import { theme } from "../theme.js";
|
|
3
|
-
// ─── Helpers ────────────────────────────────────────────────────────────────
|
|
4
|
-
function getUninstallHint(tool, method, brewFormula) {
|
|
5
|
-
switch (method) {
|
|
6
|
-
case "bun": return `bun remove -g ${tool.packageName}`;
|
|
7
|
-
case "npm": return `npm uninstall -g ${tool.packageName}`;
|
|
8
|
-
case "pnpm": return `pnpm remove -g ${tool.packageName}`;
|
|
9
|
-
case "yarn": return `yarn global remove ${tool.packageName}`;
|
|
10
|
-
case "brew": return `brew uninstall ${brewFormula || tool.name}`;
|
|
11
|
-
case "pip": return `pip uninstall ${tool.packageName}`;
|
|
12
|
-
default: return "";
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
// ─── Row renderer ──────────────────────────────────────────────────────────────
|
|
16
|
-
export function renderCliToolRow(status, _index, isSelected) {
|
|
17
|
-
const { tool, installed, installedVersion, hasUpdate, checking, allMethods } = status;
|
|
18
|
-
const hasConflict = allMethods && allMethods.length > 1;
|
|
19
|
-
let icon;
|
|
20
|
-
let iconColor;
|
|
21
|
-
if (!installed) {
|
|
22
|
-
icon = "○";
|
|
23
|
-
iconColor = theme.colors.muted;
|
|
24
|
-
}
|
|
25
|
-
else if (hasConflict) {
|
|
26
|
-
icon = "!";
|
|
27
|
-
iconColor = theme.colors.danger;
|
|
28
|
-
}
|
|
29
|
-
else if (hasUpdate) {
|
|
30
|
-
icon = "*";
|
|
31
|
-
iconColor = theme.colors.warning;
|
|
32
|
-
}
|
|
33
|
-
else {
|
|
34
|
-
icon = "●";
|
|
35
|
-
iconColor = theme.colors.success;
|
|
36
|
-
}
|
|
37
|
-
const versionText = installedVersion ? ` v${installedVersion}` : "";
|
|
38
|
-
const methodTag = installed && allMethods?.length
|
|
39
|
-
? ` ${allMethods.join("+")}`
|
|
40
|
-
: "";
|
|
41
|
-
if (isSelected) {
|
|
42
|
-
return (_jsxs("text", { bg: theme.selection.bg, fg: theme.selection.fg, children: [" ", icon, " ", tool.displayName, versionText, methodTag, checking ? " ..." : "", " "] }));
|
|
43
|
-
}
|
|
44
|
-
return (_jsxs("text", { children: [_jsxs("span", { fg: iconColor, children: [" ", icon] }), _jsxs("span", { fg: theme.colors.text, children: [" ", tool.displayName] }), versionText ? _jsx("span", { fg: theme.colors.success, children: versionText }) : null, methodTag ? _jsx("span", { fg: hasConflict ? theme.colors.danger : theme.colors.dim, children: methodTag }) : null, checking ? _jsx("span", { fg: theme.colors.muted, children: " ..." }) : null] }));
|
|
45
|
-
}
|
|
46
|
-
// ─── Detail renderer ───────────────────────────────────────────────────────────
|
|
47
|
-
export function renderCliToolDetail(status) {
|
|
48
|
-
if (!status) {
|
|
49
|
-
return (_jsx("box", { flexDirection: "column", alignItems: "center", justifyContent: "center", flexGrow: 1, children: _jsx("text", { fg: theme.colors.muted, children: "Select a tool to see details" }) }));
|
|
50
|
-
}
|
|
51
|
-
const { tool, installed, installedVersion, latestVersion, hasUpdate, checking, installMethod, allMethods, updateCommand, brewFormula } = status;
|
|
52
|
-
const hasConflict = allMethods && allMethods.length > 1;
|
|
53
|
-
return (_jsxs("box", { flexDirection: "column", children: [_jsxs("box", { marginBottom: 1, children: [_jsx("text", { fg: theme.colors.info, children: _jsxs("strong", { children: ["⚙ ", tool.displayName] }) }), hasUpdate ? _jsx("text", { fg: theme.colors.warning, children: " \u2B06" }) : null, hasConflict ? _jsx("text", { fg: theme.colors.danger, children: " !" }) : null] }), _jsx("text", { fg: theme.colors.muted, children: tool.description }), _jsxs("box", { marginTop: 1, flexDirection: "column", children: [_jsxs("box", { children: [_jsx("text", { fg: theme.colors.muted, children: "Status " }), !installed ? (_jsx("text", { fg: theme.colors.muted, children: "○ Not installed" })) : checking ? (_jsx("text", { fg: theme.colors.success, children: "● Checking..." })) : hasUpdate ? (_jsx("text", { fg: theme.colors.warning, children: "● Update available" })) : (_jsx("text", { fg: theme.colors.success, children: "● Up to date" }))] }), installedVersion ? (_jsxs("box", { children: [_jsx("text", { fg: theme.colors.muted, children: "Version " }), _jsxs("text", { children: [_jsxs("span", { fg: theme.colors.success, children: ["v", installedVersion] }), latestVersion && hasUpdate ? (_jsxs("span", { fg: theme.colors.warning, children: [" \u2192 v", latestVersion] })) : null] })] })) : latestVersion ? (_jsxs("box", { children: [_jsx("text", { fg: theme.colors.muted, children: "Latest " }), _jsxs("text", { fg: theme.colors.text, children: ["v", latestVersion] })] })) : null, installed && updateCommand ? (_jsxs("box", { children: [_jsx("text", { fg: theme.colors.muted, children: "Update " }), _jsx("text", { fg: theme.colors.accent, children: updateCommand })] })) : !installed ? (_jsxs("box", { children: [_jsx("text", { fg: theme.colors.muted, children: "Install " }), _jsx("text", { fg: theme.colors.accent, children: tool.installCommand })] })) : null, _jsxs("box", { children: [_jsx("text", { fg: theme.colors.muted, children: "Website " }), _jsx("text", { fg: theme.colors.link, children: tool.website })] })] }), hasConflict ? (_jsxs("box", { marginTop: 1, flexDirection: "column", children: [_jsx("box", { children: _jsx("text", { bg: theme.colors.danger, fg: "white", children: _jsxs("strong", { children: [" ", "Conflict: installed via ", allMethods.join(" + "), " "] }) }) }), _jsx("box", { marginTop: 1, children: _jsxs("text", { fg: theme.colors.muted, children: ["Multiple installs can cause version mismatches.", "\n", "Keep one, remove the rest:"] }) }), allMethods.map((method, i) => (_jsx("box", { children: _jsxs("text", { children: [_jsx("span", { fg: i === 0 ? theme.colors.success : theme.colors.danger, children: i === 0 ? " ● keep " : " ○ remove " }), _jsx("span", { fg: theme.colors.warning, children: method }), i > 0 ? (_jsx("span", { fg: theme.colors.dim, children: ` ${getUninstallHint(tool, method, brewFormula)}` })) : (_jsx("span", { fg: theme.colors.dim, children: " (active in PATH)" }))] }) }, method))), _jsxs("box", { marginTop: 1, children: [_jsxs("text", { bg: theme.colors.danger, fg: "white", children: [" ", "c", " "] }), _jsx("text", { fg: theme.colors.muted, children: " Resolve \u2014 pick which to keep" })] })] })) : null, _jsx("box", { marginTop: 2, flexDirection: "column", children: !installed ? (_jsxs("box", { children: [_jsxs("text", { bg: theme.colors.success, fg: "black", children: [" ", "Enter", " "] }), _jsx("text", { fg: theme.colors.muted, children: " Install" }), _jsxs("text", { fg: theme.colors.dim, children: [" ", tool.installCommand] })] })) : hasUpdate ? (_jsxs("box", { children: [_jsxs("text", { bg: theme.colors.warning, fg: "black", children: [" ", "Enter", " "] }), _jsxs("text", { fg: theme.colors.muted, children: [" Update to v", latestVersion] })] })) : (_jsxs("box", { children: [_jsxs("text", { bg: theme.colors.muted, fg: "white", children: [" ", "Enter", " "] }), _jsx("text", { fg: theme.colors.muted, children: " Reinstall" })] })) })] }));
|
|
54
|
-
}
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "@opentui/react/jsx-runtime";
|
|
2
|
-
// Two color channels that never collide:
|
|
3
|
-
// • Badge color = direction (where the item ends up): green = tracked by git,
|
|
4
|
-
// reddish = ignored/dropped from git. Same on clean AND violation rows.
|
|
5
|
-
// • Filename color = severity: dim gray when fine, red when it's a violation.
|
|
6
|
-
// No row background tints — red is reserved entirely for "needs attention".
|
|
7
|
-
const TRACK_FG = "#5fd787"; // green — item ends up tracked by git
|
|
8
|
-
const IGNORE_FG = "#d78787"; // reddish — item ends up ignored / dropped
|
|
9
|
-
const VIOLATION_PATH_FG = "#ff5f5f"; // bright red filename = look here
|
|
10
|
-
const CLEAN_PATH_FG = "gray";
|
|
11
|
-
const SELECTED_BG = "#2a2a3a"; // faint slate highlight for the cursor row
|
|
12
|
-
// Violation badges are solid chips so the required direction reads at a glance:
|
|
13
|
-
// green chip = will be tracked, red chip = will be ignored/dropped.
|
|
14
|
-
const TRACK_CHIP_BG = "#1f5132";
|
|
15
|
-
const IGNORE_CHIP_BG = "#5a2323";
|
|
16
|
-
const CHIP_FG = "white";
|
|
17
|
-
// The only marker is the cursor: ▶ on the selected row, blank otherwise.
|
|
18
|
-
// Action (track/ignore) is read from the badge word and, for clean items,
|
|
19
|
-
// from the group the row sits under — so no per-row dot is needed.
|
|
20
|
-
const MARKER_SELECTED = "▶";
|
|
21
|
-
const MARKER_NONE = " ";
|
|
22
|
-
// Layout: " M action path" — keep the prefix compact so the narrow terminal
|
|
23
|
-
// case still has room for the path without wrapping.
|
|
24
|
-
const MAX_PATH_LEN = 22;
|
|
25
|
-
function truncatePath(p) {
|
|
26
|
-
return p.length > MAX_PATH_LEN ? p.slice(0, MAX_PATH_LEN - 1) + "…" : p;
|
|
27
|
-
}
|
|
28
|
-
export function renderRuleRow(row, isSelected) {
|
|
29
|
-
const { rule, violations } = row;
|
|
30
|
-
const path = truncatePath(rule.pattern);
|
|
31
|
-
const key = `${rule.action}:${rule.pattern}`;
|
|
32
|
-
// Badge text padded to a fixed width so paths line up in a column.
|
|
33
|
-
const badge = rule.action === "ignore" ? "ignore" : "track ";
|
|
34
|
-
const badgeFg = rule.action === "ignore" ? IGNORE_FG : TRACK_FG;
|
|
35
|
-
const suffix = violations.length > 1 ? ` +${violations.length - 1}` : "";
|
|
36
|
-
const rowBg = isSelected ? SELECTED_BG : undefined;
|
|
37
|
-
const marker = isSelected ? MARKER_SELECTED : MARKER_NONE;
|
|
38
|
-
const isViolation = violations.length > 0;
|
|
39
|
-
if (isViolation) {
|
|
40
|
-
// Solid badge chip + red filename. The chip color is the fix direction.
|
|
41
|
-
const chipBg = rule.action === "ignore" ? IGNORE_CHIP_BG : TRACK_CHIP_BG;
|
|
42
|
-
return (_jsxs("text", { bg: rowBg, children: [_jsx("span", { fg: VIOLATION_PATH_FG, children: ` ${marker} ` }), _jsx("span", { bg: chipBg, fg: CHIP_FG, children: ` ${badge} ` }), _jsx("span", { fg: VIOLATION_PATH_FG, children: ` ${path}${suffix}` })] }, key));
|
|
43
|
-
}
|
|
44
|
-
// Clean row: flat colored badge word, dim path, no chip.
|
|
45
|
-
return (_jsxs("text", { bg: rowBg, children: [_jsx("span", { fg: badgeFg, children: ` ${marker} ` }), _jsx("span", { fg: badgeFg, children: badge }), _jsx("span", { fg: CLEAN_PATH_FG, children: ` ${path}` })] }, key));
|
|
46
|
-
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { jsxs as _jsxs, jsx as _jsx } from "@opentui/react/jsx-runtime";
|
|
2
|
-
import { theme } from "../theme.js";
|
|
3
|
-
// ─── Row renderers ─────────────────────────────────────────────────────────────
|
|
4
|
-
export function renderMcpRow(item, _index, isSelected) {
|
|
5
|
-
if (item.kind === "category") {
|
|
6
|
-
return (_jsx("text", { fg: theme.selection.bg, children: _jsxs("strong", { children: ["▸ ", item.label] }) }));
|
|
7
|
-
}
|
|
8
|
-
const { server, isInstalled } = item;
|
|
9
|
-
const icon = isInstalled ? "●" : "○";
|
|
10
|
-
const iconColor = isInstalled ? theme.colors.success : theme.colors.muted;
|
|
11
|
-
if (isSelected) {
|
|
12
|
-
return (_jsxs("text", { bg: theme.selection.bg, fg: theme.selection.fg, children: [" ", icon, " ", server.name, " "] }));
|
|
13
|
-
}
|
|
14
|
-
return (_jsxs("text", { children: [_jsx("span", { fg: iconColor, children: icon }), _jsxs("span", { fg: theme.colors.text, children: [" ", server.name] }), server.requiresConfig ? (_jsx("span", { fg: theme.colors.warning, children: " \u2699" })) : null] }));
|
|
15
|
-
}
|
|
16
|
-
// ─── Detail renderer ───────────────────────────────────────────────────────────
|
|
17
|
-
export function renderMcpDetail(item, loading) {
|
|
18
|
-
if (loading) {
|
|
19
|
-
return _jsx("text", { fg: theme.colors.muted, children: "Loading MCP servers..." });
|
|
20
|
-
}
|
|
21
|
-
if (!item || item.kind === "category") {
|
|
22
|
-
return (_jsx("box", { flexDirection: "column", alignItems: "center", justifyContent: "center", flexGrow: 1, children: _jsx("text", { fg: theme.colors.muted, children: "Select a server to see details" }) }));
|
|
23
|
-
}
|
|
24
|
-
const { server, isInstalled } = item;
|
|
25
|
-
return (_jsxs("box", { flexDirection: "column", children: [_jsxs("box", { marginBottom: 1, children: [_jsx("text", { fg: theme.colors.info, children: _jsxs("strong", { children: ["⚡ ", server.name] }) }), server.requiresConfig ? (_jsx("text", { fg: theme.colors.warning, children: " \u2699" })) : null] }), _jsx("text", { fg: theme.colors.muted, children: server.description }), _jsxs("box", { marginTop: 1, flexDirection: "column", children: [_jsxs("box", { children: [_jsx("text", { fg: theme.colors.muted, children: "Status " }), isInstalled ? (_jsx("text", { fg: theme.colors.success, children: "● Installed" })) : (_jsx("text", { fg: theme.colors.muted, children: "○ Not installed" }))] }), _jsxs("box", { children: [_jsx("text", { fg: theme.colors.muted, children: "Type " }), _jsx("text", { fg: theme.colors.text, children: server.type === "http" ? "HTTP" : "Command" })] }), server.type === "http" ? (_jsxs("box", { children: [_jsx("text", { fg: theme.colors.muted, children: "URL " }), _jsx("text", { fg: theme.colors.link, children: server.url })] })) : (_jsxs("box", { children: [_jsx("text", { fg: theme.colors.muted, children: "Command " }), _jsx("text", { fg: theme.colors.info, children: server.command })] })), server.requiresConfig ? (_jsxs("box", { children: [_jsx("text", { fg: theme.colors.muted, children: "Config " }), _jsxs("text", { fg: theme.colors.warning, children: [server.configFields?.length ?? 0, " fields required"] })] })) : null] }), _jsx("box", { marginTop: 2, children: isInstalled ? (_jsxs("box", { children: [_jsxs("text", { bg: theme.colors.danger, fg: "white", children: [" ", "Enter", " "] }), _jsx("text", { fg: theme.colors.muted, children: " Remove server" })] })) : (_jsxs("box", { children: [_jsxs("text", { bg: theme.colors.success, fg: "black", children: [" ", "Enter", " "] }), _jsx("text", { fg: theme.colors.muted, children: " Install server" })] })) })] }));
|
|
26
|
-
}
|