open-claude-ui
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>
  );
};

On this page