magus-cli 0.0.0-placeholder → 7.1.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/README.md +309 -0
- package/bin/magus.js +170 -0
- package/package.json +67 -3
- package/scripts/build-binaries.ts +113 -0
- package/scripts/capture-builtin.ts +412 -0
- package/scripts/check-optional-deps.ts +49 -0
- package/scripts/test-isolated.ts +49 -0
- package/scripts/verify-binary.ts +95 -0
- package/scripts/verify-community-registry.ts +272 -0
- package/src/__tests__/active-closure.test.ts +184 -0
- package/src/__tests__/alias-adopt.test.ts +365 -0
- package/src/__tests__/alias-parser.test.ts +417 -0
- package/src/__tests__/alias-shell-writer.test.ts +681 -0
- package/src/__tests__/alias-store.test.ts +163 -0
- package/src/__tests__/binary-signing.test.ts +120 -0
- package/src/__tests__/blackbox-active-closure.test.ts +119 -0
- package/src/__tests__/blackbox-convergence.test.ts +334 -0
- package/src/__tests__/blackbox-parity.test.ts +725 -0
- package/src/__tests__/blackbox-plugin-state.test.ts +545 -0
- package/src/__tests__/blackbox-tool-state.test.ts +669 -0
- package/src/__tests__/cache-provenance.test.ts +84 -0
- package/src/__tests__/capture-builtin.test.ts +92 -0
- package/src/__tests__/catalog-cache-store.test.ts +309 -0
- package/src/__tests__/catalog-notice.test.ts +155 -0
- package/src/__tests__/cli-ansi.test.ts +297 -0
- package/src/__tests__/cli-apply-seams.test.ts +288 -0
- package/src/__tests__/cli-live.test.ts +391 -0
- package/src/__tests__/cli-router.test.ts +240 -0
- package/src/__tests__/cli-tool-commands.test.ts +239 -0
- package/src/__tests__/cli-tools-adapter.test.ts +294 -0
- package/src/__tests__/cli-update-view.test.ts +524 -0
- package/src/__tests__/community-fetch.test.ts +545 -0
- package/src/__tests__/community-registry.test.ts +269 -0
- package/src/__tests__/community-staleness.test.ts +720 -0
- package/src/__tests__/component-badge.test.ts +98 -0
- package/src/__tests__/component-icons.test.ts +94 -0
- package/src/__tests__/content-drift.test.ts +353 -0
- package/src/__tests__/conventions-integration.test.ts +830 -0
- package/src/__tests__/conventions-manager.test.ts +1236 -0
- package/src/__tests__/doctor-bins.test.ts +43 -0
- package/src/__tests__/doctor.test.ts +463 -0
- package/src/__tests__/dotenv.test.ts +195 -0
- package/src/__tests__/enabled-not-installed.test.ts +94 -0
- package/src/__tests__/file-locking.test.ts +208 -0
- package/src/__tests__/fixtures/tool-probe/brew-list-versions-tmux.txt +2 -0
- package/src/__tests__/fixtures/tool-probe/brew-outdated-behind-homebrew-spec.txt +29 -0
- package/src/__tests__/fixtures/tool-probe/brew-outdated-tmux-current.txt +7 -0
- package/src/__tests__/fixtures/tool-probe/brew-outdated-unknown-formula.txt +6 -0
- package/src/__tests__/fixtures/tool-probe/go-version-m-tmux-mcp.txt +27 -0
- package/src/__tests__/fixtures/tool-probe/npm-dist-tags-missing-scoped.txt +4 -0
- package/src/__tests__/fixtures/tool-probe/npm-dist-tags-openai-codex.txt +4 -0
- package/src/__tests__/fixtures/tool-probe/pypi-cowsay.txt +3 -0
- package/src/__tests__/fixtures/tool-probe/uv-receipt-browser-use.toml +10 -0
- package/src/__tests__/fixtures/tool-probe/uv-receipt-cowsay-git.toml +8 -0
- package/src/__tests__/fixtures/tool-probe/uv-receipt-cowsay-pinned.toml +8 -0
- package/src/__tests__/fixtures/tool-probe/uv-receipt-graphifyy.toml +7 -0
- package/src/__tests__/fixtures/tool-probe/uv-receipt-mcp.toml +6 -0
- package/src/__tests__/fixtures/tool-probe/uv-receipt-serena-agent.toml +9 -0
- package/src/__tests__/fixtures/tool-probe/uv-tool-list-outdated-pinned.txt +5 -0
- package/src/__tests__/fixtures/tool-probe/uv-tool-list-outdated.txt +11 -0
- package/src/__tests__/fixtures/tool-probe/uv-tool-list.txt +22 -0
- package/src/__tests__/footer-hints.test.ts +40 -0
- package/src/__tests__/format-command.test.ts +78 -0
- package/src/__tests__/git-worktree.test.ts +108 -0
- package/src/__tests__/github-budget.test.ts +206 -0
- package/src/__tests__/gitignore-check.test.ts +71 -0
- package/src/__tests__/gitignore-detector.test.ts +176 -0
- package/src/__tests__/gitignore-fixer.test.ts +317 -0
- package/src/__tests__/gitignore-resolver.test.ts +193 -0
- package/src/__tests__/gitignore-service.test.ts +137 -0
- package/src/__tests__/go-module.test.ts +43 -0
- package/src/__tests__/hook-import-policy.test.ts +90 -0
- package/src/__tests__/hook-process.test.ts +256 -0
- package/src/__tests__/hook-registration.test.ts +224 -0
- package/src/__tests__/install-command.test.ts +81 -0
- package/src/__tests__/install-plan.test.ts +104 -0
- package/src/__tests__/launcher-signal.test.ts +308 -0
- package/src/__tests__/local-marketplace-scan.test.ts +248 -0
- package/src/__tests__/manifest.test.ts +281 -0
- package/src/__tests__/markdown-renderer.test.ts +142 -0
- package/src/__tests__/marketplace-badge.test.ts +140 -0
- package/src/__tests__/marketplace-refresh.test.ts +340 -0
- package/src/__tests__/marketplace-version.test.ts +84 -0
- package/src/__tests__/marketplaces.test.ts +128 -0
- package/src/__tests__/mate-availability.test.ts +156 -0
- package/src/__tests__/mate-catalog.test.ts +295 -0
- package/src/__tests__/mcp-registry.test.ts +158 -0
- package/src/__tests__/model-visuals.test.tsx +2466 -0
- package/src/__tests__/models-adapter.test.ts +332 -0
- package/src/__tests__/models-cli.test.ts +273 -0
- package/src/__tests__/models-core.test.ts +802 -0
- package/src/__tests__/models-manager.test.ts +500 -0
- package/src/__tests__/models-presets-marketplace.test.ts +168 -0
- package/src/__tests__/models-screen-state.test.ts +315 -0
- package/src/__tests__/open-file.test.ts +59 -0
- package/src/__tests__/owned-marketplaces.test.ts +93 -0
- package/src/__tests__/plugin-checkout.test.ts +66 -0
- package/src/__tests__/plugin-cli-argv.test.ts +109 -0
- package/src/__tests__/plugin-contents.test.ts +371 -0
- package/src/__tests__/plugin-manager-fallback.test.ts +320 -0
- package/src/__tests__/plugin-releases.test.ts +98 -0
- package/src/__tests__/plugin-requires.test.ts +187 -0
- package/src/__tests__/plugin-row-text.test.ts +345 -0
- package/src/__tests__/plugin-setup.test.ts +1016 -0
- package/src/__tests__/profile-adopt.test.ts +177 -0
- package/src/__tests__/profile-command.test.ts +110 -0
- package/src/__tests__/profile-materializer.test.ts +238 -0
- package/src/__tests__/profile-sync.test.ts +154 -0
- package/src/__tests__/registry-version-resolution.test.ts +88 -0
- package/src/__tests__/resolve-executable.test.ts +42 -0
- package/src/__tests__/resolver.test.ts +351 -0
- package/src/__tests__/scope-action.test.ts +134 -0
- package/src/__tests__/scope-squares.test.tsx +164 -0
- package/src/__tests__/selection-scope.test.ts +100 -0
- package/src/__tests__/settings-file.test.ts +179 -0
- package/src/__tests__/shell-script-callers.test.ts +92 -0
- package/src/__tests__/skill-install-files.test.ts +136 -0
- package/src/__tests__/skills-catalog-client.test.ts +48 -0
- package/src/__tests__/style-wrap.test.ts +220 -0
- package/src/__tests__/styles-manager.test.ts +1098 -0
- package/src/__tests__/styles-origins.test.ts +414 -0
- package/src/__tests__/styles-screen-state.test.ts +457 -0
- package/src/__tests__/styles-status-line.test.ts +72 -0
- package/src/__tests__/styles-sync.test.ts +452 -0
- package/src/__tests__/symlink-manager.test.ts +180 -0
- package/src/__tests__/tabbar-layout.test.ts +100 -0
- package/src/__tests__/terminology-filler.test.ts +214 -0
- package/src/__tests__/theme-adaptive-colors.test.ts +354 -0
- package/src/__tests__/theme-env.test.ts +310 -0
- package/src/__tests__/theme-resolve.test.ts +380 -0
- package/src/__tests__/tool-probe.test.ts +1400 -0
- package/src/__tests__/toolchain.test.ts +232 -0
- package/src/__tests__/ui-version-writers.test.ts +88 -0
- package/src/__tests__/update-apply.test.ts +637 -0
- package/src/__tests__/update-plan.test.ts +1396 -0
- package/src/__tests__/uppercase-keybindings.test.ts +101 -0
- package/src/__tests__/useGitignoreModal.test.ts +79 -0
- package/src/__tests__/version-check-refresh.test.ts +45 -0
- package/src/__tests__/version-snapshot.test.ts +212 -0
- package/src/__tests__/version-unknown-state.test.ts +403 -0
- package/src/__tests__/worktree-registry-resolution.test.ts +107 -0
- package/src/cli/ansi.ts +528 -0
- package/src/cli/bootstrap.ts +134 -0
- package/src/cli/doctor.ts +218 -0
- package/src/cli/hook.ts +129 -0
- package/src/cli/install.ts +382 -0
- package/src/cli/live.ts +374 -0
- package/src/cli/models.ts +297 -0
- package/src/cli/profile.ts +197 -0
- package/src/cli/prompt.ts +32 -0
- package/src/cli/router.ts +264 -0
- package/src/cli/update-view.ts +717 -0
- package/src/cli/update.ts +600 -0
- package/src/cli/upgrade.ts +97 -0
- package/src/data/alias-flags.ts +301 -0
- package/src/data/cli-tools.ts +146 -0
- package/src/data/community-styles.ts +531 -0
- package/src/data/gitignore-defaults.ts +33 -0
- package/src/data/gitignore-reasons.ts +93 -0
- package/src/data/gitignore-templates.ts +25 -0
- package/src/data/marketplaces.ts +278 -0
- package/src/data/mcp-servers.ts +51 -0
- package/src/data/models-presets.ts +329 -0
- package/src/data/predefined-profiles.ts +252 -0
- package/src/data/settings-catalog.ts +714 -0
- package/src/data/skill-repos.ts +91 -0
- package/src/data/styles/asd-ste100.md +38 -0
- package/src/data/styles/calibrated.md +24 -0
- package/src/data/styles/direct.md +23 -0
- package/src/data/styles/evidence-first.md +22 -0
- package/src/data/styles/explanatory.md +23 -0
- package/src/data/styles/index.ts +57 -0
- package/src/data/styles/no-slop.md +32 -0
- package/src/data/styles/plain-language.md +23 -0
- package/src/data/styles/structured.md +30 -0
- package/src/data/styles/terminology.md +28 -0
- package/src/data/styles/terse.md +20 -0
- package/src/main.tsx +110 -0
- package/src/markdown.d.ts +13 -0
- package/src/opentui.d.ts +203 -0
- package/src/services/active-closure.ts +117 -0
- package/src/services/alias-settings.ts +60 -0
- package/src/services/alias-shell-writer.ts +1244 -0
- package/src/services/alias-store.ts +172 -0
- package/src/services/binary-signing.ts +78 -0
- package/src/services/catalog-cache-store.ts +316 -0
- package/src/services/claude-cli.ts +304 -0
- package/src/services/claude-runner.ts +31 -0
- package/src/services/claude-settings.ts +1564 -0
- package/src/services/cli-tool-commands.ts +133 -0
- package/src/services/community-fetcher.ts +90 -0
- package/src/services/community-styles.ts +1194 -0
- package/src/services/content-drift.ts +208 -0
- package/src/services/conventions-manager.ts +945 -0
- package/src/services/doctor-bins.ts +63 -0
- package/src/services/doctor.ts +570 -0
- package/src/services/dotenv.ts +181 -0
- package/src/services/git-worktree.ts +129 -0
- package/src/services/github-budget.ts +274 -0
- package/src/services/gitignore-check.ts +63 -0
- package/src/services/gitignore-detector.ts +167 -0
- package/src/services/gitignore-fixer.ts +280 -0
- package/src/services/gitignore-resolver.ts +208 -0
- package/src/services/gitignore-service.ts +167 -0
- package/src/services/hook-registration.ts +218 -0
- package/src/services/install-plan.ts +107 -0
- package/src/services/local-marketplace.ts +657 -0
- package/src/services/manifest.ts +250 -0
- package/src/services/marketplace-catalog-git.ts +173 -0
- package/src/services/marketplace-catalog.ts +129 -0
- package/src/services/marketplace-fetcher.ts +400 -0
- package/src/services/marketplace-refresh.ts +245 -0
- package/src/services/marketplace-sync.ts +145 -0
- package/src/services/mate-availability.ts +133 -0
- package/src/services/mate-catalog.ts +265 -0
- package/src/services/mcp-registry.ts +171 -0
- package/src/services/models-core.ts +969 -0
- package/src/services/models-manager.ts +606 -0
- package/src/services/plugin-checkout.ts +186 -0
- package/src/services/plugin-manager.ts +847 -0
- package/src/services/plugin-mcp-config.ts +242 -0
- package/src/services/plugin-releases.ts +47 -0
- package/src/services/plugin-requires.ts +227 -0
- package/src/services/plugin-setup.ts +965 -0
- package/src/services/profile-adopt.ts +206 -0
- package/src/services/profile-materializer.ts +133 -0
- package/src/services/profile-sync.ts +158 -0
- package/src/services/profiles.ts +225 -0
- package/src/services/resolver.ts +377 -0
- package/src/services/settings-file.ts +69 -0
- package/src/services/settings-manager.ts +307 -0
- package/src/services/skills-manager.ts +1111 -0
- package/src/services/skillsmp-client.ts +193 -0
- package/src/services/styles-manager.ts +1294 -0
- package/src/services/symlink-manager.ts +189 -0
- package/src/services/terminology-filler.ts +266 -0
- package/src/services/tool-probe.ts +1637 -0
- package/src/services/toolchain.ts +431 -0
- package/src/services/update-engine.ts +691 -0
- package/src/services/update-plan.ts +820 -0
- package/src/services/version-check.ts +127 -0
- package/src/services/version-snapshot.ts +189 -0
- package/src/tui.tsx +112 -0
- package/src/types/bun.d.ts +27 -0
- package/src/types/gitignore.ts +68 -0
- package/src/types/index.ts +447 -0
- package/src/ui/App.tsx +535 -0
- package/src/ui/adapters/catalogNotice.ts +122 -0
- package/src/ui/adapters/cliToolsAdapter.ts +176 -0
- package/src/ui/adapters/modelsAdapter.ts +213 -0
- package/src/ui/adapters/pluginsAdapter.ts +413 -0
- package/src/ui/adapters/settingsAdapter.ts +168 -0
- package/src/ui/adapters/skillsAdapter.ts +229 -0
- package/src/ui/adapters/stylesAdapter.ts +403 -0
- package/src/ui/components/CategoryHeader.tsx +64 -0
- package/src/ui/components/EmptyFilterState.tsx +32 -0
- package/src/ui/components/FlagDetailEditor.tsx +0 -0
- package/src/ui/components/ScopeIndicator.tsx +63 -0
- package/src/ui/components/ScrollableList.tsx +95 -0
- package/src/ui/components/SearchInput.tsx +57 -0
- package/src/ui/components/StyledText.tsx +75 -0
- package/src/ui/components/TabBar.tsx +101 -0
- package/src/ui/components/layout/FooterHints.tsx +78 -0
- package/src/ui/components/layout/Panel.tsx +63 -0
- package/src/ui/components/layout/ProgressBar.tsx +44 -0
- package/src/ui/components/layout/ScopeTabs.tsx +54 -0
- package/src/ui/components/layout/ScreenLayout.tsx +321 -0
- package/src/ui/components/layout/index.ts +9 -0
- package/src/ui/components/modals/ConfirmModal.tsx +62 -0
- package/src/ui/components/modals/InputModal.tsx +75 -0
- package/src/ui/components/modals/LoadingModal.tsx +51 -0
- package/src/ui/components/modals/MessageModal.tsx +66 -0
- package/src/ui/components/modals/ModalContainer.tsx +159 -0
- package/src/ui/components/modals/SelectModal.tsx +90 -0
- package/src/ui/components/modals/index.ts +6 -0
- package/src/ui/components/primitives/ActionHints.tsx +44 -0
- package/src/ui/components/primitives/DetailSection.tsx +23 -0
- package/src/ui/components/primitives/KeyValueLine.tsx +26 -0
- package/src/ui/components/primitives/ListCategoryRow.tsx +45 -0
- package/src/ui/components/primitives/MetaText.tsx +40 -0
- package/src/ui/components/primitives/ScopeDetail.tsx +67 -0
- package/src/ui/components/primitives/ScopeSquares.tsx +58 -0
- package/src/ui/components/primitives/SelectableRow.tsx +30 -0
- package/src/ui/components/primitives/index.ts +9 -0
- package/src/ui/hooks/index.ts +2 -0
- package/src/ui/hooks/useGitignoreModal.ts +95 -0
- package/src/ui/hooks/useKeyboard.ts +24 -0
- package/src/ui/hooks/useKeyboardHandler.ts +63 -0
- package/src/ui/registry.ts +33 -0
- package/src/ui/renderers/cliToolRenderers.tsx +326 -0
- package/src/ui/renderers/gitignoreRenderers.tsx +80 -0
- package/src/ui/renderers/markdownRenderer.tsx +302 -0
- package/src/ui/renderers/mcpRenderers.tsx +149 -0
- package/src/ui/renderers/modelRenderers.tsx +1309 -0
- package/src/ui/renderers/modelVisuals.tsx +1402 -0
- package/src/ui/renderers/pluginRenderers.tsx +1089 -0
- package/src/ui/renderers/pluginRowText.ts +70 -0
- package/src/ui/renderers/pluginStatusText.ts +56 -0
- package/src/ui/renderers/profileRenderers.tsx +406 -0
- package/src/ui/renderers/settingsRenderers.tsx +218 -0
- package/src/ui/renderers/skillRenderers.tsx +479 -0
- package/src/ui/renderers/styleRenderers.tsx +813 -0
- package/src/ui/screens/AliasScreen.tsx +1558 -0
- package/src/ui/screens/CliToolsScreen.tsx +367 -0
- package/src/ui/screens/EnvVarsScreen.tsx +221 -0
- package/src/ui/screens/GitignoreScreen.tsx +562 -0
- package/src/ui/screens/McpRegistryScreen.tsx +366 -0
- package/src/ui/screens/McpScreen.tsx +256 -0
- package/src/ui/screens/ModelsScreen.tsx +542 -0
- package/src/ui/screens/PluginsScreen.tsx +1423 -0
- package/src/ui/screens/ProfilesScreen.tsx +438 -0
- package/src/ui/screens/SkillsScreen.tsx +853 -0
- package/src/ui/screens/StylesScreen.tsx +1064 -0
- package/src/ui/screens/index.ts +11 -0
- package/src/ui/state/AppContext.tsx +233 -0
- package/src/ui/state/DimensionsContext.tsx +154 -0
- package/src/ui/state/reducer.ts +810 -0
- package/src/ui/state/types.ts +503 -0
- package/src/ui/theme-mode.ts +440 -0
- package/src/ui/theme-resolve.ts +147 -0
- package/src/ui/theme.ts +175 -0
- package/src/utils/clipboard.ts +58 -0
- package/src/utils/command-utils.ts +149 -0
- package/src/utils/config-dir.ts +45 -0
- package/src/utils/file-locking.ts +146 -0
- package/src/utils/fuzzy-search.ts +138 -0
- package/src/utils/go-module.ts +36 -0
- package/src/utils/open-file.ts +84 -0
- package/src/utils/run.ts +67 -0
- package/src/utils/string-utils.ts +88 -0
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import React, { useState } from "react";
|
|
2
|
+
import { useKeyboard } from "../../hooks/useKeyboard.js";
|
|
3
|
+
import { useApp } from "../../state/AppContext.js";
|
|
4
|
+
import { ConfirmModal } from "./ConfirmModal.js";
|
|
5
|
+
import { InputModal } from "./InputModal.js";
|
|
6
|
+
import { LoadingModal } from "./LoadingModal.js";
|
|
7
|
+
import { MessageModal } from "./MessageModal.js";
|
|
8
|
+
import { SelectModal } from "./SelectModal.js";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Container that renders the active modal as an overlay
|
|
12
|
+
* Handles ALL keyboard events when a modal is open to avoid
|
|
13
|
+
* conflicts with multiple useKeyboard hooks in child components
|
|
14
|
+
*
|
|
15
|
+
* Input modal: Enter/Escape handled by OpenTUI <input> onSubmit + ModalContainer escape
|
|
16
|
+
* Select modal: keyboard fully handled here (index tracking + Enter/arrows)
|
|
17
|
+
*/
|
|
18
|
+
export function ModalContainer() {
|
|
19
|
+
const { state } = useApp();
|
|
20
|
+
const { modal } = state;
|
|
21
|
+
|
|
22
|
+
// Track select modal index here (lifted from SelectModal)
|
|
23
|
+
const [selectIndex, setSelectIndex] = useState(0);
|
|
24
|
+
|
|
25
|
+
// Reset select index when modal changes
|
|
26
|
+
const modalRef = React.useRef(modal);
|
|
27
|
+
if (modal !== modalRef.current) {
|
|
28
|
+
modalRef.current = modal;
|
|
29
|
+
if (modal?.type === "select") {
|
|
30
|
+
setSelectIndex(modal.defaultIndex ?? 0);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Handle keyboard events for modals
|
|
35
|
+
useKeyboard((key) => {
|
|
36
|
+
if (!modal) return;
|
|
37
|
+
if (modal.type === "loading") return;
|
|
38
|
+
|
|
39
|
+
// Escape — close any modal
|
|
40
|
+
if (key.name === "escape") {
|
|
41
|
+
if (modal.type === "confirm") modal.onCancel();
|
|
42
|
+
else if (modal.type === "input") modal.onCancel();
|
|
43
|
+
else if (modal.type === "select") modal.onCancel();
|
|
44
|
+
else if (modal.type === "message") modal.onDismiss();
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// 'q' to close — but NOT for input modals (need to type 'q')
|
|
49
|
+
if (key.name === "q" && modal.type !== "input") {
|
|
50
|
+
if (modal.type === "confirm") modal.onCancel();
|
|
51
|
+
else if (modal.type === "select") modal.onCancel();
|
|
52
|
+
else if (modal.type === "message") modal.onDismiss();
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Input modal — let OpenTUI <input> handle Enter via onSubmit
|
|
57
|
+
if (modal.type === "input") {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Select modal — handle navigation and selection
|
|
62
|
+
if (modal.type === "select") {
|
|
63
|
+
if (key.name === "return" || key.name === "enter") {
|
|
64
|
+
modal.onSelect(modal.options[selectIndex].value);
|
|
65
|
+
} else if (key.name === "up" || key.name === "k") {
|
|
66
|
+
setSelectIndex((prev) => Math.max(0, prev - 1));
|
|
67
|
+
} else if (key.name === "down" || key.name === "j") {
|
|
68
|
+
setSelectIndex((prev) => Math.min(modal.options.length - 1, prev + 1));
|
|
69
|
+
}
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Message modal — Enter to dismiss
|
|
74
|
+
if (modal.type === "message") {
|
|
75
|
+
if (key.name === "return" || key.name === "enter") {
|
|
76
|
+
modal.onDismiss();
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Confirm modal — Enter to confirm
|
|
81
|
+
if (modal.type === "confirm") {
|
|
82
|
+
if (key.name === "return" || key.name === "enter") {
|
|
83
|
+
modal.onConfirm();
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
if (!modal) {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const renderModal = () => {
|
|
93
|
+
switch (modal.type) {
|
|
94
|
+
case "confirm":
|
|
95
|
+
return (
|
|
96
|
+
<ConfirmModal
|
|
97
|
+
title={modal.title}
|
|
98
|
+
message={modal.message}
|
|
99
|
+
onConfirm={modal.onConfirm}
|
|
100
|
+
onCancel={modal.onCancel}
|
|
101
|
+
/>
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
case "input":
|
|
105
|
+
return (
|
|
106
|
+
<InputModal
|
|
107
|
+
title={modal.title}
|
|
108
|
+
label={modal.label}
|
|
109
|
+
defaultValue={modal.defaultValue}
|
|
110
|
+
onSubmit={modal.onSubmit}
|
|
111
|
+
onCancel={modal.onCancel}
|
|
112
|
+
/>
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
case "select":
|
|
116
|
+
return (
|
|
117
|
+
<SelectModal
|
|
118
|
+
title={modal.title}
|
|
119
|
+
message={modal.message}
|
|
120
|
+
options={modal.options}
|
|
121
|
+
defaultIndex={selectIndex}
|
|
122
|
+
onSelect={modal.onSelect}
|
|
123
|
+
onCancel={modal.onCancel}
|
|
124
|
+
/>
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
case "message":
|
|
128
|
+
return (
|
|
129
|
+
<MessageModal
|
|
130
|
+
title={modal.title}
|
|
131
|
+
message={modal.message}
|
|
132
|
+
variant={modal.variant}
|
|
133
|
+
onDismiss={modal.onDismiss}
|
|
134
|
+
/>
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
case "loading":
|
|
138
|
+
return <LoadingModal message={modal.message} />;
|
|
139
|
+
|
|
140
|
+
default:
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
// Center the modal on screen
|
|
146
|
+
return (
|
|
147
|
+
<box
|
|
148
|
+
position="absolute"
|
|
149
|
+
width="100%"
|
|
150
|
+
height="100%"
|
|
151
|
+
justifyContent="center"
|
|
152
|
+
alignItems="center"
|
|
153
|
+
>
|
|
154
|
+
{renderModal()}
|
|
155
|
+
</box>
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export default ModalContainer;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { useDimensions } from "../../state/DimensionsContext.js";
|
|
3
|
+
import type { SelectOption } from "../../state/types.js";
|
|
4
|
+
import { theme } from "../../theme.js";
|
|
5
|
+
|
|
6
|
+
interface SelectModalProps {
|
|
7
|
+
/** Modal title */
|
|
8
|
+
title: string;
|
|
9
|
+
/** Modal message */
|
|
10
|
+
message: string;
|
|
11
|
+
/** Select options */
|
|
12
|
+
options: SelectOption[];
|
|
13
|
+
/** Currently selected index (controlled by ModalContainer) */
|
|
14
|
+
defaultIndex?: number;
|
|
15
|
+
/** Callback when option selected */
|
|
16
|
+
onSelect: (value: string) => void;
|
|
17
|
+
/** Callback when cancelled */
|
|
18
|
+
onCancel: () => void;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function SelectModal({
|
|
22
|
+
title,
|
|
23
|
+
message,
|
|
24
|
+
options,
|
|
25
|
+
defaultIndex,
|
|
26
|
+
onSelect: _onSelect,
|
|
27
|
+
onCancel: _onCancel,
|
|
28
|
+
}: SelectModalProps) {
|
|
29
|
+
// Keyboard handling is done by ModalContainer
|
|
30
|
+
// defaultIndex is the live selectedIndex from ModalContainer state
|
|
31
|
+
const selectedIndex = defaultIndex ?? 0;
|
|
32
|
+
|
|
33
|
+
// Size to terminal width: 60% of available, clamped to [50, 90].
|
|
34
|
+
// Previously hardcoded at 50, which forced descriptions and long item
|
|
35
|
+
// labels (e.g. plugin specs) to wrap mid-sentence and produce orphan
|
|
36
|
+
// punctuation lines.
|
|
37
|
+
const dimensions = useDimensions();
|
|
38
|
+
const width = Math.max(
|
|
39
|
+
50,
|
|
40
|
+
Math.min(90, Math.floor(dimensions.terminalWidth * 0.6)),
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<box
|
|
45
|
+
flexDirection="column"
|
|
46
|
+
border
|
|
47
|
+
borderStyle="rounded"
|
|
48
|
+
borderColor={theme.colors.dim}
|
|
49
|
+
backgroundColor={theme.surface.bg}
|
|
50
|
+
paddingLeft={3}
|
|
51
|
+
paddingRight={3}
|
|
52
|
+
paddingTop={1}
|
|
53
|
+
paddingBottom={1}
|
|
54
|
+
width={width}
|
|
55
|
+
>
|
|
56
|
+
<box marginBottom={1}>
|
|
57
|
+
<text fg={theme.colors.text}>
|
|
58
|
+
<strong>{title}</strong>
|
|
59
|
+
</text>
|
|
60
|
+
</box>
|
|
61
|
+
|
|
62
|
+
<box marginBottom={1}>
|
|
63
|
+
<text fg={theme.colors.muted}>{message}</text>
|
|
64
|
+
</box>
|
|
65
|
+
|
|
66
|
+
<box flexDirection="column" paddingLeft={1}>
|
|
67
|
+
{options.map((option, idx) => {
|
|
68
|
+
const isSelected = idx === selectedIndex;
|
|
69
|
+
return (
|
|
70
|
+
<text
|
|
71
|
+
key={option.value}
|
|
72
|
+
fg={isSelected ? theme.colors.text : theme.colors.muted}
|
|
73
|
+
>
|
|
74
|
+
<span fg={isSelected ? theme.colors.text : theme.colors.muted}>
|
|
75
|
+
{isSelected ? "❯ " : " "}
|
|
76
|
+
</span>
|
|
77
|
+
{isSelected ? <strong>{option.label}</strong> : option.label}
|
|
78
|
+
</text>
|
|
79
|
+
);
|
|
80
|
+
})}
|
|
81
|
+
</box>
|
|
82
|
+
|
|
83
|
+
<box marginTop={1}>
|
|
84
|
+
<text fg={theme.colors.muted}>↑↓ Select • ↵ Confirm • Esc Cancel</text>
|
|
85
|
+
</box>
|
|
86
|
+
</box>
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export default SelectModal;
|
|
@@ -0,0 +1,6 @@
|
|
|
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";
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { theme } from "../../theme.js";
|
|
3
|
+
|
|
4
|
+
export interface Hint {
|
|
5
|
+
key: string;
|
|
6
|
+
label: string;
|
|
7
|
+
tone?: "default" | "primary" | "danger";
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
interface ActionHintsProps {
|
|
11
|
+
hints: Hint[];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Keyboard shortcut badges for detail panels.
|
|
16
|
+
* Renders: [bg] key [/bg] label
|
|
17
|
+
*/
|
|
18
|
+
export function ActionHints({ hints }: ActionHintsProps) {
|
|
19
|
+
return (
|
|
20
|
+
<box flexDirection="column" marginTop={1}>
|
|
21
|
+
{hints.map((hint) => (
|
|
22
|
+
// A box with no flexDirection lays out as a column here, which put
|
|
23
|
+
// every key chip on its own line above its label. The chip and what
|
|
24
|
+
// it does are one unit — the footer hints already render that way.
|
|
25
|
+
<box key={`${hint.key}:${hint.label}`} flexDirection="row">
|
|
26
|
+
<text
|
|
27
|
+
bg={
|
|
28
|
+
hint.tone === "danger"
|
|
29
|
+
? theme.hints.dangerBg
|
|
30
|
+
: hint.tone === "primary"
|
|
31
|
+
? theme.hints.primaryBg
|
|
32
|
+
: theme.hints.defaultBg
|
|
33
|
+
}
|
|
34
|
+
fg={theme.hints.fg}
|
|
35
|
+
>
|
|
36
|
+
{" "}
|
|
37
|
+
{hint.key}{" "}
|
|
38
|
+
</text>
|
|
39
|
+
<text fg={theme.colors.muted}> {hint.label}</text>
|
|
40
|
+
</box>
|
|
41
|
+
))}
|
|
42
|
+
</box>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type React from "react";
|
|
2
|
+
import { theme } from "../../theme.js";
|
|
3
|
+
|
|
4
|
+
interface DetailSectionProps {
|
|
5
|
+
title?: string;
|
|
6
|
+
children: React.ReactNode;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* A labeled section in a detail panel with optional title header.
|
|
11
|
+
*/
|
|
12
|
+
export function DetailSection({ title, children }: DetailSectionProps) {
|
|
13
|
+
return (
|
|
14
|
+
<box flexDirection="column" marginTop={1}>
|
|
15
|
+
{title ? (
|
|
16
|
+
<text fg={theme.colors.info}>
|
|
17
|
+
<strong>{title}</strong>
|
|
18
|
+
</text>
|
|
19
|
+
) : null}
|
|
20
|
+
{children}
|
|
21
|
+
</box>
|
|
22
|
+
);
|
|
23
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type React from "react";
|
|
2
|
+
import { theme } from "../../theme.js";
|
|
3
|
+
|
|
4
|
+
interface KeyValueLineProps {
|
|
5
|
+
label: string;
|
|
6
|
+
value: React.ReactNode;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Aligned label-value pair for detail panels.
|
|
11
|
+
*
|
|
12
|
+
* Labels pad to a 10-column gutter. The trailing space is inside the padding,
|
|
13
|
+
* not appended after it, so a label at or past 10 columns still separates from
|
|
14
|
+
* its value instead of running into it: every caller used to pass a short fixed
|
|
15
|
+
* label, and the first arbitrary one — a skill's `disable-model-invocation`
|
|
16
|
+
* frontmatter key — rendered as `disable-model-invocationtrue`. Labels of 9
|
|
17
|
+
* columns or fewer are unaffected.
|
|
18
|
+
*/
|
|
19
|
+
export function KeyValueLine({ label, value }: KeyValueLineProps) {
|
|
20
|
+
return (
|
|
21
|
+
<text fg={theme.colors.text}>
|
|
22
|
+
<span fg={theme.colors.muted}>{`${label} `.padEnd(10)}</span>
|
|
23
|
+
{value}
|
|
24
|
+
</text>
|
|
25
|
+
);
|
|
26
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { theme } from "../../theme.js";
|
|
3
|
+
import { SelectableRow } from "./SelectableRow.js";
|
|
4
|
+
|
|
5
|
+
interface ListCategoryRowProps {
|
|
6
|
+
title: string;
|
|
7
|
+
expanded?: boolean;
|
|
8
|
+
count?: number;
|
|
9
|
+
badge?: string;
|
|
10
|
+
tone?: keyof typeof theme.category;
|
|
11
|
+
selected: boolean;
|
|
12
|
+
/**
|
|
13
|
+
* Whether the row can be opened and closed. Default true, which is what
|
|
14
|
+
* every collapsible list wants. Set false for a heading that is purely a
|
|
15
|
+
* label — a ▶ on a row that does not respond to Enter reads as a control
|
|
16
|
+
* that is broken rather than as a section title.
|
|
17
|
+
*/
|
|
18
|
+
expandable?: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function ListCategoryRow({
|
|
22
|
+
title,
|
|
23
|
+
expanded = false,
|
|
24
|
+
count,
|
|
25
|
+
badge,
|
|
26
|
+
tone = "gray",
|
|
27
|
+
selected,
|
|
28
|
+
expandable = true,
|
|
29
|
+
}: ListCategoryRowProps) {
|
|
30
|
+
const colors = theme.category[tone];
|
|
31
|
+
const marker = expandable ? (expanded ? "▼ " : "▶ ") : "";
|
|
32
|
+
const label = `${marker}${title}${count !== undefined ? ` (${count})` : ""}`;
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<SelectableRow selected={selected}>
|
|
36
|
+
{!selected && (
|
|
37
|
+
<span bg={colors.bg} fg={colors.fg}>
|
|
38
|
+
<strong> {label} </strong>
|
|
39
|
+
</span>
|
|
40
|
+
)}
|
|
41
|
+
{selected && <strong> {label} </strong>}
|
|
42
|
+
{!selected && badge ? <span fg={colors.badgeFg}> {badge}</span> : null}
|
|
43
|
+
</SelectableRow>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { theme } from "../../theme.js";
|
|
3
|
+
|
|
4
|
+
interface MetaTextProps {
|
|
5
|
+
text: string;
|
|
6
|
+
tone?: "muted" | "warning" | "success" | "danger";
|
|
7
|
+
/**
|
|
8
|
+
* True when this sits inside a SELECTED row. Not cosmetic.
|
|
9
|
+
*
|
|
10
|
+
* Every `theme.meta` tone is chosen against the terminal's own background,
|
|
11
|
+
* and none of them survives the selection fill: measured on a screenshot,
|
|
12
|
+
* `muted` is 1.11:1 on it, `warning` 1.07:1, `danger` 1.11:1. So a selected
|
|
13
|
+
* row's `(default)`, `(new)`, `(needed)` and version strings were drawn in a
|
|
14
|
+
* colour indistinguishable from the purple behind them.
|
|
15
|
+
*
|
|
16
|
+
* On a selected row the tone's HUE is surrendered — the selection owns the
|
|
17
|
+
* colour channel there — and only the emphasis survives: `fg` for a tone
|
|
18
|
+
* that was saying something, `dim` for one that was only muttering. The
|
|
19
|
+
* label is words, so its meaning does not depend on the hue.
|
|
20
|
+
*/
|
|
21
|
+
selected?: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Subdued text for versions, stars, status indicators.
|
|
26
|
+
*/
|
|
27
|
+
export function MetaText({
|
|
28
|
+
text,
|
|
29
|
+
tone = "muted",
|
|
30
|
+
selected = false,
|
|
31
|
+
}: MetaTextProps) {
|
|
32
|
+
if (selected) {
|
|
33
|
+
return (
|
|
34
|
+
<span fg={tone === "muted" ? theme.selection.dim : theme.selection.fg}>
|
|
35
|
+
{text}
|
|
36
|
+
</span>
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
return <span fg={theme.meta[tone]}>{text}</span>;
|
|
40
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { theme } from "../../theme.js";
|
|
3
|
+
|
|
4
|
+
interface ScopeDetailScope {
|
|
5
|
+
user?: boolean;
|
|
6
|
+
project?: boolean;
|
|
7
|
+
local?: boolean;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
interface ScopeDetailPaths {
|
|
11
|
+
user?: string;
|
|
12
|
+
project?: string;
|
|
13
|
+
local?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface ScopeDetailProps {
|
|
17
|
+
scopes: ScopeDetailScope;
|
|
18
|
+
paths?: ScopeDetailPaths;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* The u/p/l badges used in detail panels.
|
|
23
|
+
* Renders each scope as: [bg=color] key [/bg] ● or ○ Label path
|
|
24
|
+
*/
|
|
25
|
+
export function ScopeDetail({ scopes, paths }: ScopeDetailProps) {
|
|
26
|
+
const items = [
|
|
27
|
+
{
|
|
28
|
+
key: "u",
|
|
29
|
+
label: "User",
|
|
30
|
+
color: theme.scopes.user,
|
|
31
|
+
active: scopes.user,
|
|
32
|
+
path: paths?.user,
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
key: "p",
|
|
36
|
+
label: "Project",
|
|
37
|
+
color: theme.scopes.project,
|
|
38
|
+
active: scopes.project,
|
|
39
|
+
path: paths?.project,
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
key: "l",
|
|
43
|
+
label: "Local",
|
|
44
|
+
color: theme.scopes.local,
|
|
45
|
+
active: scopes.local,
|
|
46
|
+
path: paths?.local,
|
|
47
|
+
},
|
|
48
|
+
].filter((i) => i.path !== undefined || i.active !== undefined);
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<box flexDirection="column">
|
|
52
|
+
{items.map((item) => (
|
|
53
|
+
<text key={item.key}>
|
|
54
|
+
<span bg={item.color} fg={theme.hints.fg}>
|
|
55
|
+
{" "}
|
|
56
|
+
{item.key}{" "}
|
|
57
|
+
</span>
|
|
58
|
+
<span fg={item.active ? item.color : theme.colors.muted}>
|
|
59
|
+
{item.active ? " ● " : " ○ "}
|
|
60
|
+
</span>
|
|
61
|
+
<span fg={item.color}>{item.label}</span>
|
|
62
|
+
{item.path && <span fg={theme.colors.muted}> {item.path}</span>}
|
|
63
|
+
</text>
|
|
64
|
+
))}
|
|
65
|
+
</box>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type React from "react";
|
|
2
|
+
import { scopeOffFill } from "../../theme-mode.js";
|
|
3
|
+
import { theme } from "../../theme.js";
|
|
4
|
+
|
|
5
|
+
interface ScopeSquaresProps {
|
|
6
|
+
user: boolean;
|
|
7
|
+
project: boolean;
|
|
8
|
+
local?: boolean;
|
|
9
|
+
selected?: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/** Every segment is a filled block; only the fill colour says lit or unlit. */
|
|
13
|
+
const SEGMENT = "■";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Scope bar: three positional segments, user / project / local.
|
|
17
|
+
*
|
|
18
|
+
* Reads as a segmented indicator — each segment lit in its scope colour, or
|
|
19
|
+
* unlit in a fill that recedes toward the page. Position identifies the scope;
|
|
20
|
+
* the first segment is always user, the second project, the third local.
|
|
21
|
+
*
|
|
22
|
+
* The unlit fill used to be `theme.colors.muted` (#6B7280), the same mid-dark
|
|
23
|
+
* grey as de-emphasised body text. Against a light page that reads as a *dark*
|
|
24
|
+
* block, giving an unlit segment the same visual weight as a lit one — "non
|
|
25
|
+
* installed state is dark as well - confusing". The unlit fill now comes from
|
|
26
|
+
* `scopeOffFill()`, which steps toward the page the resolved mode names; see
|
|
27
|
+
* theme-mode.ts for why that one value cannot be fixed.
|
|
28
|
+
*/
|
|
29
|
+
export function ScopeSquares({
|
|
30
|
+
user,
|
|
31
|
+
project,
|
|
32
|
+
local,
|
|
33
|
+
selected = false,
|
|
34
|
+
}: ScopeSquaresProps) {
|
|
35
|
+
// On a selected row the background is our own accent, so an unlit segment
|
|
36
|
+
// recedes toward that block rather than toward the page.
|
|
37
|
+
const off = selected ? theme.selection.bg : scopeOffFill();
|
|
38
|
+
const lit = (on: boolean, color: string) =>
|
|
39
|
+
on ? (selected ? theme.selection.fg : color) : off;
|
|
40
|
+
|
|
41
|
+
const segments = [
|
|
42
|
+
lit(user, theme.scopes.user),
|
|
43
|
+
lit(project, theme.scopes.project),
|
|
44
|
+
...(local !== undefined ? [lit(local, theme.scopes.local)] : []),
|
|
45
|
+
];
|
|
46
|
+
|
|
47
|
+
// Fragments of <span> — safe to embed inside a coloured <text> parent.
|
|
48
|
+
return (
|
|
49
|
+
<>
|
|
50
|
+
{segments.map((fg, i) => (
|
|
51
|
+
// biome-ignore lint/suspicious/noArrayIndexKey: fixed positional segments
|
|
52
|
+
<span key={i} fg={fg}>
|
|
53
|
+
{SEGMENT}
|
|
54
|
+
</span>
|
|
55
|
+
))}
|
|
56
|
+
</>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type React from "react";
|
|
2
|
+
import { theme } from "../../theme.js";
|
|
3
|
+
|
|
4
|
+
interface SelectableRowProps {
|
|
5
|
+
selected: boolean;
|
|
6
|
+
indent?: number;
|
|
7
|
+
children: React.ReactNode;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function SelectableRow({
|
|
11
|
+
selected,
|
|
12
|
+
indent = 0,
|
|
13
|
+
children,
|
|
14
|
+
}: SelectableRowProps) {
|
|
15
|
+
return (
|
|
16
|
+
<text
|
|
17
|
+
// Unselected: no background, so the row sits on the terminal's own page.
|
|
18
|
+
bg={selected ? theme.selection.bg : undefined}
|
|
19
|
+
// Unselected must still NAME a colour. `undefined` does not mean "the
|
|
20
|
+
// terminal's foreground" — OpenTUI's fallback for an unstyled cell is
|
|
21
|
+
// truecolor white, so every unselected row here rendered white-on-cream
|
|
22
|
+
// and vanished on a light theme. Same root cause as the original bug,
|
|
23
|
+
// reached by omission rather than by a wrong literal.
|
|
24
|
+
fg={selected ? theme.selection.fg : theme.colors.text}
|
|
25
|
+
>
|
|
26
|
+
{" ".repeat(indent)}
|
|
27
|
+
{children}
|
|
28
|
+
</text>
|
|
29
|
+
);
|
|
30
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
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 type { Hint } from "./ActionHints.js";
|
|
7
|
+
export { MetaText } from "./MetaText.js";
|
|
8
|
+
export { KeyValueLine } from "./KeyValueLine.js";
|
|
9
|
+
export { DetailSection } from "./DetailSection.js";
|