File size: 3,955 Bytes
f59fbe2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import { useState, useEffect } from "react";
import { IconChevronUp, IconStack2, IconFileCode } from "@tabler/icons-react";
import { useChatStore } from "@/stores";
import { bridge, Events } from "@/services";
import { cn } from "@/lib/utils";
import { FileChangesPanel } from "./FileChangesPanel";
import { QueuedMessagesPanel } from "./QueuedMessagesPanel";
import { ApprovalDialog } from "./ApprovalDialog";
import { QuestionDialog } from "./QuestionDialog";
import type { FileChange } from "shared/types";

type TabId = "queue" | "changes" | null;

export function BottomToolbar() {
  const { queue } = useChatStore();
  const [activeTab, setActiveTab] = useState<TabId>(null);
  const [fileChanges, setFileChanges] = useState<FileChange[]>([]);

  useEffect(() => {
    return bridge.on<FileChange[]>(Events.FileChangesUpdated, setFileChanges);
  }, []);

  // Auto-close tab when data becomes empty
  useEffect(() => {
    if (activeTab === "queue" && queue.length === 0) {
      setActiveTab(null);
    }
    if (activeTab === "changes" && fileChanges.length === 0) {
      setActiveTab(null);
    }
  }, [activeTab, queue.length, fileChanges.length]);

  const hasQueue = queue.length > 0;
  const hasChanges = fileChanges.length > 0;
  const hasTabs = hasQueue || hasChanges;

  const toggleTab = (tab: TabId) => {
    setActiveTab((prev) => (prev === tab ? null : tab));
  };

  const fileStats = fileChanges.reduce((a, c) => ({ additions: a.additions + c.additions, deletions: a.deletions + c.deletions }), { additions: 0, deletions: 0 });

  return (
    <div className="flex flex-col min-h-0 flex-1 overflow-hidden">
      {/* ApprovalDialog and QuestionDialog - priority, shrink-0 */}
      <ApprovalDialog />
      <QuestionDialog />

      {/* Queue/Changes panel - takes remaining space */}
      {activeTab && (
        <div className="flex-1 min-h-16 max-h-32 overflow-y-auto mb-0.5 border border-border/60 rounded-md bg-card">
          {activeTab === "queue" && <QueuedMessagesPanel />}
          {activeTab === "changes" && <FileChangesPanel changes={fileChanges} />}
        </div>
      )}

      {/* Tab bar - always at bottom, shrink-0 */}
      {hasTabs && (
        <div className="shrink-0 mb-0.5 rounded-md overflow-hidden bg-card">
          <div className="flex items-center gap-1 py-0.5 min-h-7">
            {hasQueue && (
              <button
                onClick={() => toggleTab("queue")}
                className={cn(
                  "flex items-center gap-1.5 px-2 py-0.5 rounded text-xs transition-colors",
                  activeTab === "queue" ? "bg-accent text-accent-foreground" : "hover:bg-muted/50 text-muted-foreground",
                )}
              >
                <IconStack2 className="size-3.5" />
                <span>{queue.length} Queued</span>
                <IconChevronUp className={cn("size-3 transition-transform", activeTab === "queue" && "rotate-180")} />
              </button>
            )}

            {hasChanges && (
              <button
                onClick={() => toggleTab("changes")}
                className={cn(
                  "flex items-center gap-1.5 px-2 py-0.5 rounded text-xs transition-colors",
                  activeTab === "changes" ? "bg-accent text-accent-foreground" : "hover:bg-muted/50 text-muted-foreground",
                )}
              >
                <IconFileCode className="size-3.5" />
                <span>{fileChanges.length} Changed</span>
                <span className="text-[10px] tabular-nums">
                  <span className="text-green-600 dark:text-green-400">+{fileStats.additions}</span> <span className="text-red-600 dark:text-red-400">-{fileStats.deletions}</span>
                </span>
                <IconChevronUp className={cn("size-3 transition-transform", activeTab === "changes" && "rotate-180")} />
              </button>
            )}
          </div>
        </div>
      )}
    </div>
  );
}