diff --git a/components/AppLogo.tsx b/components/AppLogo.tsx index b8909fa3..5118d86c 100644 --- a/components/AppLogo.tsx +++ b/components/AppLogo.tsx @@ -1,38 +1,58 @@ import React from 'react'; interface AppLogoProps { - className?: string; + className?: string; } -/** - * App logo component that dynamically uses the accent color (--primary CSS variable). - * The original logo.svg file remains unchanged; this component renders an inline SVG - * with colors bound to the current theme's accent color. - */ export const AppLogo: React.FC = ({ className }) => ( - - {/* Main background - uses accent color */} - - {/* Terminal window */} - - {/* Title bar - light accent tint */} - - {/* Window buttons */} - - - - {/* Terminal prompt arrow */} - - {/* Cursor line */} - - {/* Cat ears */} - - - {/* Cat tail */} - - {/* Connector/plug */} - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); export default AppLogo; diff --git a/components/KeychainManager.tsx b/components/KeychainManager.tsx index 19c19a85..f6857a57 100644 --- a/components/KeychainManager.tsx +++ b/components/KeychainManager.tsx @@ -520,7 +520,7 @@ echo $3 >> "$FILE"`); )} > {/* Toolbar */} -
+
{/* Filter Tabs */}
{/* KEY button with split interaction: left=switch view, right=dropdown */} @@ -528,16 +528,15 @@ echo $3 >> "$FILE"`);
@@ -589,33 +585,24 @@ echo $3 >> "$FILE"`); className={cn( "flex items-center rounded-md transition-colors", activeFilter === "certificate" - ? "bg-primary/15" - : "hover:bg-accent", + ? "bg-foreground/10 text-foreground hover:bg-foreground/15" + : "bg-foreground/5 text-foreground hover:bg-foreground/10", )} > @@ -645,7 +632,7 @@ echo $3 >> "$FILE"`); value={search} onChange={(e) => setSearch(e.target.value)} placeholder={t("common.searchPlaceholder")} - className="h-9 pl-8 w-full" + className="h-10 pl-9 w-full bg-secondary border-border/60 text-sm" />
)} @@ -654,7 +641,7 @@ echo $3 >> "$FILE"`);
diff --git a/components/SnippetsManager.tsx b/components/SnippetsManager.tsx index a1699ec6..d4f751c6 100644 --- a/components/SnippetsManager.tsx +++ b/components/SnippetsManager.tsx @@ -984,7 +984,7 @@ const SnippetsManager: React.FC = ({
-
+
{/* Search box */}
@@ -1005,7 +1005,7 @@ const SnippetsManager: React.FC = ({ }} size="sm" variant="secondary" - className="h-10 gap-2" + className="h-10 gap-2 bg-foreground/5 text-foreground hover:bg-foreground/10 border-border/40" > {t('snippets.action.newPackage')} diff --git a/components/TopTabs.tsx b/components/TopTabs.tsx index 95bed104..188bb84c 100644 --- a/components/TopTabs.tsx +++ b/components/TopTabs.tsx @@ -555,7 +555,7 @@ const TopTabsInner: React.FC = ({ onDragLeave={handleTabDragLeave} onDrop={(e) => handleTabDrop(e, session.id)} className={cn( - "netcatty-tab relative h-7 pl-3 pr-2 min-w-[140px] max-w-[240px] rounded-none text-xs font-semibold cursor-pointer flex items-center justify-between gap-2 app-no-drag flex-shrink-0", + "netcatty-tab relative h-7 pl-3 pr-2 min-w-[140px] max-w-[240px] rounded-t-md overflow-hidden text-xs font-semibold cursor-pointer flex items-center justify-between gap-2 app-no-drag flex-shrink-0", "transition-transform duration-150", isBeingDragged && isDraggingForReorder ? "opacity-40 scale-95" : "" )} @@ -581,13 +581,6 @@ const TopTabsInner: React.FC = ({ } }} > - {/* Active tab top accent line */} - {activeTabId === session.id && ( -
- )} {/* Drop indicator line - before */} {showDropIndicatorBefore && isDraggingForReorder && (
= ({ onDragLeave={handleTabDragLeave} onDrop={(e) => handleTabDrop(e, workspace.id)} className={cn( - "netcatty-tab relative h-7 pl-3 pr-2 min-w-[150px] max-w-[260px] rounded-none text-xs font-semibold cursor-pointer flex items-center justify-between gap-2 app-no-drag flex-shrink-0", + "netcatty-tab relative h-7 pl-3 pr-2 min-w-[150px] max-w-[260px] rounded-t-md overflow-hidden text-xs font-semibold cursor-pointer flex items-center justify-between gap-2 app-no-drag flex-shrink-0", "transition-transform duration-150", isBeingDragged && isDraggingForReorder ? "opacity-40 scale-95" : "" )} @@ -683,13 +676,6 @@ const TopTabsInner: React.FC = ({ } }} > - {/* Active tab top accent line */} - {isActive && ( -
- )} {/* Drop indicator line - before */} {showDropIndicatorBefore && isDraggingForReorder && (
= ({ data-state={isActive ? 'active' : 'inactive'} onClick={() => onSelectTab(logView.id)} className={cn( - "netcatty-tab relative h-7 pl-3 pr-2 min-w-[140px] max-w-[240px] rounded-none text-xs font-semibold cursor-pointer flex items-center justify-between gap-2 app-no-drag flex-shrink-0", + "netcatty-tab relative h-7 pl-3 pr-2 min-w-[140px] max-w-[240px] rounded-t-md overflow-hidden text-xs font-semibold cursor-pointer flex items-center justify-between gap-2 app-no-drag flex-shrink-0", )} style={{ backgroundColor: isActive @@ -775,13 +761,6 @@ const TopTabsInner: React.FC = ({ } }} > - {/* Active tab top accent line */} - {isActive && ( -
- )}
= ({ data-state={isSftpActive ? 'active' : 'inactive'} onClick={() => onSelectTab('sftp')} className={cn( - "netcatty-tab relative h-7 px-3 rounded-none text-xs font-semibold cursor-pointer flex items-center gap-2 app-no-drag", + "netcatty-tab relative h-7 px-3 rounded-t-md overflow-hidden text-xs font-semibold cursor-pointer flex items-center gap-2 app-no-drag", )} style={{ backgroundColor: isSftpActive @@ -900,12 +879,6 @@ const TopTabsInner: React.FC = ({ } }} > - {isSftpActive && ( -
- )} SFTP
)} diff --git a/components/VaultView.tsx b/components/VaultView.tsx index 9f4a2d46..85eb4ac5 100644 --- a/components/VaultView.tsx +++ b/components/VaultView.tsx @@ -76,6 +76,7 @@ import SerialHostDetailsPanel from "./SerialHostDetailsPanel"; import SnippetsManager from "./SnippetsManager"; import { ImportVaultDialog, ImportOptions } from "./vault/ImportVaultDialog"; import { Button } from "./ui/button"; +import { RippleButton } from "./ui/ripple"; import { ContextMenu, ContextMenuContent, @@ -1597,24 +1598,26 @@ const VaultViewInner: React.FC = ({
@@ -1627,7 +1630,7 @@ const VaultViewInner: React.FC = ({
- + {sidebarCollapsed && {t("vault.nav.hosts")}} - + {sidebarCollapsed && {t("vault.nav.keychain")}} - + {sidebarCollapsed && {t("vault.nav.portForwarding")}} - + {sidebarCollapsed && {t("vault.nav.snippets")}} - + {sidebarCollapsed && {t("vault.nav.knownHosts")}} - + {sidebarCollapsed && {t("vault.nav.logs")}} diff --git a/components/ui/ripple.tsx b/components/ui/ripple.tsx new file mode 100644 index 00000000..880042cd --- /dev/null +++ b/components/ui/ripple.tsx @@ -0,0 +1,63 @@ +import * as React from "react"; +import { cn } from "../../lib/utils"; +import { Button, ButtonProps } from "./button"; + +interface RippleState { + id: number; + x: number; + y: number; + size: number; +} + +const RIPPLE_DURATION_MS = 600; + +export const RippleButton = React.forwardRef( + ({ children, className, onPointerDown, ...props }, ref) => { + const [ripples, setRipples] = React.useState([]); + const nextId = React.useRef(0); + + const handlePointerDown = React.useCallback( + (e: React.PointerEvent) => { + const rect = e.currentTarget.getBoundingClientRect(); + const size = Math.max(rect.width, rect.height) * 2; + const x = e.clientX - rect.left - size / 2; + const y = e.clientY - rect.top - size / 2; + const id = nextId.current++; + setRipples((rs) => [...rs, { id, x, y, size }]); + window.setTimeout( + () => setRipples((rs) => rs.filter((r) => r.id !== id)), + RIPPLE_DURATION_MS, + ); + onPointerDown?.(e); + }, + [onPointerDown], + ); + + return ( + + ); + }, +); +RippleButton.displayName = "RippleButton"; diff --git a/index.css b/index.css index 11c8419b..b9589c42 100644 --- a/index.css +++ b/index.css @@ -102,6 +102,17 @@ } } +@keyframes ripple { + 0% { + transform: scale(0); + opacity: 0.35; + } + 100% { + transform: scale(1); + opacity: 0; + } +} + @keyframes split-panel-enter { 0% { width: 0; diff --git a/index.html b/index.html index 57eded81..068d19c1 100755 --- a/index.html +++ b/index.html @@ -131,7 +131,7 @@ .splash-logo { width: 64px; height: 64px; - color: hsl(var(--primary)); + border-radius: 14px; } .splash-spinner { @@ -195,15 +195,8 @@
- + +
diff --git a/public/dmg-fix-icon.png b/public/dmg-fix-icon.png index a730f4ca..fbf2462e 100644 Binary files a/public/dmg-fix-icon.png and b/public/dmg-fix-icon.png differ diff --git a/public/icon.png b/public/icon.png index 1bc91701..fbf2462e 100644 Binary files a/public/icon.png and b/public/icon.png differ diff --git a/public/logo.svg b/public/logo.svg index e744b07f..3e74f36a 100644 --- a/public/logo.svg +++ b/public/logo.svg @@ -1,12 +1,50 @@ - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/tray-icon.png b/public/tray-icon.png index 747013af..054097fa 100644 Binary files a/public/tray-icon.png and b/public/tray-icon.png differ diff --git a/public/tray-icon@2x.png b/public/tray-icon@2x.png index 5e3bc405..95a281ce 100644 Binary files a/public/tray-icon@2x.png and b/public/tray-icon@2x.png differ diff --git a/public/tray-iconTemplate.png b/public/tray-iconTemplate.png index 11ee4e53..45a32fee 100644 Binary files a/public/tray-iconTemplate.png and b/public/tray-iconTemplate.png differ diff --git a/public/tray-iconTemplate@2x.png b/public/tray-iconTemplate@2x.png index f0f73636..443c8d3a 100644 Binary files a/public/tray-iconTemplate@2x.png and b/public/tray-iconTemplate@2x.png differ