feat: port CLI, TUI, and launcher from v1.5 into v2 (#1452)
Ports the CLI, TUI, and launcher from v1.5 into v2 behind a single `mcp-inspector` entrypoint (launcher → web/cli/tui). Includes the `mcp-inspector --web --dev` launcher fix (Vite alias resolution + prod-server double-start), the root `postinstall` install cascade with simplified CI, and spec/AGENTS docs (no-workspaces rationale, Development setup). Closes #1246. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
53dc4549be
commit
2488ca5e38
1 changed files with 25594 additions and 236 deletions
61
clients/tui/src/hooks/useSelectableList.ts
Normal file
61
clients/tui/src/hooks/useSelectableList.ts
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
import { useState, useEffect, useCallback } from "react";
|
||||
|
||||
function clampFirstVisible(
|
||||
first: number,
|
||||
selected: number,
|
||||
visibleCount: number,
|
||||
): number {
|
||||
if (selected < first) return selected;
|
||||
if (selected >= first + visibleCount) return selected - visibleCount + 1;
|
||||
return first;
|
||||
}
|
||||
|
||||
export interface UseSelectableListOptions {
|
||||
/** When this reference changes, reset selection to 0 (e.g. tools list when switching servers) */
|
||||
resetWhen?: unknown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Manages selection and scroll position for a virtualized list.
|
||||
* Returns selection state and a setSelection that updates both
|
||||
* selectedIndex and firstVisible so the selected item stays in view.
|
||||
*/
|
||||
export function useSelectableList(
|
||||
itemCount: number,
|
||||
visibleCount: number,
|
||||
options?: UseSelectableListOptions,
|
||||
) {
|
||||
const [selectedIndex, setSelectedIndex] = useState(0);
|
||||
const [firstVisible, setFirstVisible] = useState(0);
|
||||
|
||||
const setSelection = useCallback(
|
||||
(newIndex: number) => {
|
||||
setSelectedIndex(newIndex);
|
||||
setFirstVisible((prev) =>
|
||||
clampFirstVisible(prev, newIndex, visibleCount),
|
||||
);
|
||||
},
|
||||
[visibleCount],
|
||||
);
|
||||
|
||||
// Reset when the list reference changes (e.g. different server).
|
||||
useEffect(() => {
|
||||
if (options?.resetWhen !== undefined) {
|
||||
setSelectedIndex(0);
|
||||
setFirstVisible(0);
|
||||
}
|
||||
}, [options?.resetWhen]);
|
||||
|
||||
// Clamp when list shrinks
|
||||
useEffect(() => {
|
||||
if (itemCount > 0 && selectedIndex >= itemCount) {
|
||||
const newIndex = itemCount - 1;
|
||||
setSelectedIndex(newIndex);
|
||||
setFirstVisible((prev) =>
|
||||
clampFirstVisible(prev, newIndex, visibleCount),
|
||||
);
|
||||
}
|
||||
}, [itemCount, selectedIndex, visibleCount]);
|
||||
|
||||
return { selectedIndex, firstVisible, setSelection };
|
||||
}
|
||||
Loading…
Reference in a new issue