Z ZPI 文档 v0.3.8
介绍页

源码

packages/desktop/src/renderer/BackgroundTaskPane.tsx

tsx89 行查看原文
import type { BackgroundTaskOutput } from "@zpi-dev/ui";
import { useEffect, useRef, useState } from "react";
import { paneTask } from "./pane-store.ts";
import { backgroundStatus } from "./TaskActivity.tsx";
import { unwrap } from "./unwrap.ts";
import { userErrorMessage } from "./user-error.ts";

export function BackgroundTaskPane({
  sessionId,
  taskId,
  visible,
}: {
  sessionId: string;
  taskId: string;
  visible: boolean;
}) {
  const [snapshot, setSnapshot] = useState<BackgroundTaskOutput>();
  const [error, setError] = useState("");
  const [stopping, setStopping] = useState(false);
  const output = useRef<HTMLPreElement>(null);
  const following = useRef(true);
  useEffect(() => {
    if (!visible) return;
    let disposed = false;
    let timer: ReturnType<typeof setTimeout>;
    const read = async () => {
      try {
        const next = unwrap(await window.zpi.readBackgroundTask(sessionId, taskId));
        if (disposed) return;
        setSnapshot(next);
        setError("");
        if (next.task.status === "running") timer = setTimeout(read, 1000);
      } catch (error) {
        if (!disposed) {
          setError(userErrorMessage(error));
          timer = setTimeout(read, 1000);
        }
      }
    };
    void read();
    return () => {
      disposed = true;
      clearTimeout(timer);
    };
  }, [sessionId, taskId, visible]);
  useEffect(() => {
    if (snapshot && following.current && output.current)
      output.current.scrollTop = output.current.scrollHeight;
  }, [snapshot]);
  return (
    <section className="background-task-pane" aria-label="后台任务输出">
      <header>
        <strong>{snapshot ? backgroundStatus[snapshot.task.status] : "读取中"}</strong>
        {snapshot?.task.exitCode != null && <span>退出码 {snapshot.task.exitCode}</span>}
        {snapshot?.task.status === "running" && (
          <button
            disabled={stopping}
            onClick={() => {
              setStopping(true);
              paneTask(
                window.zpi
                  .stopBackgroundTask(sessionId, taskId)
                  .then(unwrap)
                  .finally(() => setStopping(false)),
              );
            }}
          >
            {stopping ? "停止中…" : "停止任务"}
          </button>
        )}
      </header>
      {snapshot && <code className="background-task-command">{snapshot.task.command}</code>}
      {error && <p role="alert">{error}</p>}
      {snapshot?.task.error && <p role="alert">{snapshot.task.error}</p>}
      {snapshot?.truncated && <p>仅显示末尾 64 KiB。完整日志:{snapshot.task.outputPath}</p>}
      <pre
        ref={output}
        onScroll={() => {
          const element = output.current;
          if (element)
            following.current = element.scrollHeight - element.scrollTop - element.clientHeight < 24;
        }}
      >
        {snapshot?.output || "暂无输出"}
      </pre>
    </section>
  );
}