update:多iframe
This commit is contained in:
@@ -6,7 +6,7 @@ import type {
|
||||
ScriptType,
|
||||
} from "../../services/api";
|
||||
import { scriptIcon } from "./WorkspaceTree";
|
||||
import type { MouseEvent as ReactMouseEvent } from "react";
|
||||
import type { MouseEvent as ReactMouseEvent, RefObject } from "react";
|
||||
import { useRef, useEffect } from "react";
|
||||
|
||||
type ToastState = {
|
||||
@@ -14,8 +14,16 @@ type ToastState = {
|
||||
message: string;
|
||||
};
|
||||
|
||||
// 缓存的会话类型(多 iframe 共存方案)
|
||||
interface CachedSession {
|
||||
session: ActiveEditSession;
|
||||
jupyterUrl: string;
|
||||
lastActiveTime: number;
|
||||
}
|
||||
|
||||
type ScriptWorkspaceProps = {
|
||||
script: ScriptItem;
|
||||
sessionCache: Map<string, CachedSession>;
|
||||
editSession: ActiveEditSession | null;
|
||||
jupyterUrl: string | null;
|
||||
editBusy: boolean;
|
||||
@@ -91,6 +99,7 @@ function confineJupyterFrame(frame: HTMLIFrameElement): void {
|
||||
|
||||
export function ScriptWorkspace({
|
||||
script,
|
||||
sessionCache,
|
||||
editSession,
|
||||
jupyterUrl,
|
||||
editBusy,
|
||||
@@ -108,7 +117,9 @@ export function ScriptWorkspace({
|
||||
}: ScriptWorkspaceProps) {
|
||||
const tabbarRef = useRef<HTMLDivElement | null>(null);
|
||||
const isNotebook = script.script_type === "notebook";
|
||||
const isEditing = editSession?.session_status === "active";
|
||||
// 判断当前选中的文件是否正在编辑(需要同时检查 session_status 和 script_id)
|
||||
const isEditing = editSession?.session_status === "active"
|
||||
&& editSession?.script_id === script.script_id;
|
||||
|
||||
const scroll = (direction: "left" | "right") => {
|
||||
const tabbar = tabbarRef.current;
|
||||
@@ -232,64 +243,85 @@ export function ScriptWorkspace({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={`editor-canvas${isEditing && jupyterUrl ? " is-embedded" : ""}`}>
|
||||
{isEditing && jupyterUrl ? (
|
||||
<section className="embedded-jupyter">
|
||||
<div className="embedded-jupyter__status">
|
||||
<span>
|
||||
<i />
|
||||
Workspace Jupyter Server
|
||||
</span>
|
||||
<span>
|
||||
{isNotebook ? "Notebook Session · Python 3 Kernel" : "Jupyter 文本编辑器"}
|
||||
</span>
|
||||
<code title={editSession.runtime_id}>
|
||||
Runtime {editSession.runtime_id.slice(-8)}
|
||||
</code>
|
||||
</div>
|
||||
<iframe
|
||||
key={`${editSession.edit_session_id}:${jupyterUrl}`}
|
||||
src={jupyterUrl}
|
||||
title={`${script.script_name} Jupyter 编辑器`}
|
||||
allow="clipboard-read; clipboard-write"
|
||||
sandbox="allow-same-origin allow-scripts allow-forms allow-downloads allow-modals"
|
||||
onLoad={(event) => confineJupyterFrame(event.currentTarget)}
|
||||
/>
|
||||
</section>
|
||||
) : isNotebook ? (
|
||||
<section
|
||||
className={`editor-opening-state${openError ? " has-error" : ""}`}
|
||||
aria-live="polite"
|
||||
>
|
||||
<div className="editor-opening-state__icon">
|
||||
{openError
|
||||
? <Icon name="info" size={28} />
|
||||
: <span className="button-spinner button-spinner--blue" />}
|
||||
</div>
|
||||
<strong>
|
||||
{openError ? "Notebook 打开失败" : "正在打开 Notebook"}
|
||||
</strong>
|
||||
<p>
|
||||
{openError
|
||||
? openError
|
||||
: "正在获取编辑锁并连接 Workspace Jupyter Server…"}
|
||||
</p>
|
||||
{openError && (
|
||||
<button
|
||||
className="open-editor-button"
|
||||
type="button"
|
||||
disabled={editBusy}
|
||||
onClick={onOpenEditor}
|
||||
>
|
||||
{editBusy
|
||||
? <span className="button-spinner button-spinner--blue" />
|
||||
: <Icon name="refresh" size={16} />}
|
||||
{editBusy ? "正在重试…" : "重试打开"}
|
||||
</button>
|
||||
)}
|
||||
</section>
|
||||
) : (
|
||||
<section className="script-overview">
|
||||
{/* 多 iframe 共存方案:为每个缓存的 session 渲染独立的 iframe */}
|
||||
<div className={`editor-canvas ${sessionCache.size > 0 ? 'is-embedded' : ''}`} style={sessionCache.size > 0 ? { position: 'relative', minHeight: '0', height: '100%' } : undefined}>
|
||||
{/* 渲染所有缓存的 iframe */}
|
||||
{Array.from(sessionCache.entries()).map(([scriptId, cached]) => {
|
||||
// 当前选中的脚本就是激活的
|
||||
const isActive = scriptId === script.script_id;
|
||||
return (
|
||||
<section
|
||||
key={scriptId}
|
||||
className="embedded-jupyter"
|
||||
style={{
|
||||
position: isActive ? 'relative' : 'absolute',
|
||||
visibility: isActive ? 'visible' : 'hidden',
|
||||
pointerEvents: isActive ? 'auto' : 'none',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
overflow: 'hidden'
|
||||
}}
|
||||
>
|
||||
<div className="embedded-jupyter__status">
|
||||
<span>
|
||||
<i />
|
||||
Workspace Jupyter Server
|
||||
</span>
|
||||
<span>
|
||||
{cached.session.session_status === "active" ? "Notebook Session · Python 3 Kernel" : "Jupyter 文本编辑器"}
|
||||
</span>
|
||||
<code title={cached.session.runtime_id}>
|
||||
Runtime {cached.session.runtime_id.slice(-8)}
|
||||
</code>
|
||||
</div>
|
||||
<iframe
|
||||
src={cached.jupyterUrl}
|
||||
title={`${scriptId} Jupyter 编辑器`}
|
||||
allow="clipboard-read; clipboard-write"
|
||||
sandbox="allow-same-origin allow-scripts allow-forms allow-downloads allow-modals"
|
||||
onLoad={(event) => confineJupyterFrame(event.currentTarget)}
|
||||
/>
|
||||
</section>
|
||||
);
|
||||
})}
|
||||
|
||||
{/* 当前脚本没有缓存时的状态显示 */}
|
||||
{!sessionCache.has(script.script_id) ? (
|
||||
isNotebook ? (
|
||||
<section
|
||||
className={`editor-opening-state${openError ? " has-error" : ""}`}
|
||||
aria-live="polite"
|
||||
style={{ display: 'block' }}
|
||||
>
|
||||
<div className="editor-opening-state__icon">
|
||||
{openError
|
||||
? <Icon name="info" size={28} />
|
||||
: <span className="button-spinner button-spinner--blue" />}
|
||||
</div>
|
||||
<strong>
|
||||
{openError ? "Notebook 打开失败" : "正在打开 Notebook"}
|
||||
</strong>
|
||||
<p>
|
||||
{openError
|
||||
? openError
|
||||
: "正在获取编辑锁并连接 Workspace Jupyter Server…"}
|
||||
</p>
|
||||
{openError && (
|
||||
<button
|
||||
className="open-editor-button"
|
||||
type="button"
|
||||
disabled={editBusy}
|
||||
onClick={onOpenEditor}
|
||||
>
|
||||
{editBusy
|
||||
? <span className="button-spinner button-spinner--blue" />
|
||||
: <Icon name="refresh" size={16} />}
|
||||
{editBusy ? "正在重试…" : "重试打开"}
|
||||
</button>
|
||||
)}
|
||||
</section>
|
||||
) : (
|
||||
<section className="script-overview">
|
||||
<div className="script-overview__header">
|
||||
<div>
|
||||
<span className="section-kicker">PYTHON SCRIPT</span>
|
||||
@@ -365,7 +397,7 @@ export function ScriptWorkspace({
|
||||
</span>
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
)) : null}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user