// 左栏：logo + 项目列表（git 图标）+ WS 状态脚 · 可折叠（折叠后仅图标）
// 项目 logo：图片优先（原型无图），回退首字母色块 hsl(h 45% 60%)
function ProjectLogo({ project, size = 26 }) {
  return (
    <span style={{
      flex: 'none', width: size, height: size, borderRadius: 5,
      display: 'grid', placeItems: 'center',
      fontSize: size * 0.5, fontWeight: 700, color: '#0a0d0b',
      background: 'hsl(' + (project.hue || 140) + ' 45% 60%)',
    }}>{(project.name || '?')[0].toUpperCase()}</span>
  );
}

function MaestroGitIcon({ size = 16 }) {
  return (
    <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor"
      strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" style={{ flex: 'none' }}>
      <line x1="6" y1="3" x2="6" y2="15" />
      <circle cx="18" cy="6" r="3" />
      <circle cx="6" cy="18" r="3" />
      <path d="M18 9a9 9 0 0 1-9 9" />
    </svg>
  );
}

// 像素章鱼 mark（折叠态 logo）
function MaestroMark({ scale = 2.2 }) {
  const ref = React.useRef(null);
  React.useEffect(() => {
    const MAP = ['.....LLLLL.....','...LLLLLLLLL...','..GGGGGGGGGGG..','..GGEEGGGEEGG..','..GGEEGGGEEGG..','..GGGGGGGGGGG..','...GGGGGGGGG...','...G..G.G..G...','...G..G.G..G...','..G...G.G...G..','..D...D.D...D..','.D...D...D...D.'];
    const INK = { L: '#79ec94', G: '#5fdd7d', D: '#2e6b3d', E: '#070908' };
    const ctx = ref.current.getContext('2d');
    MAP.forEach((row, y) => [...row].forEach((ch, x) => {
      if (INK[ch]) { ctx.fillStyle = INK[ch]; ctx.fillRect(x, y, 1, 1); }
    }));
  }, []);
  return <canvas ref={ref} width="15" height="12"
    style={{ width: 15 * scale, height: 12 * scale, imageRendering: 'pixelated', filter: 'drop-shadow(0 0 6px rgba(95,221,125,.4))' }} />;
}

// 项目状态：运行中(青脉冲) / 暂停(faint) / 阻塞(琥珀) / 空闲(muted)
function projStateMeta(state, t) {
  return ({
    running: { color: 'var(--cyan)', pulse: true, label: t.projRunning },
    paused: { color: 'var(--faint)', pulse: false, label: t.projPaused },
    blocked: { color: 'var(--amber)', pulse: true, label: t.projBlocked },
    idle: { color: 'var(--muted)', pulse: false, label: t.projIdle },
  })[state] || { color: 'var(--muted)', pulse: false, label: t.projIdle };
}

function ProjStatusDot({ meta, size = 7 }) {
  return (
    <span style={{
      flex: 'none', width: size, height: size, borderRadius: '50%', background: meta.color,
      boxShadow: '0 0 7px ' + meta.color,
      animation: meta.pulse ? 'maestro-pulse 1.4s infinite' : 'none',
    }}></span>
  );
}

