更新大量 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,10 +1,9 @@
import { c as _c } from "react/compiler-runtime";
import { diffWordsWithSpace, type StructuredPatchHunk } from 'diff';
import * as React from 'react';
import { useMemo } from 'react';
import type { ThemeName } from 'src/utils/theme.js';
import { stringWidth } from '../../ink/stringWidth.js';
import { Box, NoSelect, Text, useTheme, wrapText } from '../../ink.js';
import { diffWordsWithSpace, type StructuredPatchHunk } from 'diff'
import * as React from 'react'
import { useMemo } from 'react'
import type { ThemeName } from 'src/utils/theme.js'
import { stringWidth } from '../../ink/stringWidth.js'
import { Box, NoSelect, Text, useTheme, wrapText } from '../../ink.js'
/*
* StructuredDiffFallback Component: Word-Level Diff Highlighting Example
@@ -46,82 +45,61 @@ import { Box, NoSelect, Text, useTheme, wrapText } from '../../ink.js';
// Define DiffLine interface to be used throughout the file
interface DiffLine {
code: string;
type: 'add' | 'remove' | 'nochange';
i: number;
originalCode: string;
wordDiff?: boolean; // Flag for word-level diffing
matchedLine?: DiffLine;
code: string
type: 'add' | 'remove' | 'nochange'
i: number
originalCode: string
wordDiff?: boolean // Flag for word-level diffing
matchedLine?: DiffLine
}
// Line object type for internal functions
export interface LineObject {
code: string;
i: number;
type: 'add' | 'remove' | 'nochange';
originalCode: string;
wordDiff?: boolean;
matchedLine?: LineObject;
code: string
i: number
type: 'add' | 'remove' | 'nochange'
originalCode: string
wordDiff?: boolean
matchedLine?: LineObject
}
// Type for word-level diff parts
interface DiffPart {
added?: boolean;
removed?: boolean;
value: string;
added?: boolean
removed?: boolean
value: string
}
type Props = {
patch: StructuredPatchHunk;
dim: boolean;
width: number;
};
patch: StructuredPatchHunk
dim: boolean
width: number
}
// Threshold for when we show a full-line diff instead of word-level diffing
const CHANGE_THRESHOLD = 0.4;
export function StructuredDiffFallback(t0) {
const $ = _c(10);
const {
patch,
dim,
width
} = t0;
const [theme] = useTheme();
let t1;
if ($[0] !== dim || $[1] !== patch.lines || $[2] !== patch.oldStart || $[3] !== theme || $[4] !== width) {
t1 = formatDiff(patch.lines, patch.oldStart, width, dim, theme);
$[0] = dim;
$[1] = patch.lines;
$[2] = patch.oldStart;
$[3] = theme;
$[4] = width;
$[5] = t1;
} else {
t1 = $[5];
}
const diff = t1;
let t2;
if ($[6] !== diff) {
t2 = diff.map(_temp);
$[6] = diff;
$[7] = t2;
} else {
t2 = $[7];
}
let t3;
if ($[8] !== t2) {
t3 = <Box flexDirection="column" flexGrow={1}>{t2}</Box>;
$[8] = t2;
$[9] = t3;
} else {
t3 = $[9];
}
return t3;
const CHANGE_THRESHOLD = 0.4
export function StructuredDiffFallback({
patch,
dim,
width,
}: Props): React.ReactNode {
const [theme] = useTheme()
const diff = useMemo(
() => formatDiff(patch.lines, patch.oldStart, width, dim, theme),
[patch.lines, patch.oldStart, width, dim, theme],
)
return (
<Box flexDirection="column" flexGrow={1}>
{diff.map((node, i) => (
<Box key={i}>{node}</Box>
))}
</Box>
)
}
// Transform lines to line objects with type information
function _temp(node, i) {
return <Box key={i}>{node}</Box>;
}
export function transformLinesToObjects(lines: string[]): LineObject[] {
return lines.map(code => {
if (code.startsWith('+')) {
@@ -129,358 +107,428 @@ export function transformLinesToObjects(lines: string[]): LineObject[] {
code: code.slice(1),
i: 0,
type: 'add',
originalCode: code.slice(1)
};
originalCode: code.slice(1),
}
}
if (code.startsWith('-')) {
return {
code: code.slice(1),
i: 0,
type: 'remove',
originalCode: code.slice(1)
};
originalCode: code.slice(1),
}
}
return {
code: code.slice(1),
i: 0,
type: 'nochange',
originalCode: code.slice(1)
};
});
originalCode: code.slice(1),
}
})
}
// Group adjacent add/remove lines for word-level diffing
export function processAdjacentLines(lineObjects: LineObject[]): LineObject[] {
const processedLines: LineObject[] = [];
let i = 0;
const processedLines: LineObject[] = []
let i = 0
while (i < lineObjects.length) {
const current = lineObjects[i];
const current = lineObjects[i]
if (!current) {
i++;
continue;
i++
continue
}
// Find a sequence of remove followed by add (possible word-level diff candidates)
if (current.type === 'remove') {
const removeLines: LineObject[] = [current];
let j = i + 1;
const removeLines: LineObject[] = [current]
let j = i + 1
// Collect consecutive remove lines
while (j < lineObjects.length && lineObjects[j]?.type === 'remove') {
const line = lineObjects[j];
const line = lineObjects[j]
if (line) {
removeLines.push(line);
removeLines.push(line)
}
j++;
j++
}
// Check if there are add lines following the remove lines
const addLines: LineObject[] = [];
const addLines: LineObject[] = []
while (j < lineObjects.length && lineObjects[j]?.type === 'add') {
const line = lineObjects[j];
const line = lineObjects[j]
if (line) {
addLines.push(line);
addLines.push(line)
}
j++;
j++
}
// If we have both remove and add lines, perform word-level diffing
if (removeLines.length > 0 && addLines.length > 0) {
// For word diffing, we'll compare each pair of lines or the closest available match
const pairCount = Math.min(removeLines.length, addLines.length);
const pairCount = Math.min(removeLines.length, addLines.length)
// Add paired lines with word diff info
for (let k = 0; k < pairCount; k++) {
const removeLine = removeLines[k];
const addLine = addLines[k];
const removeLine = removeLines[k]
const addLine = addLines[k]
if (removeLine && addLine) {
removeLine.wordDiff = true;
addLine.wordDiff = true;
removeLine.wordDiff = true
addLine.wordDiff = true
// Store the matched pair for later word diffing
removeLine.matchedLine = addLine;
addLine.matchedLine = removeLine;
removeLine.matchedLine = addLine
addLine.matchedLine = removeLine
}
}
// Add all remove lines (both paired and unpaired)
processedLines.push(...removeLines.filter(Boolean));
processedLines.push(...removeLines.filter(Boolean))
// Then add all add lines (both paired and unpaired)
processedLines.push(...addLines.filter(Boolean));
i = j; // Skip all the lines we've processed
processedLines.push(...addLines.filter(Boolean))
i = j // Skip all the lines we've processed
} else {
// No matching add lines, just add the current remove line
processedLines.push(current);
i++;
processedLines.push(current)
i++
}
} else {
// Not a remove line, just add it
processedLines.push(current);
i++;
processedLines.push(current)
i++
}
}
return processedLines;
return processedLines
}
// Calculate word-level diffs between two text strings
export function calculateWordDiffs(oldText: string, newText: string): DiffPart[] {
export function calculateWordDiffs(
oldText: string,
newText: string,
): DiffPart[] {
// Use diffWordsWithSpace instead of diffWords to preserve whitespace
// This ensures spaces between tokens like > and { are preserved
const result = diffWordsWithSpace(oldText, newText, {
ignoreCase: false
});
return result;
const result = diffWordsWithSpace(oldText, newText, { ignoreCase: false })
return result
}
// Process word-level diffs with manual wrapping support
function generateWordDiffElements(item: DiffLine, width: number, maxWidth: number, dim: boolean, overrideTheme?: ThemeName): React.ReactNode[] | null {
const {
type,
i,
wordDiff,
matchedLine,
originalCode
} = item;
function generateWordDiffElements(
item: DiffLine,
width: number,
maxWidth: number,
dim: boolean,
overrideTheme?: ThemeName,
): React.ReactNode[] | null {
const { type, i, wordDiff, matchedLine, originalCode } = item
if (!wordDiff || !matchedLine) {
return null; // This function only handles word-level diff rendering
return null // This function only handles word-level diff rendering
}
const removedLineText = type === 'remove' ? originalCode : matchedLine.originalCode;
const addedLineText = type === 'remove' ? matchedLine.originalCode : originalCode;
const wordDiffs = calculateWordDiffs(removedLineText, addedLineText);
const removedLineText =
type === 'remove' ? originalCode : matchedLine.originalCode
const addedLineText =
type === 'remove' ? matchedLine.originalCode : originalCode
const wordDiffs = calculateWordDiffs(removedLineText, addedLineText)
// Check if we should use word-level diffing
const totalLength = removedLineText.length + addedLineText.length;
const changedLength = wordDiffs.filter(part => part.added || part.removed).reduce((sum, part) => sum + part.value.length, 0);
const changeRatio = changedLength / totalLength;
const totalLength = removedLineText.length + addedLineText.length
const changedLength = wordDiffs
.filter(part => part.added || part.removed)
.reduce((sum, part) => sum + part.value.length, 0)
const changeRatio = changedLength / totalLength
if (changeRatio > CHANGE_THRESHOLD || dim) {
return null; // Fall back to standard rendering for major changes
return null // Fall back to standard rendering for major changes
}
// Calculate available width for content
const diffPrefix = type === 'add' ? '+' : '-';
const diffPrefixWidth = diffPrefix.length;
const availableContentWidth = Math.max(1, width - maxWidth - 1 - diffPrefixWidth);
const diffPrefix = type === 'add' ? '+' : '-'
const diffPrefixWidth = diffPrefix.length
const availableContentWidth = Math.max(
1,
width - maxWidth - 1 - diffPrefixWidth,
)
// Manually wrap the word diff parts with better space efficiency
const wrappedLines: {
content: React.ReactNode[];
contentWidth: number;
}[] = [];
let currentLine: React.ReactNode[] = [];
let currentLineWidth = 0;
const wrappedLines: { content: React.ReactNode[]; contentWidth: number }[] =
[]
let currentLine: React.ReactNode[] = []
let currentLineWidth = 0
wordDiffs.forEach((part, partIndex) => {
// Determine if this part should be shown for this line type
let shouldShow = false;
let partBgColor: 'diffAddedWord' | 'diffRemovedWord' | undefined;
let shouldShow = false
let partBgColor: 'diffAddedWord' | 'diffRemovedWord' | undefined
if (type === 'add') {
if (part.added) {
shouldShow = true;
partBgColor = 'diffAddedWord';
shouldShow = true
partBgColor = 'diffAddedWord'
} else if (!part.removed) {
shouldShow = true;
shouldShow = true
}
} else if (type === 'remove') {
if (part.removed) {
shouldShow = true;
partBgColor = 'diffRemovedWord';
shouldShow = true
partBgColor = 'diffRemovedWord'
} else if (!part.added) {
shouldShow = true;
shouldShow = true
}
}
if (!shouldShow) return;
if (!shouldShow) return
// Use wrapText to wrap this individual part if it's long
const partWrapped = wrapText(part.value, availableContentWidth, 'wrap');
const partLines = partWrapped.split('\n');
const partWrapped = wrapText(part.value, availableContentWidth, 'wrap')
const partLines = partWrapped.split('\n')
partLines.forEach((partLine, lineIdx) => {
if (!partLine) return;
if (!partLine) return
// Check if we need to start a new line
if (lineIdx > 0 || currentLineWidth + stringWidth(partLine) > availableContentWidth) {
if (
lineIdx > 0 ||
currentLineWidth + stringWidth(partLine) > availableContentWidth
) {
if (currentLine.length > 0) {
wrappedLines.push({
content: [...currentLine],
contentWidth: currentLineWidth
});
currentLine = [];
currentLineWidth = 0;
contentWidth: currentLineWidth,
})
currentLine = []
currentLineWidth = 0
}
}
currentLine.push(<Text key={`part-${partIndex}-${lineIdx}`} backgroundColor={partBgColor}>
currentLine.push(
<Text
key={`part-${partIndex}-${lineIdx}`}
backgroundColor={partBgColor}
>
{partLine}
</Text>);
currentLineWidth += stringWidth(partLine);
});
});
</Text>,
)
currentLineWidth += stringWidth(partLine)
})
})
if (currentLine.length > 0) {
wrappedLines.push({
content: currentLine,
contentWidth: currentLineWidth
});
wrappedLines.push({ content: currentLine, contentWidth: currentLineWidth })
}
// Render each wrapped line as a separate Text element
return wrappedLines.map(({
content,
contentWidth
}, lineIndex) => {
const key = `${type}-${i}-${lineIndex}`;
const lineBgColor = type === 'add' ? dim ? 'diffAddedDimmed' : 'diffAdded' : dim ? 'diffRemovedDimmed' : 'diffRemoved';
const lineNum = lineIndex === 0 ? i : undefined;
const lineNumStr = (lineNum !== undefined ? lineNum.toString().padStart(maxWidth) : ' '.repeat(maxWidth)) + ' ';
return wrappedLines.map(({ content, contentWidth }, lineIndex) => {
const key = `${type}-${i}-${lineIndex}`
const lineBgColor =
type === 'add'
? dim
? 'diffAddedDimmed'
: 'diffAdded'
: dim
? 'diffRemovedDimmed'
: 'diffRemoved'
const lineNum = lineIndex === 0 ? i : undefined
const lineNumStr =
(lineNum !== undefined
? lineNum.toString().padStart(maxWidth)
: ' '.repeat(maxWidth)) + ' '
// Calculate padding to fill the entire terminal width
const usedWidth = lineNumStr.length + diffPrefixWidth + contentWidth;
const padding = Math.max(0, width - usedWidth);
return <Box key={key} flexDirection="row">
const usedWidth = lineNumStr.length + diffPrefixWidth + contentWidth
const padding = Math.max(0, width - usedWidth)
return (
<Box key={key} flexDirection="row">
<NoSelect fromLeftEdge>
<Text color={overrideTheme ? 'text' : undefined} backgroundColor={lineBgColor} dimColor={dim}>
<Text
color={overrideTheme ? 'text' : undefined}
backgroundColor={lineBgColor}
dimColor={dim}
>
{lineNumStr}
{diffPrefix}
</Text>
</NoSelect>
<Text color={overrideTheme ? 'text' : undefined} backgroundColor={lineBgColor} dimColor={dim}>
<Text
color={overrideTheme ? 'text' : undefined}
backgroundColor={lineBgColor}
dimColor={dim}
>
{content}
{' '.repeat(padding)}
</Text>
</Box>;
});
</Box>
)
})
}
function formatDiff(lines: string[], startingLineNumber: number, width: number, dim: boolean, overrideTheme?: ThemeName): React.ReactNode[] {
function formatDiff(
lines: string[],
startingLineNumber: number,
width: number,
dim: boolean,
overrideTheme?: ThemeName,
): React.ReactNode[] {
// Ensure width is at least 1 to prevent rendering issues with very narrow terminals
const safeWidth = Math.max(1, Math.floor(width));
const safeWidth = Math.max(1, Math.floor(width))
// Step 1: Transform lines to line objects with type information
const lineObjects = transformLinesToObjects(lines);
const lineObjects = transformLinesToObjects(lines)
// Step 2: Group adjacent add/remove lines for word-level diffing
const processedLines = processAdjacentLines(lineObjects);
const processedLines = processAdjacentLines(lineObjects)
// Step 3: Number the diff lines
const ls = numberDiffLines(processedLines, startingLineNumber);
const ls = numberDiffLines(processedLines, startingLineNumber)
// Find max line number width for alignment
const maxLineNumber = Math.max(...ls.map(({
i
}) => i), 0);
const maxWidth = Math.max(maxLineNumber.toString().length + 1, 0);
const maxLineNumber = Math.max(...ls.map(({ i }) => i), 0)
const maxWidth = Math.max(maxLineNumber.toString().length + 1, 0)
// Step 4: Render formatting
return ls.flatMap((item): React.ReactNode[] => {
const {
type,
code,
i,
wordDiff,
matchedLine
} = item;
const { type, code, i, wordDiff, matchedLine } = item
// Handle word-level diffing for add/remove pairs
if (wordDiff && matchedLine) {
const wordDiffElements = generateWordDiffElements(item, safeWidth, maxWidth, dim, overrideTheme);
const wordDiffElements = generateWordDiffElements(
item,
safeWidth,
maxWidth,
dim,
overrideTheme,
)
// word-diff might refuse (e.g. due to lines being substantially different) in which
// case we'll fall through to normal renderin gbelow
if (wordDiffElements !== null) {
return wordDiffElements;
return wordDiffElements
}
}
// Standard rendering for lines without word diffing or as fallback
// Calculate available width accounting for line number + space + diff prefix
const diffPrefixWidth = 2; // " " for unchanged, "+ " or "- " for changes
const availableContentWidth = Math.max(1, safeWidth - maxWidth - 1 - diffPrefixWidth); // -1 for space after line number
const wrappedText = wrapText(code, availableContentWidth, 'wrap');
const wrappedLines = wrappedText.split('\n');
const diffPrefixWidth = 2 // " " for unchanged, "+ " or "- " for changes
const availableContentWidth = Math.max(
1,
safeWidth - maxWidth - 1 - diffPrefixWidth,
) // -1 for space after line number
const wrappedText = wrapText(code, availableContentWidth, 'wrap')
const wrappedLines = wrappedText.split('\n')
return wrappedLines.map((line, lineIndex) => {
const key = `${type}-${i}-${lineIndex}`;
const lineNum = lineIndex === 0 ? i : undefined;
const lineNumStr = (lineNum !== undefined ? lineNum.toString().padStart(maxWidth) : ' '.repeat(maxWidth)) + ' ';
const sigil = type === 'add' ? '+' : type === 'remove' ? '-' : ' ';
const key = `${type}-${i}-${lineIndex}`
const lineNum = lineIndex === 0 ? i : undefined
const lineNumStr =
(lineNum !== undefined
? lineNum.toString().padStart(maxWidth)
: ' '.repeat(maxWidth)) + ' '
const sigil = type === 'add' ? '+' : type === 'remove' ? '-' : ' '
// Calculate padding to fill the entire terminal width
const contentWidth = lineNumStr.length + 1 + stringWidth(line); // lineNum + sigil + code
const padding = Math.max(0, safeWidth - contentWidth);
const bgColor = type === 'add' ? dim ? 'diffAddedDimmed' : 'diffAdded' : type === 'remove' ? dim ? 'diffRemovedDimmed' : 'diffRemoved' : undefined;
const contentWidth = lineNumStr.length + 1 + stringWidth(line) // lineNum + sigil + code
const padding = Math.max(0, safeWidth - contentWidth)
const bgColor =
type === 'add'
? dim
? 'diffAddedDimmed'
: 'diffAdded'
: type === 'remove'
? dim
? 'diffRemovedDimmed'
: 'diffRemoved'
: undefined
// Gutter (line number + sigil) is wrapped in <NoSelect> so fullscreen
// text selection yields clean code. bgColor carries across both boxes
// so the visual continuity (solid red/green bar) is unchanged.
return <Box key={key} flexDirection="row">
return (
<Box key={key} flexDirection="row">
<NoSelect fromLeftEdge>
<Text color={overrideTheme ? 'text' : undefined} backgroundColor={bgColor} dimColor={dim || type === 'nochange'}>
<Text
color={overrideTheme ? 'text' : undefined}
backgroundColor={bgColor}
dimColor={dim || type === 'nochange'}
>
{lineNumStr}
{sigil}
</Text>
</NoSelect>
<Text color={overrideTheme ? 'text' : undefined} backgroundColor={bgColor} dimColor={dim}>
<Text
color={overrideTheme ? 'text' : undefined}
backgroundColor={bgColor}
dimColor={dim}
>
{line}
{' '.repeat(padding)}
</Text>
</Box>;
});
});
</Box>
)
})
})
}
export function numberDiffLines(diff: LineObject[], startLine: number): DiffLine[] {
let i = startLine;
const result: DiffLine[] = [];
const queue = [...diff];
export function numberDiffLines(
diff: LineObject[],
startLine: number,
): DiffLine[] {
let i = startLine
const result: DiffLine[] = []
const queue = [...diff]
while (queue.length > 0) {
const current = queue.shift()!;
const {
code,
type,
originalCode,
wordDiff,
matchedLine
} = current;
const current = queue.shift()!
const { code, type, originalCode, wordDiff, matchedLine } = current
const line = {
code,
type,
i,
originalCode,
wordDiff,
matchedLine
};
matchedLine,
}
// Update counters based on change type
switch (type) {
case 'nochange':
i++;
result.push(line);
break;
i++
result.push(line)
break
case 'add':
i++;
result.push(line);
break;
case 'remove':
{
result.push(line);
let numRemoved = 0;
while (queue[0]?.type === 'remove') {
i++;
const current = queue.shift()!;
const {
code,
type,
originalCode,
wordDiff,
matchedLine
} = current;
const line = {
code,
type,
i,
originalCode,
wordDiff,
matchedLine
};
result.push(line);
numRemoved++;
i++
result.push(line)
break
case 'remove': {
result.push(line)
let numRemoved = 0
while (queue[0]?.type === 'remove') {
i++
const current = queue.shift()!
const { code, type, originalCode, wordDiff, matchedLine } = current
const line = {
code,
type,
i,
originalCode,
wordDiff,
matchedLine,
}
i -= numRemoved;
break;
result.push(line)
numRemoved++
}
i -= numRemoved
break
}
}
}
return result;
return result
}