From 09f964451d7d92e9891430ec4595c1276d486aab Mon Sep 17 00:00:00 2001
From: zwlucas <lucas.fariamo08@gmail.com>
Date: Fri, 5 Jun 2026 17:52:54 -0300
Subject: feat: upload

Signed-off-by: zwlucas <lucas.fariamo08@gmail.com>
---
 src/components/TabsBar/TabsBar.tsx | 136 +++++++++++++++++++++++++++++++++++++
 1 file changed, 136 insertions(+)
 create mode 100644 src/components/TabsBar/TabsBar.tsx

(limited to 'src/components/TabsBar/TabsBar.tsx')

diff --git a/src/components/TabsBar/TabsBar.tsx b/src/components/TabsBar/TabsBar.tsx
new file mode 100644
index 0000000..86c77ea
--- /dev/null
+++ b/src/components/TabsBar/TabsBar.tsx
@@ -0,0 +1,136 @@
+import { useRef, useEffect, useCallback, useState } from 'react'
+import { useTabsStore } from '@/stores/useTabsStore'
+import { useContextMenuStore } from '@/stores/useContextMenuStore'
+
+function CloseIcon() {
+  return (
+    <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
+      <line x1="18" y1="6" x2="6" y2="18" />
+      <line x1="6" y1="6" x2="18" y2="18" />
+    </svg>
+  )
+}
+
+export default function TabsBar() {
+  const tabs = useTabsStore((s) => s.tabs)
+  const activeTabId = useTabsStore((s) => s.activeTabId)
+  const setActiveTab = useTabsStore((s) => s.setActiveTab)
+  const closeTab = useTabsStore((s) => s.closeTab)
+  const openContextMenu = useContextMenuStore((s) => s.open)
+
+  const scrollRef = useRef<HTMLDivElement>(null)
+  const activeTabRef = useRef<HTMLDivElement>(null)
+  const [showLeftFade, setShowLeftFade] = useState(false)
+  const [showRightFade, setShowRightFade] = useState(true)
+
+  const handleTabContextMenu = useCallback((e: React.MouseEvent, tabId: string) => {
+    e.preventDefault()
+    const numTabs = tabs.length
+    openContextMenu(e.clientX, e.clientY, [
+      { id: 'close', label: 'Close', shortcut: 'Ctrl+W', icon: 'close', action: () => closeTab(tabId) },
+      { id: 'closeOthers', label: 'Close Others', action: () => { tabs.forEach((t) => { if (t.id !== tabId) closeTab(t.id) }) }, disabled: numTabs < 2 },
+      { id: 'closeAll', label: 'Close All', action: () => { tabs.forEach((t) => closeTab(t.id)) }, disabled: numTabs < 1 },
+      { id: 'sep1', label: '', separator: true, action: () => {} },
+      { id: 'copyPath', label: 'Copy Path', icon: 'link', action: () => { const tab = tabs.find((t) => t.id === tabId); if (tab) navigator.clipboard.writeText(tab.path) } },
+    ])
+  }, [tabs, closeTab, openContextMenu])
+
+  const updateFades = useCallback(() => {
+    const el = scrollRef.current
+    if (!el) return
+    setShowLeftFade(el.scrollLeft > 4)
+    setShowRightFade(el.scrollLeft < el.scrollWidth - el.clientWidth - 4)
+  }, [])
+
+  useEffect(() => {
+    const el = scrollRef.current
+    if (!el) return
+    el.addEventListener('scroll', updateFades, { passive: true })
+    updateFades()
+    return () => el.removeEventListener('scroll', updateFades)
+  }, [updateFades, tabs.length])
+
+  useEffect(() => {
+    const el = scrollRef.current
+    const tabEl = activeTabRef.current
+    if (!el || !tabEl) return
+
+    const containerRect = el.getBoundingClientRect()
+    const tabRect = tabEl.getBoundingClientRect()
+    const isVisible =
+      tabRect.left >= containerRect.left && tabRect.right <= containerRect.right
+
+    if (!isVisible) {
+      const offset = tabRect.left - containerRect.left
+      const centerOffset = offset - containerRect.width / 2 + tabRect.width / 2
+      el.scrollBy({ left: centerOffset, behavior: 'smooth' })
+    }
+  }, [activeTabId])
+
+  return (
+    <div className="relative shrink-0 h-tabs">
+      {showLeftFade && (
+        <div className="absolute left-0 top-0 bottom-0 w-6 z-10 pointer-events-none bg-gradient-to-r from-panel-bg to-transparent" />
+      )}
+      {showRightFade && (
+        <div className="absolute right-0 top-0 bottom-0 w-6 z-10 pointer-events-none bg-gradient-to-l from-panel-bg to-transparent" />
+      )}
+
+      <div
+        ref={scrollRef}
+        className="flex overflow-x-auto overflow-y-hidden h-full scroll-smooth"
+        role="tablist"
+        aria-label="Open files"
+      >
+        {tabs.length === 0 && (
+          <div className="flex items-center px-3 text-ui-sm text-text-muted shrink-0">
+            No files open
+          </div>
+        )}
+
+        {tabs.map((tab) => {
+          const isActive = tab.id === activeTabId
+
+          return (
+            <div
+              key={tab.id}
+              ref={isActive ? activeTabRef : undefined}
+              role="tab"
+              aria-selected={isActive ? 'true' : 'false'}
+              aria-label={`${tab.name}${tab.modified ? ' (modified)' : ''}`}
+              className={`group relative flex items-center gap-1.5 px-4 py-2 text-ui-sm cursor-pointer shrink-0 transition-colors duration-100 select-none border-0 ${
+                isActive
+                  ? 'bg-editor-bg text-text-primary'
+                  : 'bg-panel-bg text-text-muted hover:bg-panel-hover hover:text-text-secondary'
+              }`}
+              onClick={() => setActiveTab(tab.id)}
+              onContextMenu={(e) => handleTabContextMenu(e, tab.id)}
+            >
+              {isActive && (
+                <span className="absolute top-0 left-0 right-0 h-[2px] bg-accent rounded-b-sm" />
+              )}
+
+              <span className="truncate max-w-32">{tab.name}</span>
+
+              {tab.modified && (
+                <span className="w-[6px] h-[6px] rounded-full shrink-0 bg-text-muted group-hover:hidden" />
+              )}
+
+              <button
+                className={`flex items-center justify-center w-[18px] h-[18px] rounded-sm transition-all duration-100 -mr-1 shrink-0 hover:bg-panel-border text-text-muted hover:text-text-primary ${
+                  tab.modified ? 'hidden group-hover:flex' : 'opacity-0 group-hover:opacity-100'
+                }`}
+                onClick={(e) => {
+                  e.stopPropagation()
+                  closeTab(tab.id)
+                }}
+              >
+                <CloseIcon />
+              </button>
+            </div>
+          )
+        })}
+      </div>
+    </div>
+  )
+}
-- 
cgit v1.2.3