// 侧栏底部小行：图标 + 主文 + 副文（折叠时仅图标，hover 高亮）
function SideRow({ icon, label, detail, onClick, collapsed, title, accent }) {
  const interactive = !!onClick;
  return (
    <div onClick={onClick} title={title || label}
      style={{
        display: 'flex', alignItems: 'center', gap: 10,
        padding: collapsed ? '9px 0' : '8px 12px', minHeight: 40,
        justifyContent: collapsed ? 'center' : 'flex-start',
        cursor: interactive ? 'pointer' : 'default', transition: 'background .1s',
      }}
      onMouseEnter={interactive ? (e) => { e.currentTarget.style.background = 'var(--panel)'; } : undefined}
      onMouseLeave={interactive ? (e) => { e.currentTarget.style.background = 'transparent'; } : undefined}>
      <span style={{ flex: 'none', color: accent || 'var(--muted)', display: 'inline-flex' }}>{icon}</span>
      {!collapsed ? (
        <span style={{ display: 'flex', flexDirection: 'column', gap: 1, minWidth: 0, flex: 1 }}>
          <span style={{ fontSize: 12, fontWeight: 600, color: 'var(--ink)', whiteSpace: 'nowrap' }}>{label}</span>
          {detail ? <span style={{ fontSize: 10, color: 'var(--faint)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{detail}</span> : null}
        </span>
      ) : null}
      {!collapsed && interactive ? <span style={{ flex: 'none', color: 'var(--faint)', fontSize: 11 }}>›</span> : null}
    </div>
  );
}

const sIco = { width: 16, height: 16, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: 2, strokeLinecap: 'round', strokeLinejoin: 'round', style: { flex: 'none' } };
function IcoAgents() { return <svg {...sIco}><circle cx="12" cy="12" r="8" /><circle cx="12" cy="12" r="3" fill="currentColor" stroke="none" /></svg>; }
function IcoGlobalCfg() { return <svg {...sIco}><circle cx="12" cy="12" r="9" /><line x1="3" y1="12" x2="21" y2="12" /><path d="M12 3a14 14 0 0 0 0 18a14 14 0 0 0 0-18" /></svg>; }

// 全局设置弹框：用户级默认配置（新建项目套用）。保存 / 取消，落库经 onSave。
function SettingsModal({ settings, onSave, onClose, t }) {
  const { Button, Input, Select } = window.MaestroDesignSystem_a6a290;
  const s = settings || {};
  const [autonomy, setAutonomy] = React.useState(s.autonomy || 'manual');
  const [concurrency, setConcurrency] = React.useState(String(s.concurrency ?? 1));
  const [maxRetries, setMaxRetries] = React.useState(String(s.maxRetries ?? 2));
  const [timeoutMin, setTimeoutMin] = React.useState(String(Math.round((s.timeoutMs ?? 1800000) / 60000)));
  const [autoPlan, setAutoPlan] = React.useState(!!s.autoApprovePlan);
  const [autoExec, setAutoExec] = React.useState(!!s.autoApproveExec);
  const [budgetUsd, setBudgetUsd] = React.useState(s.budgetUsd != null ? String(s.budgetUsd) : '');
  const [budgetPeriod, setBudgetPeriod] = React.useState(s.budgetPeriod || 'month');
  const [model, setModel] = React.useState(s.model || '');
  React.useEffect(() => {
    const onKey = (e) => { if (e.key === 'Escape') onClose(); };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [onClose]);
  const submit = () => {
    onSave({
      autonomy, concurrency: Number(concurrency) || 1, maxRetries: Number(maxRetries) || 0,
      timeoutMs: Math.max(1, Number(timeoutMin) || 30) * 60000,
      autoApprovePlan: autoPlan, autoApproveExec: autoExec,
      budgetUsd: budgetUsd.trim() === '' ? null : Number(budgetUsd),
      budgetPeriod, model: model.trim() === '' ? null : model.trim(),
    });
    onClose();
  };
  const Toggle = ({ on, set }) => (
    <button type="button" onClick={() => set(!on)} style={{
      alignSelf: 'flex-start', fontFamily: 'var(--mono)', fontSize: 11, fontWeight: 700, letterSpacing: '.08em',
      background: on ? 'rgba(95,221,125,.1)' : 'var(--bg-deep)', color: on ? 'var(--green)' : 'var(--faint)',
      border: '1px solid ' + (on ? 'var(--green-dim)' : 'var(--line)'), borderRadius: 'var(--radius-sm,4px)',
      padding: '6px 14px', cursor: 'pointer', transition: 'all .1s',
    }}>{on ? 'ON' : 'OFF'}</button>
  );
  const field = (label, node) => (
    <label style={{ display: 'flex', flexDirection: 'column', gap: 4, fontSize: 11, color: 'var(--muted)', letterSpacing: '.05em' }}>
      <span>{label}</span>{node}
    </label>
  );
  return (
    <div style={{ position: 'fixed', inset: 0, zIndex: 2000, display: 'grid', placeItems: 'center' }}>
      <div onClick={onClose} style={{ position: 'absolute', inset: 0, background: 'rgba(4,6,5,.86)', backdropFilter: 'blur(3px)' }}></div>
      <div style={{
        position: 'relative', width: 460, maxWidth: '92vw', maxHeight: '88vh', overflowY: 'auto',
        background: 'var(--bg-deep)', border: '1px solid var(--line)', borderRadius: 'var(--radius-md,6px)',
        boxShadow: '0 18px 50px rgba(0,0,0,.7)', padding: '18px 20px', animation: 'maestro-rise .14s ease both',
      }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 12, fontWeight: 700, letterSpacing: '.16em', color: 'var(--muted)', textTransform: 'uppercase', marginBottom: 3 }}>
          <span style={{ color: 'var(--green)' }}>▍</span>{t.gGlobalSettings}
        </div>
        <div style={{ fontSize: 11, color: 'var(--faint)', marginBottom: 15 }}>{t.gSettingsHint}</div>
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 13 }}>
          {field(t.workMode, <Select value={autonomy} onChange={setAutonomy} options={Object.entries(t.autonomy).map(([value, label]) => ({ value, label }))} />)}
          {field(t.maxConcurrency, <Input type="number" value={concurrency} onChange={setConcurrency} style={{ width: '100%' }} />)}
          {field(t.gMaxRetries, <Input type="number" value={maxRetries} onChange={setMaxRetries} style={{ width: '100%' }} />)}
          {field(t.gTimeoutMin, <Input type="number" value={timeoutMin} onChange={setTimeoutMin} style={{ width: '100%' }} />)}
          {field(t.gAutoPlan, <Toggle on={autoPlan} set={setAutoPlan} />)}
          {field(t.gAutoExec, <Toggle on={autoExec} set={setAutoExec} />)}
          {field(t.gBudget, <Input type="number" placeholder="∞" value={budgetUsd} onChange={setBudgetUsd} style={{ width: '100%' }} />)}
          {field(t.gBudgetPeriod, <Select value={budgetPeriod} onChange={setBudgetPeriod} options={[{ value: 'day', label: t.gPeriodDay }, { value: 'month', label: t.gPeriodMonth }]} />)}
        </div>
        <div style={{ marginTop: 13 }}>
          {field(t.model, <Input placeholder={t.modelPh} value={model} onChange={setModel} style={{ width: '100%' }} />)}
        </div>
        <div style={{ display: 'flex', gap: 10, justifyContent: 'flex-end', marginTop: 18 }}>
          <Button onClick={onClose}>{t.cancel}</Button>
          <Button variant="solid" onClick={submit}>{t.save}</Button>
        </div>
      </div>
    </div>
  );
}

function UserRow({ user, t, collapsed, settings, onSaveSettings }) {
  const [open, setOpen] = React.useState(false);
  const [showSettings, setShowSettings] = React.useState(false);
  const ref = React.useRef(null);
  React.useEffect(() => {
    if (!open) return;
    const onDoc = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    document.addEventListener('mousedown', onDoc);
    return () => document.removeEventListener('mousedown', onDoc);
  }, [open]);
  const avatar = (
    <span style={{
      flex: 'none', width: 26, height: 26, borderRadius: '50%', display: 'grid', placeItems: 'center',
      fontSize: 12, fontWeight: 700, color: '#0a0d0b', background: 'hsl(' + (user.hue || 200) + ' 50% 62%)',
    }}>{user.initial}</span>
  );
  return (
    <div ref={ref} style={{ position: 'relative', borderTop: '1px solid var(--line-soft)' }}>
      <div onClick={() => setOpen(!open)} title={user.name + ' · ' + user.plan}
        style={{ display: 'flex', alignItems: 'center', gap: 10, padding: collapsed ? '10px 0' : '10px 12px', justifyContent: collapsed ? 'center' : 'flex-start', cursor: 'pointer', transition: 'background .1s' }}
        onMouseEnter={(e) => { e.currentTarget.style.background = 'var(--panel)'; }}
        onMouseLeave={(e) => { e.currentTarget.style.background = 'transparent'; }}>
        {avatar}
        {!collapsed ? (
          <React.Fragment>
            <span style={{ display: 'flex', flexDirection: 'column', gap: 1, minWidth: 0, flex: 1 }}>
              <span style={{ fontSize: 12.5, fontWeight: 600, color: 'var(--ink)', whiteSpace: 'nowrap' }}>{user.name}</span>
              <span style={{ fontSize: 10, color: 'var(--green)', whiteSpace: 'nowrap' }}>{user.plan}</span>
            </span>
            <span style={{ flex: 'none', color: 'var(--faint)', fontSize: 11, transform: open ? 'rotate(180deg)' : 'none', transition: 'transform .12s' }}>▴</span>
          </React.Fragment>
        ) : null}
      </div>
      {open ? (
        <div style={{
          position: collapsed ? 'fixed' : 'absolute',
          ...(collapsed
            ? { left: 58, bottom: 14, width: 180 }
            : { bottom: 'calc(100% + 4px)', left: 12, right: 12, minWidth: 160 }),
          zIndex: 80,
          background: 'var(--bg-deep)', border: '1px solid var(--line)', borderRadius: 'var(--radius-md,6px)',
          boxShadow: '0 10px 30px rgba(0,0,0,.65)', padding: 6, animation: 'maestro-rise .12s ease both',
        }}>
          <div style={{ padding: '6px 10px', borderBottom: '1px solid var(--line-soft)', marginBottom: 4 }}>
            <div style={{ fontSize: 12, fontWeight: 600 }}>{user.name} <span style={{ color: 'var(--faint)', fontWeight: 400 }}>{user.handle}</span></div>
            <div style={{ fontSize: 10.5, color: 'var(--green)', marginTop: 2 }}>{t.gUserPlan} · {user.plan}</div>
          </div>
          {[
            { label: t.gGlobalSettings, onClick: () => { setShowSettings(true); setOpen(false); }, red: false },
            { label: t.gUserSettings, onClick: null, red: false },
            { label: t.gUserSignOut, onClick: null, red: true },
          ].map((it) => (
            <div key={it.label} onClick={it.onClick || undefined} style={{ padding: '6px 10px', fontSize: 12, color: it.red ? 'var(--red)' : 'var(--ink)', cursor: 'pointer', borderRadius: 3 }}
              onMouseEnter={(e) => { e.currentTarget.style.background = 'var(--panel-2)'; }}
              onMouseLeave={(e) => { e.currentTarget.style.background = 'transparent'; }}>{it.label}</div>
          ))}
        </div>
      ) : null}
      {showSettings ? <SettingsModal settings={settings} onSave={onSaveSettings} onClose={() => setShowSettings(false)} t={t} /> : null}
    </div>
  );
}

function fmtTokens(n) {
  if (n >= 1e6) return (n / 1e6).toFixed(1).replace(/\.0$/, '') + 'M';
  if (n >= 1e3) return Math.round(n / 1e3) + 'K';
  return String(n);
}

// 全局 AGENT 卡片（内联常驻；按项目默认 3 个、多余折叠、按最近活跃排序）
function AgentCard({ global, summary, t, collapsed }) {
  const [showAll, setShowAll] = React.useState(false);
  if (collapsed) return null;
  // 「最近活跃」排序：运行中优先，其次本周 token 用量（无活跃时间戳时的代理）
  const projs = [...summary.byProject].sort((a, b) => (b.active - a.active) || (b.tokens - a.tokens));
  const maxTok = Math.max(...projs.map((p) => p.tokens), 1);
  const shown = showAll ? projs : projs.slice(0, 3);
  return (
    <div style={{
      margin: '10px 12px 4px', border: '1px solid var(--line)', borderRadius: 'var(--radius-md,6px)',
      background: 'var(--bg-deep)', padding: '11px 12px',
    }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 7, fontSize: 10.5, fontWeight: 700, letterSpacing: '.18em', color: 'var(--muted)', marginBottom: 10 }}>
        <span style={{ color: 'var(--cyan)', display: 'inline-flex' }}><IcoAgents /></span>{t.gAgents.toUpperCase()}
        <span style={{ marginLeft: 'auto', fontWeight: 400, letterSpacing: '.04em', color: 'var(--faint)' }}>{t.apWeek}</span>
      </div>
      {/* 总结 */}
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 1, background: 'var(--line-soft)', border: '1px solid var(--line-soft)', borderRadius: 4, overflow: 'hidden', marginBottom: 11 }}>
        {[
          [fmtTokens(summary.tokensWeek), t.apTokens, 'var(--cyan)'],
          ['$' + summary.costWeek.toFixed(1), t.apCost, 'var(--green)'],
          [summary.runsWeek, t.apRuns, 'var(--ink)'],
          [summary.activeNow + ' / ' + global.maxAgents, t.apActive, summary.activeNow ? 'var(--cyan)' : 'var(--faint)'],
        ].map(([v, k, c], i) => (
          <div key={i} style={{ background: 'var(--bg-deep)', padding: '7px 9px' }}>
            <div style={{ fontSize: 15, fontWeight: 700, color: c, letterSpacing: '.02em' }}>{v}</div>
            <div style={{ fontSize: 9.5, color: 'var(--muted)', letterSpacing: '.06em', marginTop: 1 }}>{k}</div>
          </div>
        ))}
      </div>
      {/* 按项目（默认 3，多余折叠，按活跃排序）*/}
      <div style={{ fontSize: 9.5, fontWeight: 700, letterSpacing: '.18em', color: 'var(--faint)', marginBottom: 7 }}>{t.apPerProj}</div>
      <div style={{ display: 'grid', gap: 8 }}>
        {shown.map((p) => (
          <div key={p.id}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 3 }}>
              <span style={{ flex: 'none', width: 16, height: 16, borderRadius: 4, display: 'grid', placeItems: 'center', fontSize: 9, fontWeight: 700, color: '#0a0d0b', background: 'hsl(' + p.hue + ' 45% 60%)' }}>{p.name[0].toUpperCase()}</span>
              <span style={{ fontSize: 12, fontWeight: 600, color: 'var(--ink)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{p.name}</span>
              {p.active > 0 ? (
                <span style={{ flex: 'none', display: 'inline-flex', alignItems: 'center', gap: 4, fontSize: 9.5, color: 'var(--cyan)', border: '1px solid var(--cyan-dim)', borderRadius: 3, padding: '0 5px' }}>
                  <span style={{ width: 5, height: 5, borderRadius: '50%', background: 'var(--cyan)', boxShadow: '0 0 6px var(--cyan)', animation: 'maestro-pulse .9s infinite' }}></span>{p.active}
                </span>
              ) : <span style={{ flex: 'none', fontSize: 9.5, color: 'var(--faint)' }}>{t.apIdle}</span>}
              <span style={{ marginLeft: 'auto', flex: 'none', fontSize: 11, fontWeight: 600, color: 'var(--cyan)' }}>{fmtTokens(p.tokens)}</span>
            </div>
            <div style={{ display: 'flex', alignItems: 'center', gap: 8, paddingLeft: 24 }}>
              <span style={{ flex: 1, height: 4, background: 'var(--panel-2)', borderRadius: 2, overflow: 'hidden' }}>
                <span style={{ display: 'block', height: '100%', width: (p.tokens / maxTok * 100) + '%', background: 'var(--cyan)', boxShadow: '0 0 6px rgba(89,200,216,.5)' }}></span>
              </span>
              <span style={{ flex: 'none', fontSize: 10, color: 'var(--faint)' }}>{p.runs} {t.apRuns}</span>
            </div>
          </div>
        ))}
      </div>
      {projs.length > 3 ? (
        <button type="button" onClick={() => setShowAll((s) => !s)} style={{
          marginTop: 9, width: '100%', background: 'transparent', border: '1px dashed var(--line)', color: 'var(--faint)',
          borderRadius: 4, padding: '4px 0', fontSize: 10.5, fontFamily: 'var(--mono)', cursor: 'pointer', letterSpacing: '.05em',
        }}
          onMouseEnter={(e) => { e.currentTarget.style.color = 'var(--muted)'; e.currentTarget.style.borderColor = 'var(--muted)'; }}
          onMouseLeave={(e) => { e.currentTarget.style.color = 'var(--faint)'; e.currentTarget.style.borderColor = 'var(--line)'; }}>
          {showAll ? t.apCollapse : t.apMore.replace('{n}', projs.length - 3)}
        </button>
      ) : null}
    </div>
  );
}

