File size: 3,814 Bytes
aea470f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
/**
 * DownloadWorkspaceButton — FIX-6
 * Bottone compatto stile Replit per scaricare il VFS come ZIP.
 * Da montare nella StatusBar (slot destra).
 */
import { useState, useEffect, useCallback } from "react";
import { vfsAsync, onVfsChanged } from "@/lib/vfsDb";
import { downloadBlob } from "@/lib/fileConverter";

export function DownloadWorkspaceButton() {
  const [fileCount, setFileCount] = useState(0);
  const [state, setState] = useState<"idle" | "loading" | "done">("idle");

  const reload = useCallback(() => {
    vfsAsync.list().then(f => setFileCount(f.length)).catch(() => {});
  }, []);

  useEffect(() => {
    reload();
    return onVfsChanged(reload);
  }, [reload]);

  useEffect(() => {
    if (state !== "done") return;
    const t = setTimeout(() => setState("idle"), 2000);
    return () => clearTimeout(t);
  }, [state]);

  if (fileCount === 0) return null;

  const handleClick = async () => {
    if (state !== "idle") return;
    setState("loading");
    try {
      const { default: JSZip } = await import("jszip");
      const zip   = new JSZip();
      const files = await vfsAsync.list();
      for (const f of files) {
        const c = f.opfs ? (await vfsAsync.read(f.path) ?? "") : f.content;
        zip.file(f.path.startsWith("/") ? f.path.slice(1) : f.path, c);
      }
      const blob = await zip.generateAsync({ type: "blob", compression: "DEFLATE", compressionOptions: { level: 6 } });
      downloadBlob(blob, `workspace-${Date.now()}.zip`);
      setState("done");
    } catch {
      setState("idle");
    }
  };

  const isDone    = state === "done";
  const isLoading = state === "loading";

  return (
    <button
      onClick={handleClick}
      disabled={isLoading}
      title={`Scarica workspace (${fileCount} file) come ZIP`}
      style={{
        all: "unset",
        display: "flex",
        alignItems: "center",
        gap: 4,
        height: "100%",
        padding: "0 10px",
        borderLeft: "1px solid rgba(255,255,255,0.04)",
        fontSize: "0.62rem",
        fontFamily: '"JetBrains Mono", ui-monospace, monospace',
        fontWeight: 600,
        letterSpacing: "0.025em",
        color: isDone ? "#34d399" : "rgba(148,163,184,0.55)",
        cursor: isLoading ? "wait" : "pointer",
        transition: "color 0.15s",
        whiteSpace: "nowrap",
        WebkitTapHighlightColor: "transparent",
      } as React.CSSProperties}
      onPointerEnter={e => {
        if (!isLoading && !isDone)
          (e.currentTarget as HTMLButtonElement).style.color = "rgba(148,163,184,0.85)";
      }}
      onPointerLeave={e => {
        if (!isDone)
          (e.currentTarget as HTMLButtonElement).style.color = "rgba(148,163,184,0.55)";
      }}
    >
      {isDone ? (
        <svg width={9} height={9} viewBox="0 0 24 24" fill="none"
          stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
          <polyline points="20 6 9 17 4 12" />
        </svg>
      ) : isLoading ? (
        <svg width={9} height={9} viewBox="0 0 24 24" fill="none"
          stroke="currentColor" strokeWidth="2.5" strokeLinecap="round"
          style={{ animation: "sb-spin 0.8s linear infinite" }}>
          <path d="M21 12a9 9 0 1 1-6.219-8.56" />
        </svg>
      ) : (
        <svg width={9} height={9} viewBox="0 0 24 24" fill="none"
          stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
          <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" />
          <polyline points="7 10 12 15 17 10" />
          <line x1="12" y1="15" x2="12" y2="3" />
        </svg>
      )}
      {isDone ? "salvato" : isLoading ? "zip…" : "zip"}
      <style>{`@keyframes sb-spin{to{transform:rotate(360deg)}}`}</style>
    </button>
  );
}

export default DownloadWorkspaceButton;