Files
BossAI/src/web/pages/ComfyUI.tsx
T

142 lines
4.1 KiB
TypeScript

import React from 'react';
export default function ComfyUI() {
const [isDark, setIsDark] = React.useState(true);
const [available, setAvailable] = React.useState(false);
const [message, setMessage] = React.useState('正在检查外部 ComfyUI...');
const iframeRef = React.useRef<HTMLIFrameElement | null>(null);
const openFullscreen = () => {
window.open('/api/comfyui/proxy/', '_blank', 'noopener,noreferrer');
};
const reloadFrame = () => {
if (iframeRef.current) {
iframeRef.current.src = '/api/comfyui/proxy/';
}
};
React.useEffect(() => {
const el = document.documentElement;
const check = () => setIsDark(el.getAttribute('data-theme') !== 'light');
check();
const mo = new MutationObserver(check);
mo.observe(el, { attributes: true, attributeFilter: ['data-theme'] });
return () => mo.disconnect();
}, []);
React.useEffect(() => {
let cancelled = false;
const controller = new AbortController();
void fetch('/api/comfyui/proxy/', { method: 'GET', signal: controller.signal })
.then((response) => {
if (cancelled) return;
setAvailable(response.ok);
setMessage(response.ok
? ''
: '外部 ComfyUI 不可达。请在 config.toml 中配置 comfyui.base_url,或使用工作流。');
})
.catch((err) => {
if (cancelled || err?.name === 'AbortError') return;
setAvailable(false);
setMessage('外部 ComfyUI 不可达。请在 config.toml 中配置 comfyui.base_url,或使用工作流。');
});
return () => {
cancelled = true;
controller.abort();
};
}, []);
return (
<div
style={{
width: '100%',
height: 'calc(100vh - 56px)',
display: 'flex',
flexDirection: 'column',
background: isDark ? '#1a1a2e' : '#f5f5f5',
}}
>
<div style={toolbarStyle}>
<div style={toolbarTitleStyle}>外部 ComfyUI</div>
<div style={toolbarActionsStyle}>
<button type="button" onClick={reloadFrame} disabled={!available} style={toolbarButtonStyle}>
刷新
</button>
<button type="button" onClick={openFullscreen} disabled={!available} style={primaryToolbarButtonStyle}>
新窗口打开
</button>
</div>
</div>
{available ? (
<iframe
ref={iframeRef}
src="/api/comfyui/proxy/"
style={{ width: '100%', height: '100%', border: 'none' }}
title="External ComfyUI"
allow="clipboard-read; clipboard-write"
/>
) : (
<div style={{
height: '100%',
display: 'grid',
placeItems: 'center',
color: 'var(--color-text-muted)',
fontSize: 14,
textAlign: 'center',
padding: 24,
}}>
<div>
<div style={{ fontWeight: 700, color: 'var(--color-text-primary)', marginBottom: 8 }}>
内置 Docker ComfyUI 工作台已移除
</div>
<div style={{ maxWidth: 560, lineHeight: 1.7 }}>{message}</div>
</div>
</div>
)}
</div>
);
}
const toolbarStyle: React.CSSProperties = {
minHeight: 44,
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
gap: 12,
padding: '8px 12px',
borderBottom: '1px solid var(--color-border-light)',
background: 'var(--color-surface)',
};
const toolbarTitleStyle: React.CSSProperties = {
fontSize: 14,
fontWeight: 800,
color: 'var(--color-text-primary)',
};
const toolbarActionsStyle: React.CSSProperties = {
display: 'flex',
alignItems: 'center',
gap: 8,
};
const toolbarButtonStyle: React.CSSProperties = {
minHeight: 30,
border: '1px solid var(--color-border)',
borderRadius: 8,
padding: '5px 10px',
background: 'var(--color-bg)',
color: 'var(--color-text-primary)',
cursor: 'pointer',
fontSize: 12,
};
const primaryToolbarButtonStyle: React.CSSProperties = {
...toolbarButtonStyle,
borderColor: 'var(--color-primary)',
background: 'var(--color-primary)',
color: '#fff',
fontWeight: 700,
};