更新大量 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,68 +1,96 @@
import { c as _c } from "react/compiler-runtime";
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, logEvent } from 'src/services/analytics/index.js';
import { setupTerminal, shouldOfferTerminalSetup } from '../commands/terminalSetup/terminalSetup.js';
import { useExitOnCtrlCDWithKeybindings } from '../hooks/useExitOnCtrlCDWithKeybindings.js';
import { Box, Link, Newline, Text, useTheme } from '../ink.js';
import { useKeybindings } from '../keybindings/useKeybinding.js';
import { isAnthropicAuthEnabled } from '../utils/auth.js';
import { normalizeApiKeyForConfig } from '../utils/authPortable.js';
import { getCustomApiKeyStatus } from '../utils/config.js';
import { env } from '../utils/env.js';
import { isRunningOnHomespace } from '../utils/envUtils.js';
import { PreflightStep } from '../utils/preflightChecks.js';
import type { ThemeSetting } from '../utils/theme.js';
import { ApproveApiKey } from './ApproveApiKey.js';
import { ConsoleOAuthFlow } from './ConsoleOAuthFlow.js';
import { Select } from './CustomSelect/select.js';
import { WelcomeV2 } from './LogoV2/WelcomeV2.js';
import { PressEnterToContinue } from './PressEnterToContinue.js';
import { ThemePicker } from './ThemePicker.js';
import { OrderedList } from './ui/OrderedList.js';
type StepId = 'preflight' | 'theme' | 'oauth' | 'api-key' | 'security' | 'terminal-setup';
import React, { useCallback, useEffect, useMemo, useState } from 'react'
import {
type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
logEvent,
} from 'src/services/analytics/index.js'
import {
setupTerminal,
shouldOfferTerminalSetup,
} from '../commands/terminalSetup/terminalSetup.js'
import { useExitOnCtrlCDWithKeybindings } from '../hooks/useExitOnCtrlCDWithKeybindings.js'
import { Box, Link, Newline, Text, useTheme } from '../ink.js'
import { useKeybindings } from '../keybindings/useKeybinding.js'
import { isAnthropicAuthEnabled } from '../utils/auth.js'
import { normalizeApiKeyForConfig } from '../utils/authPortable.js'
import { getCustomApiKeyStatus } from '../utils/config.js'
import { env } from '../utils/env.js'
import { isRunningOnHomespace } from '../utils/envUtils.js'
import { PreflightStep } from '../utils/preflightChecks.js'
import type { ThemeSetting } from '../utils/theme.js'
import { ApproveApiKey } from './ApproveApiKey.js'
import { ConsoleOAuthFlow } from './ConsoleOAuthFlow.js'
import { Select } from './CustomSelect/select.js'
import { WelcomeV2 } from './LogoV2/WelcomeV2.js'
import { PressEnterToContinue } from './PressEnterToContinue.js'
import { ThemePicker } from './ThemePicker.js'
import { OrderedList } from './ui/OrderedList.js'
type StepId =
| 'preflight'
| 'theme'
| 'oauth'
| 'api-key'
| 'security'
| 'terminal-setup'
interface OnboardingStep {
id: StepId;
component: React.ReactNode;
id: StepId
component: React.ReactNode
}
type Props = {
onDone(): void;
};
export function Onboarding({
onDone
}: Props): React.ReactNode {
const [currentStepIndex, setCurrentStepIndex] = useState(0);
const [skipOAuth, setSkipOAuth] = useState(false);
const [oauthEnabled] = useState(() => isAnthropicAuthEnabled());
const [theme, setTheme] = useTheme();
onDone(): void
}
export function Onboarding({ onDone }: Props): React.ReactNode {
const [currentStepIndex, setCurrentStepIndex] = useState(0)
const [skipOAuth, setSkipOAuth] = useState(false)
const [oauthEnabled] = useState(() => isAnthropicAuthEnabled())
const [theme, setTheme] = useTheme()
useEffect(() => {
logEvent('tengu_began_setup', {
oauthEnabled
});
}, [oauthEnabled]);
oauthEnabled,
})
}, [oauthEnabled])
function goToNextStep() {
if (currentStepIndex < steps.length - 1) {
const nextIndex = currentStepIndex + 1;
setCurrentStepIndex(nextIndex);
const nextIndex = currentStepIndex + 1
setCurrentStepIndex(nextIndex)
logEvent('tengu_onboarding_step', {
oauthEnabled,
stepId: steps[nextIndex]?.id as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS
});
stepId: steps[nextIndex]
?.id as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
})
} else {
onDone();
onDone()
}
}
function handleThemeSelection(newTheme: ThemeSetting) {
setTheme(newTheme);
goToNextStep();
setTheme(newTheme)
goToNextStep()
}
const exitState = useExitOnCtrlCDWithKeybindings();
const exitState = useExitOnCtrlCDWithKeybindings()
// Define all onboarding steps
const themeStep = <Box marginX={1}>
<ThemePicker onThemeSelect={handleThemeSelection} showIntroText={true} helpText="To change this later, run /theme" hideEscToCancel={true} skipExitHandling={true} // Skip exit handling as Onboarding already handles it
/>
</Box>;
const securityStep = <Box flexDirection="column" gap={1} paddingLeft={1}>
const themeStep = (
<Box marginX={1}>
<ThemePicker
onThemeSelect={handleThemeSelection}
showIntroText={true}
helpText="To change this later, run /theme"
hideEscToCancel={true}
skipExitHandling={true} // Skip exit handling as Onboarding already handles it
/>
</Box>
)
const securityStep = (
<Box flexDirection="column" gap={1} paddingLeft={1}>
<Text bold>Security notes:</Text>
<Box flexDirection="column" width={70}>
{/**
@@ -92,152 +120,182 @@ export function Onboarding({
</OrderedList>
</Box>
<PressEnterToContinue />
</Box>;
const preflightStep = <PreflightStep onSuccess={goToNextStep} />;
</Box>
)
const preflightStep = <PreflightStep onSuccess={goToNextStep} />
// Create the steps array - determine which steps to include based on reAuth and oauthEnabled
const apiKeyNeedingApproval = useMemo(() => {
// Add API key step if needed
// On homespace, ANTHROPIC_API_KEY is preserved in process.env for child
// processes but ignored by Claude Code itself (see auth.ts).
if (!process.env.ANTHROPIC_API_KEY || isRunningOnHomespace()) {
return '';
return ''
}
const customApiKeyTruncated = normalizeApiKeyForConfig(process.env.ANTHROPIC_API_KEY);
const customApiKeyTruncated = normalizeApiKeyForConfig(
process.env.ANTHROPIC_API_KEY,
)
if (getCustomApiKeyStatus(customApiKeyTruncated) === 'new') {
return customApiKeyTruncated;
return customApiKeyTruncated
}
}, []);
}, [])
function handleApiKeyDone(approved: boolean) {
if (approved) {
setSkipOAuth(true);
setSkipOAuth(true)
}
goToNextStep();
goToNextStep()
}
const steps: OnboardingStep[] = [];
const steps: OnboardingStep[] = []
if (oauthEnabled) {
steps.push({
id: 'preflight',
component: preflightStep
});
steps.push({ id: 'preflight', component: preflightStep })
}
steps.push({
id: 'theme',
component: themeStep
});
steps.push({ id: 'theme', component: themeStep })
if (apiKeyNeedingApproval) {
steps.push({
id: 'api-key',
component: <ApproveApiKey customApiKeyTruncated={apiKeyNeedingApproval} onDone={handleApiKeyDone} />
});
component: (
<ApproveApiKey
customApiKeyTruncated={apiKeyNeedingApproval}
onDone={handleApiKeyDone}
/>
),
})
}
if (oauthEnabled) {
steps.push({
id: 'oauth',
component: <SkippableStep skip={skipOAuth} onSkip={goToNextStep}>
component: (
<SkippableStep skip={skipOAuth} onSkip={goToNextStep}>
<ConsoleOAuthFlow onDone={goToNextStep} />
</SkippableStep>
});
),
})
}
steps.push({
id: 'security',
component: securityStep
});
steps.push({ id: 'security', component: securityStep })
if (shouldOfferTerminalSetup()) {
steps.push({
id: 'terminal-setup',
component: <Box flexDirection="column" gap={1} paddingLeft={1}>
component: (
<Box flexDirection="column" gap={1} paddingLeft={1}>
<Text bold>Use Claude Code&apos;s terminal setup?</Text>
<Box flexDirection="column" width={70} gap={1}>
<Text>
For the optimal coding experience, enable the recommended settings
<Newline />
for your terminal:{' '}
{env.terminal === 'Apple_Terminal' ? 'Option+Enter for newlines and visual bell' : 'Shift+Enter for newlines'}
{env.terminal === 'Apple_Terminal'
? 'Option+Enter for newlines and visual bell'
: 'Shift+Enter for newlines'}
</Text>
<Select options={[{
label: 'Yes, use recommended settings',
value: 'install'
}, {
label: 'No, maybe later with /terminal-setup',
value: 'no'
}]} onChange={value => {
if (value === 'install') {
// Errors already logged in setupTerminal, just swallow and proceed
void setupTerminal(theme).catch(() => {}).finally(goToNextStep);
} else {
goToNextStep();
}
}} onCancel={() => goToNextStep()} />
<Select
options={[
{
label: 'Yes, use recommended settings',
value: 'install',
},
{
label: 'No, maybe later with /terminal-setup',
value: 'no',
},
]}
onChange={value => {
if (value === 'install') {
// Errors already logged in setupTerminal, just swallow and proceed
void setupTerminal(theme)
.catch(() => {})
.finally(goToNextStep)
} else {
goToNextStep()
}
}}
onCancel={() => goToNextStep()}
/>
<Text dimColor>
{exitState.pending ? <>Press {exitState.keyName} again to exit</> : <>Enter to confirm · Esc to skip</>}
{exitState.pending ? (
<>Press {exitState.keyName} again to exit</>
) : (
<>Enter to confirm · Esc to skip</>
)}
</Text>
</Box>
</Box>
});
),
})
}
const currentStep = steps[currentStepIndex];
const currentStep = steps[currentStepIndex]
// Handle Enter on security step and Escape on terminal-setup step
// Dependencies match what goToNextStep uses internally
const handleSecurityContinue = useCallback(() => {
if (currentStepIndex === steps.length - 1) {
onDone();
onDone()
} else {
goToNextStep();
goToNextStep()
}
}, [currentStepIndex, steps.length, oauthEnabled, onDone]);
}, [currentStepIndex, steps.length, oauthEnabled, onDone])
const handleTerminalSetupSkip = useCallback(() => {
goToNextStep();
}, [currentStepIndex, steps.length, oauthEnabled, onDone]);
useKeybindings({
'confirm:yes': handleSecurityContinue
}, {
context: 'Confirmation',
isActive: currentStep?.id === 'security'
});
useKeybindings({
'confirm:no': handleTerminalSetupSkip
}, {
context: 'Confirmation',
isActive: currentStep?.id === 'terminal-setup'
});
return <Box flexDirection="column">
goToNextStep()
}, [currentStepIndex, steps.length, oauthEnabled, onDone])
useKeybindings(
{
'confirm:yes': handleSecurityContinue,
},
{
context: 'Confirmation',
isActive: currentStep?.id === 'security',
},
)
useKeybindings(
{
'confirm:no': handleTerminalSetupSkip,
},
{
context: 'Confirmation',
isActive: currentStep?.id === 'terminal-setup',
},
)
return (
<Box flexDirection="column">
<WelcomeV2 />
<Box flexDirection="column" marginTop={1}>
{currentStep?.component}
{exitState.pending && <Box padding={1}>
{exitState.pending && (
<Box padding={1}>
<Text dimColor>Press {exitState.keyName} again to exit</Text>
</Box>}
</Box>
)}
</Box>
</Box>;
</Box>
)
}
export function SkippableStep(t0) {
const $ = _c(4);
const {
skip,
onSkip,
children
} = t0;
let t1;
let t2;
if ($[0] !== onSkip || $[1] !== skip) {
t1 = () => {
if (skip) {
onSkip();
}
};
t2 = [skip, onSkip];
$[0] = onSkip;
$[1] = skip;
$[2] = t1;
$[3] = t2;
} else {
t1 = $[2];
t2 = $[3];
}
useEffect(t1, t2);
export function SkippableStep({
skip,
onSkip,
children,
}: {
skip: boolean
onSkip(): void
children: React.ReactNode
}): React.ReactNode {
useEffect(() => {
if (skip) {
onSkip()
}
}, [skip, onSkip])
if (skip) {
return null;
return null
}
return children;
return children
}