claudeup 4.6.0 → 4.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/data/settings-catalog.js +2 -7
- package/src/data/settings-catalog.ts +2 -7
- package/src/opentui.d.ts +7 -2
- package/src/services/claude-settings.js +31 -4
- package/src/services/claude-settings.ts +31 -4
- package/src/services/settings-manager.js +84 -5
- package/src/services/settings-manager.ts +86 -5
- package/src/types/index.ts +1 -1
- package/src/ui/adapters/settingsAdapter.js +8 -8
- package/src/ui/adapters/settingsAdapter.ts +8 -8
- package/src/ui/components/TabBar.js +1 -23
- package/src/ui/components/TabBar.tsx +1 -26
- package/src/ui/components/modals/ConfirmModal.js +1 -1
- package/src/ui/components/modals/ConfirmModal.tsx +17 -16
- package/src/ui/components/modals/InputModal.js +2 -13
- package/src/ui/components/modals/InputModal.tsx +21 -24
- package/src/ui/components/modals/LoadingModal.js +1 -1
- package/src/ui/components/modals/LoadingModal.tsx +6 -6
- package/src/ui/components/modals/MessageModal.js +4 -4
- package/src/ui/components/modals/MessageModal.tsx +13 -13
- package/src/ui/components/modals/ModalContainer.js +25 -2
- package/src/ui/components/modals/ModalContainer.tsx +25 -2
- package/src/ui/components/modals/SelectModal.js +3 -4
- package/src/ui/components/modals/SelectModal.tsx +18 -15
- package/src/ui/renderers/settingsRenderers.js +1 -1
- package/src/ui/renderers/settingsRenderers.tsx +5 -3
- package/src/ui/screens/CliToolsScreen.js +2 -2
- package/src/ui/screens/CliToolsScreen.tsx +3 -1
- package/src/ui/screens/EnvVarsScreen.js +27 -10
- package/src/ui/screens/EnvVarsScreen.tsx +33 -16
- package/src/ui/screens/McpRegistryScreen.js +2 -2
- package/src/ui/screens/McpRegistryScreen.tsx +3 -1
- package/src/ui/screens/McpScreen.js +1 -1
- package/src/ui/screens/McpScreen.tsx +2 -1
- package/src/ui/screens/ModelSelectorScreen.js +2 -2
- package/src/ui/screens/ModelSelectorScreen.tsx +3 -2
- package/src/ui/screens/ProfilesScreen.js +1 -1
- package/src/ui/screens/ProfilesScreen.tsx +2 -1
- package/src/ui/screens/StatusLineScreen.js +1 -1
- package/src/ui/screens/StatusLineScreen.tsx +2 -1
- package/src/ui/state/DimensionsContext.js +2 -2
- package/src/ui/state/DimensionsContext.tsx +3 -3
- package/src/ui/components/ScrollableDetail.js +0 -23
- package/src/ui/components/ScrollableDetail.tsx +0 -55
|
@@ -3,7 +3,7 @@ import { useApp, useModal, useNavigation } from "../state/AppContext.js";
|
|
|
3
3
|
import { useDimensions } from "../state/DimensionsContext.js";
|
|
4
4
|
import { useKeyboard } from "../hooks/useKeyboard.js";
|
|
5
5
|
import { ScreenLayout } from "../components/layout/index.js";
|
|
6
|
-
|
|
6
|
+
|
|
7
7
|
import {
|
|
8
8
|
getMcpServersByCategory,
|
|
9
9
|
getCategoryDisplayName,
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
renderMcpDetail,
|
|
22
22
|
type McpListItem,
|
|
23
23
|
} from "../renderers/mcpRenderers.js";
|
|
24
|
+
import { ScrollableList } from "../components/ScrollableList.js";
|
|
24
25
|
|
|
25
26
|
export function McpScreen() {
|
|
26
27
|
const { state, dispatch } = useApp();
|
|
@@ -2,9 +2,9 @@ import { jsxs as _jsxs, jsx as _jsx } from "@opentui/react/jsx-runtime";
|
|
|
2
2
|
import { useMemo, useEffect } from "react";
|
|
3
3
|
import { useApp } from "../state/AppContext.js";
|
|
4
4
|
import { useDimensions } from "../state/DimensionsContext.js";
|
|
5
|
-
import { ScrollableList } from "../components/ScrollableList.js";
|
|
6
5
|
import { fuzzyFilter, highlightMatches } from "../../utils/fuzzy-search.js";
|
|
7
6
|
import { useKeyboard } from "../hooks/useKeyboard.js";
|
|
7
|
+
import { ScrollableList } from "../components/ScrollableList.js";
|
|
8
8
|
const RECENT_MODELS = [
|
|
9
9
|
{
|
|
10
10
|
id: "recent-1",
|
|
@@ -285,6 +285,6 @@ export function ModelSelectorScreen() {
|
|
|
285
285
|
// Cursor line: > c
|
|
286
286
|
// Separator: handled by list? or explicit?
|
|
287
287
|
const listHeight = Math.max(5, dimensions.contentHeight - 5);
|
|
288
|
-
return (_jsxs("box", { flexDirection: "column", height: dimensions.contentHeight, children: [_jsx("box", { flexDirection: "row", border: true, borderStyle: "single", borderColor: "#7e57c2", paddingLeft: 1, paddingRight: 1, children: _jsxs("box", { flexDirection: "column", flexGrow: 1, children: [_jsx("box", { flexDirection: "row", justifyContent: "space-between", children: _jsxs("box", { children: [_jsx("text", { fg: "#7e57c2", children: "Switch Model " }), _jsxs("text", { fg: modelSelector.taskSize === "large" ? "white" : "gray", children: [modelSelector.taskSize === "large" ? "◎" : "○", " Large Task", " "] }), _jsxs("text", { fg: modelSelector.taskSize === "small" ? "white" : "gray", children: [modelSelector.taskSize === "small" ? "◎" : "○", " Small Task"] })] }) }), _jsxs("box", { flexDirection: "row", marginTop: 1, children: [_jsx("text", { fg: "green", children: "> " }), _jsx("text", { children: modelSelector.searchQuery }), _jsx("text", { bg: "gray", fg: "black", children: " " })] })] }) }), _jsx("box", { flexGrow: 1, paddingLeft: 1, paddingRight: 1, children: _jsx(ScrollableList, { items: filteredItems, selectedIndex: modelSelector.selectedIndex, renderItem: renderItem, maxHeight: listHeight,
|
|
288
|
+
return (_jsxs("box", { flexDirection: "column", height: dimensions.contentHeight, children: [_jsx("box", { flexDirection: "row", border: true, borderStyle: "single", borderColor: "#7e57c2", paddingLeft: 1, paddingRight: 1, children: _jsxs("box", { flexDirection: "column", flexGrow: 1, children: [_jsx("box", { flexDirection: "row", justifyContent: "space-between", children: _jsxs("box", { children: [_jsx("text", { fg: "#7e57c2", children: "Switch Model " }), _jsxs("text", { fg: modelSelector.taskSize === "large" ? "white" : "gray", children: [modelSelector.taskSize === "large" ? "◎" : "○", " Large Task", " "] }), _jsxs("text", { fg: modelSelector.taskSize === "small" ? "white" : "gray", children: [modelSelector.taskSize === "small" ? "◎" : "○", " Small Task"] })] }) }), _jsxs("box", { flexDirection: "row", marginTop: 1, children: [_jsx("text", { fg: "green", children: "> " }), _jsx("text", { children: modelSelector.searchQuery }), _jsx("text", { bg: "gray", fg: "black", children: " " })] })] }) }), _jsx("box", { flexGrow: 1, paddingLeft: 1, paddingRight: 1, children: _jsx(ScrollableList, { items: filteredItems, selectedIndex: modelSelector.selectedIndex, renderItem: renderItem, maxHeight: listHeight, getKey: (item) => item.id }) }), _jsx("box", { height: 1, children: _jsx("text", { fg: "#888888", children: footerHints }) })] }));
|
|
289
289
|
}
|
|
290
290
|
export default ModelSelectorScreen;
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import React, { useMemo, useEffect } from "react";
|
|
2
2
|
import { useApp } from "../state/AppContext.js";
|
|
3
3
|
import { useDimensions } from "../state/DimensionsContext.js";
|
|
4
|
-
|
|
4
|
+
|
|
5
5
|
import { fuzzyFilter, highlightMatches } from "../../utils/fuzzy-search.js";
|
|
6
6
|
import { useKeyboard } from "../hooks/useKeyboard.js";
|
|
7
|
+
import { ScrollableList } from "../components/ScrollableList.js";
|
|
7
8
|
|
|
8
9
|
interface ModelItem {
|
|
9
10
|
id: string;
|
|
@@ -423,7 +424,7 @@ export function ModelSelectorScreen() {
|
|
|
423
424
|
selectedIndex={modelSelector.selectedIndex}
|
|
424
425
|
renderItem={renderItem}
|
|
425
426
|
maxHeight={listHeight}
|
|
426
|
-
|
|
427
|
+
getKey={(item) => item.id}
|
|
427
428
|
/>
|
|
428
429
|
</box>
|
|
429
430
|
|
|
@@ -4,12 +4,12 @@ import { useApp, useModal } from "../state/AppContext.js";
|
|
|
4
4
|
import { useDimensions } from "../state/DimensionsContext.js";
|
|
5
5
|
import { useKeyboard } from "../hooks/useKeyboard.js";
|
|
6
6
|
import { ScreenLayout } from "../components/layout/index.js";
|
|
7
|
-
import { ScrollableList } from "../components/ScrollableList.js";
|
|
8
7
|
import { listProfiles, applyProfile, renameProfile, deleteProfile, exportProfileToJson, importProfileFromJson, } from "../../services/profiles.js";
|
|
9
8
|
import { readSettings, writeSettings, } from "../../services/claude-settings.js";
|
|
10
9
|
import { writeClipboard, readClipboard, ClipboardUnavailableError, } from "../../utils/clipboard.js";
|
|
11
10
|
import { PREDEFINED_PROFILES, } from "../../data/predefined-profiles.js";
|
|
12
11
|
import { buildProfileListItems, renderProfileRow, renderProfileDetail, } from "../renderers/profileRenderers.js";
|
|
12
|
+
import { ScrollableList } from "../components/ScrollableList.js";
|
|
13
13
|
export function ProfilesScreen() {
|
|
14
14
|
const { state, dispatch } = useApp();
|
|
15
15
|
const { profiles: profilesState } = state;
|
|
@@ -3,7 +3,7 @@ import { useApp, useModal } from "../state/AppContext.js";
|
|
|
3
3
|
import { useDimensions } from "../state/DimensionsContext.js";
|
|
4
4
|
import { useKeyboard } from "../hooks/useKeyboard.js";
|
|
5
5
|
import { ScreenLayout } from "../components/layout/index.js";
|
|
6
|
-
|
|
6
|
+
|
|
7
7
|
import {
|
|
8
8
|
listProfiles,
|
|
9
9
|
applyProfile,
|
|
@@ -31,6 +31,7 @@ import {
|
|
|
31
31
|
renderProfileDetail,
|
|
32
32
|
type ProfileListItem,
|
|
33
33
|
} from "../renderers/profileRenderers.js";
|
|
34
|
+
import { ScrollableList } from "../components/ScrollableList.js";
|
|
34
35
|
|
|
35
36
|
export function ProfilesScreen() {
|
|
36
37
|
const { state, dispatch } = useApp();
|
|
@@ -4,8 +4,8 @@ import { useApp, useModal } from "../state/AppContext.js";
|
|
|
4
4
|
import { useDimensions } from "../state/DimensionsContext.js";
|
|
5
5
|
import { useKeyboard } from "../hooks/useKeyboard.js";
|
|
6
6
|
import { ScreenLayout } from "../components/layout/index.js";
|
|
7
|
-
import { ScrollableList } from "../components/ScrollableList.js";
|
|
8
7
|
import { statusLineCategories } from "../../data/statuslines.js";
|
|
8
|
+
import { ScrollableList } from "../components/ScrollableList.js";
|
|
9
9
|
import { setStatusLine, getStatusLine, setGlobalStatusLine, getGlobalStatusLine, } from "../../services/claude-settings.js";
|
|
10
10
|
export function StatusLineScreen() {
|
|
11
11
|
const { state, dispatch } = useApp();
|
|
@@ -3,9 +3,10 @@ import { useApp, useModal } from "../state/AppContext.js";
|
|
|
3
3
|
import { useDimensions } from "../state/DimensionsContext.js";
|
|
4
4
|
import { useKeyboard } from "../hooks/useKeyboard.js";
|
|
5
5
|
import { ScreenLayout } from "../components/layout/index.js";
|
|
6
|
-
|
|
6
|
+
|
|
7
7
|
import { statusLineCategories } from "../../data/statuslines.js";
|
|
8
8
|
import type { StatusLineConfig } from "../../types/index.js";
|
|
9
|
+
import { ScrollableList } from "../components/ScrollableList.js";
|
|
9
10
|
import {
|
|
10
11
|
setStatusLine,
|
|
11
12
|
getStatusLine,
|
|
@@ -21,9 +21,9 @@ function calculateDimensions(columns, rows, showProgress, showDebug, showUpdateB
|
|
|
21
21
|
contentHeight = Math.max(10, contentHeight); // Minimum 10 lines for full layout
|
|
22
22
|
// Calculate available content width (accounting for padding)
|
|
23
23
|
const contentWidth = Math.max(40, terminalWidth - 4);
|
|
24
|
-
// Calculate list panel height for
|
|
24
|
+
// Calculate list panel height for scrollbox
|
|
25
25
|
// ScreenLayout uses: panelHeight = contentHeight - 4 (header) - 1 (footer)
|
|
26
|
-
// The
|
|
26
|
+
// The scrollbox sits inside the panel
|
|
27
27
|
const listPanelHeight = Math.max(3, contentHeight - SCREEN_HEADER_HEIGHT - SCREEN_FOOTER_HEIGHT);
|
|
28
28
|
return {
|
|
29
29
|
terminalWidth,
|
|
@@ -10,7 +10,7 @@ interface Dimensions {
|
|
|
10
10
|
contentHeight: number;
|
|
11
11
|
/** Available width for content (excluding borders, padding) */
|
|
12
12
|
contentWidth: number;
|
|
13
|
-
/** Available lines for
|
|
13
|
+
/** Available lines for scrollbox in list panels */
|
|
14
14
|
listPanelHeight: number;
|
|
15
15
|
}
|
|
16
16
|
|
|
@@ -52,9 +52,9 @@ function calculateDimensions(
|
|
|
52
52
|
// Calculate available content width (accounting for padding)
|
|
53
53
|
const contentWidth = Math.max(40, terminalWidth - 4);
|
|
54
54
|
|
|
55
|
-
// Calculate list panel height for
|
|
55
|
+
// Calculate list panel height for scrollbox
|
|
56
56
|
// ScreenLayout uses: panelHeight = contentHeight - 4 (header) - 1 (footer)
|
|
57
|
-
// The
|
|
57
|
+
// The scrollbox sits inside the panel
|
|
58
58
|
const listPanelHeight = Math.max(
|
|
59
59
|
3,
|
|
60
60
|
contentHeight - SCREEN_HEADER_HEIGHT - SCREEN_FOOTER_HEIGHT,
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { jsxs as _jsxs, jsx as _jsx } from "@opentui/react/jsx-runtime";
|
|
2
|
-
import { useState, useEffect } from "react";
|
|
3
|
-
/**
|
|
4
|
-
* A scrollable detail panel that renders an array of lines
|
|
5
|
-
* with automatic scroll tracking. When content exceeds maxHeight,
|
|
6
|
-
* it shows a scroll indicator and clips to fit.
|
|
7
|
-
*/
|
|
8
|
-
export function ScrollableDetail({ lines, maxHeight, scrollTrigger = 0, }) {
|
|
9
|
-
const [scrollOffset, setScrollOffset] = useState(0);
|
|
10
|
-
// Reset scroll when content changes (new item selected)
|
|
11
|
-
useEffect(() => {
|
|
12
|
-
setScrollOffset(0);
|
|
13
|
-
}, [scrollTrigger]);
|
|
14
|
-
const totalLines = lines.length;
|
|
15
|
-
const visibleLines = Math.max(1, maxHeight - 1); // -1 for scroll indicator
|
|
16
|
-
const canScroll = totalLines > visibleLines;
|
|
17
|
-
const maxOffset = Math.max(0, totalLines - visibleLines);
|
|
18
|
-
const clampedOffset = Math.min(scrollOffset, maxOffset);
|
|
19
|
-
const visibleContent = lines.slice(clampedOffset, clampedOffset + visibleLines);
|
|
20
|
-
const scrollUp = clampedOffset > 0;
|
|
21
|
-
const scrollDown = clampedOffset < maxOffset;
|
|
22
|
-
return (_jsxs("box", { flexDirection: "column", children: [scrollUp && (_jsx("box", { children: _jsxs("text", { fg: "cyan", children: ["\u2191 ", clampedOffset, " more"] }) })), visibleContent, scrollDown && (_jsx("box", { children: _jsxs("text", { fg: "cyan", children: ["\u2193 ", totalLines - clampedOffset - visibleLines, " more"] }) }))] }));
|
|
23
|
-
}
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import React, { useState, useEffect } from "react";
|
|
2
|
-
|
|
3
|
-
interface ScrollableDetailProps {
|
|
4
|
-
/** Array of content lines to display */
|
|
5
|
-
lines: React.ReactNode[];
|
|
6
|
-
/** Maximum visible height */
|
|
7
|
-
maxHeight: number;
|
|
8
|
-
/** External scroll trigger — changes when list selection changes */
|
|
9
|
-
scrollTrigger?: number;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* A scrollable detail panel that renders an array of lines
|
|
14
|
-
* with automatic scroll tracking. When content exceeds maxHeight,
|
|
15
|
-
* it shows a scroll indicator and clips to fit.
|
|
16
|
-
*/
|
|
17
|
-
export function ScrollableDetail({
|
|
18
|
-
lines,
|
|
19
|
-
maxHeight,
|
|
20
|
-
scrollTrigger = 0,
|
|
21
|
-
}: ScrollableDetailProps) {
|
|
22
|
-
const [scrollOffset, setScrollOffset] = useState(0);
|
|
23
|
-
|
|
24
|
-
// Reset scroll when content changes (new item selected)
|
|
25
|
-
useEffect(() => {
|
|
26
|
-
setScrollOffset(0);
|
|
27
|
-
}, [scrollTrigger]);
|
|
28
|
-
|
|
29
|
-
const totalLines = lines.length;
|
|
30
|
-
const visibleLines = Math.max(1, maxHeight - 1); // -1 for scroll indicator
|
|
31
|
-
const canScroll = totalLines > visibleLines;
|
|
32
|
-
const maxOffset = Math.max(0, totalLines - visibleLines);
|
|
33
|
-
const clampedOffset = Math.min(scrollOffset, maxOffset);
|
|
34
|
-
|
|
35
|
-
const visibleContent = lines.slice(clampedOffset, clampedOffset + visibleLines);
|
|
36
|
-
|
|
37
|
-
const scrollUp = clampedOffset > 0;
|
|
38
|
-
const scrollDown = clampedOffset < maxOffset;
|
|
39
|
-
|
|
40
|
-
return (
|
|
41
|
-
<box flexDirection="column">
|
|
42
|
-
{scrollUp && (
|
|
43
|
-
<box>
|
|
44
|
-
<text fg="cyan">↑ {clampedOffset} more</text>
|
|
45
|
-
</box>
|
|
46
|
-
)}
|
|
47
|
-
{visibleContent}
|
|
48
|
-
{scrollDown && (
|
|
49
|
-
<box>
|
|
50
|
-
<text fg="cyan">↓ {totalLines - clampedOffset - visibleLines} more</text>
|
|
51
|
-
</box>
|
|
52
|
-
)}
|
|
53
|
-
</box>
|
|
54
|
-
);
|
|
55
|
-
}
|