Refactors UI font handling to support dynamic font loading and improves selection in settings. Implements "Load More" pagination for connection logs, enhancing performance and user experience with large datasets. Adds lazy loading for the Connection Logs Manager. Addresses an issue in `usePortForwardingState` to prevent duplicate backend sync calls in React StrictMode. Improves IPC communication robustness by checking window destruction status. Refines the visual presentation of the snippets empty state.
285 lines
5.6 KiB
CSS
285 lines
5.6 KiB
CSS
@import "tailwindcss";
|
|
|
|
/* ============================================
|
|
Tailwind CSS v4 Theme Configuration
|
|
============================================ */
|
|
@theme {
|
|
/* Default border color - ensures 'border' class uses theme color instead of currentColor */
|
|
--default-border-color: var(--color-border);
|
|
|
|
/* Colors - mapped from CSS variables */
|
|
--color-border: hsl(var(--border));
|
|
--color-input: hsl(var(--input));
|
|
--color-ring: hsl(var(--ring));
|
|
--color-background: hsl(var(--background));
|
|
--color-foreground: hsl(var(--foreground));
|
|
|
|
--color-primary: hsl(var(--primary));
|
|
--color-primary-foreground: hsl(var(--primary-foreground));
|
|
|
|
--color-secondary: hsl(var(--secondary));
|
|
--color-secondary-foreground: hsl(var(--secondary-foreground));
|
|
|
|
--color-destructive: hsl(var(--destructive));
|
|
--color-destructive-foreground: hsl(var(--destructive-foreground));
|
|
|
|
--color-muted: hsl(var(--muted));
|
|
--color-muted-foreground: hsl(var(--muted-foreground));
|
|
|
|
--color-accent: hsl(var(--accent));
|
|
--color-accent-foreground: hsl(var(--accent-foreground));
|
|
|
|
--color-popover: hsl(var(--popover));
|
|
--color-popover-foreground: hsl(var(--popover-foreground));
|
|
|
|
--color-card: hsl(var(--card));
|
|
--color-card-foreground: hsl(var(--card-foreground));
|
|
|
|
/* Border Radius */
|
|
--radius-lg: var(--radius);
|
|
--radius-md: calc(var(--radius) - 2px);
|
|
--radius-sm: calc(var(--radius) - 4px);
|
|
|
|
/* Fonts */
|
|
--font-sans: "Space Grotesk", system-ui, sans-serif;
|
|
--font-mono: "JetBrains Mono", monospace;
|
|
|
|
/* Animations */
|
|
--animate-accordion-down: accordion-down 0.2s ease-out;
|
|
--animate-accordion-up: accordion-up 0.2s ease-out;
|
|
--animate-fade-in: fade-in 0.2s ease-out;
|
|
|
|
@keyframes accordion-down {
|
|
from {
|
|
height: 0;
|
|
}
|
|
|
|
to {
|
|
height: var(--radix-collapsible-content-height);
|
|
}
|
|
}
|
|
|
|
@keyframes accordion-up {
|
|
from {
|
|
height: var(--radix-collapsible-content-height);
|
|
}
|
|
|
|
to {
|
|
height: 0;
|
|
}
|
|
}
|
|
|
|
@keyframes fade-in {
|
|
from {
|
|
opacity: 0;
|
|
}
|
|
|
|
to {
|
|
opacity: 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
:root {
|
|
color-scheme: light;
|
|
}
|
|
|
|
/* Global default border color - ensures 'border' class always uses theme color */
|
|
*,
|
|
*::before,
|
|
*::after {
|
|
border-color: hsl(var(--border));
|
|
}
|
|
|
|
body {
|
|
min-height: 100vh;
|
|
font-family: var(--font-sans);
|
|
background:
|
|
radial-gradient(900px circle at 15% 0%, hsl(var(--primary) / 0.10), transparent 38%),
|
|
radial-gradient(1200px circle at 85% 10%, hsl(var(--accent) / 0.16), transparent 40%),
|
|
hsl(var(--background));
|
|
color: hsl(var(--foreground));
|
|
}
|
|
|
|
.dark {
|
|
color-scheme: dark;
|
|
}
|
|
|
|
.dark body {
|
|
background:
|
|
radial-gradient(1200px circle at 10% 0%, hsl(var(--primary) / 0.08), transparent 32%),
|
|
radial-gradient(900px circle at 85% 10%, hsl(var(--accent) / 0.12), transparent 36%),
|
|
hsl(var(--background));
|
|
color: hsl(var(--foreground));
|
|
}
|
|
|
|
.netcatty-shell {
|
|
position: relative;
|
|
background: linear-gradient(180deg, hsl(var(--background)) 0%, hsl(var(--background)) 60%, hsl(var(--background) / 0.9) 100%);
|
|
}
|
|
|
|
@keyframes shimmer {
|
|
0% {
|
|
transform: translateX(-100%);
|
|
}
|
|
|
|
100% {
|
|
transform: translateX(100%);
|
|
}
|
|
}
|
|
|
|
@keyframes progress-shimmer {
|
|
0% {
|
|
transform: translateX(-200%);
|
|
}
|
|
|
|
100% {
|
|
transform: translateX(200%);
|
|
}
|
|
}
|
|
|
|
.glass-panel {
|
|
background: hsl(var(--secondary) / 0.95);
|
|
border: 1px solid hsl(var(--border) / 0.8);
|
|
backdrop-filter: blur(12px);
|
|
box-shadow: 0 14px 40px hsl(var(--foreground) / 0.12);
|
|
}
|
|
|
|
.soft-card {
|
|
background: hsl(var(--card));
|
|
border: 1px solid hsl(var(--border) / 0.8);
|
|
box-shadow: 0 4px 12px hsl(var(--foreground) / 0.06);
|
|
}
|
|
|
|
.card-highlight {
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.card-highlight::before {
|
|
content: "";
|
|
position: absolute;
|
|
inset: -40%;
|
|
background: radial-gradient(500px circle at 20% 20%, hsl(var(--primary) / 0.18), transparent 55%);
|
|
opacity: 0;
|
|
transition: opacity 180ms ease;
|
|
}
|
|
|
|
.card-highlight:hover::before {
|
|
opacity: 1;
|
|
}
|
|
|
|
.elevate {
|
|
transition: transform 140ms ease, border-color 140ms ease;
|
|
}
|
|
|
|
.elevate:hover {
|
|
transform: translateY(-2px);
|
|
}
|
|
|
|
::selection {
|
|
background: hsl(var(--primary) / 0.22);
|
|
color: hsl(var(--foreground));
|
|
}
|
|
|
|
/* Port Forwarding Traffic Animation */
|
|
@keyframes traffic-flow {
|
|
0% {
|
|
stroke-dashoffset: 24;
|
|
}
|
|
|
|
100% {
|
|
stroke-dashoffset: 0;
|
|
}
|
|
}
|
|
|
|
@keyframes traffic-flow-reverse {
|
|
0% {
|
|
stroke-dashoffset: 0;
|
|
}
|
|
|
|
100% {
|
|
stroke-dashoffset: 24;
|
|
}
|
|
}
|
|
|
|
@keyframes pulse-glow {
|
|
|
|
0%,
|
|
100% {
|
|
opacity: 0.6;
|
|
}
|
|
|
|
50% {
|
|
opacity: 1;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.traffic-line {
|
|
stroke-dasharray: 8 4;
|
|
animation: traffic-flow 0.8s linear infinite;
|
|
}
|
|
|
|
.traffic-line-reverse {
|
|
stroke-dasharray: 8 4;
|
|
animation: traffic-flow-reverse 0.8s linear infinite;
|
|
}
|
|
|
|
.traffic-glow {
|
|
animation: pulse-glow 1.5s ease-in-out infinite;
|
|
}
|
|
|
|
/* Custom titlebar drag regions for Electron */
|
|
.app-drag {
|
|
-webkit-app-region: drag;
|
|
app-region: drag;
|
|
user-select: none;
|
|
-webkit-user-select: none;
|
|
}
|
|
|
|
.app-no-drag {
|
|
-webkit-app-region: no-drag;
|
|
app-region: no-drag;
|
|
user-select: auto;
|
|
-webkit-user-select: auto;
|
|
}
|
|
|
|
.app-no-drag * {
|
|
-webkit-app-region: no-drag;
|
|
app-region: no-drag;
|
|
user-select: auto;
|
|
-webkit-user-select: auto;
|
|
}
|
|
|
|
.no-scrollbar {
|
|
scrollbar-width: none;
|
|
-ms-overflow-style: none;
|
|
}
|
|
|
|
.no-scrollbar::-webkit-scrollbar {
|
|
display: none;
|
|
}
|
|
|
|
.workspace-pane {
|
|
outline: none;
|
|
overflow: visible;
|
|
}
|
|
|
|
.workspace-pane::after {
|
|
content: "";
|
|
position: absolute;
|
|
inset: 0;
|
|
border-radius: 0;
|
|
border: 2px solid hsl(var(--primary));
|
|
pointer-events: none;
|
|
opacity: 0;
|
|
transition: opacity 120ms ease, box-shadow 120ms ease;
|
|
z-index: 40;
|
|
}
|
|
|
|
.workspace-pane:focus-within::after {
|
|
opacity: 1;
|
|
}
|