import type { PipController } from '../../lib/usePictureInPicture.js'; interface Props { pip: PipController; className?: string; /** * Hide the button entirely if Document PiP is unsupported. Default true. * Set false to render a disabled "PiP 非対応" tag with an explanatory * tooltip — useful when you want to surface why PiP isn't available. */ hideWhenUnsupported?: boolean; } const REASON_HINT: Record = { browser: 'Picture-in-Picture は Chromium 系ブラウザ (Chrome / Edge / Arc / Opera 116+) のみ対応', 'insecure-context': 'Picture-in-Picture は HTTPS / localhost からのアクセスでのみ使えます', iframe: 'iframe 内では親フレームに document-picture-in-picture 権限が必要です', }; /** * Small toolbar button that toggles a Document Picture-in-Picture window for * a noVNC iframe. Surfaces `lastError` from the controller as the title * tooltip + a one-line indicator below the button so failed clicks aren't * silent (Document PiP can fail for several reasons — popup blocker, missing * user gesture, iframe permission policy, etc). */ export function PipButton({ pip, className, hideWhenUnsupported = true }: Props) { if (!pip.supported && hideWhenUnsupported) return null; const baseClass = 'text-2xs px-2 py-1 rounded-md border border-hairline bg-white hover:bg-surface text-slate-700 disabled:opacity-50'; const merged = className ? `${baseClass} ${className}` : baseClass; if (!pip.supported) { const hint = REASON_HINT[pip.unsupportedReason ?? 'browser'] ?? REASON_HINT.browser; return ( ); } const tooltip = pip.lastError ? `直前のエラー: ${pip.lastError}` : pip.isOpen ? 'PiP ウィンドウを閉じてここに戻す' : '別ウィンドウに切り出す(常に最前面)'; return ( {pip.lastError && !pip.isOpen && ( {pip.lastError} )} ); }