feat: initial public release (MAESTRO v0.1.0)
Open-source release of MAESTRO, an agent orchestration platform that runs LLM-driven tasks through sandboxed tools, with a web UI. Apache-2.0. See README.md and docs/ (getting-started, configuration, architecture).
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
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<string, string> = {
|
||||
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 (
|
||||
<button type="button" disabled className={merged} title={hint}>
|
||||
PiP 非対応
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
const tooltip = pip.lastError
|
||||
? `直前のエラー: ${pip.lastError}`
|
||||
: pip.isOpen
|
||||
? 'PiP ウィンドウを閉じてここに戻す'
|
||||
: '別ウィンドウに切り出す(常に最前面)';
|
||||
|
||||
return (
|
||||
<span className="inline-flex items-center gap-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
if (pip.isOpen) pip.close();
|
||||
else void pip.open();
|
||||
}}
|
||||
className={merged}
|
||||
title={tooltip}
|
||||
>
|
||||
{pip.isOpen ? '↩ PiP を戻す' : '⇱ PiP'}
|
||||
</button>
|
||||
{pip.lastError && !pip.isOpen && (
|
||||
<span
|
||||
className="text-[10px] text-rose-600 max-w-[260px] truncate"
|
||||
title={pip.lastError}
|
||||
>
|
||||
{pip.lastError}
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user