function GlobalConfigRow({ global, t, collapsed }) {
  const { Select } = window.MaestroDesignSystem_a6a290;
  const [open, setOpen] = React.useState(false);
  const [pos, setPos] = React.useState(null);
  const ref = React.useRef(null);
  const rowRef = React.useRef(null);
  React.useEffect(() => {
    if (!open) return;
    const onDoc = (e) => { if (ref.current && !ref.current.contains(e.target) && rowRef.current && !rowRef.current.contains(e.target)) setOpen(false); };
    document.addEventListener('mousedown', onDoc);
    return () => document.removeEventListener('mousedown', onDoc);
  }, [open]);
  const toggle = () => {
    if (!open && rowRef.current) {
      const r = rowRef.current.getBoundingClientRect();
      const W = 250;
      // 优先在行上方对齐左缘；折叠态贴侧栏右侧
      const left = collapsed ? r.right + 6 : Math.min(r.left, window.innerWidth - W - 8);
      setPos({ left, bottom: window.innerHeight - r.top + 6, width: W });
    }
    setOpen((o) => !o);
  };
  const stat = (k, v, accent) => (
    <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', gap: 12, padding: '5px 0', borderBottom: '1px solid var(--line-soft)' }}>
      <span style={{ fontSize: 10.5, color: 'var(--muted)', letterSpacing: '.06em' }}>{k}</span>
      <span style={{ fontSize: 12, fontWeight: 600, color: accent || 'var(--ink)' }}>{v}</span>
    </div>
  );
  return (
    <div style={{ position: 'relative' }}>
      <div ref={rowRef}>
        <SideRow collapsed={collapsed} icon={<IcoGlobalCfg />} onClick={toggle}
          label={t.gConfig}
          detail={t.gConfigDetail.replace('{n}', global.maxAgents).replace('{v}', global.daemon)}
          title={t.gConfig} />
      </div>
      {open && pos ? (
        <div ref={ref} style={{
          position: 'fixed', left: pos.left, bottom: pos.bottom, width: pos.width,
          zIndex: 1000, background: 'var(--bg-deep)', border: '1px solid var(--line)', borderRadius: 'var(--radius-md,6px)',
          boxShadow: '0 12px 34px rgba(0,0,0,.7)', padding: '12px 14px', animation: 'maestro-rise .12s ease both',
        }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 7, fontSize: 10.5, fontWeight: 700, letterSpacing: '.18em', color: 'var(--muted)', marginBottom: 8 }}>
            <span style={{ color: 'var(--cyan)' }}>▍</span>{t.gConfig.toUpperCase()}
          </div>
          <div style={{ marginBottom: 12 }}>
            {stat('daemon', global.daemon, 'var(--green)')}
            {stat('port', ':' + global.port)}
            {stat('uptime', global.uptime)}
            {stat(t.gAgents, global.runningAgents + ' / ' + global.maxAgents, 'var(--cyan)')}
          </div>
          <div style={{ display: 'grid', gap: 10 }}>
            <label style={{ display: 'flex', flexDirection: 'column', gap: 4, fontSize: 11, color: 'var(--muted)', letterSpacing: '.08em' }}>
              <span>{t.maxConcurrency}</span>
              <Select defaultValue={String(global.maxAgents)} style={{ width: '100%' }} options={['1', '2', '4', '6', '8'].map((n) => ({ value: n, label: n }))} />
            </label>
            <label style={{ display: 'flex', flexDirection: 'column', gap: 4, fontSize: 11, color: 'var(--muted)', letterSpacing: '.08em' }}>
              <span>{t.workMode}</span>
              <Select defaultValue={global.autonomy} style={{ width: '100%' }} options={Object.entries(t.autonomy).map(([value, label]) => ({ value, label }))} />
            </label>
          </div>
        </div>
      ) : null}
    </div>
  );
}

