更新大量 tsx 原始文件; 已经迁移 login panel; 部分 (#121)

* style(B1-1): 格式化 ink/buddy/cli/context/screens/tasks/services/keybindings/state (43 files)

纯格式化:移除分号、React Compiler import、import 多行展开。
修复了 Box.tsx 和 ScrollBox.tsx 中无效的 global.d.ts import。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* style(B1-2): 格式化 commands (79 files)

纯格式化:移除分号、React Compiler import、import 多行展开。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* style(B1-3): 格式化 components/messages,permissions,mcp,sandbox,shell (104 files)

纯格式化:移除分号、React Compiler import、import 多行展开。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* style(B1-4): 格式化 components/PromptInput,FeedbackSurvey,tasks,agents,skills,design-system,wizard (73 files)

纯格式化:移除分号、React Compiler import、import 多行展开。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* style(B1-5): 格式化 components其余 + hooks + tools (232 files)

纯格式化:移除分号、React Compiler import、import 多行展开。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* style(B1-6): 格式化 main/entrypoints/utils/moreright (21 files)

纯格式化:移除分号、React Compiler import、import 多行展开。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: 更新 README,新增 Run.ps1/TODO.md,删除 V6.md

- README.md: 大幅重写,更详细版本历史和配置示例
- Run.ps1: 新增 Windows 启动脚本
- TODO.md: 新增包完成清单
- V6.md: 删除(架构重构规划已不适用)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: 修复以前的问题

* fix: 修复 login 面板的问题

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
claude-code-best
2026-04-04 23:24:27 +08:00
committed by GitHub
parent 02694918b5
commit 5b1a52b8e0
559 changed files with 103807 additions and 101817 deletions

View File

@@ -1,292 +1,248 @@
import { c as _c } from "react/compiler-runtime";
import * as React from 'react';
import { memo, type ReactNode } from 'react';
import { useTerminalSize } from '../../hooks/useTerminalSize.js';
import { stringWidth } from '../../ink/stringWidth.js';
import { Box, Text } from '../../ink.js';
import { truncatePathMiddle, truncateToWidth } from '../../utils/format.js';
import type { Theme } from '../../utils/theme.js';
import * as React from 'react'
import { memo, type ReactNode } from 'react'
import { useTerminalSize } from '../../hooks/useTerminalSize.js'
import { stringWidth } from '../../ink/stringWidth.js'
import { Box, Text } from '../../ink.js'
import { truncatePathMiddle, truncateToWidth } from '../../utils/format.js'
import type { Theme } from '../../utils/theme.js'
export type SuggestionItem = {
id: string;
displayText: string;
tag?: string;
description?: string;
metadata?: unknown;
color?: keyof Theme;
};
export type SuggestionType = 'command' | 'file' | 'directory' | 'agent' | 'shell' | 'custom-title' | 'slack-channel' | 'none';
export const OVERLAY_MAX_ITEMS = 5;
id: string
displayText: string
tag?: string
description?: string
metadata?: unknown
color?: keyof Theme
}
export type SuggestionType =
| 'command'
| 'file'
| 'directory'
| 'agent'
| 'shell'
| 'custom-title'
| 'slack-channel'
| 'none'
export const OVERLAY_MAX_ITEMS = 5
/**
* Get the icon for a suggestion based on its type
* Icons: + for files, ◇ for MCP resources, * for agents
*/
function getIcon(itemId: string): string {
if (itemId.startsWith('file-')) return '+';
if (itemId.startsWith('mcp-resource-')) return '◇';
if (itemId.startsWith('agent-')) return '*';
return '+';
if (itemId.startsWith('file-')) return '+'
if (itemId.startsWith('mcp-resource-')) return '◇'
if (itemId.startsWith('agent-')) return '*'
return '+'
}
/**
* Check if an item is a unified suggestion type (file, mcp-resource, or agent)
*/
function isUnifiedSuggestion(itemId: string): boolean {
return itemId.startsWith('file-') || itemId.startsWith('mcp-resource-') || itemId.startsWith('agent-');
return (
itemId.startsWith('file-') ||
itemId.startsWith('mcp-resource-') ||
itemId.startsWith('agent-')
)
}
const SuggestionItemRow = memo(function SuggestionItemRow(t0: { item: SuggestionItem; maxColumnWidth: number; isSelected: boolean }) {
const $ = _c(36);
const {
item,
maxColumnWidth,
isSelected
} = t0;
const columns = useTerminalSize().columns;
const isUnified = isUnifiedSuggestion(item.id);
const SuggestionItemRow = memo(function SuggestionItemRow({
item,
maxColumnWidth,
isSelected,
}: {
item: SuggestionItem
maxColumnWidth?: number
isSelected: boolean
}): ReactNode {
const columns = useTerminalSize().columns
const isUnified = isUnifiedSuggestion(item.id)
// For unified suggestions (file, mcp-resource, agent), use single-line layout with icon
if (isUnified) {
let t1;
if ($[0] !== item.id) {
t1 = getIcon(item.id);
$[0] = item.id;
$[1] = t1;
} else {
t1 = $[1];
}
const icon = t1;
const textColor = isSelected ? "suggestion" : undefined;
const dimColor = !isSelected;
const isFile = item.id.startsWith("file-");
const isMcpResource = item.id.startsWith("mcp-resource-");
const separatorWidth = item.description ? 3 : 0;
let displayText;
const icon = getIcon(item.id)
const textColor: keyof Theme | undefined = isSelected
? 'suggestion'
: undefined
const dimColor = !isSelected
const isFile = item.id.startsWith('file-')
const isMcpResource = item.id.startsWith('mcp-resource-')
// Calculate layout widths
// Layout: "X " (2) + displayText + " " (3) + description + padding (4)
const iconWidth = 2 // icon + space (fixed)
const paddingWidth = 4
const separatorWidth = item.description ? 3 : 0 // ' ' separator
// For files, truncate middle of path to show both directory context and filename
// For MCP resources, limit displayText to 30 chars (truncate from end)
// For agents, no truncation
let displayText: string
if (isFile) {
let t2;
if ($[2] !== item.description) {
t2 = item.description ? Math.min(20, stringWidth(item.description)) : 0;
$[2] = item.description;
$[3] = t2;
} else {
t2 = $[3];
}
const descReserve = t2;
const maxPathLength = columns - 2 - 4 - separatorWidth - descReserve;
let t3;
if ($[4] !== item.displayText || $[5] !== maxPathLength) {
t3 = truncatePathMiddle(item.displayText, maxPathLength);
$[4] = item.displayText;
$[5] = maxPathLength;
$[6] = t3;
} else {
t3 = $[6];
}
displayText = t3;
// Reserve space for description if present, otherwise use all available space
const descReserve = item.description
? Math.min(20, stringWidth(item.description))
: 0
const maxPathLength =
columns - iconWidth - paddingWidth - separatorWidth - descReserve
displayText = truncatePathMiddle(item.displayText, maxPathLength)
} else if (isMcpResource) {
const maxDisplayTextLength = 30
displayText = truncateToWidth(item.displayText, maxDisplayTextLength)
} else {
if (isMcpResource) {
let t2;
if ($[7] !== item.displayText) {
t2 = truncateToWidth(item.displayText, 30);
$[7] = item.displayText;
$[8] = t2;
} else {
t2 = $[8];
}
displayText = t2;
} else {
displayText = item.displayText;
}
displayText = item.displayText
}
const availableWidth = columns - 2 - stringWidth(displayText) - separatorWidth - 4;
let lineContent;
const availableWidth =
columns -
iconWidth -
stringWidth(displayText) -
separatorWidth -
paddingWidth
// Build the full line as a single string to prevent wrapping
let lineContent: string
if (item.description) {
const maxDescLength = Math.max(0, availableWidth);
let t2;
if ($[9] !== item.description || $[10] !== maxDescLength) {
t2 = truncateToWidth(item.description.replace(/\s+/g, " "), maxDescLength);
$[9] = item.description;
$[10] = maxDescLength;
$[11] = t2;
} else {
t2 = $[11];
}
const truncatedDesc = t2;
lineContent = `${icon} ${displayText} ${truncatedDesc}`;
const maxDescLength = Math.max(0, availableWidth)
const truncatedDesc = truncateToWidth(
item.description.replace(/\s+/g, ' '),
maxDescLength,
)
lineContent = `${icon} ${displayText} ${truncatedDesc}`
} else {
lineContent = `${icon} ${displayText}`;
lineContent = `${icon} ${displayText}`
}
let t2;
if ($[12] !== dimColor || $[13] !== lineContent || $[14] !== textColor) {
t2 = <Text color={textColor} dimColor={dimColor} wrap="truncate">{lineContent}</Text>;
$[12] = dimColor;
$[13] = lineContent;
$[14] = textColor;
$[15] = t2;
} else {
t2 = $[15];
}
return t2;
return (
<Text color={textColor} dimColor={dimColor} wrap="truncate">
{lineContent}
</Text>
)
}
const maxNameWidth = Math.floor(columns * 0.4);
const displayTextWidth = Math.min(maxColumnWidth ?? stringWidth(item.displayText) + 5, maxNameWidth);
const textColor_0 = item.color || (isSelected ? "suggestion" : undefined);
const shouldDim = !isSelected;
let displayText_0 = item.displayText;
if (stringWidth(displayText_0) > displayTextWidth - 2) {
const t1 = displayTextWidth - 2;
let t2;
if ($[16] !== displayText_0 || $[17] !== t1) {
t2 = truncateToWidth(displayText_0, t1);
$[16] = displayText_0;
$[17] = t1;
$[18] = t2;
} else {
t2 = $[18];
}
displayText_0 = t2;
// For non-unified suggestions (commands, shell, etc.), use improved layout from main
// Cap the command name column at 40% of terminal width to ensure description has space
const maxNameWidth = Math.floor(columns * 0.4)
const displayTextWidth = Math.min(
maxColumnWidth ?? stringWidth(item.displayText) + 5,
maxNameWidth,
)
const textColor = item.color || (isSelected ? 'suggestion' : undefined)
const shouldDim = !isSelected
// Truncate and pad the display text to fixed width
let displayText = item.displayText
if (stringWidth(displayText) > displayTextWidth - 2) {
displayText = truncateToWidth(displayText, displayTextWidth - 2)
}
const paddedDisplayText = displayText_0 + " ".repeat(Math.max(0, displayTextWidth - stringWidth(displayText_0)));
const tagText = item.tag ? `[${item.tag}] ` : "";
const tagWidth = stringWidth(tagText);
const descriptionWidth = Math.max(0, columns - displayTextWidth - tagWidth - 4);
let t1;
if ($[19] !== descriptionWidth || $[20] !== item.description) {
t1 = item.description ? truncateToWidth(item.description.replace(/\s+/g, " "), descriptionWidth) : "";
$[19] = descriptionWidth;
$[20] = item.description;
$[21] = t1;
} else {
t1 = $[21];
}
const truncatedDescription = t1;
let t2;
if ($[22] !== paddedDisplayText || $[23] !== shouldDim || $[24] !== textColor_0) {
t2 = <Text color={textColor_0} dimColor={shouldDim}>{paddedDisplayText}</Text>;
$[22] = paddedDisplayText;
$[23] = shouldDim;
$[24] = textColor_0;
$[25] = t2;
} else {
t2 = $[25];
}
let t3;
if ($[26] !== tagText) {
t3 = tagText ? <Text dimColor={true}>{tagText}</Text> : null;
$[26] = tagText;
$[27] = t3;
} else {
t3 = $[27];
}
const t4 = isSelected ? "suggestion" : undefined;
const t5 = !isSelected;
let t6;
if ($[28] !== t4 || $[29] !== t5 || $[30] !== truncatedDescription) {
t6 = <Text color={t4} dimColor={t5}>{truncatedDescription}</Text>;
$[28] = t4;
$[29] = t5;
$[30] = truncatedDescription;
$[31] = t6;
} else {
t6 = $[31];
}
let t7;
if ($[32] !== t2 || $[33] !== t3 || $[34] !== t6) {
t7 = <Text wrap="truncate">{t2}{t3}{t6}</Text>;
$[32] = t2;
$[33] = t3;
$[34] = t6;
$[35] = t7;
} else {
t7 = $[35];
}
return t7;
});
const paddedDisplayText =
displayText +
' '.repeat(Math.max(0, displayTextWidth - stringWidth(displayText)))
const tagText = item.tag ? `[${item.tag}] ` : ''
const tagWidth = stringWidth(tagText)
const descriptionWidth = Math.max(
0,
columns - displayTextWidth - tagWidth - 4,
)
// Skill descriptions can contain newlines (e.g. /claude-api's "TRIGGER
// when:" block). A multi-line row grows the overlay past minHeight; when
// the filter narrows past that skill, the overlay shrinks and leaves
// ghost rows. Flatten to one line before truncating.
const truncatedDescription = item.description
? truncateToWidth(item.description.replace(/\s+/g, ' '), descriptionWidth)
: ''
return (
<Text wrap="truncate">
<Text color={textColor} dimColor={shouldDim}>
{paddedDisplayText}
</Text>
{tagText ? <Text dimColor>{tagText}</Text> : null}
<Text
color={isSelected ? 'suggestion' : undefined}
dimColor={!isSelected}
>
{truncatedDescription}
</Text>
</Text>
)
})
type Props = {
suggestions: SuggestionItem[];
selectedSuggestion: number;
maxColumnWidth?: number;
suggestions: SuggestionItem[]
selectedSuggestion: number
maxColumnWidth?: number
/**
* When true, the suggestions are rendered inside a position=absolute
* overlay. We omit minHeight and flex-end so the y-clamp in the
* renderer doesn't push fewer items down into the prompt area.
*/
overlay?: boolean;
};
export function PromptInputFooterSuggestions(t0) {
const $ = _c(22);
const {
suggestions,
selectedSuggestion,
maxColumnWidth: maxColumnWidthProp,
overlay
} = t0;
const {
rows
} = useTerminalSize();
const maxVisibleItems = overlay ? OVERLAY_MAX_ITEMS : Math.min(6, Math.max(1, rows - 3));
overlay?: boolean
}
export function PromptInputFooterSuggestions({
suggestions,
selectedSuggestion,
maxColumnWidth: maxColumnWidthProp,
overlay,
}: Props): ReactNode {
const { rows } = useTerminalSize()
// Maximum number of suggestions to show at once (leaving space for prompt).
// Overlay mode (fullscreen) uses a fixed 5 — the floating box sits over
// the ScrollBox, so terminal height isn't the constraint.
const maxVisibleItems = overlay
? OVERLAY_MAX_ITEMS
: Math.min(6, Math.max(1, rows - 3))
// No suggestions to display
if (suggestions.length === 0) {
return null;
return null
}
let t1;
if ($[0] !== maxColumnWidthProp || $[1] !== suggestions) {
t1 = maxColumnWidthProp ?? Math.max(...suggestions.map(_temp)) + 5;
$[0] = maxColumnWidthProp;
$[1] = suggestions;
$[2] = t1;
} else {
t1 = $[2];
}
const maxColumnWidth = t1;
const startIndex = Math.max(0, Math.min(selectedSuggestion - Math.floor(maxVisibleItems / 2), suggestions.length - maxVisibleItems));
const endIndex = Math.min(startIndex + maxVisibleItems, suggestions.length);
let T0;
let t2;
let t3;
let t4;
if ($[3] !== endIndex || $[4] !== maxColumnWidth || $[5] !== overlay || $[6] !== selectedSuggestion || $[7] !== startIndex || $[8] !== suggestions) {
const visibleItems = suggestions.slice(startIndex, endIndex);
T0 = Box;
t2 = "column";
t3 = overlay ? undefined : "flex-end";
let t5;
if ($[13] !== maxColumnWidth || $[14] !== selectedSuggestion || $[15] !== suggestions) {
t5 = item_0 => <SuggestionItemRow key={item_0.id} item={item_0} maxColumnWidth={maxColumnWidth} isSelected={item_0.id === suggestions[selectedSuggestion]?.id} />;
$[13] = maxColumnWidth;
$[14] = selectedSuggestion;
$[15] = suggestions;
$[16] = t5;
} else {
t5 = $[16];
}
t4 = visibleItems.map(t5);
$[3] = endIndex;
$[4] = maxColumnWidth;
$[5] = overlay;
$[6] = selectedSuggestion;
$[7] = startIndex;
$[8] = suggestions;
$[9] = T0;
$[10] = t2;
$[11] = t3;
$[12] = t4;
} else {
T0 = $[9];
t2 = $[10];
t3 = $[11];
t4 = $[12];
}
let t5;
if ($[17] !== T0 || $[18] !== t2 || $[19] !== t3 || $[20] !== t4) {
t5 = <T0 flexDirection={t2} justifyContent={t3}>{t4}</T0>;
$[17] = T0;
$[18] = t2;
$[19] = t3;
$[20] = t4;
$[21] = t5;
} else {
t5 = $[21];
}
return t5;
// Use prop if provided (stable width from all commands), otherwise calculate from visible
const maxColumnWidth =
maxColumnWidthProp ??
Math.max(...suggestions.map(item => stringWidth(item.displayText))) + 5
// Calculate visible items range based on selected index
const startIndex = Math.max(
0,
Math.min(
selectedSuggestion - Math.floor(maxVisibleItems / 2),
suggestions.length - maxVisibleItems,
),
)
const endIndex = Math.min(startIndex + maxVisibleItems, suggestions.length)
const visibleItems = suggestions.slice(startIndex, endIndex)
// In non-overlay (inline) mode, justifyContent keeps suggestions
// anchored to the bottom (near the prompt). In overlay mode we omit
// both minHeight and flex-end: the parent is position=absolute with
// bottom='100%', so its y is clamped to 0 by the renderer when it
// would go negative. Adding minHeight + flex-end would create empty
// padding rows that shift the visible items down into the prompt area
// when the list has fewer items than maxVisibleItems.
return (
<Box
flexDirection="column"
justifyContent={overlay ? undefined : 'flex-end'}
>
{visibleItems.map(item => (
<SuggestionItemRow
key={item.id}
item={item}
maxColumnWidth={maxColumnWidth}
isSelected={item.id === suggestions[selectedSuggestion]?.id}
/>
))}
</Box>
)
}
function _temp(item) {
return stringWidth(item.displayText);
}
export default memo(PromptInputFooterSuggestions);
export default memo(PromptInputFooterSuggestions)