v2 · Claude Desktop 2.0 逆向手册
第五章:生产级参考实现代码
5. 参考实现
5.1 模型选择与算力调节器
📄 独立工程源码:
code-examples/ModelSelectorEffort.tsx(已集成至src/index.ts)
import React, { useState } from 'react';
export type EffortLevel = 'low' | 'medium' | 'high' | 'xhigh' | 'max';
export interface ModelEffortConfig {
effortLevels: EffortLevel[];
recommended: EffortLevel;
modes: ('auto' | 'extended' | 'off')[];
disallowThinkingDisabled?: boolean;
}
const EFFORT_META: Record<EffortLevel, { name: string; desc: string; warning?: string }> = {
low: { name: 'Low', desc: 'Quick replies to simple questions' },
medium: { name: 'Medium', desc: 'Light, casual tasks' },
high: { name: 'High', desc: 'Balanced for everyday work' },
xhigh: { name: 'Extra', desc: 'Complex, detailed work' },
max: {
name: 'Max',
desc: 'The hardest problems. Takes longest.',
warning: 'May use excessive tokens resulting in long response times and may hit token limits. Use sparingly for the hardest tasks.'
}
};
export const ModelSelectorEffort: React.FC<{
currentEffort: EffortLevel;
onEffortChange: (effort: EffortLevel) => void;
config?: ModelEffortConfig;
}> = ({
currentEffort,
onEffortChange,
config = {
effortLevels: ['low', 'medium', 'high', 'xhigh', 'max'],
recommended: 'high',
modes: ['auto']
}
}) => {
const [isOpen, setIsOpen] = useState(false);
const activeMeta = EFFORT_META[currentEffort];
return (
<div className="relative inline-block text-left" data-cds="ModelSelectorEffort">
{/* 触发胶囊按钮 */}
<button
type="button"
onClick={() => setIsOpen(!isOpen)}
className="inline-flex items-center gap-1.5 px-2.5 py-1 text-xs font-medium rounded-full bg-[var(--cds-bg-secondary,#F3EFE0)] text-[var(--cds-text-primary,#1F1E1D)] hover:bg-[var(--cds-bg-tertiary,#E8E4D5)] transition-colors border border-[var(--cds-border-subtle,rgba(0,0,0,0.08))]"
>
<span className="w-1.5 h-1.5 rounded-full bg-[var(--cds-accent,#D97706)] animate-pulse" />
<span>Thinking: {activeMeta.name}</span>
{currentEffort === config.recommended && (
<span className="text-[10px] px-1 py-0.2 rounded bg-[var(--cds-fill-badge-neutral,rgba(0,0,0,0.06))] text-[var(--cds-text-secondary,#6B6966)]">
Default
</span>
)}
</button>
{/* 下拉面板 */}
{isOpen && (
<div className="absolute bottom-full left-0 mb-2 w-72 rounded-xl shadow-xl bg-[var(--cds-surface,#FCFBF7)] border border-[var(--cds-border-subtle,rgba(0,0,0,0.1))] p-2 z-50 animate-in fade-in zoom-in-95 duration-150">
<div className="px-2 py-1.5 border-b border-[var(--cds-border-subtle,rgba(0,0,0,0.06))] mb-1.5">
<p className="text-xs font-semibold text-[var(--cds-text-primary,#1F1E1D)]">Thinking Effort</p>
<p className="text-[11px] text-[var(--cds-text-secondary,#6B6966)] leading-tight mt-0.5">
Higher effort means more thorough responses, but takes longer and uses limits faster.
</p>
</div>
<div className="space-y-1">
{config.effortLevels.map((lvl) => {
const meta = EFFORT_META[lvl];
const isSelected = lvl === currentEffort;
const isRecommended = lvl === config.recommended;
return (
<button
key={lvl}
type="button"
onClick={() => {
onEffortChange(lvl);
setIsOpen(false);
}}
className={`w-full text-left px-2.5 py-2 rounded-lg text-xs transition-all flex items-start justify-between ${
isSelected
? 'bg-[var(--cds-fill-accent-subtle,#FEF3C7)] text-[var(--cds-text-accent,#92400E)] font-medium'
: 'hover:bg-[var(--cds-bg-secondary,#F3EFE0)] text-[var(--cds-text-primary,#1F1E1D)]'
}`}
>
<div>
<div className="flex items-center gap-1.5">
<span>{meta.name}</span>
{isRecommended && (
<span className="text-[9px] px-1 rounded bg-[var(--cds-fill-badge-neutral,rgba(0,0,0,0.08))] text-[var(--cds-text-secondary,#6B6966)]">
Recommended
</span>
)}
</div>
<div className="text-[11px] text-[var(--cds-text-secondary,#6B6966)] mt-0.5 font-normal">
{meta.desc}
</div>
{meta.warning && lvl === 'max' && (
<div className="text-[10px] text-amber-700 bg-amber-50 p-1.5 rounded mt-1 border border-amber-200 leading-tight">
⚠️ {meta.warning}
</div>
)}
</div>
{isSelected && <span className="text-amber-600 text-sm">✓</span>}
</button>
);
})}
</div>
</div>
)}
</div>
);
};5.2 输入框底部工具栏
📄 独立工程源码:
code-examples/ChatComposerChin.tsx(已集成至src/index.ts)
import React from 'react';
import { ModelSelectorEffort, EffortLevel } from './ModelSelectorEffort';
export interface ChatComposerChinProps {
currentEffort: EffortLevel;
onEffortChange: (effort: EffortLevel) => void;
failedQueueCount?: number;
onRetryQueue?: () => void;
}
export const ChatComposerChin: React.FC<ChatComposerChinProps> = ({
currentEffort,
onEffortChange,
failedQueueCount = 0,
onRetryQueue
}) => {
return (
<div
className="cds-root w-full border-t border-[var(--cds-border-subtle,rgba(0,0,0,0.08))] bg-[var(--cds-surface,#FCFBF7)] px-3 py-1.5"
data-cds="ChatComposerChin"
>
{/* 离线失败消息队列横幅 */}
{failedQueueCount > 0 && (
<div
data-cds="ChatComposerQueuedMessages"
className="mb-2 flex items-center justify-between rounded-lg bg-amber-50 px-2.5 py-1.5 text-xs text-amber-800 border border-amber-200"
>
<div className="flex items-center gap-1.5">
<span className="w-2 h-2 rounded-full bg-amber-500" />
<span>{failedQueueCount} message(s) failed to send. Staged in local storage.</span>
</div>
<button
type="button"
onClick={onRetryQueue}
className="font-medium underline hover:text-amber-900"
>
Retry Now
</button>
</div>
)}
{/* 下颌操作行 (自带触控补偿) */}
<div
data-touch-size="true"
className="flex items-center justify-between min-h-[28px]"
style={{
// 原生 CDS 负边距补偿算法
'--cds-h-control': '36px',
'--cmp-chin-row-h': '28px',
} as React.CSSProperties}
>
<div className="flex items-center gap-2">
{/* 模型与推理调节器 */}
<ModelSelectorEffort currentEffort={currentEffort} onEffortChange={onEffortChange} />
<button
type="button"
data-cds="Button"
className="px-2 py-1 text-xs text-neutral-600 hover:text-neutral-900 rounded hover:bg-neutral-100 transition-colors"
>
+ Attach
</button>
</div>
<div className="flex items-center gap-2">
<span className="text-[11px] text-neutral-400">⌘ + Enter to send</span>
<button
type="button"
data-cds="Button"
className="w-7 h-7 rounded-full bg-neutral-900 text-white flex items-center justify-center hover:bg-neutral-800 transition-colors"
>
↑
</button>
</div>
</div>
</div>
);
};5.3 动效引擎与微交互
📄 独立工程源码:
code-examples/CdsMotionEngine.tsx(已集成至src/index.ts)
import React, { useState } from 'react';
export const CdsMotionEngine: React.FC = () => {
const [popped, setPopped] = useState(false);
const chips = ['AST-Engine', 'Electron-Fuses', 'View-Transitions', 'Scroll-Timeline', 'Off-Line-Bundle'];
return (
<div className="p-6 space-y-6 bg-[#FCFBF7] rounded-xl border border-neutral-200">
<h3 className="text-sm font-bold text-neutral-800">CDS 2.0 物理动效引擎实机呈现</h3>
{/* 1. Pill 弹性过冲 */}
<div>
<p className="text-xs text-neutral-500 mb-2">1. Pill Elastic Pop (1.32x Overshoot)</p>
<button
type="button"
onClick={() => {
setPopped(true);
setTimeout(() => setPopped(false), 240);
}}
className={`px-3 py-1.5 rounded-full bg-amber-600 text-white text-xs font-medium transition-transform ${
popped ? 'scale-125' : 'scale-100 hover:scale-105 active:scale-95'
}`}
style={{
transitionTimingFunction: 'cubic-bezier(0.34, 1.3, 0.64, 1)',
transitionDuration: '200ms'
}}
>
Pill Spring Trigger
</button>
</div>
{/* 2. Chip 原生级联交错渐入 */}
<div>
<p className="text-xs text-neutral-500 mb-2">2. Staggered Chip Cascade Entrance</p>
<div className="flex flex-wrap gap-2">
{chips.map((chip, idx) => (
<span
key={chip}
className="px-2.5 py-1 text-xs rounded-md bg-neutral-100 border border-neutral-200 text-neutral-700 animate-in fade-in zoom-in-75 duration-200"
style={{
animationDelay: `${idx * 30}ms`,
animationFillMode: 'backwards'
}}
>
{chip}
</span>
))}
</div>
</div>
{/* 3. Skeleton 105deg 线性扫光带 */}
<div>
<p className="text-xs text-neutral-500 mb-2">3. Skeleton 105° Linear Sweep (Wave Mode)</p>
<div className="space-y-2 max-w-sm">
<div className="h-3.5 rounded bg-neutral-200/60 overflow-hidden relative">
<div
className="absolute inset-0 -translate-x-full animate-[sweep_2s_infinite]"
style={{
backgroundImage: 'linear-gradient(105deg, transparent 35%, rgba(255,255,255,0.8) 50%, transparent 65%)'
}}
/>
</div>
<div className="h-3.5 w-2/3 rounded bg-neutral-200/60 overflow-hidden relative">
<div
className="absolute inset-0 -translate-x-full animate-[sweep_2s_infinite]"
style={{
animationDelay: '90ms',
backgroundImage: 'linear-gradient(105deg, transparent 35%, rgba(255,255,255,0.8) 50%, transparent 65%)'
}}
/>
</div>
</div>
</div>
</div>
);
};