feat: polish workflow studio and workspace assets

This commit is contained in:
boos
2026-05-25 20:42:38 +08:00
parent cb35d5fc6e
commit 1ba293c262
29 changed files with 2299 additions and 158 deletions
+44 -14
View File
@@ -1,34 +1,51 @@
import React from 'react';
type WorkflowRuntime = {
mode?: string;
};
const LOCAL_WORKFLOW_URL = '/workflow-ui/';
const DOCKER_WORKFLOW_URL = '/api/comfyui/proxy/';
export default function Workflows() {
const [ready, setReady] = React.useState(false);
const [message, setMessage] = React.useState('正在加载工作流前端...');
const [workflowUrl, setWorkflowUrl] = React.useState(LOCAL_WORKFLOW_URL);
const [isCloud, setIsCloud] = React.useState(false);
const iframeRef = React.useRef<HTMLIFrameElement | null>(null);
const openFullscreen = () => {
window.open('/workflow-ui/', '_blank', 'noopener,noreferrer');
window.open(workflowUrl, '_blank', 'noopener,noreferrer');
};
const reloadFrame = () => {
if (iframeRef.current) {
iframeRef.current.src = '/workflow-ui/';
iframeRef.current.src = workflowUrl;
}
};
React.useEffect(() => {
let cancelled = false;
const controller = new AbortController();
void fetch('/workflow-ui/', { signal: controller.signal })
.then((response) => {
if (cancelled) return;
setReady(response.ok);
setMessage(response.ok ? '' : 'Workflow Studio 前端未构建,请先执行 workflow-ui:install');
})
.catch((error) => {
if (cancelled || error?.name === 'AbortError') return;
setReady(false);
setMessage('Workflow Studio 前端未构建,请先执行 workflow-ui:install');
});
const loadRuntime = async () => {
const runtimeResponse = await fetch('/api/workflow-ui/runtime', { signal: controller.signal });
const runtime = runtimeResponse.ok ? ((await runtimeResponse.json()) as WorkflowRuntime) : {};
const cloudMode = runtime.mode === 'cloud';
const dockerMode = runtime.mode === 'docker';
const targetUrl = dockerMode ? DOCKER_WORKFLOW_URL : LOCAL_WORKFLOW_URL;
if (cancelled) return;
setIsCloud(cloudMode);
setWorkflowUrl(targetUrl);
const response = await fetch(targetUrl, { signal: controller.signal });
if (cancelled) return;
setReady(response.ok);
setMessage(response.ok ? '' : 'Workflow Studio 前端未构建,请先执行 workflow-ui:install');
};
void loadRuntime().catch((error) => {
if (cancelled || error?.name === 'AbortError') return;
setReady(false);
setMessage('工作流入口不可用,请检查服务配置');
});
return () => {
cancelled = true;
controller.abort();
@@ -39,6 +56,8 @@ export default function Workflows() {
<div style={{ width: '100%', height: 'calc(100vh - 56px)', display: 'flex', flexDirection: 'column' }}>
<div style={toolbarStyle}>
<div style={toolbarTitleStyle}>工作流</div>
{!isCloud && workflowUrl === DOCKER_WORKFLOW_URL && <div style={cloudBadgeStyle}>Docker 完整版 ComfyUI</div>}
{isCloud && <div style={cloudBadgeStyle}>Comfy Cloud API 后端</div>}
<div style={toolbarActionsStyle}>
<button type="button" onClick={reloadFrame} disabled={!ready} style={toolbarButtonStyle}>
刷新
@@ -51,9 +70,10 @@ export default function Workflows() {
{ready ? (
<iframe
ref={iframeRef}
src="/workflow-ui/"
src={workflowUrl}
title="Workflow Studio"
allow="clipboard-read; clipboard-write"
referrerPolicy="no-referrer"
style={{ width: '100%', height: '100%', border: 'none', display: 'block' }}
/>
) : (
@@ -79,6 +99,16 @@ const toolbarStyle: React.CSSProperties = {
background: 'var(--color-surface)',
};
const cloudBadgeStyle: React.CSSProperties = {
marginRight: 'auto',
border: '1px solid var(--color-border)',
borderRadius: 999,
padding: '3px 8px',
color: 'var(--color-text-secondary)',
fontSize: 12,
fontWeight: 700,
};
const toolbarTitleStyle: React.CSSProperties = {
fontSize: 14,
fontWeight: 800,