[client] Migrate the desktop UI to Tailwind CSS 4 and fix frontend npm audit findings (#8277)

* [proxy] Fix npm audit findings in the web UI

npm audit reported 10 vulnerabilities (7 high), all in build tooling:
Vite/launch-editor, PostCSS, Babel, browserslist, js-yaml, nanoid,
brace-expansion and source-map-js. All are resolved by the lockfile
update; Vite is pinned exactly, so it is bumped from 7.3.2 to 7.3.7.

* [client] Migrate the desktop UI to Tailwind CSS 4 and fix npm audit findings

pnpm audit reported 36 vulnerabilities in the desktop UI. All but two
(braces and postcss-selector-parser, both pulled in by Tailwind 3 with
no patched version in range) are fixed by updating within the existing
ranges; react-router moves to 7.18.4, which also fixes an open redirect
and an XSS in its error handler.

The remaining two go away by moving to Tailwind CSS 4, which the proxy
web UI already uses:

- The config moves into an @theme block in globals.css; the official
  upgrade tool applied the v4 utility renames across the components.
- @tailwindcss/vite replaces the PostCSS plugin and autoprefixer.
- tw-animate-css replaces tailwindcss-animate with the same classes.
- The Prettier plugin reads the theme from the stylesheet.

A screenshot comparison of every screen against the Tailwind 3 build
found three regressions caused by v4's changed utility and variant
ordering, fixed here so the UI renders as before:

- Label combined inline-block and flex; v4 let inline-block win, which
  made every settings row taller. The dead inline-block is removed.
- The active settings tab lost its highlight in dark mode because
  dark: now outranks group-data-[state=active]:. The active color is
  set for dark mode explicitly.
- v4 rebuilt its default palette in OKLCH, shifting colors the theme
  does not override such as sky-400. The Tailwind 3 values for the
  neutral, zinc, sky, orange and amber families are pinned in @theme.

The only remaining visible difference is 2px less height per row in
the profiles list, because v4's preflight resets the browser's default
table-cell padding.
This commit is contained in:
Eduard Gert
2026-10-21 15:53:05 +02:00
committed by GitHub
parent 1e63b68c4d
commit 031a4bd70d
54 changed files with 1913 additions and 2009 deletions
+1
View File
@@ -8,5 +8,6 @@
"arrowParens": "always",
"endOfLine": "lf",
"plugins": ["prettier-plugin-tailwindcss"],
"tailwindStylesheet": "./src/globals.css",
"tailwindFunctions": ["cn", "clsx", "cva", "tw"]
}
+4 -4
View File
@@ -46,11 +46,11 @@
},
"devDependencies": {
"@eslint/js": "^10.0.1",
"@tailwindcss/vite": "^4.3.3",
"@types/node": "^25.6.0",
"@types/react": "^18.3.18",
"@types/react-dom": "^18.3.5",
"@vitejs/plugin-react": "^4.3.4",
"autoprefixer": "^10.4.20",
"eslint": "^9.39.4",
"eslint-plugin-jsx-a11y": "^6.10.2",
"eslint-plugin-react": "^7.37.5",
@@ -59,9 +59,9 @@
"globals": "^17.6.0",
"postcss": "^8.5.1",
"prettier": "^3.8.3",
"prettier-plugin-tailwindcss": "^0.8.0",
"tailwindcss": "^3.4.17",
"tailwindcss-animate": "^1.0.7",
"prettier-plugin-tailwindcss": "^0.8.1",
"tailwindcss": "^4.3.3",
"tw-animate-css": "^1.4.0",
"typescript": "^5.7.3",
"typescript-eslint": "^8.61.1",
"vite": "^6.0.7"
+1280 -1475
View File
File diff suppressed because it is too large Load Diff
-6
View File
@@ -1,6 +0,0 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};
+1 -1
View File
@@ -30,7 +30,7 @@ export const Badge = forwardRef<HTMLSpanElement, Props>(function Badge(
ref={ref}
className={cn(
"relative top-px inline-flex items-center gap-1 rounded-full px-1.5 py-[0.15rem]",
"shrink-0 text-[0.64rem] font-semibold leading-none",
"shrink-0 text-[0.64rem] leading-none font-semibold",
VARIANT_CLASSES[variant],
className,
)}
@@ -4,8 +4,8 @@ import { Check, Copy } from "lucide-react";
import { cn } from "@/lib/cn";
const VARIANT_HOVER = {
default: "group-hover/copy:[&_*]:text-nb-gray-300",
bright: "group-hover/copy:[&_*]:text-nb-gray-200",
default: "**:group-hover/copy:text-nb-gray-300",
bright: "**:group-hover/copy:text-nb-gray-200",
} as const;
type CopyToClipboardVariant = keyof typeof VARIANT_HOVER;
@@ -80,7 +80,7 @@ export const CopyToClipboard = ({
aria-label={resolvedLabel}
aria-live={"polite"}
className={cn(
"group/copy wails-no-draggable pointer-events-auto inline-flex cursor-default items-center gap-2 rounded-sm text-start outline-none",
"group/copy wails-no-draggable pointer-events-auto inline-flex cursor-default items-center gap-2 rounded-xs text-start outline-hidden",
"focus-visible:ring-2 focus-visible:ring-nb-gray-50/60 focus-visible:ring-offset-2 focus-visible:ring-offset-nb-gray-940",
className,
)}
@@ -88,8 +88,8 @@ export const CopyToClipboard = ({
<span
className={cn(
"relative min-w-0",
wrap ? "whitespace-pre-wrap break-all" : "truncate",
"[&_*]:transition-colors",
wrap ? "break-all whitespace-pre-wrap" : "truncate",
"**:transition-colors",
VARIANT_HOVER[variant],
)}
>
@@ -104,7 +104,7 @@ export const CopyToClipboard = ({
<span
aria-hidden={"true"}
className={cn(
"relative end-[1px] top-[2px] inline-flex shrink-0",
"relative inset-e-[1px] top-[2px] inline-flex shrink-0",
iconAlignment === "left" ? "order-first" : "order-last",
iconClassName,
)}
@@ -36,8 +36,8 @@ const DropdownMenuSubTrigger = React.forwardRef<
<DropdownMenuPrimitive.SubTrigger
ref={ref}
className={cn(
"relative flex cursor-default select-none items-center rounded-md py-1.5 pe-2 ps-3 text-sm outline-none",
"transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
"relative flex cursor-default items-center rounded-md py-1.5 ps-3 pe-2 text-sm outline-hidden select-none",
"transition-colors data-disabled:pointer-events-none data-disabled:opacity-50",
inset && "ps-8",
menuItemVariants({ variant }),
className,
@@ -57,8 +57,8 @@ const DropdownMenuSubContent = React.forwardRef<
<DropdownMenuPrimitive.SubContent
ref={ref}
className={cn(
"z-50 min-w-[8rem] overflow-hidden rounded-md border border-nb-gray-900 bg-nb-gray-930 p-1 text-nb-gray-200 shadow-lg",
"data-[state=open]:animate-in data-[state=closed]:animate-out",
"z-50 min-w-32 overflow-hidden rounded-md border border-nb-gray-900 bg-nb-gray-930 p-1 text-nb-gray-200 shadow-lg",
"data-[state=closed]:animate-out data-[state=open]:animate-in",
"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
"data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
"data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2",
@@ -79,8 +79,8 @@ const DropdownMenuContent = React.forwardRef<
ref={ref}
sideOffset={sideOffset}
className={cn(
"z-50 min-w-[8rem] overflow-hidden rounded-lg border border-nb-gray-900 bg-nb-gray-935 p-1 text-nb-gray-200 shadow-lg",
"data-[state=open]:animate-in data-[state=closed]:animate-out",
"z-50 min-w-32 overflow-hidden rounded-lg border border-nb-gray-900 bg-nb-gray-935 p-1 text-nb-gray-200 shadow-lg",
"data-[state=closed]:animate-out data-[state=open]:animate-in",
"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
"data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
"data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2",
@@ -106,8 +106,8 @@ const DropdownMenuItem = React.forwardRef<
<DropdownMenuPrimitive.Item
ref={ref}
className={cn(
"relative flex cursor-default select-none items-center rounded-md px-2 py-1.5 text-sm outline-none",
"transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
"relative flex cursor-default items-center rounded-md px-2 py-1.5 text-sm outline-hidden select-none",
"transition-colors data-disabled:pointer-events-none data-disabled:opacity-50",
inset && "ps-8",
menuItemVariants({ variant }),
className,
@@ -138,15 +138,15 @@ const DropdownMenuCheckboxItem = React.forwardRef<
<DropdownMenuPrimitive.CheckboxItem
ref={ref}
className={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pe-2 ps-8 text-sm outline-none",
"relative flex cursor-default items-center rounded-xs py-1.5 ps-8 pe-2 text-sm outline-hidden select-none",
"text-nb-gray-200 transition-colors hover:bg-nb-gray-900 hover:text-nb-gray-50 focus-visible:bg-nb-gray-900 focus-visible:text-nb-gray-50",
"data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
"data-disabled:pointer-events-none data-disabled:opacity-50",
className,
)}
checked={checked}
{...props}
>
<span className={"absolute start-2 flex h-3.5 w-3.5 items-center justify-center"}>
<span className={"absolute inset-s-2 flex h-3.5 w-3.5 items-center justify-center"}>
<DropdownMenuPrimitive.ItemIndicator>
<Check className={"h-4 w-4"} />
</DropdownMenuPrimitive.ItemIndicator>
@@ -163,10 +163,10 @@ const DropdownMenuRadioItem = React.forwardRef<
<DropdownMenuPrimitive.RadioItem
ref={ref}
className={cn(
"my-0.5 flex cursor-default select-none items-center gap-2 rounded-md px-2 py-2 outline-none",
"my-0.5 flex cursor-default items-center gap-2 rounded-md px-2 py-2 outline-hidden select-none",
"text-xs font-semibold text-nb-gray-200 transition-colors",
"data-[highlighted]:bg-nb-gray-850 data-[highlighted]:text-nb-gray-50",
"data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
"data-highlighted:bg-nb-gray-850 data-highlighted:text-nb-gray-50",
"data-disabled:pointer-events-none data-disabled:opacity-50",
className,
)}
{...props}
@@ -102,7 +102,7 @@ export function LanguagePicker() {
"inline-flex h-[40px] min-w-[240px] items-center gap-2 px-3",
"rounded-md border bg-white dark:bg-nb-gray-900",
"border-neutral-200 dark:border-nb-gray-700",
"cursor-default text-xs font-semibold text-nb-gray-100 outline-none",
"cursor-default text-xs font-semibold text-nb-gray-100 outline-hidden",
"hover:border-nb-gray-700 data-[state=open]:border-nb-gray-700 dark:hover:border-nb-gray-600 dark:data-[state=open]:border-nb-gray-600",
isFocusVisible &&
"focus-visible:ring-2 focus-visible:ring-nb-gray-50/60 focus-visible:ring-offset-2 focus-visible:ring-offset-nb-gray-940",
@@ -130,7 +130,7 @@ export function LanguagePicker() {
align={"start"}
sideOffset={6}
className={cn(
"w-[var(--radix-popover-trigger-width)]",
"w-(--radix-popover-trigger-width)",
"z-50 rounded-lg border border-nb-gray-850 bg-nb-gray-920 p-1 shadow-lg",
"data-[side=bottom]:origin-top data-[side=top]:origin-bottom",
"data-[state=open]:animate-in",
@@ -145,7 +145,7 @@ export function LanguagePicker() {
loop
className={cn(
"flex flex-col",
"[&_[cmdk-input-wrapper]]:flex [&_[cmdk-input-wrapper]]:items-center",
"**:[[cmdk-input-wrapper]]:flex **:[[cmdk-input-wrapper]]:items-center",
)}
>
<div className={"px-1 pb-1"}>
@@ -164,7 +164,7 @@ export function LanguagePicker() {
aria-label={t("settings.general.language.search")}
className={cn(
"w-full bg-transparent text-xs text-nb-gray-100 placeholder:text-nb-gray-600 dark:placeholder:text-nb-gray-300",
"border-none outline-none",
"border-none outline-hidden",
)}
/>
</div>
@@ -195,7 +195,7 @@ export function LanguagePicker() {
value={`${lang.displayName} ${lang.englishName} ${lang.code}`}
onSelect={() => void select(lang.code)}
className={cn(
"my-0.5 flex cursor-default items-center gap-2 rounded-md px-2 py-2 outline-none",
"my-0.5 flex cursor-default items-center gap-2 rounded-md px-2 py-2 outline-hidden",
"text-xs font-semibold text-nb-gray-200",
"data-[selected=true]:bg-nb-gray-850 data-[selected=true]:text-nb-gray-50",
)}
@@ -224,7 +224,7 @@ export function LanguagePicker() {
<ScrollArea.Scrollbar
orientation={"vertical"}
className={cn(
"flex touch-none select-none transition-colors",
"flex touch-none transition-colors select-none",
"w-1.5 bg-transparent py-1",
)}
>
@@ -26,7 +26,7 @@ export const ManagementServerSwitch = ({ value, onChange, fullWidth = false }: P
src={netbirdLogo}
alt={""}
aria-hidden={"true"}
className={"aspect-[31/23] h-[0.8rem] shrink-0"}
className={"aspect-31/23 h-[0.8rem] shrink-0"}
/>
{t("settings.general.management.cloud")}
</SwitchItem>
@@ -84,8 +84,8 @@ export const Tooltip = ({
onPointerLeave={interactive ? scheduleClose : undefined}
onPointerDownOutside={interactive ? undefined : (e) => e.preventDefault()}
className={cn(
"z-50 select-none text-xs text-nb-gray-100 shadow-sm dark:shadow-lg",
"data-[state=delayed-open]:animate-in data-[state=closed]:animate-out",
"z-50 text-xs text-nb-gray-100 shadow-xs select-none dark:shadow-lg",
"data-[state=closed]:animate-out data-[state=delayed-open]:animate-in",
"data-[state=closed]:fade-out-0 data-[state=delayed-open]:fade-in-0",
!interactive && "pointer-events-none",
contentClassName ??
@@ -49,7 +49,7 @@ const Trigger = forwardRef<HTMLButtonElement, TriggerProps>(function VerticalTab
<Tabs.Trigger
ref={ref}
className={cn(
"group flex w-full cursor-default items-center gap-3 rounded-md border border-transparent px-2 py-2.5 text-start outline-none dark:border-0",
"group flex w-full cursor-default items-center gap-3 rounded-md border border-transparent px-2 py-2.5 text-start outline-hidden dark:border-0",
"transition-colors duration-150",
"data-[state=active]:border-nb-gray-800 data-[state=active]:bg-white dark:data-[state=active]:bg-nb-gray-930",
"data-[state=inactive]:hover:bg-nb-gray-850 dark:data-[state=inactive]:hover:bg-nb-gray-935",
@@ -65,20 +65,20 @@ const Trigger = forwardRef<HTMLButtonElement, TriggerProps>(function VerticalTab
className={cn(
"ms-2 shrink-0 transition-colors duration-150",
"text-nb-gray-350 dark:text-nb-gray-400",
"group-data-[state=active]:text-nb-gray-100",
"group-data-[state=active]:text-nb-gray-100 dark:group-data-[state=active]:text-nb-gray-100",
)}
/>
<span
className={cn(
"min-w-0 truncate text-sm font-medium transition-colors duration-150",
"text-nb-gray-350 dark:text-nb-gray-400",
"group-data-[state=active]:font-semibold group-data-[state=active]:text-nb-gray-100 dark:group-data-[state=active]:font-medium",
"group-data-[state=active]:font-semibold group-data-[state=active]:text-nb-gray-100 dark:group-data-[state=active]:font-medium dark:group-data-[state=active]:text-nb-gray-100",
)}
>
{title}
</span>
{adornment && (
<div aria-hidden={"true"} className={"me-2 ms-auto shrink-0"}>
<div aria-hidden={"true"} className={"ms-auto me-2 shrink-0"}>
{adornment}
</div>
)}
@@ -94,7 +94,7 @@ const Content = forwardRef<HTMLDivElement, Tabs.TabsContentProps>(function Verti
<Tabs.Content
ref={ref}
tabIndex={-1}
className={cn("outline-none", className)}
className={cn("outline-hidden", className)}
{...props}
/>
);
@@ -16,9 +16,9 @@ interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement>, ButtonVar
const buttonVariants = cva(
[
"relative",
"cursor-default select-none whitespace-nowrap text-sm font-medium shadow-sm focus:z-10 focus:outline-none focus:ring-2",
"cursor-default text-sm font-medium whitespace-nowrap shadow-xs select-none focus:z-10 focus:ring-2 focus:outline-hidden",
"inline-flex items-center justify-center gap-2 transition-colors focus:ring-offset-1",
"disabled:cursor-not-allowed disabled:opacity-40 dark:ring-offset-neutral-950/50 disabled:dark:text-nb-gray-300",
"disabled:cursor-not-allowed disabled:opacity-40 dark:ring-offset-neutral-950/50 dark:disabled:text-nb-gray-300",
],
{
variants: {
@@ -28,7 +28,7 @@ const buttonVariants = cva(
"dark:border-gray-700/30 dark:bg-nb-gray dark:text-gray-400 dark:hover:bg-zinc-800/50 dark:hover:text-nb-gray-50 dark:focus:ring-zinc-800/50",
],
primary: [
"dark:text-gray-100 dark:ring-offset-neutral-950/50 dark:focus:ring-netbird-600/50 enabled:dark:bg-netbird enabled:dark:hover:bg-netbird-500/80 enabled:dark:hover:text-nb-gray-50 disabled:dark:bg-nb-gray-900",
"dark:text-gray-100 dark:ring-offset-neutral-950/50 dark:focus:ring-netbird-600/50 dark:enabled:bg-netbird dark:enabled:hover:bg-netbird-500/80 dark:enabled:hover:text-nb-gray-50 dark:disabled:bg-nb-gray-900",
"enabled:bg-netbird enabled:text-white enabled:hover:bg-netbird-500 enabled:focus:ring-netbird-400/50 disabled:bg-nb-gray-700",
],
secondary: [
@@ -63,11 +63,11 @@ const buttonVariants = cva(
],
tertiary: [
"border-neutral-200 bg-white text-neutral-900 hover:bg-neutral-100 hover:text-black focus:ring-neutral-200/50",
"dark:border-gray-700/40 dark:bg-white dark:text-gray-800 dark:hover:bg-neutral-200 dark:focus:ring-zinc-800/50 disabled:dark:bg-nb-gray-920 disabled:dark:text-nb-gray-300",
"dark:border-gray-700/40 dark:bg-white dark:text-gray-800 dark:hover:bg-neutral-200 dark:focus:ring-zinc-800/50 dark:disabled:bg-nb-gray-920 dark:disabled:text-nb-gray-300",
],
white: [
"border-white bg-white text-neutral-800 outline-none hover:bg-neutral-200 focus:ring-white/50 dark:text-gray-800 disabled:dark:bg-nb-gray-920 disabled:dark:text-nb-gray-300",
"disabled:dark:border-nb-gray-900 disabled:dark:bg-nb-gray-900 disabled:dark:text-nb-gray-300",
"border-white bg-white text-neutral-800 outline-hidden hover:bg-neutral-200 focus:ring-white/50 dark:text-gray-800 dark:disabled:bg-nb-gray-920 dark:disabled:text-nb-gray-300",
"dark:disabled:border-nb-gray-900 dark:disabled:bg-nb-gray-900 dark:disabled:text-nb-gray-300",
],
outline: [
"border-neutral-200 bg-white text-neutral-900 hover:bg-neutral-100 hover:text-black focus:ring-neutral-200/50",
@@ -75,10 +75,10 @@ const buttonVariants = cva(
],
"danger-outline": [
"bg-transparent text-red-600 enabled:hover:bg-red-50 enabled:focus:ring-red-200/50",
"dark:bg-transparent dark:text-red-500 enabled:dark:hover:border-red-800/50 enabled:hover:dark:bg-red-950/50 enabled:dark:focus:bg-red-950/40 enabled:dark:focus:ring-red-800/20",
"dark:bg-transparent dark:text-red-500 dark:enabled:hover:border-red-800/50 dark:enabled:hover:bg-red-950/50 dark:enabled:focus:bg-red-950/40 dark:enabled:focus:ring-red-800/20",
],
"danger-text": [
"rounded-sm border-transparent bg-transparent !px-0 !py-0 text-red-600 !shadow-none hover:text-red-700 focus:ring-red-500/30",
"rounded-xs border-transparent bg-transparent px-0! py-0! text-red-600 shadow-none! hover:text-red-700 focus:ring-red-500/30",
"dark:border-transparent dark:bg-transparent dark:text-red-500 dark:ring-offset-neutral-950/50 dark:hover:text-red-600",
],
"default-outline": [
@@ -92,12 +92,12 @@ const buttonVariants = cva(
],
danger: [
"bg-red-600 text-red-50 hover:bg-red-700 focus:bg-red-700 focus:ring-red-700/20",
"dark:bg-red-600 dark:text-red-100 dark:hover:border-red-800/50 hover:dark:bg-red-700 dark:focus:bg-red-700 dark:focus:ring-red-700/20",
"dark:bg-red-600 dark:text-red-100 dark:hover:border-red-800/50 dark:hover:bg-red-700 dark:focus:bg-red-700 dark:focus:ring-red-700/20",
],
},
size: {
xs: "px-3.5 py-2.5 text-xs",
xs2: "px-4 py-[1.1rem] text-[0.78rem] leading-[0]",
xs2: "px-4 py-[1.1rem] text-[0.78rem] leading-0",
sm: "px-4 py-[9px] text-sm",
md: "px-4 py-[9px]",
lg: "px-4 py-[9px] text-lg",
@@ -109,7 +109,7 @@ const buttonVariants = cva(
border: {
0: "border",
1: "border border-transparent",
2: "border border-b-0 border-t-0",
2: "border border-t-0 border-b-0",
},
},
},
@@ -21,7 +21,7 @@ export const IconButton = forwardRef<HTMLButtonElement, Props>(function IconButt
disabled={disabled}
tabIndex={disabled ? -1 : 0}
className={cn(
"flex h-10 w-10 cursor-default items-center justify-center rounded-lg outline-none",
"flex h-10 w-10 cursor-default items-center justify-center rounded-lg outline-hidden",
"text-nb-gray-400 hover:bg-nb-gray-900 hover:text-nb-gray-300",
isFocusVisible &&
"focus-visible:ring-2 focus-visible:ring-nb-gray-50/60 focus-visible:ring-offset-2 focus-visible:ring-offset-nb-gray-940",
@@ -18,13 +18,13 @@ export const ConfirmDialog = forwardRef<HTMLDivElement, ConfirmDialogProps>(func
aria-label={ariaLabel}
aria-labelledby={ariaLabelledBy}
className={
"wails-draggable static m-0 flex max-h-none w-full max-w-none select-none flex-col items-center border-0 bg-transparent p-0 text-inherit"
"wails-draggable static m-0 flex max-h-none w-full max-w-none flex-col items-center border-0 bg-transparent p-0 text-inherit select-none"
}
>
<div
ref={ref}
className={cn(
"flex flex-col items-center gap-5 px-8 pb-7 pt-6 text-center",
"flex flex-col items-center gap-5 px-8 pt-6 pb-7 text-center",
isMacOS() && "pt-10",
)}
>
@@ -65,8 +65,8 @@ export const Content = forwardRef<ElementRef<typeof DialogPrimitive.Content>, Co
<DialogPrimitive.Content
ref={ref}
className={cn(
"relative z-[52] mx-auto w-full outline-none ring-0",
"focus:outline-none focus:ring-0 focus-visible:outline-none focus-visible:ring-0",
"relative z-52 mx-auto w-full ring-0 outline-hidden",
"focus:ring-0 focus:outline-hidden focus-visible:ring-0 focus-visible:outline-hidden",
"rounded-lg border border-nb-gray-800 bg-nb-gray-940 py-7 shadow-2xl dark:border-nb-gray-900 dark:bg-nb-gray",
"data-[state=open]:animate-in data-[state=open]:fade-in-0",
"data-[state=open]:zoom-in-95 data-[state=open]:slide-in-from-left-1",
@@ -95,9 +95,9 @@ export const Content = forwardRef<ElementRef<typeof DialogPrimitive.Content>, Co
{showClose && (
<DialogPrimitive.Close
className={cn(
"absolute end-3 top-3 z-10 rounded-md p-3 transition-colors",
"absolute inset-e-3 top-3 z-10 rounded-md p-3 transition-colors",
"text-nb-gray-300 hover:text-nb-gray-100",
"focus:outline-none disabled:pointer-events-none",
"focus:outline-hidden disabled:pointer-events-none",
)}
aria-label={t("common.close")}
>
@@ -119,7 +119,7 @@ export const Title = forwardRef<
<DialogPrimitive.Title
ref={ref}
className={cn(
"text-md font-semibold leading-none tracking-tight text-nb-gray-50",
"text-md leading-none font-semibold tracking-tight text-nb-gray-50",
className,
)}
{...props}
@@ -149,7 +149,7 @@ export const Footer = ({ className, separator = true, ...props }: FooterProps) =
<div
className={cn(
"flex flex-col-reverse gap-3 sm:flex-row sm:justify-end",
"[&>*]:w-full sm:[&>*]:w-auto",
"*:w-full sm:*:w-auto",
"px-8 pt-6",
className,
)}
@@ -20,7 +20,7 @@ export const DialogDescription = ({
className,
align = "center",
}: DialogDescriptionProps) => (
<p className={cn("w-full select-none text-sm text-nb-gray-300", alignClass[align], className)}>
<p className={cn("w-full text-sm text-nb-gray-300 select-none", alignClass[align], className)}>
{children}
</p>
);
@@ -25,7 +25,7 @@ export const DialogHeading = ({
<h2
id={id}
className={cn(
"w-full select-none text-base font-semibold text-nb-gray-50",
"w-full text-base font-semibold text-nb-gray-50 select-none",
alignClass[align],
className,
)}
@@ -41,7 +41,7 @@ export const DaemonOutdatedOverlay = () => {
return (
<div
className={
"wails-draggable fixed inset-0 z-[100] flex cursor-default select-none items-center justify-center bg-nb-gray-950 backdrop-blur-sm"
"wails-draggable fixed inset-0 z-100 flex cursor-default items-center justify-center bg-nb-gray-950 backdrop-blur-xs select-none"
}
>
<div className={"flex max-w-lg flex-col items-center gap-5 px-8 text-center"}>
@@ -19,7 +19,7 @@ export const DaemonUnavailableOverlay = () => {
return (
<div
className={
"wails-draggable fixed inset-0 z-[100] flex cursor-default select-none items-center justify-center bg-nb-gray-950 backdrop-blur-sm"
"wails-draggable fixed inset-0 z-100 flex cursor-default items-center justify-center bg-nb-gray-950 backdrop-blur-xs select-none"
}
>
<div className={"flex max-w-lg flex-col items-center gap-5 px-8 text-center"}>
@@ -16,7 +16,7 @@ export const NoResults = ({ icon = FunnelXIcon, title, description }: Props) =>
icon={icon}
title={title ?? t("common.noResults.title")}
description={description ?? t("common.noResults.description")}
className={"pointer-events-none relative -top-[3.8rem]"}
className={"pointer-events-none relative top-[-3.8rem]"}
/>
);
};
@@ -5,7 +5,7 @@ import { EmptyState } from "./EmptyState";
export const NotConnectedState = () => {
const { t } = useTranslation();
return (
<div className={"relative top-[3rem] w-full"}>
<div className={"relative top-12 w-full"}>
<EmptyState
icon={GlobeOffIcon}
title={t("notConnected.title")}
@@ -82,17 +82,17 @@ function buildInputClassName(
): string {
return cn(
inputVariants({ variant: opts.variant }),
"flex h-[40px] w-full select-text rounded-md bg-white px-3 py-2 text-sm",
"flex h-[40px] w-full rounded-md bg-white px-3 py-2 text-sm select-text",
"file:border-0 file:bg-transparent file:text-sm file:font-medium",
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2",
"focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-hidden",
"disabled:cursor-not-allowed disabled:opacity-40",
opts.hasCustomPrefix && "!rounded-s-none !border-s-0",
opts.hasSuffix && "!pe-9",
opts.hasIcon && "!ps-10",
opts.hasCustomPrefix && "rounded-s-none! border-s-0!",
opts.hasSuffix && "pe-9!",
opts.hasIcon && "ps-10!",
"border",
opts.readOnly && "!border-nb-gray-800 !bg-nb-gray-910 text-nb-gray-350",
opts.readOnly && "border-nb-gray-800! bg-nb-gray-910! text-nb-gray-350",
opts.showStepper &&
"!rounded-e-none [-moz-appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none",
"rounded-e-none! [-moz-appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none",
opts.className,
);
}
@@ -108,7 +108,7 @@ function InputAffix({
className={cn(
inputVariants({ prefixSuffixVariant: error ? "error" : "default" }),
"flex h-[40px] w-auto rounded-s-md bg-white px-3 py-2 text-sm",
"items-center whitespace-nowrap border",
"items-center border whitespace-nowrap",
disabled && "opacity-40",
className,
)}
@@ -122,7 +122,7 @@ function InputIconSlot({ icon, disabled }: Readonly<{ icon: ReactNode; disabled?
return (
<div
className={cn(
"absolute start-0 top-0 flex h-full items-center ps-3 text-xs leading-[0] dark:text-nb-gray-300",
"absolute inset-s-0 top-0 flex h-full items-center ps-3 text-xs leading-0 dark:text-nb-gray-300",
disabled && "opacity-40",
)}
>
@@ -138,7 +138,7 @@ function InputSuffixSlot({
return (
<div
className={cn(
"pointer-events-none absolute end-0 top-0 flex h-full select-none items-center pe-3 text-xs leading-[0] dark:text-nb-gray-300",
"pointer-events-none absolute inset-e-0 top-0 flex h-full items-center pe-3 text-xs leading-0 select-none dark:text-nb-gray-300",
disabled && "opacity-30",
)}
>
@@ -349,7 +349,7 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(function Input(
{icon && <InputIconSlot icon={icon} disabled={props.disabled} />}
<div className={"relative flex min-w-0 flex-grow"}>
<div className={"relative flex min-w-0 grow"}>
<input
id={inputId}
type={inputType}
@@ -34,7 +34,7 @@ export const SearchInput = forwardRef<HTMLInputElement, Props>(function SearchIn
{...props}
className={cn(
"w-full bg-transparent text-sm text-nb-gray-200 placeholder:text-nb-gray-600 dark:placeholder:text-nb-gray-400",
"border-none outline-none",
"border-none outline-hidden",
disabled && "cursor-not-allowed",
className,
)}
@@ -45,7 +45,7 @@ export const SearchInput = forwardRef<HTMLInputElement, Props>(function SearchIn
className={cn(
"shrink-0 select-none",
"inline-flex items-center justify-center",
"h-5 min-w-[20px] rounded px-1.5",
"h-5 min-w-[20px] rounded-sm px-1.5",
"border border-nb-gray-850 bg-nb-gray-920",
"text-[10px] font-medium text-nb-gray-400",
"wails-no-draggable",
@@ -49,7 +49,7 @@ export function Select<T extends string>({
"inline-flex h-[40px] min-w-[160px] items-center gap-2 px-3",
"rounded-md border bg-white dark:bg-nb-gray-900",
"border-neutral-200 dark:border-nb-gray-700",
"cursor-default text-xs font-semibold text-nb-gray-100 outline-none",
"cursor-default text-xs font-semibold text-nb-gray-100 outline-hidden",
"hover:border-nb-gray-700 data-[state=open]:border-nb-gray-700 dark:hover:border-nb-gray-600 dark:data-[state=open]:border-nb-gray-600",
isFocusVisible &&
"focus-visible:ring-2 focus-visible:ring-nb-gray-50/60 focus-visible:ring-offset-2 focus-visible:ring-offset-nb-gray-940",
@@ -76,7 +76,7 @@ export function Select<T extends string>({
align={"start"}
sideOffset={6}
className={
"w-[var(--radix-dropdown-menu-trigger-width)] border-nb-gray-850 bg-nb-gray-920"
"w-(--radix-dropdown-menu-trigger-width) border-nb-gray-850 bg-nb-gray-920"
}
>
<DropdownMenuRadioGroup value={value} onValueChange={(v) => onChange(v as T)}>
@@ -36,7 +36,7 @@ export default function FancyToggleSwitch({
if (loading) {
const shimmer =
"text-transparent select-none rounded bg-nb-gray-920 box-decoration-clone animate-pulse";
"text-transparent select-none rounded-sm bg-nb-gray-920 box-decoration-clone animate-pulse";
return (
<div
role={"status"}
@@ -72,7 +72,7 @@ export default function FancyToggleSwitch({
<div
{...(disabled ? { inert: "" } : {})}
className={cn(
"relative z-[1] cursor-default transition-all duration-300",
"relative z-1 cursor-default transition-all duration-300",
"inline-block w-full text-start",
disabled && "pointer-events-none opacity-30",
className,
@@ -19,7 +19,7 @@ export const SwitchItem = ({ value, children, className }: Props) => {
value={value}
className={cn(
"relative inline-flex items-center justify-center gap-1 rounded-md px-3.5 py-2 text-xs font-semibold",
"cursor-default outline-none",
"cursor-default outline-hidden",
"focus-visible:ring-2 focus-visible:ring-nb-gray-50/60 focus-visible:ring-offset-2 focus-visible:ring-offset-nb-gray-940",
active
? "text-nb-gray-100"
@@ -31,7 +31,7 @@ export const SwitchItem = ({ value, children, className }: Props) => {
<motion.span
layoutId={layoutId}
className={
"absolute inset-0 rounded-md bg-white shadow-sm dark:bg-nb-gray-700 dark:shadow-none"
"absolute inset-0 rounded-md bg-white shadow-xs dark:bg-nb-gray-700 dark:shadow-none"
}
transition={{ type: "spring", stiffness: 500, damping: 35 }}
/>
@@ -36,9 +36,9 @@ const switchVariants = cva("", {
},
"thumb-size": {
default:
"h-5 w-5 data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0 rtl:data-[state=checked]:-translate-x-5",
small: "h-[14px] w-[14px] data-[state=checked]:translate-x-[17px] data-[state=unchecked]:translate-x-0 rtl:data-[state=checked]:-translate-x-[17px]",
large: "h-[30px] w-[30px] data-[state=checked]:translate-x-[31px] data-[state=unchecked]:translate-x-[1px] rtl:data-[state=checked]:-translate-x-[31px] rtl:data-[state=unchecked]:-translate-x-[1px]",
"h-5 w-5 data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0 data-[state=checked]:rtl:-translate-x-5",
small: "h-[14px] w-[14px] data-[state=checked]:translate-x-[17px] data-[state=unchecked]:translate-x-0 data-[state=checked]:rtl:translate-x-[-17px]",
large: "h-[30px] w-[30px] data-[state=checked]:translate-x-[31px] data-[state=unchecked]:translate-x-px data-[state=checked]:rtl:translate-x-[-31px] data-[state=unchecked]:rtl:-translate-x-px",
},
},
});
@@ -52,7 +52,7 @@ const ToggleSwitch = React.forwardRef<
disabled={disabled}
tabIndex={disabled ? -1 : 0}
className={cn(
"wails-no-draggable peer inline-flex shrink-0 cursor-default items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-nb-gray-50/60 focus-visible:ring-offset-2 focus-visible:ring-offset-nb-gray-940 disabled:cursor-not-allowed disabled:opacity-50",
"wails-no-draggable peer inline-flex shrink-0 cursor-default items-center rounded-full border-2 border-transparent transition-colors focus-visible:ring-2 focus-visible:ring-nb-gray-50/60 focus-visible:ring-offset-2 focus-visible:ring-offset-nb-gray-940 focus-visible:outline-hidden disabled:cursor-not-allowed disabled:opacity-50",
className,
switchVariants({ size, variant }),
)}
@@ -4,7 +4,7 @@ import { type ComponentPropsWithoutRef, forwardRef, type Ref } from "react";
import { cn } from "@/lib/cn";
const labelVariants = cva(
"mb-1.5 inline-block flex items-center gap-2 text-sm font-medium leading-none tracking-wider peer-disabled:cursor-not-allowed peer-disabled:opacity-70 dark:text-nb-gray-100",
"mb-1.5 flex items-center gap-2 text-sm leading-none font-medium tracking-wider peer-disabled:cursor-not-allowed peer-disabled:opacity-70 dark:text-nb-gray-100",
);
type LabelProps = ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
@@ -20,7 +20,7 @@ export const Label = forwardRef<HTMLElement, LabelProps>(function Label(
const classes = cn(
labelVariants(),
className,
"select-none transition-all duration-300",
"transition-all duration-300 select-none",
disabled && "pointer-events-none opacity-30",
);
+272 -14
View File
@@ -1,22 +1,280 @@
@font-face {
font-family: "Inter Variable";
font-style: normal;
font-weight: 100 900;
src: url("./assets/fonts/inter-variable.ttf") format("truetype");
@import "tailwindcss";
@import "tw-animate-css";
@custom-variant dark (&:is(.dark *));
@theme {
--font-*: initial;
--font-sans: "Inter Variable", ui-sans-serif, system-ui, sans-serif;
--font-mono: "JetBrains Mono Variable", ui-monospace, monospace;
--color-nb-gray-50: rgb(var(--nb-gray-50));
--color-nb-gray-100: rgb(var(--nb-gray-100));
--color-nb-gray-200: rgb(var(--nb-gray-200));
--color-nb-gray-250: rgb(var(--nb-gray-250));
--color-nb-gray-300: rgb(var(--nb-gray-300));
--color-nb-gray-350: rgb(var(--nb-gray-350));
--color-nb-gray-400: rgb(var(--nb-gray-400));
--color-nb-gray-500: rgb(var(--nb-gray-500));
--color-nb-gray-600: rgb(var(--nb-gray-600));
--color-nb-gray-700: rgb(var(--nb-gray-700));
--color-nb-gray-800: rgb(var(--nb-gray-800));
--color-nb-gray-850: rgb(var(--nb-gray-850));
--color-nb-gray-900: rgb(var(--nb-gray-900));
--color-nb-gray-910: rgb(var(--nb-gray-910));
--color-nb-gray-920: rgb(var(--nb-gray-920));
--color-nb-gray-925: rgb(var(--nb-gray-925));
--color-nb-gray-930: rgb(var(--nb-gray-930));
--color-nb-gray-935: rgb(var(--nb-gray-935));
--color-nb-gray-940: rgb(var(--nb-gray-940));
--color-nb-gray-950: rgb(var(--nb-gray-950));
--color-nb-gray-960: rgb(var(--nb-gray-960));
--color-nb-gray: rgb(var(--nb-gray-DEFAULT));
--color-gray-50: #f9fafb;
--color-gray-100: #f3f4f6;
--color-gray-200: #e5e7eb;
--color-gray-300: #d1d5db;
--color-gray-400: #9ca3af;
--color-gray-500: #6b7280;
--color-gray-600: #4b5563;
--color-gray-700: #374151;
--color-gray-800: #1f2937;
--color-gray-900: #111827;
--color-red-50: #fdf2f2;
--color-red-100: #fde8e8;
--color-red-200: #fbd5d5;
--color-red-300: #f8b4b4;
--color-red-400: #f98080;
--color-red-500: #f05252;
--color-red-600: #e02424;
--color-red-700: #c81e1e;
--color-red-800: #9b1c1c;
--color-red-900: #771d1d;
--color-yellow-50: #fdfdea;
--color-yellow-100: #fdf6b2;
--color-yellow-200: #fce96a;
--color-yellow-300: #faca15;
--color-yellow-400: #e3a008;
--color-yellow-500: #c27803;
--color-yellow-600: #9f580a;
--color-yellow-700: #8e4b10;
--color-yellow-800: #723b13;
--color-yellow-900: #633112;
--color-green-50: #f3faf7;
--color-green-100: #def7ec;
--color-green-200: #bcf0da;
--color-green-300: #84e1bc;
--color-green-400: #31c48d;
--color-green-500: #0e9f6e;
--color-green-600: #057a55;
--color-green-700: #046c4e;
--color-green-800: #03543f;
--color-green-900: #014737;
--color-blue-50: #ebf5ff;
--color-blue-100: #e1effe;
--color-blue-200: #c3ddfd;
--color-blue-300: #a4cafe;
--color-blue-400: #76a9fa;
--color-blue-500: #3f83f8;
--color-blue-600: #1c64f2;
--color-blue-700: #1a56db;
--color-blue-800: #1e429f;
--color-blue-900: #233876;
--color-indigo-50: #f0f5ff;
--color-indigo-100: #e5edff;
--color-indigo-200: #cddbfe;
--color-indigo-300: #b4c6fc;
--color-indigo-400: #8da2fb;
--color-indigo-500: #6875f5;
--color-indigo-600: #5850ec;
--color-indigo-700: #5145cd;
--color-indigo-800: #42389d;
--color-indigo-900: #362f78;
--color-purple-50: #f6f5ff;
--color-purple-100: #edebfe;
--color-purple-200: #dcd7fe;
--color-purple-300: #cabffd;
--color-purple-400: #ac94fa;
--color-purple-500: #9061f9;
--color-purple-600: #7e3af2;
--color-purple-700: #6c2bd9;
--color-purple-800: #5521b5;
--color-purple-900: #4a1d96;
--color-pink-50: #fdf2f8;
--color-pink-100: #fce8f3;
--color-pink-200: #fad1e8;
--color-pink-300: #f8b4d9;
--color-pink-400: #f17eb8;
--color-pink-500: #e74694;
--color-pink-600: #d61f69;
--color-pink-700: #bf125d;
--color-pink-800: #99154b;
--color-pink-900: #751a3d;
--color-netbird-50: #fff6ed;
--color-netbird-100: #feecd6;
--color-netbird-150: #ffdfb8;
--color-netbird-200: #ffd4a6;
--color-netbird-300: #fab677;
--color-netbird-400: #f68330;
--color-netbird-500: #f46d1b;
--color-netbird-600: #e55311;
--color-netbird-700: #be3e10;
--color-netbird-800: #973215;
--color-netbird-900: #7a2b14;
--color-netbird-950: #421308;
--color-netbird: #f68330;
/* Tailwind 4 rebuilt its default palette in OKLCH, which shifts hues such as
sky-400 and orange-400 noticeably. These are the Tailwind 3 values for the
families the UI still uses from the default palette, so colors stay as they
were before the upgrade. */
--color-neutral-50: #fafafa;
--color-neutral-100: #f5f5f5;
--color-neutral-200: #e5e5e5;
--color-neutral-300: #d4d4d4;
--color-neutral-400: #a3a3a3;
--color-neutral-500: #737373;
--color-neutral-600: #525252;
--color-neutral-700: #404040;
--color-neutral-800: #262626;
--color-neutral-900: #171717;
--color-neutral-950: #0a0a0a;
--color-zinc-50: #fafafa;
--color-zinc-100: #f4f4f5;
--color-zinc-200: #e4e4e7;
--color-zinc-300: #d4d4d8;
--color-zinc-400: #a1a1aa;
--color-zinc-500: #71717a;
--color-zinc-600: #52525b;
--color-zinc-700: #3f3f46;
--color-zinc-800: #27272a;
--color-zinc-900: #18181b;
--color-zinc-950: #09090b;
--color-sky-50: #f0f9ff;
--color-sky-100: #e0f2fe;
--color-sky-200: #bae6fd;
--color-sky-300: #7dd3fc;
--color-sky-400: #38bdf8;
--color-sky-500: #0ea5e9;
--color-sky-600: #0284c7;
--color-sky-700: #0369a1;
--color-sky-800: #075985;
--color-sky-900: #0c4a6e;
--color-sky-950: #082f49;
--color-orange-50: #fff7ed;
--color-orange-100: #ffedd5;
--color-orange-200: #fed7aa;
--color-orange-300: #fdba74;
--color-orange-400: #fb923c;
--color-orange-500: #f97316;
--color-orange-600: #ea580c;
--color-orange-700: #c2410c;
--color-orange-800: #9a3412;
--color-orange-900: #7c2d12;
--color-orange-950: #431407;
--color-amber-50: #fffbeb;
--color-amber-100: #fef3c7;
--color-amber-200: #fde68a;
--color-amber-300: #fcd34d;
--color-amber-400: #fbbf24;
--color-amber-500: #f59e0b;
--color-amber-600: #d97706;
--color-amber-700: #b45309;
--color-amber-800: #92400e;
--color-amber-900: #78350f;
--color-amber-950: #451a03;
--background-image-conic-netbird: conic-gradient(
from 0deg,
#e55311 0%,
#f68330 10%,
#e55311 20%,
#e55311 100%
);
--animate-ping-slow: ping-slow 2s cubic-bezier(0, 0, 0.2, 1) infinite;
--animate-pulse-slow: pulse-reverse 2s cubic-bezier(0.5, 0, 0.6, 1) infinite;
--animate-pulse-slower: pulse-reverse 3s cubic-bezier(0.5, 0, 0.6, 1) infinite;
--animate-spin-slow: spin-slow 2s linear infinite;
@keyframes pulse-reverse {
0%,
100% {
opacity: 1;
}
50% {
opacity: 0.4;
}
}
@keyframes spin-slow {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
@keyframes ping-slow {
0% {
transform: scale(1);
opacity: 1;
}
75%,
100% {
transform: scale(2);
opacity: 0;
}
}
}
@font-face {
font-family: "JetBrains Mono Variable";
font-style: normal;
font-weight: 100 800;
src: url("./assets/fonts/jetbrains-mono-variable.ttf") format("truetype");
/*
The default border color has changed to `currentcolor` in Tailwind CSS v4,
so we've added these compatibility styles to make sure everything still
looks the same as it did with Tailwind CSS v3.
If we ever want to remove these styles, we need to add an explicit border
color utility to any element that depends on these defaults.
*/
@layer base {
*,
::after,
::before,
::backdrop,
::file-selector-button {
border-color: var(--color-gray-200, currentcolor);
}
}
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
@font-face {
font-family: "Inter Variable";
font-style: normal;
font-weight: 100 900;
src: url("./assets/fonts/inter-variable.ttf") format("truetype");
}
/* nb-gray channels (space-separated RGB) consumed by tailwind.config.ts.
@font-face {
font-family: "JetBrains Mono Variable";
font-style: normal;
font-weight: 100 800;
src: url("./assets/fonts/jetbrains-mono-variable.ttf") format("truetype");
}
}
/* nb-gray channels (space-separated RGB) consumed by the @theme block above.
:root is the inverted light ramp — high stops are surfaces (near-white),
low stops are text (near-black); .dark restores the original dark ramp.
Surfaces stay darker than content cards to preserve their separation. */
@@ -54,7 +54,7 @@ export default function ErrorDialog() {
<DialogDescription className={"text-balance"}>
{/* select-text: the message often names a path, a flag or an
address the user needs to act on. */}
<span className={"select-text whitespace-pre-wrap break-words"}>
<span className={"wrap-break-word whitespace-pre-wrap select-text"}>
{message}
</span>
</DialogDescription>
@@ -71,7 +71,7 @@ export default function ErrorDialog() {
aria-label={t("common.copy")}
>
<code
className={"select-text break-all font-mono text-xs text-nb-gray-200"}
className={"font-mono text-xs break-all text-nb-gray-200 select-text"}
>
{command}
</code>
@@ -255,7 +255,7 @@ export const MainConnectionStatusSwitch = () => {
role={"status"}
aria-live={"polite"}
className={
"wails-no-draggable mb-1 select-none text-sm font-medium tracking-wide text-nb-gray-200 transition-colors duration-300"
"wails-no-draggable mb-1 text-sm font-medium tracking-wide text-nb-gray-200 transition-colors duration-300 select-none"
}
>
{t(STATUS_KEY[connState])}
@@ -267,7 +267,7 @@ export const MainConnectionStatusSwitch = () => {
tabIndex={show && fqdn ? 0 : -1}
className={cn(
"mt-1 max-h-[1em] min-h-[1em] max-w-full transition-opacity duration-300",
"relative start-[0.55rem]",
"relative inset-s-[0.55rem]",
show && fqdn ? "opacity-100" : "pointer-events-none opacity-0",
)}
>
@@ -299,7 +299,7 @@ const LocalIpLine = ({ ip, ipv6, show }: { ip: string; ipv6: string; show: boole
tabIndex={show && ip ? 0 : -1}
className={cn(
"mt-1 max-h-[1em] min-h-[1em] transition-opacity duration-300",
"relative start-[0.55rem]",
"relative inset-s-[0.55rem]",
show && ip ? "opacity-100" : "pointer-events-none opacity-0",
)}
>
@@ -327,7 +327,7 @@ const LocalIpLine = ({ ip, ipv6, show }: { ip: string; ipv6: string; show: boole
aria-haspopup={"dialog"}
aria-expanded={open}
className={cn(
"group relative inline-flex cursor-default items-center rounded-sm outline-none",
"group relative inline-flex cursor-default items-center rounded-xs outline-hidden",
isFocusVisible &&
"focus-visible:ring-2 focus-visible:ring-nb-gray-50/60 focus-visible:ring-offset-2 focus-visible:ring-offset-nb-gray-940",
"transition-colors",
@@ -346,7 +346,7 @@ const LocalIpLine = ({ ip, ipv6, show }: { ip: string; ipv6: string; show: boole
size={14}
aria-hidden={"true"}
className={cn(
"absolute -end-5 top-1/2 -translate-y-1/2",
"absolute -inset-e-5 top-1/2 -translate-y-1/2",
"shrink-0 text-nb-gray-300 transition-colors",
"group-hover:text-nb-gray-200",
"group-data-[state=open]:text-nb-gray-200",
@@ -361,9 +361,9 @@ const LocalIpLine = ({ ip, ipv6, show }: { ip: string; ipv6: string; show: boole
sideOffset={6}
onOpenAutoFocus={(e) => e.preventDefault()}
className={cn(
"z-50 min-w-64 max-w-[280px] overflow-hidden",
"z-50 max-w-[280px] min-w-64 overflow-hidden",
"rounded-lg border border-nb-gray-900 bg-nb-gray-935",
"p-1 text-nb-gray-200 shadow-lg outline-none",
"p-1 text-nb-gray-200 shadow-lg outline-hidden",
"flex flex-col",
)}
>
@@ -401,9 +401,9 @@ const IpRow = ({ value }: { value: string }) => {
"group/iprow relative flex items-center justify-between gap-3",
"rounded-md px-2 py-1.5 text-start",
"text-nb-gray-200 hover:bg-nb-gray-900 hover:text-nb-gray-50",
"cursor-default outline-none transition-colors",
"cursor-default outline-hidden transition-colors",
isFocusVisible &&
"focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-nb-gray-50/60",
"focus-visible:ring-2 focus-visible:ring-nb-gray-50/60 focus-visible:ring-inset",
)}
>
<span className={"min-w-0 truncate font-mono text-[0.75rem]"}>{value}</span>
@@ -81,8 +81,8 @@ export const MainExitNodeSwitcher = () => {
}}
style={{ width: "var(--radix-popover-trigger-width)" }}
className={cn(
"wails-no-draggable z-50 select-none overflow-hidden rounded-lg border border-nb-gray-900 bg-nb-gray-935 p-1 text-nb-gray-200 shadow-lg",
"data-[state=open]:animate-in data-[state=closed]:animate-out",
"wails-no-draggable z-50 overflow-hidden rounded-lg border border-nb-gray-900 bg-nb-gray-935 p-1 text-nb-gray-200 shadow-lg select-none",
"data-[state=closed]:animate-out data-[state=open]:animate-in",
"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
"data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
"data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2",
@@ -93,12 +93,16 @@ export const MainExitNodeSwitcher = () => {
loop
shouldFilter={false}
onKeyDown={(e) => e.stopPropagation()}
className={"outline-none focus:outline-none focus-visible:outline-none"}
className={
"outline-hidden focus:outline-hidden focus-visible:outline-hidden"
}
>
<Command.List
ref={listRef}
aria-label={t("exitNodes.dropdown.trigger")}
className={"outline-none focus:outline-none focus-visible:outline-none"}
className={
"outline-hidden focus:outline-hidden focus-visible:outline-hidden"
}
>
<NoneRow isActive={!active} onSelect={() => handleSelect(NONE_VALUE)} />
{hasAny && <div className={"-mx-1 my-1 h-px bg-nb-gray-910"} />}
@@ -122,7 +126,7 @@ export const MainExitNodeSwitcher = () => {
<ScrollArea.Scrollbar
orientation={"vertical"}
className={cn(
"flex touch-none select-none transition-colors",
"flex touch-none transition-colors select-none",
"w-1.5 bg-transparent",
)}
>
@@ -161,7 +165,7 @@ const ExitNodeTriggerCard = forwardRef<HTMLButtonElement, TriggerProps>(
tabIndex={0}
disabled={disabled}
className={cn(
"flex w-full items-center gap-3 rounded-xl p-2.5 pe-5 text-start outline-none",
"flex w-full items-center gap-3 rounded-xl p-2.5 pe-5 text-start outline-hidden",
"border border-nb-gray-800 bg-nb-gray-940 dark:border-nb-gray-920",
"transition-colors duration-150",
"wails-no-draggable",
@@ -219,7 +223,7 @@ const NoneRow = ({ isActive, onSelect }: NoneRowProps) => {
onSelect={onSelect}
className={cn(
"flex items-center gap-2 px-2 py-2 pe-3",
"cursor-default rounded-md text-sm outline-none",
"cursor-default rounded-md text-sm outline-hidden",
"data-[selected=true]:bg-nb-gray-900",
)}
>
@@ -244,7 +248,7 @@ const ExitNodeRow = ({ id, label, isActive, onSelect }: ExitNodeRowProps) => (
onSelect={onSelect}
className={cn(
"flex items-center gap-2 px-2 py-2 pe-3",
"cursor-default rounded-md text-sm outline-none",
"cursor-default rounded-md text-sm outline-hidden",
"data-[selected=true]:bg-nb-gray-900",
)}
>
@@ -89,7 +89,7 @@ export const MainHeader = () => {
align={pinSettingsRight && dir === "rtl" ? "start" : "end"}
sideOffset={8}
className={
"min-w-52 select-none data-[state=closed]:!animate-none data-[state=closed]:!duration-0"
"min-w-52 select-none data-[state=closed]:animate-none! data-[state=closed]:duration-0!"
}
>
{updateAvailable && (
@@ -141,7 +141,7 @@ export const MainHeader = () => {
<span
aria-hidden={"true"}
className={
"pointer-events-none absolute end-1.5 top-1.5 flex h-2.5 w-2.5 items-center justify-center"
"pointer-events-none absolute inset-e-1.5 top-1.5 flex h-2.5 w-2.5 items-center justify-center"
}
>
<span
@@ -177,7 +177,7 @@ export const MainHeader = () => {
<div
className={cn(
"absolute top-1/2 -translate-y-1/2",
pinSettingsRight ? "right-[1.3rem]" : "end-[1.3rem]",
pinSettingsRight ? "right-[1.3rem]" : "inset-e-[1.3rem]",
)}
>
{settingsSlot}
@@ -110,10 +110,10 @@ export const Navigation = () => {
className={cn(
"group relative flex flex-1 items-center justify-center",
"gap-2.5 px-5 py-3.5",
"outline-none transition-all",
"outline-hidden transition-all",
isFirst && "rounded-ss-xl",
isLast && "rounded-se-xl",
"focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-nb-gray-50/60",
"focus-visible:ring-2 focus-visible:ring-nb-gray-50/60 focus-visible:ring-inset",
isActive ? "text-netbird" : "text-nb-gray-400 hover:text-nb-gray-300",
isDisabled ? "cursor-not-allowed opacity-50" : "cursor-default",
)}
@@ -42,7 +42,7 @@ export const NetworkFilters = ({ value, onChange, counts, disabled }: Props) =>
className={cn(
"inline-flex h-9 items-center gap-1.5 rounded-md px-2",
"text-sm text-nb-gray-200",
"outline-none transition-colors duration-150 hover:bg-nb-gray-900 data-[state=open]:bg-nb-gray-900",
"outline-hidden transition-colors duration-150 hover:bg-nb-gray-900 data-[state=open]:bg-nb-gray-900",
"focus-visible:ring-2 focus-visible:ring-nb-gray-50/60 focus-visible:ring-offset-2 focus-visible:ring-offset-nb-gray-940",
"disabled:pointer-events-none disabled:opacity-50",
"wails-no-draggable cursor-default",
@@ -54,7 +54,7 @@ export const NetworkFilters = ({ value, onChange, counts, disabled }: Props) =>
</span>
<ChevronDown size={14} aria-hidden={"true"} className={"ms-0.5 shrink-0"} />
</DropdownMenuTrigger>
<DropdownMenuContent align={"end"} className={"min-w-[10rem]"}>
<DropdownMenuContent align={"end"} className={"min-w-40"}>
{filters.map((f) => {
const checked = f.value === value;
return (
@@ -194,7 +194,7 @@ export const Networks = () => {
<ScrollArea.Scrollbar
orientation={"vertical"}
className={cn(
"flex touch-none select-none transition-colors",
"flex touch-none transition-colors select-none",
"w-1.5 bg-transparent py-1",
)}
>
@@ -213,7 +213,7 @@ export const Networks = () => {
"border-t border-nb-gray-910",
)}
>
<span className={"flex-1 text-xs font-medium tabular-nums text-nb-gray-300"}>
<span className={"flex-1 text-xs font-medium text-nb-gray-300 tabular-nums"}>
{t("networks.bulk.selectionCount", {
selected: selectedInView,
total: filtered.length,
@@ -228,7 +228,7 @@ export const Networks = () => {
"inline-flex h-8 items-center rounded-md px-3",
"text-xs font-medium text-nb-gray-100",
"border border-nb-gray-900 bg-nb-gray-920 hover:border-nb-gray-850 hover:bg-nb-gray-910",
"wails-no-draggable cursor-pointer outline-none transition-colors",
"wails-no-draggable cursor-pointer outline-hidden transition-colors",
"focus-visible:ring-2 focus-visible:ring-nb-gray-50/60 focus-visible:ring-offset-2 focus-visible:ring-offset-nb-gray-940",
)}
>
@@ -349,7 +349,7 @@ const NetworkRow = ({ network: n, index, onKeyDown, onToggle, setRowRef }: Netwo
return (
<div
className={cn(
"group relative flex min-w-0 items-start gap-2.5 py-3 pe-9 ps-6",
"group relative flex min-w-0 items-start gap-2.5 py-3 ps-6 pe-9",
"transition-colors hover:bg-nb-gray-900/40",
"wails-no-draggable",
)}
@@ -363,8 +363,8 @@ const NetworkRow = ({ network: n, index, onKeyDown, onToggle, setRowRef }: Netwo
onClick={() => onToggle(n.id, n.selected)}
onKeyDown={handleKey}
className={cn(
"absolute inset-0 cursor-pointer outline-none",
"focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-nb-gray-50/60",
"absolute inset-0 cursor-pointer outline-hidden",
"focus-visible:ring-2 focus-visible:ring-nb-gray-50/60 focus-visible:ring-inset",
)}
/>
<ResourceIconBadge type={resourceTypeOf(n)} />
@@ -401,7 +401,7 @@ const ResourceIconBadge = ({ type }: { type: ResourceType }) => {
<div
aria-hidden={"true"}
className={cn(
"mt-[0.25rem] flex h-9 w-9 shrink-0 items-center justify-center rounded-md",
"mt-1 flex h-9 w-9 shrink-0 items-center justify-center rounded-md",
"border border-nb-gray-800 bg-white text-nb-gray-300 transition-colors group-hover:border-nb-gray-700",
"dark:border-nb-gray-900 dark:bg-nb-gray-920 dark:group-hover:border-nb-gray-900",
)}
@@ -487,7 +487,7 @@ const ResolvedIpsTooltip = ({ ips }: { ips: string[] }) => {
const { t } = useTranslation();
return (
<>
<div className={"px-1 pb-1 text-[10px] uppercase tracking-wide text-nb-gray-300"}>
<div className={"px-1 pb-1 text-[10px] tracking-wide text-nb-gray-300 uppercase"}>
{t("networks.ips.heading")}
</div>
<ul className={"flex flex-col"}>
@@ -496,7 +496,7 @@ const ResolvedIpsTooltip = ({ ips }: { ips: string[] }) => {
<CopyToClipboard message={ip} className={"px-1 py-0.5"}>
<span
className={
"whitespace-nowrap font-mono text-[0.72rem] text-nb-gray-100"
"font-mono text-[0.72rem] whitespace-nowrap text-nb-gray-100"
}
>
{ip}
@@ -516,7 +516,7 @@ type ToggleProps = {
const NetworkToggle = ({ checked, mixed }: ToggleProps) => {
const checkedTranslate = checked
? "translate-x-[1.125rem] rtl:-translate-x-[1.125rem]"
? "translate-x-4.5 rtl:-translate-x-4.5"
: "translate-x-0.5 rtl:-translate-x-0.5";
return (
<span
@@ -197,7 +197,7 @@ export const PeerDetailPanel = ({ transition = DEFAULT_TRANSITION }: Props) => {
className={cn(
"flex h-8 w-8 shrink-0 items-center justify-center rounded-md",
"text-nb-gray-300 hover:bg-nb-gray-910 hover:text-nb-gray-100",
"cursor-default outline-none transition-colors",
"cursor-default outline-hidden transition-colors",
"focus-visible:ring-2 focus-visible:ring-nb-gray-50/60 focus-visible:ring-offset-2 focus-visible:ring-offset-nb-gray-940",
"wails-no-draggable",
)}
@@ -241,7 +241,7 @@ export const PeerDetailPanel = ({ transition = DEFAULT_TRANSITION }: Props) => {
className={cn(
"flex h-8 w-8 shrink-0 items-center justify-center rounded-md",
"text-nb-gray-300 hover:bg-nb-gray-910 hover:text-nb-gray-100",
"cursor-default outline-none transition-colors",
"cursor-default outline-hidden transition-colors",
"focus-visible:ring-2 focus-visible:ring-nb-gray-50/60 focus-visible:ring-offset-2 focus-visible:ring-offset-nb-gray-940",
"wails-no-draggable",
"disabled:opacity-50 disabled:hover:bg-transparent",
@@ -266,7 +266,7 @@ export const PeerDetailPanel = ({ transition = DEFAULT_TRANSITION }: Props) => {
<ScrollArea.Scrollbar
orientation={"vertical"}
className={cn(
"flex touch-none select-none transition-colors",
"flex touch-none transition-colors select-none",
"w-1.5 bg-transparent py-1",
)}
>
@@ -317,7 +317,7 @@ const PeerDetails = ({ peer, now }: { peer: PeerStatus; now: number }) => {
<CopyToClipboard
message={peer.ipv6}
alwaysShowIcon
className={"min-w-0 max-w-full"}
className={"max-w-full min-w-0"}
iconClassName={"top-0"}
>
<TruncatedRowValue value={peer.ipv6} mono />
@@ -335,7 +335,7 @@ const PeerDetails = ({ peer, now }: { peer: PeerStatus; now: number }) => {
<CopyToClipboard
message={peer.relayAddress}
alwaysShowIcon
className={"min-w-0 max-w-full"}
className={"max-w-full min-w-0"}
iconClassName={"top-0"}
>
<TruncatedRowValue value={peer.relayAddress} mono />
@@ -408,7 +408,7 @@ const PeerDetails = ({ peer, now }: { peer: PeerStatus; now: number }) => {
<CopyToClipboard
message={peer.pubKey}
alwaysShowIcon
className={"min-w-0 max-w-full"}
className={"max-w-full min-w-0"}
iconClassName={"top-0"}
>
<TruncatedRowValue value={peer.pubKey} mono />
@@ -446,7 +446,7 @@ const IceRow = ({ icon, baseLabel, type, endpoint }: IceRowProps) => {
<CopyToClipboard
message={endpoint}
alwaysShowIcon
className={"min-w-0 max-w-full"}
className={"max-w-full min-w-0"}
iconClassName={"top-0"}
>
<TruncatedRowValue value={endpoint} mono />
@@ -474,11 +474,11 @@ const ResourcesPopover = ({ networks }: { networks: string[] }) => {
aria-haspopup={"dialog"}
aria-expanded={open}
className={cn(
"inline-flex shrink-0 items-center gap-1 rounded",
"inline-flex shrink-0 items-center gap-1 rounded-sm",
"bg-nb-gray-930 hover:bg-nb-gray-910/80 data-[state=open]:bg-nb-gray-910",
"border border-nb-gray-900",
"py-1 pe-2 ps-2.5 text-xs font-medium text-nb-gray-300",
"wails-no-draggable cursor-default outline-none transition-all",
"py-1 ps-2.5 pe-2 text-xs font-medium text-nb-gray-300",
"wails-no-draggable cursor-default outline-hidden transition-all",
"focus-visible:ring-2 focus-visible:ring-nb-gray-50/60 focus-visible:ring-offset-2 focus-visible:ring-offset-nb-gray-940",
)}
>
@@ -497,9 +497,9 @@ const ResourcesPopover = ({ networks }: { networks: string[] }) => {
sideOffset={6}
onOpenAutoFocus={(e) => e.preventDefault()}
className={cn(
"z-50 max-h-72 min-w-64 max-w-[280px] overflow-auto",
"z-50 max-h-72 max-w-[280px] min-w-64 overflow-auto",
"rounded-lg border border-nb-gray-900 bg-nb-gray-935",
"p-1 text-nb-gray-200 shadow-lg outline-none",
"p-1 text-nb-gray-200 shadow-lg outline-hidden",
"flex flex-col",
)}
>
@@ -539,9 +539,9 @@ const ResourceRow = ({ value }: { value: string }) => {
"group/resourcerow relative flex items-center justify-between gap-3",
"rounded-md px-2 py-1.5 text-start",
"text-nb-gray-200 hover:bg-nb-gray-900 hover:text-nb-gray-50",
"cursor-default outline-none transition-colors",
"cursor-default outline-hidden transition-colors",
isFocusVisible &&
"focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-nb-gray-50/60",
"focus-visible:ring-2 focus-visible:ring-nb-gray-50/60 focus-visible:ring-inset",
)}
>
<span className={"min-w-0 truncate font-mono text-[0.75rem]"}>{value}</span>
@@ -560,7 +560,7 @@ const TruncatedRowValue = ({ value, mono }: { value: string; mono?: boolean }) =
text={value}
dir={mono ? "ltr" : "auto"}
className={cn(
"inline-block min-w-0 max-w-[260px] truncate align-middle",
"inline-block max-w-[260px] min-w-0 truncate align-middle",
mono && "font-mono",
)}
/>
@@ -42,7 +42,7 @@ export const PeerFilters = ({ value, onChange, counts, disabled }: Props) => {
className={cn(
"inline-flex h-9 items-center gap-1.5 rounded-md px-2",
"text-sm text-nb-gray-200",
"outline-none transition-colors duration-150 hover:bg-nb-gray-900 data-[state=open]:bg-nb-gray-900",
"outline-hidden transition-colors duration-150 hover:bg-nb-gray-900 data-[state=open]:bg-nb-gray-900",
"focus-visible:ring-2 focus-visible:ring-nb-gray-50/60 focus-visible:ring-offset-2 focus-visible:ring-offset-nb-gray-940",
"disabled:pointer-events-none disabled:opacity-50",
"wails-no-draggable cursor-default",
@@ -54,7 +54,7 @@ export const PeerFilters = ({ value, onChange, counts, disabled }: Props) => {
</span>
<ChevronDown size={14} aria-hidden={"true"} className={"ms-0.5 shrink-0"} />
</DropdownMenuTrigger>
<DropdownMenuContent align={"end"} className={"min-w-[10rem]"}>
<DropdownMenuContent align={"end"} className={"min-w-40"}>
{filters.map((f) => {
const checked = f.value === value;
return (
@@ -148,7 +148,7 @@ export const Peers = () => {
<ScrollArea.Scrollbar
orientation={"vertical"}
className={cn(
"flex touch-none select-none transition-colors",
"flex touch-none transition-colors select-none",
"w-1.5 bg-transparent py-1",
)}
>
@@ -280,7 +280,7 @@ const PeerRow = ({ peer, index, onKeyDown, onSelect, setRowRef }: PeerRowProps)
return (
<div
className={cn(
"group relative flex min-w-0 items-start gap-2.5 py-3 pe-4 ps-6",
"group relative flex min-w-0 items-start gap-2.5 py-3 ps-6 pe-4",
"transition-colors hover:bg-nb-gray-900/40",
"wails-no-draggable",
)}
@@ -293,8 +293,8 @@ const PeerRow = ({ peer, index, onKeyDown, onSelect, setRowRef }: PeerRowProps)
onClick={() => onSelect(peer)}
onKeyDown={handleKey}
className={cn(
"absolute inset-0 cursor-default outline-none",
"focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-nb-gray-50/60",
"absolute inset-0 cursor-default outline-hidden",
"focus-visible:ring-2 focus-visible:ring-nb-gray-50/60 focus-visible:ring-inset",
)}
/>
<Tooltip content={statusLabel} side={"left"}>
@@ -62,7 +62,7 @@ export const ProfileAvatar = forwardRef<HTMLButtonElement, Props>(function Profi
type={type}
className={cn(
"inline-grid place-items-center rounded-full bg-nb-gray-900 p-0 text-center",
"cursor-default outline-none",
"cursor-default outline-hidden",
"transition-colors duration-150 hover:bg-nb-gray-850",
"data-[state=open]:bg-nb-gray-850",
className,
@@ -94,8 +94,8 @@ export const ProfileDropdown = ({ onManageProfiles }: ProfileDropdownProps) => {
listRef.current?.focus();
}}
className={cn(
"wails-no-draggable z-50 min-w-64 select-none overflow-hidden rounded-lg border border-nb-gray-800 bg-nb-gray-950 p-1 text-nb-gray-200 shadow-lg dark:border-nb-gray-900 dark:bg-nb-gray-935",
"data-[state=open]:animate-in data-[state=closed]:animate-out",
"wails-no-draggable z-50 min-w-64 overflow-hidden rounded-lg border border-nb-gray-800 bg-nb-gray-950 p-1 text-nb-gray-200 shadow-lg select-none dark:border-nb-gray-900 dark:bg-nb-gray-935",
"data-[state=closed]:animate-out data-[state=open]:animate-in",
"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
"data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
"data-[side=bottom]:origin-top data-[side=top]:origin-bottom",
@@ -108,12 +108,16 @@ export const ProfileDropdown = ({ onManageProfiles }: ProfileDropdownProps) => {
loop
shouldFilter={false}
onKeyDown={(e) => e.stopPropagation()}
className={"outline-none focus:outline-none focus-visible:outline-none"}
className={
"outline-hidden focus:outline-hidden focus-visible:outline-hidden"
}
>
<Command.List
ref={listRef}
aria-label={t("header.profile.switch")}
className={"outline-none focus:outline-none focus-visible:outline-none"}
className={
"outline-hidden focus:outline-hidden focus-visible:outline-hidden"
}
>
{sortedProfiles.length > 0 && (
<>
@@ -135,7 +139,7 @@ export const ProfileDropdown = ({ onManageProfiles }: ProfileDropdownProps) => {
<ScrollArea.Scrollbar
orientation={"vertical"}
className={cn(
"flex touch-none select-none transition-colors",
"flex touch-none transition-colors select-none",
"w-1.5 bg-transparent",
)}
>
@@ -157,7 +161,7 @@ export const ProfileDropdown = ({ onManageProfiles }: ProfileDropdownProps) => {
disabled={!onManageProfiles}
className={cn(
"flex items-center gap-2 px-2 py-1.5",
"cursor-default rounded-md text-sm outline-none",
"cursor-default rounded-md text-sm outline-hidden",
"data-[selected=true]:bg-nb-gray-900",
"data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50",
)}
@@ -185,13 +189,13 @@ const ProfileTriggerSkeleton = () => (
role={"status"}
aria-busy={"true"}
aria-live={"polite"}
className={"wails-no-draggable flex h-10 select-none items-center gap-2 rounded-lg px-3"}
className={"wails-no-draggable flex h-10 items-center gap-2 rounded-lg px-3 select-none"}
>
<div
aria-hidden={"true"}
className={"size-4 shrink-0 animate-pulse rounded-full bg-nb-gray-900"}
/>
<div aria-hidden={"true"} className={"h-4 w-24 animate-pulse rounded bg-nb-gray-900"} />
<div aria-hidden={"true"} className={"h-4 w-24 animate-pulse rounded-sm bg-nb-gray-900"} />
</div>
);
@@ -213,7 +217,7 @@ const ProfileTriggerButton = forwardRef<HTMLButtonElement, ProfileTriggerButtonP
aria-label={t("header.profile.switch")}
aria-haspopup={"listbox"}
className={cn(
"wails-no-draggable flex h-10 cursor-default select-none items-center gap-2 rounded-lg px-3 outline-none",
"wails-no-draggable flex h-10 cursor-default items-center gap-2 rounded-lg px-3 outline-hidden select-none",
"text-nb-gray-200 hover:bg-nb-gray-800 dark:hover:bg-nb-gray-900",
"data-[state=open]:bg-nb-gray-800 dark:data-[state=open]:bg-nb-gray-900",
"disabled:opacity-50 disabled:hover:bg-transparent dark:disabled:hover:bg-transparent",
@@ -256,7 +260,7 @@ const ProfileRow = ({ profile, isActive, onSelect }: ProfileRowProps) => {
onSelect={() => onSelect(profile.id)}
className={cn(
"flex w-auto gap-2 px-2 py-2 pe-3 last:mb-1",
"cursor-default rounded-md text-sm outline-none",
"cursor-default rounded-md text-sm outline-hidden",
"data-[selected=true]:bg-nb-gray-900",
showEmail ? "items-start" : "items-center",
)}
@@ -204,7 +204,7 @@ export function ProfilesTab() {
<p className={"text-sm font-semibold text-nb-gray-200"}>
{t("settings.profiles.emptyTitle")}
</p>
<p className={"mt-1 max-w-sm text-balance text-xs text-nb-gray-400"}>
<p className={"mt-1 max-w-sm text-xs text-balance text-nb-gray-400"}>
{t("settings.profiles.emptyDescription")}
</p>
</div>
@@ -409,10 +409,10 @@ const ProfileRow = ({
className={cn(
"flex items-center gap-4 px-4 py-2.5",
"border-b border-nb-gray-910 last:border-b-0",
"outline-none",
"outline-hidden",
isFirst && "rounded-t-xl",
isLast && "rounded-b-xl",
"focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-nb-gray-50/60",
"focus-visible:ring-2 focus-visible:ring-nb-gray-50/60 focus-visible:ring-inset",
)}
>
<td
@@ -430,7 +430,7 @@ const ProfileRow = ({
<div className={"flex min-w-0 items-center gap-2"}>
<span
className={
"cursor-text select-text truncate font-medium text-nb-gray-100"
"cursor-text truncate font-medium text-nb-gray-100 select-text"
}
>
{profile.name}
@@ -470,7 +470,7 @@ const TruncatedEmail = ({ email }: { email: string }) => {
const span = (
<span
ref={ref}
className={"mt-0.5 cursor-text select-text truncate text-xs text-nb-gray-300"}
className={"mt-0.5 cursor-text truncate text-xs text-nb-gray-300 select-text"}
>
{email}
</span>
@@ -558,7 +558,7 @@ const RowMoreMenu = ({
aria-label={moreLabel}
tabIndex={rowFocused ? 0 : -1}
className={cn(
"inline-flex h-9 w-9 cursor-default items-center justify-center rounded-md outline-none",
"inline-flex h-9 w-9 cursor-default items-center justify-center rounded-md outline-hidden",
"text-nb-gray-400 hover:bg-nb-gray-900 hover:text-nb-gray-100",
"transition-colors duration-150",
"focus-visible:ring-2 focus-visible:ring-nb-gray-50/60 focus-visible:ring-offset-2 focus-visible:ring-offset-nb-gray-940",
@@ -605,7 +605,7 @@ const DeleteMenuItem = ({ disabled, disabledReason, onDelete }: DeleteMenuItemPr
<DropdownMenuItem
disabled={disabled}
onClick={disabled ? undefined : onDelete}
className={cn(!disabled && "text-red-500 hover:!text-red-500 focus:text-red-500")}
className={cn(!disabled && "text-red-500 hover:text-red-500! focus:text-red-500")}
>
<div className={"flex w-full items-center gap-2"}>
<Trash2 size={14} aria-hidden={"true"} />
@@ -653,7 +653,7 @@ const ActionIconButton = ({
aria-disabled={disabled || undefined}
tabIndex={hidden || !tabbable ? -1 : 0}
className={cn(
"inline-flex h-9 w-9 cursor-default items-center justify-center rounded-md outline-none",
"inline-flex h-9 w-9 cursor-default items-center justify-center rounded-md outline-hidden",
"transition-colors duration-150",
"focus-visible:ring-2 focus-visible:ring-nb-gray-50/60 focus-visible:ring-offset-2 focus-visible:ring-offset-nb-gray-940",
variant === "danger"
@@ -661,7 +661,7 @@ const ActionIconButton = ({
: "text-nb-gray-400 hover:bg-nb-gray-900 hover:text-nb-gray-100",
hidden && "pointer-events-none opacity-0",
disabled &&
"cursor-not-allowed opacity-40 hover:!bg-transparent hover:!text-nb-gray-400",
"cursor-not-allowed opacity-40 hover:bg-transparent! hover:text-nb-gray-400!",
)}
>
<Icon size={16} aria-hidden={"true"} />
@@ -221,7 +221,7 @@ export default function SessionExpirationDialog() {
{!expired && (
<div
className={
"font-mono text-2xl font-semibold tabular-nums tracking-wider text-nb-gray-50"
"font-mono text-2xl font-semibold tracking-wider text-nb-gray-50 tabular-nums"
}
aria-live={"polite"}
>
@@ -106,7 +106,7 @@ export function SettingsAbout() {
type={"button"}
onClick={handleVersionClick}
className={
"cursor-text select-text bg-transparent text-sm font-semibold text-nb-gray-100 outline-none"
"cursor-text bg-transparent text-sm font-semibold text-nb-gray-100 outline-hidden select-text"
}
>
{daemonVersion === "development" ? (
@@ -120,7 +120,7 @@ export function SettingsAbout() {
t("settings.about.client", { version: daemonVersion })
)}
</button>
<p className={"cursor-text select-text text-sm font-medium text-nb-gray-250"}>
<p className={"cursor-text text-sm font-medium text-nb-gray-250 select-text"}>
{guiVersion === "development" ? (
<span>
{t("settings.about.guiName")}{" "}
@@ -149,7 +149,7 @@ export function SettingsAbout() {
tabIndex={0}
onClick={() => openUrl(url)}
className={
"inline-flex items-center gap-1.5 rounded-sm decoration-[0.5px] underline-offset-4 outline-none transition hover:text-nb-gray-100 hover:underline focus-visible:ring-2 focus-visible:ring-nb-gray-50/60 focus-visible:ring-offset-2 focus-visible:ring-offset-nb-gray-940"
"inline-flex items-center gap-1.5 rounded-xs decoration-[0.5px] underline-offset-4 outline-hidden transition hover:text-nb-gray-100 hover:underline focus-visible:ring-2 focus-visible:ring-nb-gray-50/60 focus-visible:ring-offset-2 focus-visible:ring-offset-nb-gray-940"
}
>
<Icon aria-hidden={"true"} className={iconClassName ?? "h-3.5 w-3.5"} />
@@ -167,7 +167,7 @@ export function SettingsAbout() {
tabIndex={0}
onClick={() => openUrl(link.url)}
className={
"rounded-sm decoration-[0.5px] underline-offset-4 outline-none transition hover:text-nb-gray-100 hover:underline focus-visible:ring-2 focus-visible:ring-nb-gray-50/60 focus-visible:ring-offset-2 focus-visible:ring-offset-nb-gray-940"
"rounded-xs decoration-[0.5px] underline-offset-4 outline-hidden transition hover:text-nb-gray-100 hover:underline focus-visible:ring-2 focus-visible:ring-nb-gray-50/60 focus-visible:ring-offset-2 focus-visible:ring-offset-nb-gray-940"
}
>
{link.label}
@@ -28,7 +28,7 @@ export const SettingsNavigation = () => {
) : undefined;
return (
<div className={"flex w-52 shrink-0 select-none flex-col items-center"}>
<div className={"flex w-52 shrink-0 flex-col items-center select-none"}>
<VerticalTabs.List aria-label={t("settings.nav.label")}>
<VerticalTabs.Trigger
value={"general"}
@@ -113,7 +113,7 @@ export const SettingsPage = () => {
<ScrollArea.Scrollbar
orientation={"vertical"}
className={cn(
"flex touch-none select-none transition-colors",
"flex touch-none transition-colors select-none",
"w-1.5 bg-transparent py-1",
)}
>
@@ -261,7 +261,7 @@ function GuardedHint({
<HintBox>
<span>{t("settings.ssh.privilege.hint", { actor })}</span>
<CopyToClipboard message={command} alwaysShowIcon wrap variant={"bright"}>
<code className={"select-text break-all font-mono text-xs text-nb-gray-200"}>
<code className={"font-mono text-xs break-all text-nb-gray-200 select-text"}>
{command}
</code>
</CopyToClipboard>
@@ -15,12 +15,12 @@ export const SectionGroup = ({
tabIndex={disabled ? -1 : 0}
{...(disabled ? { inert: "" } : {})}
className={cn(
"mb-8 rounded-md px-1 outline-none last:mb-1",
"mb-8 rounded-md px-1 outline-hidden last:mb-1",
"focus-visible:ring-2 focus-visible:ring-nb-gray-50/60 focus-visible:ring-offset-2 focus-visible:ring-offset-nb-gray-940",
disabled && "pointer-events-none opacity-30",
)}
>
<h2 className={"mb-4 text-xs font-semibold uppercase tracking-wider text-nb-gray-400"}>
<h2 className={"mb-4 text-xs font-semibold tracking-wider text-nb-gray-400 uppercase"}>
{title}
</h2>
<div className={"flex flex-col gap-5"}>{children}</div>
@@ -30,7 +30,7 @@ export const SectionGroup = ({
export const SettingsBottomBar = ({ children }: { children: ReactNode }) => (
<>
<div className={"h-[3.2rem] shrink-0"} aria-hidden={"true"} />
<div className={"absolute bottom-0 start-0 w-full"}>
<div className={"absolute inset-s-0 bottom-0 w-full"}>
<div
className={
"flex w-full justify-end gap-3 border-t border-nb-gray-800 bg-nb-gray-940 px-8 py-5 dark:border-nb-gray-920"
@@ -79,7 +79,7 @@ export function SettingsTroubleshooting() {
{t("settings.troubleshooting.anonymize.label")}
<Tooltip
content={
<div className={"max-w-xs whitespace-normal leading-relaxed"}>
<div className={"max-w-xs leading-relaxed whitespace-normal"}>
{t("settings.troubleshooting.anonymize.info")}
</div>
}
@@ -213,7 +213,7 @@ function ProgressSection({
{stage.kind === "capturing" && (
<div
className={
"font-mono text-2xl font-semibold tabular-nums tracking-wider text-nb-gray-50"
"font-mono text-2xl font-semibold tracking-wider text-nb-gray-50 tabular-nums"
}
aria-live={"polite"}
>
@@ -32,7 +32,7 @@ export function WelcomeStepTray({ onContinue }: Readonly<WelcomeStepTrayProps>)
<img
src={trayScreenshot}
alt={""}
className={"pointer-events-none h-auto w-full select-none rounded-2xl"}
className={"pointer-events-none h-auto w-full rounded-2xl select-none"}
draggable={false}
/>
</div>
-180
View File
@@ -1,180 +0,0 @@
import type { Config } from "tailwindcss";
const config: Config = {
content: ["./index.html", "./src/**/*.{ts,tsx}"],
darkMode: "class",
theme: {
fontFamily: {
sans: ['"Inter Variable"', 'ui-sans-serif', 'system-ui', 'sans-serif'],
mono: ['"JetBrains Mono Variable"', 'ui-monospace', 'monospace'],
},
extend: {
colors: {
// nb-gray is theme-aware: channels come from CSS variables in
// globals.css (:root = light ramp, .dark = original dark ramp). The
// rgb(var(...) / <alpha-value>) form keeps opacity modifiers working.
"nb-gray": {
DEFAULT: "rgb(var(--nb-gray-DEFAULT) / <alpha-value>)",
50: "rgb(var(--nb-gray-50) / <alpha-value>)",
100: "rgb(var(--nb-gray-100) / <alpha-value>)",
200: "rgb(var(--nb-gray-200) / <alpha-value>)",
250: "rgb(var(--nb-gray-250) / <alpha-value>)",
300: "rgb(var(--nb-gray-300) / <alpha-value>)",
350: "rgb(var(--nb-gray-350) / <alpha-value>)",
400: "rgb(var(--nb-gray-400) / <alpha-value>)",
500: "rgb(var(--nb-gray-500) / <alpha-value>)",
600: "rgb(var(--nb-gray-600) / <alpha-value>)",
700: "rgb(var(--nb-gray-700) / <alpha-value>)",
800: "rgb(var(--nb-gray-800) / <alpha-value>)",
850: "rgb(var(--nb-gray-850) / <alpha-value>)",
900: "rgb(var(--nb-gray-900) / <alpha-value>)",
910: "rgb(var(--nb-gray-910) / <alpha-value>)",
920: "rgb(var(--nb-gray-920) / <alpha-value>)",
925: "rgb(var(--nb-gray-925) / <alpha-value>)",
930: "rgb(var(--nb-gray-930) / <alpha-value>)",
935: "rgb(var(--nb-gray-935) / <alpha-value>)",
940: "rgb(var(--nb-gray-940) / <alpha-value>)",
950: "rgb(var(--nb-gray-950) / <alpha-value>)",
960: "rgb(var(--nb-gray-960) / <alpha-value>)",
},
gray: {
50: "#F9FAFB",
100: "#F3F4F6",
200: "#E5E7EB",
300: "#D1D5DB",
400: "#9CA3AF",
500: "#6B7280",
600: "#4B5563",
700: "#374151",
800: "#1F2937",
900: "#111827",
},
red: {
50: "#FDF2F2",
100: "#FDE8E8",
200: "#FBD5D5",
300: "#F8B4B4",
400: "#F98080",
500: "#F05252",
600: "#E02424",
700: "#C81E1E",
800: "#9B1C1C",
900: "#771D1D",
},
yellow: {
50: "#FDFDEA",
100: "#FDF6B2",
200: "#FCE96A",
300: "#FACA15",
400: "#E3A008",
500: "#C27803",
600: "#9F580A",
700: "#8E4B10",
800: "#723B13",
900: "#633112",
},
green: {
50: "#F3FAF7",
100: "#DEF7EC",
200: "#BCF0DA",
300: "#84E1BC",
400: "#31C48D",
500: "#0E9F6E",
600: "#057A55",
700: "#046C4E",
800: "#03543F",
900: "#014737",
},
blue: {
50: "#EBF5FF",
100: "#E1EFFE",
200: "#C3DDFD",
300: "#A4CAFE",
400: "#76A9FA",
500: "#3F83F8",
600: "#1C64F2",
700: "#1A56DB",
800: "#1E429F",
900: "#233876",
},
indigo: {
50: "#F0F5FF",
100: "#E5EDFF",
200: "#CDDBFE",
300: "#B4C6FC",
400: "#8DA2FB",
500: "#6875F5",
600: "#5850EC",
700: "#5145CD",
800: "#42389D",
900: "#362F78",
},
purple: {
50: "#F6F5FF",
100: "#EDEBFE",
200: "#DCD7FE",
300: "#CABFFD",
400: "#AC94FA",
500: "#9061F9",
600: "#7E3AF2",
700: "#6C2BD9",
800: "#5521B5",
900: "#4A1D96",
},
pink: {
50: "#FDF2F8",
100: "#FCE8F3",
200: "#FAD1E8",
300: "#F8B4D9",
400: "#F17EB8",
500: "#E74694",
600: "#D61F69",
700: "#BF125D",
800: "#99154B",
900: "#751A3D",
},
netbird: {
DEFAULT: "#f68330",
50: "#fff6ed",
100: "#feecd6",
150: "#ffdfb8",
200: "#ffd4a6",
300: "#fab677",
400: "#f68330",
500: "#f46d1b",
600: "#e55311",
700: "#be3e10",
800: "#973215",
900: "#7a2b14",
950: "#421308",
},
},
backgroundImage: {
"conic-netbird": "conic-gradient(from 0deg, #e55311 0%, #f68330 10%, #e55311 20%, #e55311 100%)",
},
keyframes: {
"pulse-reverse": {
"0%, 100%": { opacity: "1" },
"50%": { opacity: "0.4" },
},
"spin-slow": {
"0%": { transform: "rotate(0deg)" },
"100%": { transform: "rotate(360deg)" },
},
"ping-slow": {
"0%": { transform: "scale(1)", opacity: "1" },
"75%, 100%": { transform: "scale(2)", opacity: "0" },
},
},
animation: {
"ping-slow": "ping-slow 2s cubic-bezier(0, 0, 0.2, 1) infinite",
"pulse-slow": "pulse-reverse 2s cubic-bezier(0.5, 0, 0.6, 1) infinite",
"pulse-slower": "pulse-reverse 3s cubic-bezier(0.5, 0, 0.6, 1) infinite",
"spin-slow": "spin-slow 2s linear infinite",
},
},
},
plugins: [require("tailwindcss-animate")],
};
export default config;
+2 -1
View File
@@ -1,6 +1,7 @@
import path from "path";
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import tailwindcss from "@tailwindcss/vite";
import wails from "@wailsio/runtime/plugins/vite";
// https://vitejs.dev/config/
@@ -14,7 +15,7 @@ export default defineConfig({
),
},
},
plugins: [react(), wails("./bindings")],
plugins: [react(), tailwindcss(), wails("./bindings")],
server: {
host: "127.0.0.1",
port: 9245,
+168 -151
View File
@@ -29,17 +29,17 @@
"tsx": "^4.21.0",
"typescript": "~5.9.3",
"typescript-eslint": "^8.46.4",
"vite": "7.3.2"
"vite": "7.3.7"
}
},
"node_modules/@babel/code-frame": {
"version": "7.29.0",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz",
"integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==",
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz",
"integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-validator-identifier": "^7.28.5",
"@babel/helper-validator-identifier": "^7.29.7",
"js-tokens": "^4.0.0",
"picocolors": "^1.1.1"
},
@@ -48,9 +48,9 @@
}
},
"node_modules/@babel/compat-data": {
"version": "7.29.0",
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz",
"integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==",
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz",
"integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -58,22 +58,21 @@
}
},
"node_modules/@babel/core": {
"version": "7.29.0",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz",
"integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz",
"integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/code-frame": "^7.29.0",
"@babel/generator": "^7.29.0",
"@babel/helper-compilation-targets": "^7.28.6",
"@babel/helper-module-transforms": "^7.28.6",
"@babel/helpers": "^7.28.6",
"@babel/parser": "^7.29.0",
"@babel/template": "^7.28.6",
"@babel/traverse": "^7.29.0",
"@babel/types": "^7.29.0",
"@babel/code-frame": "^7.29.7",
"@babel/generator": "^7.29.7",
"@babel/helper-compilation-targets": "^7.29.7",
"@babel/helper-module-transforms": "^7.29.7",
"@babel/helpers": "^7.29.7",
"@babel/parser": "^7.29.7",
"@babel/template": "^7.29.7",
"@babel/traverse": "^7.29.7",
"@babel/types": "^7.29.7",
"@jridgewell/remapping": "^2.3.5",
"convert-source-map": "^2.0.0",
"debug": "^4.1.0",
@@ -90,14 +89,14 @@
}
},
"node_modules/@babel/generator": {
"version": "7.29.0",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.0.tgz",
"integrity": "sha512-vSH118/wwM/pLR38g/Sgk05sNtro6TlTJKuiMXDaZqPUfjTFcudpCOt00IhOfj+1BFAX+UFAlzCU+6WXr3GLFQ==",
"version": "7.29.8",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.8.tgz",
"integrity": "sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/parser": "^7.29.0",
"@babel/types": "^7.29.0",
"@babel/parser": "^7.29.8",
"@babel/types": "^7.29.8",
"@jridgewell/gen-mapping": "^0.3.12",
"@jridgewell/trace-mapping": "^0.3.28",
"jsesc": "^3.0.2"
@@ -107,14 +106,14 @@
}
},
"node_modules/@babel/helper-compilation-targets": {
"version": "7.28.6",
"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz",
"integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==",
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz",
"integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/compat-data": "^7.28.6",
"@babel/helper-validator-option": "^7.27.1",
"@babel/compat-data": "^7.29.7",
"@babel/helper-validator-option": "^7.29.7",
"browserslist": "^4.24.0",
"lru-cache": "^5.1.1",
"semver": "^6.3.1"
@@ -124,9 +123,9 @@
}
},
"node_modules/@babel/helper-globals": {
"version": "7.28.0",
"resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz",
"integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==",
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz",
"integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -134,29 +133,29 @@
}
},
"node_modules/@babel/helper-module-imports": {
"version": "7.28.6",
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz",
"integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==",
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz",
"integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/traverse": "^7.28.6",
"@babel/types": "^7.28.6"
"@babel/traverse": "^7.29.7",
"@babel/types": "^7.29.7"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-module-transforms": {
"version": "7.28.6",
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz",
"integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==",
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz",
"integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-module-imports": "^7.28.6",
"@babel/helper-validator-identifier": "^7.28.5",
"@babel/traverse": "^7.28.6"
"@babel/helper-module-imports": "^7.29.7",
"@babel/helper-validator-identifier": "^7.29.7",
"@babel/traverse": "^7.29.7"
},
"engines": {
"node": ">=6.9.0"
@@ -176,9 +175,9 @@
}
},
"node_modules/@babel/helper-string-parser": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
"integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz",
"integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -186,9 +185,9 @@
}
},
"node_modules/@babel/helper-validator-identifier": {
"version": "7.28.5",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
"integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz",
"integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -196,9 +195,9 @@
}
},
"node_modules/@babel/helper-validator-option": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz",
"integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==",
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz",
"integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -206,27 +205,27 @@
}
},
"node_modules/@babel/helpers": {
"version": "7.28.6",
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz",
"integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==",
"version": "7.29.10",
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.10.tgz",
"integrity": "sha512-EMCx3AGZ3WfAl6ht7AyQoPm2Lh+BGYEMdc4Q6Hf7uSCDOZFuLq44qNwL+iaim8VlyYhRAo4X43+weadK8ZB4Yg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/template": "^7.28.6",
"@babel/types": "^7.28.6"
"@babel/template": "^7.29.7",
"@babel/types": "^7.29.8"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/parser": {
"version": "7.29.0",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz",
"integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==",
"version": "7.29.9",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.9.tgz",
"integrity": "sha512-CjXrNHTnvqBVqHgdBysY3vk2T8tpJHb5/RMeHJBTyVa9xgugCB0CJTx/3oO8RV2QRQP391RWpB7D6hLjm8V9uA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/types": "^7.29.0"
"@babel/types": "^7.29.8"
},
"bin": {
"parser": "bin/babel-parser.js"
@@ -268,33 +267,33 @@
}
},
"node_modules/@babel/template": {
"version": "7.28.6",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz",
"integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==",
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz",
"integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.28.6",
"@babel/parser": "^7.28.6",
"@babel/types": "^7.28.6"
"@babel/code-frame": "^7.29.7",
"@babel/parser": "^7.29.7",
"@babel/types": "^7.29.7"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/traverse": {
"version": "7.29.0",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz",
"integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==",
"version": "7.29.10",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.10.tgz",
"integrity": "sha512-B3lYAs2nkKskbgQ1nfy8Ma9ao9Q1rntTAQHrzzyDuj+TSO+X3le6PfWwqDvodsb+OumE10eMFfuMCTQSQ5iuIQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.29.0",
"@babel/generator": "^7.29.0",
"@babel/helper-globals": "^7.28.0",
"@babel/parser": "^7.29.0",
"@babel/template": "^7.28.6",
"@babel/types": "^7.29.0",
"@babel/code-frame": "^7.29.7",
"@babel/generator": "^7.29.8",
"@babel/helper-globals": "^7.29.7",
"@babel/parser": "^7.29.9",
"@babel/template": "^7.29.7",
"@babel/types": "^7.29.8",
"debug": "^4.3.1"
},
"engines": {
@@ -302,14 +301,14 @@
}
},
"node_modules/@babel/types": {
"version": "7.29.0",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz",
"integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==",
"version": "7.29.8",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz",
"integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-string-parser": "^7.27.1",
"@babel/helper-validator-identifier": "^7.28.5"
"@babel/helper-string-parser": "^7.29.7",
"@babel/helper-validator-identifier": "^7.29.7"
},
"engines": {
"node": ">=6.9.0"
@@ -915,29 +914,43 @@
}
},
"node_modules/@humanfs/core": {
"version": "0.19.1",
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
"integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==",
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz",
"integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@humanfs/types": "^0.15.0"
},
"engines": {
"node": ">=18.18.0"
}
},
"node_modules/@humanfs/node": {
"version": "0.16.7",
"resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz",
"integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==",
"version": "0.16.8",
"resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz",
"integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@humanfs/core": "^0.19.1",
"@humanfs/core": "^0.19.2",
"@humanfs/types": "^0.15.0",
"@humanwhocodes/retry": "^0.4.0"
},
"engines": {
"node": ">=18.18.0"
}
},
"node_modules/@humanfs/types": {
"version": "0.15.0",
"resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz",
"integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==",
"dev": true,
"license": "Apache-2.0",
"engines": {
"node": ">=18.18.0"
}
},
"node_modules/@humanwhocodes/module-importer": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
@@ -1710,7 +1723,6 @@
"integrity": "sha512-+0/4J266CBGPUq/ELg7QUHhN25WYjE0wYTPSQJn1xeu8DOlIOPxXxrNGiLmfAWl7HMMgWFWXpt9IDjMWrF5Iow==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"undici-types": "~7.16.0"
}
@@ -1721,7 +1733,6 @@
"integrity": "sha512-WPigyYuGhgZ/cTPRXB2EwUw+XvsRA3GqHlsP4qteqrnnjDrApbS7MxcGr/hke5iUoeB7E/gQtrs9I37zAJ0Vjw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"csstype": "^3.2.2"
}
@@ -1781,7 +1792,6 @@
"integrity": "sha512-BtE0k6cjwjLZoZixN0t5AKP0kSzlGu7FctRXYuPAm//aaiZhmfq1JwdYpYr1brzEspYyFeF+8XF5j2VK6oalrA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@typescript-eslint/scope-manager": "8.54.0",
"@typescript-eslint/types": "8.54.0",
@@ -1926,9 +1936,9 @@
}
},
"node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.3.tgz",
"integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==",
"version": "2.1.7",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.7.tgz",
"integrity": "sha512-uZbew1NqdmPDTMJ8ah1y+b+9QEJrfkXFk3RcTQw3X0jW/xRUvFKsg1CfQdSYGdTbXZWExtU3J3ccxtnfw1Fi0g==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2033,7 +2043,6 @@
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"dev": true,
"license": "MIT",
"peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -2099,19 +2108,22 @@
"license": "MIT"
},
"node_modules/baseline-browser-mapping": {
"version": "2.9.19",
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz",
"integrity": "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==",
"version": "2.11.30",
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.30.tgz",
"integrity": "sha512-YJs6Pz17RMNv8HoyKREtCY38E3BV1nPkWVmIb+XN1uHC1Vqt3KZCH/uJH9ZKRoiKb/icZ6tvehyee6RZNbFfRA==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"baseline-browser-mapping": "dist/cli.js"
"baseline-browser-mapping": "dist/cli.cjs"
},
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/brace-expansion": {
"version": "1.1.13",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz",
"integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==",
"version": "1.1.21",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz",
"integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2120,9 +2132,9 @@
}
},
"node_modules/browserslist": {
"version": "4.28.1",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz",
"integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==",
"version": "4.29.4",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.29.4.tgz",
"integrity": "sha512-2TcbbiwXEq+N0kD36tykpCfyyQigUXmuwNQbnPp4Xk4PX9JOqVIKjJlIDjpH7uY6tmRGoAd+apeT1p60T8wsdA==",
"dev": true,
"funding": [
{
@@ -2139,13 +2151,12 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"baseline-browser-mapping": "^2.9.0",
"caniuse-lite": "^1.0.30001759",
"electron-to-chromium": "^1.5.263",
"node-releases": "^2.0.27",
"update-browserslist-db": "^1.2.0"
"baseline-browser-mapping": "^2.11.29",
"caniuse-lite": "^1.0.30001816",
"electron-to-chromium": "^1.5.459",
"node-releases": "^2.0.59",
"update-browserslist-db": "^1.3.4"
},
"bin": {
"browserslist": "cli.js"
@@ -2165,9 +2176,9 @@
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001767",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001767.tgz",
"integrity": "sha512-34+zUAMhSH+r+9eKmYG+k2Rpt8XttfE4yXAjoZvkAPs15xcYQhyBYdalJ65BzivAvGRMViEjy6oKr/S91loekQ==",
"version": "1.0.30001817",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001817.tgz",
"integrity": "sha512-uZ1JG80BwhtMalIAGPxPfmNhHyd/B/nTdr3hITWlRamSyAdUStP1Gop01e7SD5q6J90ixJvWDbvmiFVfDgn4bw==",
"dev": true,
"funding": [
{
@@ -2303,9 +2314,9 @@
}
},
"node_modules/electron-to-chromium": {
"version": "1.5.286",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.286.tgz",
"integrity": "sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A==",
"version": "1.5.463",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.463.tgz",
"integrity": "sha512-nVWNU6ltFjopvOdD44J4zoBkgO0M+WZiFKKFV7EjebOH9z0KNphk6bCEjplXtJofL21ARIuuwWOCsFkBVklm6w==",
"dev": true,
"license": "ISC"
},
@@ -2394,7 +2405,6 @@
"integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.1",
@@ -2846,10 +2856,20 @@
"license": "MIT"
},
"node_modules/js-yaml": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
"integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz",
"integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/puzrin"
},
{
"type": "github",
"url": "https://github.com/sponsors/nodeca"
}
],
"license": "MIT",
"dependencies": {
"argparse": "^2.0.1"
@@ -3263,9 +3283,9 @@
"license": "MIT"
},
"node_modules/nanoid": {
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
"version": "3.3.20",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.20.tgz",
"integrity": "sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==",
"dev": true,
"funding": [
{
@@ -3289,11 +3309,14 @@
"license": "MIT"
},
"node_modules/node-releases": {
"version": "2.0.27",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz",
"integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==",
"version": "2.0.59",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.59.tgz",
"integrity": "sha512-5qr2okC4ItAI0hG5SaV0HoYMpnM/Y6+Oqr7AmPYy/2uEoGvS+GbSKLeRxSS1uAdu59yz0ouzcuXievSgg///WA==",
"dev": true,
"license": "MIT"
"license": "MIT",
"engines": {
"node": ">=18"
}
},
"node_modules/optionator": {
"version": "0.9.4",
@@ -3391,7 +3414,6 @@
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
"dev": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">=12"
},
@@ -3400,9 +3422,9 @@
}
},
"node_modules/postcss": {
"version": "8.5.6",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
"integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
"version": "8.5.31",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.31.tgz",
"integrity": "sha512-YUbWYCgErwnMAVkjUnYa+KlMBzGpHwiIt/DE7f0G33AuX2LH9UcTk6KgWxd3zRU+pN3u/C+Xl3EnMLM4Hea7Sg==",
"dev": true,
"funding": [
{
@@ -3420,9 +3442,9 @@
],
"license": "MIT",
"dependencies": {
"nanoid": "^3.3.11",
"nanoid": "^3.3.20",
"picocolors": "^1.1.1",
"source-map-js": "^1.2.1"
"source-map-js": "^1.2.2"
},
"engines": {
"node": "^10 || ^12 || >=14"
@@ -3453,7 +3475,6 @@
"resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz",
"integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=0.10.0"
}
@@ -3585,9 +3606,9 @@
}
},
"node_modules/source-map-js": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz",
"integrity": "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==",
"dev": true,
"license": "BSD-3-Clause",
"engines": {
@@ -3687,7 +3708,6 @@
"integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"esbuild": "~0.27.0",
"get-tsconfig": "^4.7.5"
@@ -3721,7 +3741,6 @@
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"dev": true,
"license": "Apache-2.0",
"peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@@ -3762,9 +3781,9 @@
"license": "MIT"
},
"node_modules/update-browserslist-db": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
"integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==",
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.4.tgz",
"integrity": "sha512-t8wonIOdQyOjtPd2VktlBu6QjTVTks6v8bSi7kd0r2Vll8TK1JYDZtRRdbmCHOAMphkZgrzK+L+vXK4pyZ8t4A==",
"dev": true,
"funding": [
{
@@ -3803,14 +3822,13 @@
}
},
"node_modules/vite": {
"version": "7.3.2",
"resolved": "https://registry.npmjs.org/vite/-/vite-7.3.2.tgz",
"integrity": "sha512-Bby3NOsna2jsjfLVOHKes8sGwgl4TT0E6vvpYgnAYDIF/tie7MRaFthmKuHx1NSXjiTueXH3do80FMQgvEktRg==",
"version": "7.3.7",
"resolved": "https://registry.npmjs.org/vite/-/vite-7.3.7.tgz",
"integrity": "sha512-AHEzTORMbi7/7aXp7w7KZ5+DCUdSeLsJz0grY8xHpc9DHdYb0vQu6DqJfm2tOw3gDExNgPgnJTXxM6syYFHaCA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"esbuild": "^0.27.0",
"esbuild": "^0.27.0 || ^0.28.0",
"fdir": "^6.5.0",
"picomatch": "^4.0.3",
"postcss": "^8.5.6",
@@ -3930,7 +3948,6 @@
"integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==",
"dev": true,
"license": "MIT",
"peer": true,
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
+1 -1
View File
@@ -31,6 +31,6 @@
"tsx": "^4.21.0",
"typescript": "~5.9.3",
"typescript-eslint": "^8.46.4",
"vite": "7.3.2"
"vite": "7.3.7"
}
}