更新大量 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,26 +1,29 @@
import { c as _c } from "react/compiler-runtime";
import { marked, type Token, type Tokens } from 'marked';
import React, { Suspense, use, useMemo, useRef } from 'react';
import { useSettings } from '../hooks/useSettings.js';
import { Ansi, Box, useTheme } from '../ink.js';
import { type CliHighlight, getCliHighlightPromise } from '../utils/cliHighlight.js';
import { hashContent } from '../utils/hash.js';
import { configureMarked, formatToken } from '../utils/markdown.js';
import { stripPromptXMLTags } from '../utils/messages.js';
import { MarkdownTable } from './MarkdownTable.js';
import { marked, type Token, type Tokens } from 'marked'
import React, { Suspense, use, useMemo, useRef } from 'react'
import { useSettings } from '../hooks/useSettings.js'
import { Ansi, Box, useTheme } from '../ink.js'
import {
type CliHighlight,
getCliHighlightPromise,
} from '../utils/cliHighlight.js'
import { hashContent } from '../utils/hash.js'
import { configureMarked, formatToken } from '../utils/markdown.js'
import { stripPromptXMLTags } from '../utils/messages.js'
import { MarkdownTable } from './MarkdownTable.js'
type Props = {
children: string;
children: string
/** When true, render all text content as dim */
dimColor?: boolean;
};
dimColor?: boolean
}
// Module-level token cache — marked.lexer is the hot cost on virtual-scroll
// remounts (~3ms per message). useMemo doesn't survive unmount→remount, so
// scrolling back to a previously-visible message re-parses. Messages are
// immutable in history; same content → same tokens. Keyed by hash to avoid
// retaining full content strings (turn50→turn99 RSS regression, #24180).
const TOKEN_CACHE_MAX = 500;
const tokenCache = new Map<string, Token[]>();
const TOKEN_CACHE_MAX = 500
const tokenCache = new Map<string, Token[]>()
// Characters that indicate markdown syntax. If none are present, skip the
// ~3ms marked.lexer call entirely — render as a single paragraph. Covers
@@ -28,46 +31,45 @@ const tokenCache = new Map<string, Token[]>();
// plain sentences. Checked via indexOf (not regex) for speed.
// Single regex: matches any MD marker or ordered-list start (N. at line start).
// One pass instead of 10× includes scans.
const MD_SYNTAX_RE = /[#*`|[>\-_~]|\n\n|^\d+\. |\n\d+\. /;
const MD_SYNTAX_RE = /[#*`|[>\-_~]|\n\n|^\d+\. |\n\d+\. /
function hasMarkdownSyntax(s: string): boolean {
// Sample first 500 chars — if markdown exists it's usually early (headers,
// code fence, list). Long tool outputs are mostly plain text tails.
return MD_SYNTAX_RE.test(s.length > 500 ? s.slice(0, 500) : s);
return MD_SYNTAX_RE.test(s.length > 500 ? s.slice(0, 500) : s)
}
function cachedLexer(content: string): Token[] {
// Fast path: plain text with no markdown syntax → single paragraph token.
// Skips marked.lexer's full GFM parse (~3ms on long content). Not cached —
// reconstruction is a single object allocation, and caching would retain
// 4× content in raw/text fields plus the hash key for zero benefit.
if (!hasMarkdownSyntax(content)) {
return [{
type: 'paragraph',
raw: content,
text: content,
tokens: [{
type: 'text',
return [
{
type: 'paragraph',
raw: content,
text: content
}]
} as Token];
text: content,
tokens: [{ type: 'text', raw: content, text: content }],
} as Token,
]
}
const key = hashContent(content);
const hit = tokenCache.get(key);
const key = hashContent(content)
const hit = tokenCache.get(key)
if (hit) {
// Promote to MRU — without this the eviction is FIFO (scrolling back to
// an early message evicts the very item you're looking at).
tokenCache.delete(key);
tokenCache.set(key, hit);
return hit;
tokenCache.delete(key)
tokenCache.set(key, hit)
return hit
}
const tokens = marked.lexer(content);
const tokens = marked.lexer(content)
if (tokenCache.size >= TOKEN_CACHE_MAX) {
// LRU-ish: drop oldest. Map preserves insertion order.
const first = tokenCache.keys().next().value;
if (first !== undefined) tokenCache.delete(first);
const first = tokenCache.keys().next().value
if (first !== undefined) tokenCache.delete(first)
}
tokenCache.set(key, tokens);
return tokens;
tokenCache.set(key, tokens)
return tokens
}
/**
@@ -75,103 +77,78 @@ function cachedLexer(content: string): Token[] {
* - Tables are rendered as React components with proper flexbox layout
* - Other content is rendered as ANSI strings via formatToken
*/
export function Markdown(props) {
const $ = _c(4);
const settings = useSettings();
export function Markdown(props: Props): React.ReactNode {
const settings = useSettings()
if (settings.syntaxHighlightingDisabled) {
let t0;
if ($[0] !== props) {
t0 = <MarkdownBody {...props} highlight={null} />;
$[0] = props;
$[1] = t0;
} else {
t0 = $[1];
}
return t0;
return <MarkdownBody {...props} highlight={null} />
}
let t0;
if ($[2] !== props) {
t0 = <Suspense fallback={<MarkdownBody {...props} highlight={null} />}><MarkdownWithHighlight {...props} /></Suspense>;
$[2] = props;
$[3] = t0;
} else {
t0 = $[3];
}
return t0;
// Suspense fallback renders with highlight=null — plain markdown shows
// for ~50ms on first ever render while cli-highlight loads.
return (
<Suspense fallback={<MarkdownBody {...props} highlight={null} />}>
<MarkdownWithHighlight {...props} />
</Suspense>
)
}
function MarkdownWithHighlight(props) {
const $ = _c(4);
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t0 = getCliHighlightPromise();
$[0] = t0;
} else {
t0 = $[0];
}
const highlight = use(t0);
let t1;
if ($[1] !== highlight || $[2] !== props) {
t1 = <MarkdownBody {...props} highlight={highlight} />;
$[1] = highlight;
$[2] = props;
$[3] = t1;
} else {
t1 = $[3];
}
return t1;
function MarkdownWithHighlight(props: Props): React.ReactNode {
const highlight = use(getCliHighlightPromise())
return <MarkdownBody {...props} highlight={highlight} />
}
function MarkdownBody(t0) {
const $ = _c(7);
const {
children,
dimColor,
highlight
} = t0;
const [theme] = useTheme();
configureMarked();
let elements: React.ReactNode[];
if ($[0] !== children || $[1] !== dimColor || $[2] !== highlight || $[3] !== theme) {
const tokens = cachedLexer(stripPromptXMLTags(children));
elements = [];
let nonTableContent = "";
const flushNonTableContent = function flushNonTableContent() {
function MarkdownBody({
children,
dimColor,
highlight,
}: Props & { highlight: CliHighlight | null }): React.ReactNode {
const [theme] = useTheme()
configureMarked()
const elements = useMemo(() => {
const tokens = cachedLexer(stripPromptXMLTags(children))
const elements: React.ReactNode[] = []
let nonTableContent = ''
function flushNonTableContent(): void {
if (nonTableContent) {
elements.push(<Ansi key={elements.length} dimColor={dimColor}>{nonTableContent.trim()}</Ansi>);
nonTableContent = "";
}
};
for (const token of tokens) {
if (token.type === "table") {
flushNonTableContent();
elements.push(<MarkdownTable key={elements.length} token={token as Tokens.Table} highlight={highlight} />);
} else {
nonTableContent = nonTableContent + formatToken(token, theme, 0, null, null, highlight);
nonTableContent;
elements.push(
<Ansi key={elements.length} dimColor={dimColor}>
{nonTableContent.trim()}
</Ansi>,
)
nonTableContent = ''
}
}
flushNonTableContent();
$[0] = children;
$[1] = dimColor;
$[2] = highlight;
$[3] = theme;
$[4] = elements;
} else {
elements = $[4] as React.ReactNode[];
}
const elements_0 = elements;
let t1;
if ($[5] !== elements_0) {
t1 = <Box flexDirection="column" gap={1}>{elements_0}</Box>;
$[5] = elements_0;
$[6] = t1;
} else {
t1 = $[6];
}
return t1;
for (const token of tokens) {
if (token.type === 'table') {
flushNonTableContent()
elements.push(
<MarkdownTable
key={elements.length}
token={token as Tokens.Table}
highlight={highlight}
/>,
)
} else {
nonTableContent += formatToken(token, theme, 0, null, null, highlight)
}
}
flushNonTableContent()
return elements
}, [children, dimColor, highlight, theme])
return (
<Box flexDirection="column" gap={1}>
{elements}
</Box>
)
}
type StreamingProps = {
children: string;
};
children: string
}
/**
* Renders markdown during streaming by splitting at the last top-level block
@@ -184,52 +161,55 @@ type StreamingProps = {
* between turns (streamingText → null), resetting the ref.
*/
export function StreamingMarkdown({
children
children,
}: StreamingProps): React.ReactNode {
// React Compiler: this component reads and writes stablePrefixRef.current
// during render by design. The boundary only advances (monotonic), so
// the ref mutation is idempotent under StrictMode double-render — but the
// compiler can't prove that, and memoizing around the ref reads would
// break the algorithm (stale boundary). Opt out.
'use no memo';
configureMarked();
'use no memo'
configureMarked()
// Strip before boundary tracking so it matches <Markdown>'s stripping
// (line 29). When a closing tag arrives, stripped(N+1) is not a prefix
// of stripped(N), but the startsWith reset below handles that with a
// one-time re-lex on the smaller stripped string.
const stripped = stripPromptXMLTags(children);
const stablePrefixRef = useRef('');
const stripped = stripPromptXMLTags(children)
const stablePrefixRef = useRef('')
// Reset if text was replaced (defensive; normally unmount handles this)
if (!stripped.startsWith(stablePrefixRef.current)) {
stablePrefixRef.current = '';
stablePrefixRef.current = ''
}
// Lex only from current boundary — O(unstable length), not O(full text)
const boundary = stablePrefixRef.current.length;
const tokens = marked.lexer(stripped.substring(boundary));
const boundary = stablePrefixRef.current.length
const tokens = marked.lexer(stripped.substring(boundary))
// Last non-space token is the growing block; everything before is final
let lastContentIdx = tokens.length - 1;
let lastContentIdx = tokens.length - 1
while (lastContentIdx >= 0 && tokens[lastContentIdx]!.type === 'space') {
lastContentIdx--;
lastContentIdx--
}
let advance = 0;
let advance = 0
for (let i = 0; i < lastContentIdx; i++) {
advance += tokens[i]!.raw.length;
advance += tokens[i]!.raw.length
}
if (advance > 0) {
stablePrefixRef.current = stripped.substring(0, boundary + advance);
stablePrefixRef.current = stripped.substring(0, boundary + advance)
}
const stablePrefix = stablePrefixRef.current;
const unstableSuffix = stripped.substring(stablePrefix.length);
const stablePrefix = stablePrefixRef.current
const unstableSuffix = stripped.substring(stablePrefix.length)
// stablePrefix is memoized inside <Markdown> via useMemo([children, ...])
// so it never re-parses as the unstable suffix grows
return <Box flexDirection="column" gap={1}>
return (
<Box flexDirection="column" gap={1}>
{stablePrefix && <Markdown>{stablePrefix}</Markdown>}
{unstableSuffix && <Markdown>{unstableSuffix}</Markdown>}
</Box>;
</Box>
)
}