function Sidebar({ projects, currentId, onSelect, onNewProject, collapsed, onToggleCollapse, t, global, user, summary, onGlobalConfig, onReorder, settings, onSaveSettings }) {
  const { Button } = window.MaestroDesignSystem_a6a290;
  const [dragId, setDragId] = React.useState(null);
  const [overId, setOverId] = React.useState(null);
  const onDrop = (targetId) => {
    if (dragId && dragId !== targetId && onReorder) {
      const ids = projects.map((x) => x.id);
      const from = ids.indexOf(dragId);
      const to = ids.indexOf(targetId);
      if (from > -1 && to > -1) { ids.splice(to, 0, ids.splice(from, 1)[0]); onReorder(ids); }
    }
    setDragId(null); setOverId(null);
  };
  return (
    <aside style={{ background: 'var(--bg-deep)', borderRight: '1px solid var(--line)', display: 'flex', flexDirection: 'column', overflowY: 'auto', overflowX: 'hidden' }}>
      <div style={{ padding: collapsed ? '16px 0 12px' : '20px 16px 14px', borderBottom: '1px solid var(--line-soft)', display: 'flex', flexDirection: 'column', alignItems: collapsed ? 'center' : 'flex-start' }}>
        {collapsed ? <MaestroMark /> : (
          <React.Fragment>
            <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
              <MaestroMark scale={1.8} />
              <span style={{ fontSize: 19, fontWeight: 700, letterSpacing: '.28em', color: 'var(--green)', textShadow: '0 0 12px rgba(95,221,125,.45)' }}>
                MAESTRO<span style={{ marginLeft: 1, animation: 'maestro-blink 1.1s steps(1) infinite' }}>▮</span>
              </span>
            </div>
            <div style={{ marginTop: 5, fontSize: 9, fontWeight: 600, color: 'var(--muted)', letterSpacing: '.2em', lineHeight: 1.7, textTransform: 'uppercase' }}>{t.logoSub}</div>
          </React.Fragment>
        )}
      </div>
      {!collapsed ? (
        <div style={{ display: 'flex', alignItems: 'center', gap: 6, fontSize: 11, fontWeight: 600, letterSpacing: '.22em', color: 'var(--muted)', padding: '16px 16px 8px' }}>
          <span style={{ color: 'var(--green)' }}>▍</span>{t.projects}
          <span style={{ marginLeft: 'auto' }}><Button variant="ghost" size="xs" onClick={onNewProject}>{t.newProject}</Button></span>
        </div>
      ) : <div style={{ height: 12 }}></div>}
      <ul style={{ listStyle: 'none', flex: 1, margin: 0, padding: 0 }}>
        {projects.map((p) => {
          const active = p.id === currentId;
          const meta = projStateMeta(p.state, t);
          return (
            <li key={p.id} onClick={() => onSelect(p.id)} title={p.name + ' · ' + meta.label + (p.pending ? ' · ' + t.projPendingTip.replace('{n}', p.pending) : '')}
              draggable={!collapsed}
              onDragStart={(e) => { setDragId(p.id); e.dataTransfer.effectAllowed = 'move'; }}
              onDragOver={(e) => { if (dragId) { e.preventDefault(); if (p.id !== dragId && overId !== p.id) setOverId(p.id); } }}
              onDragLeave={() => setOverId((o) => (o === p.id ? null : o))}
              onDrop={(e) => { e.preventDefault(); onDrop(p.id); }}
              onDragEnd={() => { setDragId(null); setOverId(null); }}
              style={{
                position: 'relative',
                padding: collapsed ? '10px 0' : '9px 14px 9px 12px', cursor: dragId ? 'grabbing' : 'pointer',
                display: 'flex', alignItems: 'center', gap: 9,
                justifyContent: collapsed ? 'center' : 'flex-start',
                borderLeft: '2px solid ' + (active ? 'var(--green)' : 'transparent'),
                background: active ? 'var(--panel-2)' : 'transparent',
                color: active ? 'var(--green)' : 'var(--muted)',
                opacity: dragId === p.id ? 0.4 : 1,
                boxShadow: overId === p.id ? 'inset 0 2px 0 var(--green)' : 'none',
              }}
              onMouseEnter={(e) => { if (!active && !dragId) e.currentTarget.style.background = 'var(--panel)'; }}
              onMouseLeave={(e) => { if (!active) e.currentTarget.style.background = 'transparent'; }}>
              <ProjectLogo project={p} size={collapsed ? 26 : 24} />
              {!collapsed ? (
                <React.Fragment>
                  <span style={{ display: 'flex', flexDirection: 'column', gap: 1, minWidth: 0, flex: 1 }}>
                    <span style={{ fontWeight: 600, fontSize: 13, color: active ? 'var(--green)' : 'var(--ink)' }}>{p.name}</span>
                    <span style={{ fontSize: 10.5, color: 'var(--faint)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{p.path}</span>
                  </span>
                  <span style={{ flex: 'none', display: 'flex', alignItems: 'center', gap: 6 }}>
                    {p.pending > 0 ? (
                      <span title={t.projPendingTip.replace('{n}', p.pending)} style={{
                        fontFamily: 'var(--mono)', fontSize: 9.5, fontWeight: 700, lineHeight: '15px',
                        minWidth: 15, height: 15, textAlign: 'center', padding: '0 4px',
                        color: 'var(--bg-deep)', background: 'var(--violet)', borderRadius: 8,
                      }}>{p.pending}</span>
                    ) : null}
                    <ProjStatusDot meta={meta} />
                  </span>
                </React.Fragment>
              ) : (
                <span style={{ position: 'absolute', top: 7, right: 9, display: 'flex', alignItems: 'center' }}>
                  <ProjStatusDot meta={meta} size={p.pending > 0 ? 8 : 6} />
                </span>
              )}
            </li>
          );
        })}
      </ul>
      {/* ── 全局 AGENT 卡片（常驻）+ 用户（全局设置已并入用户菜单）── */}
      <div style={{ borderTop: '1px solid var(--line-soft)' }}>
        <AgentCard global={global} summary={summary} t={t} collapsed={collapsed} />
      </div>
      <UserRow user={user} t={t} collapsed={collapsed} settings={settings} onSaveSettings={onSaveSettings} />
      <div style={{ borderTop: '1px solid var(--line-soft)', padding: collapsed ? '10px 0' : '10px 12px', fontSize: 11, color: 'var(--muted)', display: 'flex', alignItems: 'center', gap: 7, justifyContent: collapsed ? 'center' : 'flex-start' }}>
        {!collapsed ? (
          <React.Fragment>
            <span style={{ width: 7, height: 7, borderRadius: '50%', background: 'var(--green)', boxShadow: '0 0 8px var(--green)', display: 'inline-block', flex: 'none' }}></span>
            <span style={{ overflow: 'hidden', whiteSpace: 'nowrap', textOverflow: 'ellipsis' }}>{t.connected}</span>
          </React.Fragment>
        ) : null}
        <button onClick={onToggleCollapse} title={collapsed ? t.expandSide : t.collapseSide}
          style={{
            marginLeft: collapsed ? 0 : 'auto', flex: 'none', cursor: 'pointer',
            fontFamily: 'var(--mono)', fontSize: 12, lineHeight: 1,
            background: 'transparent', color: 'var(--muted)',
            border: '1px solid var(--line)', borderRadius: 'var(--radius-sm, 4px)', padding: '4px 7px',
          }}
          onMouseEnter={(e) => { e.currentTarget.style.color = 'var(--green)'; e.currentTarget.style.borderColor = 'var(--green-dim)'; }}
          onMouseLeave={(e) => { e.currentTarget.style.color = 'var(--muted)'; e.currentTarget.style.borderColor = 'var(--line)'; }}>
          {collapsed ? '»' : '«'}
        </button>
      </div>
    </aside>
  );
}
window.MaestroKitSidebar = Sidebar;
