/* 复用 Persom 2.1 的视觉定义与基础组件，保留原生按钮和弹窗的键盘支持。 */
(() => {
  const P = window.Playbook;
  const UI = window.P21;
  /* 颜色和字体取自现有产品，不在新原型维护另一份值。 */
  P.designStyle = Object.fromEntries(Object.entries(UI.C).map(([key, value]) => [`--pb-${key}`, value]));
  Object.assign(P.designStyle, { '--pb-serif': UI.SERIF, '--pb-mono': UI.MONO });
  function Sidebar() {
    /* 只展示同一套窗口环境；本原型不扩展其他页面与账号操作。 */
    const shell = React.useMemo(() => ({ s: { page: 'Playbook', paused: false, avatar: false }, set: () => {}, go: () => {} }), []);
    return <UI.Ctx.Provider value={shell}><UI.Sidebar /></UI.Ctx.Provider>;
  }
  function AgentTags({ names }) { return names.length ? <UI.AgentTags ids={names.map(a => a === 'Codex' ? 'CX' : 'CC')} /> : null; }
  function Button({ children, primary, danger, icon, small, className = '', ...props }) {
    return <button type="button" className={`pb-btn ${className}`} {...props}><UI.Btn kind={danger ? 'danger' : primary ? 'primary' : 'ghost'} disabled={props.disabled} small={small} style={icon ? { width: 34, height: 34, padding: 0, justifyContent: 'center' } : undefined}>{children}</UI.Btn></button>;
  }
  /* 产品样式的选项面板；浮层独立于 Tweaks 的滚动区，避免选项被裁切。 */
  function Select({ label, value, options, onChange }) {
    const [open, setOpen] = React.useState(false);
    const selected = Math.max(0, options.findIndex(o => o.value === value));
    const [active, setActive] = React.useState(selected);
    const [position, setPosition] = React.useState({});
    const trigger = React.useRef(null), list = React.useRef(null);
    const id = React.useId();
    const close = focus => { setOpen(false); if (focus) trigger.current?.focus(); };
    const place = () => {
      const r = trigger.current.getBoundingClientRect();
      const below = window.innerHeight - r.bottom - 14, above = r.top - 14;
      const up = below < 200 && above > below;
      const height = Math.min(240, Math.max(60, up ? above : below));
      setPosition({ left: Math.max(8, Math.min(r.left, window.innerWidth - r.width - 8)), width: r.width, maxHeight: height, ...(up ? { bottom: window.innerHeight - r.top + 6 } : { top: r.bottom + 6 }) });
    };
    const show = index => { place(); setActive(index); setOpen(true); };
    const choose = index => { onChange(options[index].value); close(true); };
    React.useEffect(() => {
      if (!open) return;
      list.current?.focus();
      const outside = e => { if (!trigger.current?.contains(e.target) && !list.current?.contains(e.target)) close(false); };
      const scroll = e => { if (!list.current?.contains(e.target)) place(); };
      document.addEventListener('pointerdown', outside);
      window.addEventListener('resize', place);
      window.addEventListener('scroll', scroll, true);
      return () => { document.removeEventListener('pointerdown', outside); window.removeEventListener('resize', place); window.removeEventListener('scroll', scroll, true); };
    }, [open]);
    React.useEffect(() => { if (open) document.getElementById(`${id}-${active}`)?.scrollIntoView?.({ block: 'nearest' }); }, [open, active]);
    const navigate = e => {
      if (['ArrowDown', 'ArrowUp', 'Home', 'End', 'Enter', ' ', 'Escape'].includes(e.key)) { e.preventDefault(); e.stopPropagation(); }
      if (e.key === 'ArrowDown') setActive(i => (i + 1) % options.length);
      else if (e.key === 'ArrowUp') setActive(i => (i - 1 + options.length) % options.length);
      else if (e.key === 'Home') setActive(0);
      else if (e.key === 'End') setActive(options.length - 1);
      else if (e.key === 'Enter' || e.key === ' ') choose(active);
      else if (e.key === 'Escape' || e.key === 'Tab') close(true);
    };
    return <>
      <button type="button" ref={trigger} className="pb-select-trigger" aria-label={label} aria-haspopup="listbox" aria-expanded={open} aria-controls={open ? id : undefined} onClick={() => open ? close(true) : show(selected)} onKeyDown={e => {
        if (['ArrowDown', 'ArrowUp', 'Home', 'End'].includes(e.key)) { e.preventDefault(); show(e.key === 'Home' ? 0 : e.key === 'End' ? options.length - 1 : selected); }
      }}><span>{options[selected].label}</span><svg aria-hidden="true" width="12" height="12" viewBox="0 0 12 12"><path d="M2 4l4 4 4-4" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" /></svg></button>
      {open && ReactDOM.createPortal(<div id={id} ref={list} className="p21 pb pb-select-options p21-scroll" style={{ ...P.designStyle, ...position }} role="listbox" aria-label={label} aria-activedescendant={`${id}-${active}`} tabIndex={-1} onKeyDown={navigate}>
        {options.map((o, i) => <div id={`${id}-${i}`} key={o.value} className={`pb-select-option ${i === active ? 'pb-select-option-active' : ''}`} role="option" aria-selected={o.value === value} data-value={o.value} onMouseEnter={() => setActive(i)} onMouseDown={e => e.preventDefault()} onClick={() => choose(i)}><span>{o.label}</span><span className="pb-select-check" aria-hidden="true">{o.value === value ? '✓' : ''}</span></div>)}
      </div>, document.body)}
    </>;
  }
  function Logo({ small }) { return <span className={`pb-logo ${small ? 'pb-logo-small' : ''}`}>P</span>; }
  function Tag({ children, green, ...props }) { return <span {...props} className={`pb-tag ${green ? 'pb-tag-green' : ''}`}>{children}</span>; }
  function AgentChoices({ value, onChange }) {
    return <div className="pb-agent-choices">{['Claude Code', 'Codex'].map(a => <label key={a}><input type="checkbox" checked={value.includes(a)} onChange={() => onChange(value.includes(a) ? value.filter(x => x !== a) : [...value, a])} />{a}</label>)}</div>;
  }
  function Modal({ title, children, footer, onClose, wide, className = '', titleClassName = 'pb-modal-title', suspended = false, sharedFocusId, overlayClassName = '' }) {
    const ref = React.useRef(null), opener = React.useRef(null), resume = React.useRef(null), close = React.useRef(onClose);
    close.current = onClose;
    React.useEffect(() => { opener.current = document.activeElement; return () => { if (opener.current?.isConnected) opener.current.focus(); }; }, []);
    React.useEffect(() => {
      const shared = () => sharedFocusId ? document.getElementById(sharedFocusId) : null;
      const inside = target => ref.current?.contains(target) || shared()?.contains(target);
      if (suspended) { if (inside(document.activeElement)) resume.current = document.activeElement; return; }
      if (!inside(document.activeElement)) {
        if (resume.current?.isConnected && inside(resume.current)) resume.current.focus(); else ref.current?.focus();
      }
      const focusable = el => el ? [...el.querySelectorAll('button:not(:disabled),input:not(:disabled),textarea,a[href],summary,[tabindex="0"]')].filter(node => !node.closest('[inert],[aria-hidden="true"]')) : [];
      const key = e => {
        if (!inside(e.target)) return;
        if (e.key === 'Escape') {
          /* 在全局 Ask 中按 Escape 只折叠对话，不同时关闭详情。 */
          if (!shared()?.contains(e.target)) { e.stopPropagation(); close.current(); }
          return;
        }
        if (e.key !== 'Tab') return;
        const local = focusable(ref.current), global = focusable(shared());
        const first = local[0], last = local[local.length - 1], globalFirst = global[0], globalLast = global[global.length - 1];
        const focus = el => { e.preventDefault(); el?.focus(); };
        if (!first) { focus(globalFirst || ref.current); return; }
        const active = document.activeElement;
        if (global.length) {
          if (!e.shiftKey && active === last) focus(globalFirst);
          else if (e.shiftKey && active === globalFirst) focus(last);
          else if (!e.shiftKey && active === globalLast) focus(first);
          else if (e.shiftKey && (active === first || active === ref.current)) focus(globalLast);
          else if (!e.shiftKey && active === ref.current) focus(first);
        } else {
          if (e.shiftKey && (active === first || active === ref.current)) focus(last);
          else if (!e.shiftKey && (active === last || active === ref.current)) focus(first);
        }
      };
      const target = sharedFocusId ? document : ref.current;
      target?.addEventListener('keydown', key);
      return () => target?.removeEventListener('keydown', key);
    }, [suspended, sharedFocusId]);
    return <div className={`pb-overlay ${overlayClassName} ${suspended ? 'pb-overlay-suspended' : ''}`} onMouseDown={e => !suspended && e.target === e.currentTarget && close.current()}>
      <section className={`pb-modal ${wide ? 'pb-modal-wide' : ''} ${className}`} role="dialog" aria-modal={suspended ? undefined : true} aria-hidden={suspended || undefined} inert={suspended ? '' : undefined} aria-label={title} aria-owns={!suspended ? sharedFocusId : undefined} ref={ref} tabIndex={-1}>
        <header className="pb-modal-head"><div className={titleClassName}>{title}</div><button type="button" className="pb-close" aria-label="Close dialog" onClick={() => close.current()}>×</button></header>
        <div className="pb-modal-content">{children}</div>{footer && <footer className="pb-modal-foot">{footer}</footer>}
      </section>
    </div>;
  }
  /* 同一套 Skill 正文排版，附带文件和脚本仍只读、不执行。 */
  function Markdown({ text }) { return <div className="pb-markdown"><UI.Md text={text} /></div>; }
  function Changes({ before, after }) {
    const rows = P.diff(before, after);
    return <div className="pb-changes">{rows.length ? rows.map(r => <section key={r.path} className="pb-change-file"><div className="pb-row"><code>{r.path}</code><Tag green={r.status === 'Added'}>{r.status}</Tag></div><div className="pb-diff-grid"><div><div className="pb-caption">Before</div><pre>{r.before || '(not present)'}</pre></div><div><div className="pb-caption">After</div><pre>{r.after || '(removed)'}</pre></div></div></section>) : <p>No changes to publish.</p>}</div>;
  }
  Object.assign(P, { Select, Button, Logo, Tag, Modal, AgentChoices, Markdown, Changes, Sidebar, AgentTags });
})();
