mirror of
https://github.com/netbirdio/netbird.git
synced 2026-10-25 22:09:08 +01:00
lint and add frontend-ui workflow
This commit is contained in:
@@ -0,0 +1,94 @@
|
|||||||
|
name: UI Frontend
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- "client/ui/frontend/**"
|
||||||
|
- "client/ui/i18n/**"
|
||||||
|
- "client/ui/**/*.go"
|
||||||
|
- ".github/workflows/frontend-ui.yml"
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
paths:
|
||||||
|
- "client/ui/frontend/**"
|
||||||
|
- "client/ui/i18n/**"
|
||||||
|
- "client/ui/**/*.go"
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.head_ref || github.actor_id }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
lint-and-build:
|
||||||
|
name: Lint & Build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 15
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: client/ui/frontend
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
|
- name: Set up Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: "22"
|
||||||
|
|
||||||
|
- name: Set up pnpm
|
||||||
|
uses: pnpm/action-setup@a3252b78c470c02df07e9d59298aecedc3ccdd6d # v3.0.0
|
||||||
|
with:
|
||||||
|
version: 11
|
||||||
|
|
||||||
|
# Bindings are generated by wails3 from the Go service definitions and
|
||||||
|
# are not checked in (see client/ui/frontend/bindings/). Without them,
|
||||||
|
# typecheck/build fail on missing module imports.
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
|
||||||
|
with:
|
||||||
|
go-version-file: "go.mod"
|
||||||
|
cache: false
|
||||||
|
|
||||||
|
- name: Install wails3 CLI
|
||||||
|
# Version derived from go.mod so the binding generator always matches
|
||||||
|
# the wails runtime the daemon links against.
|
||||||
|
working-directory: ${{ github.workspace }}
|
||||||
|
run: |
|
||||||
|
WAILS_VERSION=$(go list -m -f '{{.Version}}' github.com/wailsapp/wails/v3)
|
||||||
|
go install github.com/wailsapp/wails/v3/cmd/wails3@$WAILS_VERSION
|
||||||
|
|
||||||
|
- name: Get pnpm store directory
|
||||||
|
id: pnpm-store
|
||||||
|
run: echo "path=$(pnpm store path --silent)" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
- name: Cache pnpm store
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: ${{ steps.pnpm-store.outputs.path }}
|
||||||
|
key: ${{ runner.os }}-pnpm-${{ hashFiles('client/ui/frontend/pnpm-lock.yaml') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-pnpm-
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: pnpm install --frozen-lockfile
|
||||||
|
|
||||||
|
- name: Generate Wails bindings
|
||||||
|
run: pnpm run bindings
|
||||||
|
|
||||||
|
- name: ESLint
|
||||||
|
run: pnpm lint
|
||||||
|
|
||||||
|
- name: Type-check
|
||||||
|
run: pnpm typecheck
|
||||||
|
|
||||||
|
- name: Prettier check
|
||||||
|
run: pnpm format:check
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: pnpm build
|
||||||
@@ -28,7 +28,7 @@ export const Badge = forwardRef<HTMLSpanElement, Props>(function Badge(
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"relative top-px inline-flex items-center gap-1 rounded-full px-1.5 py-[0.15rem]",
|
"relative top-px inline-flex items-center gap-1 rounded-full px-1.5 py-[0.15rem]",
|
||||||
"text-[0.64rem] leading-none font-semibold shrink-0",
|
"shrink-0 text-[0.64rem] font-semibold leading-none",
|
||||||
VARIANT_CLASSES[variant],
|
VARIANT_CLASSES[variant],
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -76,14 +76,14 @@ export const CopyToClipboard = ({
|
|||||||
aria-label={resolvedLabel}
|
aria-label={resolvedLabel}
|
||||||
aria-live={"polite"}
|
aria-live={"polite"}
|
||||||
className={cn(
|
className={cn(
|
||||||
"inline-flex gap-2 items-center group/copy cursor-default wails-no-draggable text-left pointer-events-auto rounded-sm outline-none",
|
"group/copy wails-no-draggable pointer-events-auto inline-flex cursor-default items-center gap-2 rounded-sm text-left outline-none",
|
||||||
"focus-visible:ring-2 focus-visible:ring-white/60 focus-visible:ring-offset-2 focus-visible:ring-offset-nb-gray-940",
|
"focus-visible:ring-2 focus-visible:ring-white/60 focus-visible:ring-offset-2 focus-visible:ring-offset-nb-gray-940",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
className={cn(
|
className={cn(
|
||||||
"relative truncate min-w-0",
|
"relative min-w-0 truncate",
|
||||||
"[&_*]:transition-colors",
|
"[&_*]:transition-colors",
|
||||||
VARIANT_HOVER[variant],
|
VARIANT_HOVER[variant],
|
||||||
)}
|
)}
|
||||||
@@ -91,13 +91,15 @@ export const CopyToClipboard = ({
|
|||||||
{children}
|
{children}
|
||||||
<span
|
<span
|
||||||
aria-hidden={"true"}
|
aria-hidden={"true"}
|
||||||
className={"absolute bottom-0 left-0 right-0 border-b border-dashed border-transparent group-hover/copy:border-nb-gray-500 pointer-events-none"}
|
className={
|
||||||
|
"pointer-events-none absolute bottom-0 left-0 right-0 border-b border-dashed border-transparent group-hover/copy:border-nb-gray-500"
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
aria-hidden={"true"}
|
aria-hidden={"true"}
|
||||||
className={cn(
|
className={cn(
|
||||||
"shrink-0 inline-flex relative top-[2px] right-[1px]",
|
"relative right-[1px] top-[2px] inline-flex shrink-0",
|
||||||
iconAlignment === "left" ? "order-first" : "order-last",
|
iconAlignment === "left" ? "order-first" : "order-last",
|
||||||
iconClassName,
|
iconClassName,
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ const DropdownMenuSubTrigger = React.forwardRef<
|
|||||||
<DropdownMenuPrimitive.SubTrigger
|
<DropdownMenuPrimitive.SubTrigger
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"relative flex select-none items-center rounded-md pl-3 pr-2 py-1.5 text-sm outline-none cursor-default",
|
"relative flex cursor-default select-none items-center rounded-md py-1.5 pl-3 pr-2 text-sm outline-none",
|
||||||
"transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
"transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||||
inset && "pl-8",
|
inset && "pl-8",
|
||||||
menuItemVariants({ variant }),
|
menuItemVariants({ variant }),
|
||||||
@@ -102,7 +102,7 @@ const DropdownMenuItem = React.forwardRef<
|
|||||||
<DropdownMenuPrimitive.Item
|
<DropdownMenuPrimitive.Item
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"relative flex select-none items-center rounded-md pl-2 pr-2 py-1.5 text-sm outline-none cursor-default",
|
"relative flex cursor-default select-none items-center rounded-md py-1.5 pl-2 pr-2 text-sm outline-none",
|
||||||
"transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
"transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||||
inset && "pl-8",
|
inset && "pl-8",
|
||||||
menuItemVariants({ variant }),
|
menuItemVariants({ variant }),
|
||||||
@@ -135,7 +135,7 @@ const DropdownMenuCheckboxItem = React.forwardRef<
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none",
|
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none",
|
||||||
"transition-colors text-nb-gray-200 focus:bg-nb-gray-900 focus:text-nb-gray-50",
|
"text-nb-gray-200 transition-colors focus:bg-nb-gray-900 focus:text-nb-gray-50",
|
||||||
"data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
"data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
@@ -160,7 +160,7 @@ const DropdownMenuRadioItem = React.forwardRef<
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none",
|
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none",
|
||||||
"transition-colors text-nb-gray-200 focus:bg-nb-gray-900 focus:text-nb-gray-50",
|
"text-nb-gray-200 transition-colors focus:bg-nb-gray-900 focus:text-nb-gray-50",
|
||||||
"data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
"data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -74,8 +74,8 @@ export function LanguagePicker() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={"flex items-center gap-6 justify-between"}>
|
<div className={"flex items-center justify-between gap-6"}>
|
||||||
<div className={"flex-1 max-w-md"}>
|
<div className={"max-w-md flex-1"}>
|
||||||
<Label as={"div"}>{t("settings.general.language.label")}</Label>
|
<Label as={"div"}>{t("settings.general.language.label")}</Label>
|
||||||
<HelpText margin={false}>{t("settings.general.language.help")}</HelpText>
|
<HelpText margin={false}>{t("settings.general.language.help")}</HelpText>
|
||||||
</div>
|
</div>
|
||||||
@@ -91,10 +91,10 @@ export function LanguagePicker() {
|
|||||||
aria-haspopup={"listbox"}
|
aria-haspopup={"listbox"}
|
||||||
aria-expanded={open}
|
aria-expanded={open}
|
||||||
className={cn(
|
className={cn(
|
||||||
"inline-flex items-center gap-2 h-[40px] px-3 min-w-[240px]",
|
"inline-flex h-[40px] min-w-[240px] items-center gap-2 px-3",
|
||||||
"rounded-md border bg-white dark:bg-nb-gray-900",
|
"rounded-md border bg-white dark:bg-nb-gray-900",
|
||||||
"border-neutral-200 dark:border-nb-gray-700",
|
"border-neutral-200 dark:border-nb-gray-700",
|
||||||
"text-xs font-semibold text-nb-gray-100 cursor-default outline-none",
|
"cursor-default text-xs font-semibold text-nb-gray-100 outline-none",
|
||||||
"hover:border-nb-gray-600 data-[state=open]:border-nb-gray-600",
|
"hover:border-nb-gray-600 data-[state=open]:border-nb-gray-600",
|
||||||
"focus-visible:ring-2 focus-visible:ring-white/60 focus-visible:ring-offset-2 focus-visible:ring-offset-nb-gray-940",
|
"focus-visible:ring-2 focus-visible:ring-white/60 focus-visible:ring-offset-2 focus-visible:ring-offset-nb-gray-940",
|
||||||
"disabled:opacity-50",
|
"disabled:opacity-50",
|
||||||
@@ -103,15 +103,15 @@ export function LanguagePicker() {
|
|||||||
<LanguagesIcon
|
<LanguagesIcon
|
||||||
size={16}
|
size={16}
|
||||||
aria-hidden={"true"}
|
aria-hidden={"true"}
|
||||||
className={"text-nb-gray-200 shrink-0"}
|
className={"shrink-0 text-nb-gray-200"}
|
||||||
/>
|
/>
|
||||||
<span className={"truncate flex-1 text-left"}>
|
<span className={"flex-1 truncate text-left"}>
|
||||||
{current ? labelFor(current) : "—"}
|
{current ? labelFor(current) : "—"}
|
||||||
</span>
|
</span>
|
||||||
<ChevronDown
|
<ChevronDown
|
||||||
size={12}
|
size={12}
|
||||||
aria-hidden={"true"}
|
aria-hidden={"true"}
|
||||||
className={"text-nb-gray-400 shrink-0"}
|
className={"shrink-0 text-nb-gray-400"}
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
</Popover.Trigger>
|
</Popover.Trigger>
|
||||||
@@ -122,7 +122,7 @@ export function LanguagePicker() {
|
|||||||
sideOffset={6}
|
sideOffset={6}
|
||||||
className={cn(
|
className={cn(
|
||||||
"w-[var(--radix-popover-trigger-width)]",
|
"w-[var(--radix-popover-trigger-width)]",
|
||||||
"rounded-lg border border-nb-gray-850 bg-nb-gray-920 shadow-lg p-1 z-50",
|
"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-[side=bottom]:origin-top data-[side=top]:origin-bottom",
|
||||||
"data-[state=open]:animate-in",
|
"data-[state=open]:animate-in",
|
||||||
"data-[state=open]:fade-in-0",
|
"data-[state=open]:fade-in-0",
|
||||||
@@ -142,12 +142,12 @@ export function LanguagePicker() {
|
|||||||
<div className={"px-1 pb-1"}>
|
<div className={"px-1 pb-1"}>
|
||||||
<div
|
<div
|
||||||
role={"search"}
|
role={"search"}
|
||||||
className={"group flex items-center gap-2 px-1 h-8"}
|
className={"group flex h-8 items-center gap-2 px-1"}
|
||||||
>
|
>
|
||||||
<Search
|
<Search
|
||||||
size={14}
|
size={14}
|
||||||
aria-hidden={"true"}
|
aria-hidden={"true"}
|
||||||
className={"text-nb-gray-200 shrink-0"}
|
className={"shrink-0 text-nb-gray-200"}
|
||||||
/>
|
/>
|
||||||
<Command.Input
|
<Command.Input
|
||||||
autoFocus
|
autoFocus
|
||||||
@@ -155,18 +155,20 @@ export function LanguagePicker() {
|
|||||||
aria-label={t("settings.general.language.search")}
|
aria-label={t("settings.general.language.search")}
|
||||||
className={cn(
|
className={cn(
|
||||||
"w-full bg-transparent text-xs text-nb-gray-100 placeholder:text-nb-gray-300",
|
"w-full bg-transparent text-xs text-nb-gray-100 placeholder:text-nb-gray-300",
|
||||||
"outline-none border-none",
|
"border-none outline-none",
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ScrollArea.Root type={"auto"} className={"overflow-hidden -mx-1"}>
|
<ScrollArea.Root type={"auto"} className={"-mx-1 overflow-hidden"}>
|
||||||
<ScrollArea.Viewport className={"max-h-64 px-1"}>
|
<ScrollArea.Viewport className={"max-h-64 px-1"}>
|
||||||
<Command.List>
|
<Command.List>
|
||||||
<Command.Empty>
|
<Command.Empty>
|
||||||
<div
|
<div
|
||||||
className={"px-3 py-4 text-center text-[0.7rem] text-nb-gray-400"}
|
className={
|
||||||
|
"px-3 py-4 text-center text-[0.7rem] text-nb-gray-400"
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{t("settings.general.language.empty")}
|
{t("settings.general.language.empty")}
|
||||||
</div>
|
</div>
|
||||||
@@ -180,17 +182,19 @@ export function LanguagePicker() {
|
|||||||
value={`${lang.displayName} ${lang.englishName} ${lang.code}`}
|
value={`${lang.displayName} ${lang.englishName} ${lang.code}`}
|
||||||
onSelect={() => void select(lang.code)}
|
onSelect={() => void select(lang.code)}
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex items-center gap-2 px-2 py-2 rounded-md cursor-default outline-none my-0.5",
|
"my-0.5 flex cursor-default items-center gap-2 rounded-md px-2 py-2 outline-none",
|
||||||
"text-xs font-semibold text-nb-gray-200",
|
"text-xs font-semibold text-nb-gray-200",
|
||||||
"data-[selected=true]:bg-nb-gray-850 data-[selected=true]:text-nb-gray-50",
|
"data-[selected=true]:bg-nb-gray-850 data-[selected=true]:text-nb-gray-50",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<span className={"flex-1 min-w-0 truncate"}>
|
<span className={"min-w-0 flex-1 truncate"}>
|
||||||
{labelFor(lang)}
|
{labelFor(lang)}
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
aria-hidden={"true"}
|
aria-hidden={"true"}
|
||||||
className={"w-4 shrink-0 flex items-center justify-center"}
|
className={
|
||||||
|
"flex w-4 shrink-0 items-center justify-center"
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{checked && (
|
{checked && (
|
||||||
<CheckIcon
|
<CheckIcon
|
||||||
@@ -207,12 +211,14 @@ export function LanguagePicker() {
|
|||||||
<ScrollArea.Scrollbar
|
<ScrollArea.Scrollbar
|
||||||
orientation={"vertical"}
|
orientation={"vertical"}
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex select-none touch-none transition-colors",
|
"flex touch-none select-none transition-colors",
|
||||||
"w-1.5 bg-transparent py-1",
|
"w-1.5 bg-transparent py-1",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<ScrollArea.Thumb
|
<ScrollArea.Thumb
|
||||||
className={"flex-1 rounded-full bg-nb-gray-800 hover:bg-nb-gray-700 relative"}
|
className={
|
||||||
|
"relative flex-1 rounded-full bg-nb-gray-800 hover:bg-nb-gray-700"
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</ScrollArea.Scrollbar>
|
</ScrollArea.Scrollbar>
|
||||||
</ScrollArea.Root>
|
</ScrollArea.Root>
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ export const ManagementServerSwitch = ({ value, onChange, fullWidth = false }: P
|
|||||||
src={netbirdLogo}
|
src={netbirdLogo}
|
||||||
alt={""}
|
alt={""}
|
||||||
aria-hidden={"true"}
|
aria-hidden={"true"}
|
||||||
className={"h-[0.8rem] aspect-[31/23] shrink-0"}
|
className={"aspect-[31/23] h-[0.8rem] shrink-0"}
|
||||||
/>
|
/>
|
||||||
{t("settings.general.management.cloud")}
|
{t("settings.general.management.cloud")}
|
||||||
</SwitchItem>
|
</SwitchItem>
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export const SquareIcon = ({
|
|||||||
<div
|
<div
|
||||||
aria-hidden={"true"}
|
aria-hidden={"true"}
|
||||||
className={cn(
|
className={cn(
|
||||||
"h-11 w-11 rounded-lg flex items-center justify-center border bg-nb-gray-920 border-nb-gray-900",
|
"flex h-11 w-11 items-center justify-center rounded-lg border border-nb-gray-900 bg-nb-gray-920",
|
||||||
variantClass[variant],
|
variantClass[variant],
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ const Root = forwardRef<HTMLDivElement, Omit<Tabs.TabsProps, "orientation">>(
|
|||||||
<Tabs.Root
|
<Tabs.Root
|
||||||
ref={ref}
|
ref={ref}
|
||||||
orientation={"vertical"}
|
orientation={"vertical"}
|
||||||
className={cn("flex flex-1 min-h-0", className)}
|
className={cn("flex min-h-0 flex-1", className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@@ -23,7 +23,7 @@ const List = forwardRef<HTMLDivElement, Tabs.TabsListProps>(function VerticalTab
|
|||||||
return (
|
return (
|
||||||
<Tabs.List
|
<Tabs.List
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn("w-full flex flex-col gap-1 p-5 pr-0", className)}
|
className={cn("flex w-full flex-col gap-1 p-5 pr-0", className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@@ -44,7 +44,7 @@ const Trigger = forwardRef<HTMLButtonElement, TriggerProps>(function VerticalTab
|
|||||||
<Tabs.Trigger
|
<Tabs.Trigger
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"group w-full flex items-center gap-3 py-2.5 px-2 rounded-lg cursor-default outline-none text-left",
|
"group flex w-full cursor-default items-center gap-3 rounded-lg px-2 py-2.5 text-left outline-none",
|
||||||
"transition-colors duration-150",
|
"transition-colors duration-150",
|
||||||
"data-[state=active]:bg-nb-gray-930",
|
"data-[state=active]:bg-nb-gray-930",
|
||||||
"data-[state=inactive]:hover:bg-nb-gray-935",
|
"data-[state=inactive]:hover:bg-nb-gray-935",
|
||||||
@@ -57,13 +57,13 @@ const Trigger = forwardRef<HTMLButtonElement, TriggerProps>(function VerticalTab
|
|||||||
size={iconSize}
|
size={iconSize}
|
||||||
aria-hidden={"true"}
|
aria-hidden={"true"}
|
||||||
className={cn(
|
className={cn(
|
||||||
"shrink-0 ml-2 transition-colors duration-150",
|
"ml-2 shrink-0 transition-colors duration-150",
|
||||||
"text-nb-gray-400 group-data-[state=active]:text-nb-gray-100",
|
"text-nb-gray-400 group-data-[state=active]:text-nb-gray-100",
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
className={cn(
|
className={cn(
|
||||||
"font-medium text-sm truncate min-w-0 transition-colors duration-150",
|
"min-w-0 truncate text-sm font-medium transition-colors duration-150",
|
||||||
"text-nb-gray-400 group-data-[state=active]:text-nb-gray-100",
|
"text-nb-gray-400 group-data-[state=active]:text-nb-gray-100",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -16,88 +16,88 @@ interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement>, ButtonVar
|
|||||||
const buttonVariants = cva(
|
const buttonVariants = cva(
|
||||||
[
|
[
|
||||||
"relative",
|
"relative",
|
||||||
"text-sm focus:z-10 focus:ring-2 font-medium focus:outline-none whitespace-nowrap shadow-sm select-none cursor-default",
|
"cursor-default select-none whitespace-nowrap text-sm font-medium shadow-sm focus:z-10 focus:outline-none focus:ring-2",
|
||||||
"inline-flex gap-2 items-center justify-center transition-colors focus:ring-offset-1",
|
"inline-flex items-center justify-center gap-2 transition-colors focus:ring-offset-1",
|
||||||
"disabled:opacity-40 disabled:cursor-not-allowed disabled:dark:text-nb-gray-300 dark:ring-offset-neutral-950/50",
|
"disabled:cursor-not-allowed disabled:opacity-40 dark:ring-offset-neutral-950/50 disabled:dark:text-nb-gray-300",
|
||||||
],
|
],
|
||||||
{
|
{
|
||||||
variants: {
|
variants: {
|
||||||
variant: {
|
variant: {
|
||||||
default: [
|
default: [
|
||||||
"bg-white hover:text-black focus:ring-zinc-200/50 hover:bg-gray-100 border-gray-200 text-gray-900",
|
"border-gray-200 bg-white text-gray-900 hover:bg-gray-100 hover:text-black focus:ring-zinc-200/50",
|
||||||
"dark:focus:ring-zinc-800/50 dark:bg-nb-gray dark:text-gray-400 dark:border-gray-700/30 dark:hover:text-white dark:hover:bg-zinc-800/50",
|
"dark:border-gray-700/30 dark:bg-nb-gray dark:text-gray-400 dark:hover:bg-zinc-800/50 dark:hover:text-white dark:focus:ring-zinc-800/50",
|
||||||
],
|
],
|
||||||
primary: [
|
primary: [
|
||||||
"dark:focus:ring-netbird-600/50 dark:ring-offset-neutral-950/50 enabled:dark:bg-netbird disabled:dark:bg-nb-gray-900 dark:text-gray-100 enabled:dark:hover:text-white enabled:dark:hover:bg-netbird-500/80",
|
"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-white disabled:dark:bg-nb-gray-900",
|
||||||
"enabled:bg-netbird enabled:text-white enabled:focus:ring-netbird-400/50 enabled:hover:bg-netbird-500",
|
"enabled:bg-netbird enabled:text-white enabled:hover:bg-netbird-500 enabled:focus:ring-netbird-400/50",
|
||||||
],
|
],
|
||||||
secondary: [
|
secondary: [
|
||||||
"bg-white hover:text-black focus:ring-zinc-200/50 hover:bg-gray-100 border-gray-200 text-gray-900",
|
"border-gray-200 bg-white text-gray-900 hover:bg-gray-100 hover:text-black focus:ring-zinc-200/50",
|
||||||
"dark:ring-offset-neutral-950/50 dark:focus:ring-neutral-500/20",
|
"dark:ring-offset-neutral-950/50 dark:focus:ring-neutral-500/20",
|
||||||
"dark:bg-nb-gray-920 dark:text-gray-400 dark:border-gray-700/40 dark:hover:text-white dark:hover:bg-nb-gray-910",
|
"dark:border-gray-700/40 dark:bg-nb-gray-920 dark:text-gray-400 dark:hover:bg-nb-gray-910 dark:hover:text-white",
|
||||||
],
|
],
|
||||||
secondaryLighter: [
|
secondaryLighter: [
|
||||||
"bg-white hover:text-black focus:ring-zinc-200/50 hover:bg-gray-100 border-gray-200 text-gray-900",
|
"border-gray-200 bg-white text-gray-900 hover:bg-gray-100 hover:text-black focus:ring-zinc-200/50",
|
||||||
"dark:ring-offset-neutral-950/50 dark:focus:ring-neutral-500/20",
|
"dark:ring-offset-neutral-950/50 dark:focus:ring-neutral-500/20",
|
||||||
"dark:bg-nb-gray-900/70 dark:text-gray-400 dark:border-gray-700/70 dark:hover:text-white dark:hover:bg-nb-gray-800/60",
|
"dark:border-gray-700/70 dark:bg-nb-gray-900/70 dark:text-gray-400 dark:hover:bg-nb-gray-800/60 dark:hover:text-white",
|
||||||
],
|
],
|
||||||
subtle: [
|
subtle: [
|
||||||
"bg-nb-gray-50 hover:bg-nb-gray-100 focus:ring-nb-gray-200/60 border-nb-gray-200 text-nb-gray-900",
|
"border-nb-gray-200 bg-nb-gray-50 text-nb-gray-900 hover:bg-nb-gray-100 focus:ring-nb-gray-200/60",
|
||||||
"dark:ring-offset-neutral-950/50 dark:focus:ring-nb-gray-200/40",
|
"dark:ring-offset-neutral-950/50 dark:focus:ring-nb-gray-200/40",
|
||||||
"dark:bg-nb-gray-50 dark:text-nb-gray-900 dark:border-nb-gray-200 dark:hover:bg-nb-gray-100 dark:hover:text-nb-gray-950",
|
"dark:border-nb-gray-200 dark:bg-nb-gray-50 dark:text-nb-gray-900 dark:hover:bg-nb-gray-100 dark:hover:text-nb-gray-950",
|
||||||
],
|
],
|
||||||
input: [
|
input: [
|
||||||
"bg-white hover:text-black focus:ring-zinc-200/50 hover:bg-gray-100 border-neutral-200 text-gray-900",
|
"border-neutral-200 bg-white text-gray-900 hover:bg-gray-100 hover:text-black focus:ring-zinc-200/50",
|
||||||
"dark:ring-offset-neutral-950/50 dark:focus:ring-neutral-500/20",
|
"dark:ring-offset-neutral-950/50 dark:focus:ring-neutral-500/20",
|
||||||
"dark:bg-nb-gray-900 dark:text-gray-400 dark:border-nb-gray-700 dark:hover:bg-nb-gray-900/80",
|
"dark:border-nb-gray-700 dark:bg-nb-gray-900 dark:text-gray-400 dark:hover:bg-nb-gray-900/80",
|
||||||
],
|
],
|
||||||
dropdown: [
|
dropdown: [
|
||||||
"bg-white hover:text-black focus:ring-zinc-200/50 hover:bg-gray-100 border-neutral-200 text-gray-900",
|
"border-neutral-200 bg-white text-gray-900 hover:bg-gray-100 hover:text-black focus:ring-zinc-200/50",
|
||||||
"dark:ring-offset-neutral-950/50 dark:focus:ring-neutral-500/20",
|
"dark:ring-offset-neutral-950/50 dark:focus:ring-neutral-500/20",
|
||||||
"dark:bg-nb-gray-900/40 dark:text-gray-400 dark:border-nb-gray-900 dark:hover:bg-nb-gray-900/50",
|
"dark:border-nb-gray-900 dark:bg-nb-gray-900/40 dark:text-gray-400 dark:hover:bg-nb-gray-900/50",
|
||||||
],
|
],
|
||||||
dotted: [
|
dotted: [
|
||||||
"bg-white hover:text-black focus:ring-zinc-200/50 hover:bg-gray-100 border-gray-200 text-gray-900 border-dashed",
|
"border-dashed border-gray-200 bg-white text-gray-900 hover:bg-gray-100 hover:text-black focus:ring-zinc-200/50",
|
||||||
"dark:ring-offset-neutral-950/50 dark:focus:ring-neutral-500/20",
|
"dark:ring-offset-neutral-950/50 dark:focus:ring-neutral-500/20",
|
||||||
"dark:bg-nb-gray-900/30 dark:text-gray-400 dark:border-gray-500/40 dark:hover:text-white dark:hover:bg-nb-gray-900/50",
|
"dark:border-gray-500/40 dark:bg-nb-gray-900/30 dark:text-gray-400 dark:hover:bg-nb-gray-900/50 dark:hover:text-white",
|
||||||
],
|
],
|
||||||
tertiary: [
|
tertiary: [
|
||||||
"bg-white hover:text-black focus:ring-zinc-200/50 hover:bg-gray-100 border-gray-200 text-gray-900",
|
"border-gray-200 bg-white text-gray-900 hover:bg-gray-100 hover:text-black focus:ring-zinc-200/50",
|
||||||
"dark:focus:ring-zinc-800/50 dark:bg-white dark:text-gray-800 dark:border-gray-700/40 dark:hover:bg-neutral-200 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 disabled:dark:bg-nb-gray-920 disabled:dark:text-nb-gray-300",
|
||||||
],
|
],
|
||||||
white: [
|
white: [
|
||||||
"focus:ring-white/50 bg-white text-gray-800 border-white outline-none hover:bg-neutral-200 disabled:dark:bg-nb-gray-920 disabled:dark:text-nb-gray-300",
|
"border-white bg-white text-gray-800 outline-none hover:bg-neutral-200 focus:ring-white/50 disabled:dark:bg-nb-gray-920 disabled:dark:text-nb-gray-300",
|
||||||
"disabled:dark:bg-nb-gray-900 disabled:dark:text-nb-gray-300 disabled:dark:border-nb-gray-900",
|
"disabled:dark:border-nb-gray-900 disabled:dark:bg-nb-gray-900 disabled:dark:text-nb-gray-300",
|
||||||
],
|
],
|
||||||
outline: [
|
outline: [
|
||||||
"bg-white hover:text-black focus:ring-zinc-200/50 hover:bg-gray-100 border-gray-200 text-gray-900",
|
"border-gray-200 bg-white text-gray-900 hover:bg-gray-100 hover:text-black focus:ring-zinc-200/50",
|
||||||
"dark:focus:ring-zinc-800/50 dark:bg-transparent dark:text-netbird dark:border-netbird dark:hover:bg-nb-gray-900/30",
|
"dark:border-netbird dark:bg-transparent dark:text-netbird dark:hover:bg-nb-gray-900/30 dark:focus:ring-zinc-800/50",
|
||||||
],
|
],
|
||||||
"danger-outline": [
|
"danger-outline": [
|
||||||
"enabled:dark:focus:ring-red-800/20 enabled:dark:focus:bg-red-950/40 enabled:hover:dark:bg-red-950/50 enabled:dark:hover:border-red-800/50 dark:bg-transparent dark:text-red-500",
|
"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",
|
||||||
],
|
],
|
||||||
"danger-text": [
|
"danger-text": [
|
||||||
"dark:bg-transparent dark:text-red-500 dark:hover:text-red-600 dark:border-transparent !px-0 !shadow-none !py-0 focus:ring-red-500/30 dark:ring-offset-neutral-950/50 rounded-sm",
|
"rounded-sm !px-0 !py-0 !shadow-none 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": [
|
"default-outline": [
|
||||||
"dark:ring-offset-nb-gray-950/50 dark:focus:ring-nb-gray-500/20",
|
"dark:ring-offset-nb-gray-950/50 dark:focus:ring-nb-gray-500/20",
|
||||||
"dark:bg-transparent dark:text-nb-gray-400 dark:border-transparent dark:hover:text-white dark:hover:bg-nb-gray-900/30 dark:hover:border-nb-gray-800/50",
|
"dark:border-transparent dark:bg-transparent dark:text-nb-gray-400 dark:hover:border-nb-gray-800/50 dark:hover:bg-nb-gray-900/30 dark:hover:text-white",
|
||||||
"data-[state=open]:dark:text-white data-[state=open]:dark:bg-nb-gray-900/30 data-[state=open]:dark:border-nb-gray-800/50",
|
"data-[state=open]:dark:border-nb-gray-800/50 data-[state=open]:dark:bg-nb-gray-900/30 data-[state=open]:dark:text-white",
|
||||||
],
|
],
|
||||||
ghost: [
|
ghost: [
|
||||||
"dark:ring-offset-nb-gray-950/50 dark:focus:ring-nb-gray-500/20",
|
"dark:ring-offset-nb-gray-950/50 dark:focus:ring-nb-gray-500/20",
|
||||||
"dark:bg-transparent dark:text-nb-gray-400 dark:border-transparent dark:hover:text-white dark:hover:bg-nb-gray-900/30",
|
"dark:border-transparent dark:bg-transparent dark:text-nb-gray-400 dark:hover:bg-nb-gray-900/30 dark:hover:text-white",
|
||||||
],
|
],
|
||||||
danger: [
|
danger: [
|
||||||
"dark:focus:ring-red-700/20 dark:focus:bg-red-700 hover:dark:bg-red-700 dark:hover:border-red-800/50 dark:bg-red-600 dark:text-red-100",
|
"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",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
size: {
|
size: {
|
||||||
xs: "text-xs py-2.5 px-3.5",
|
xs: "px-3.5 py-2.5 text-xs",
|
||||||
xs2: "text-[0.78rem] py-[1.1rem] px-4 leading-[0]",
|
xs2: "px-4 py-[1.1rem] text-[0.78rem] leading-[0]",
|
||||||
sm: "text-sm py-[9px] px-4",
|
sm: "px-4 py-[9px] text-sm",
|
||||||
md: "py-[9px] px-4",
|
md: "px-4 py-[9px]",
|
||||||
lg: "text-lg py-[9px] px-4",
|
lg: "px-4 py-[9px] text-lg",
|
||||||
},
|
},
|
||||||
rounded: {
|
rounded: {
|
||||||
true: "rounded-md",
|
true: "rounded-md",
|
||||||
@@ -106,7 +106,7 @@ const buttonVariants = cva(
|
|||||||
border: {
|
border: {
|
||||||
0: "border",
|
0: "border",
|
||||||
1: "border border-transparent",
|
1: "border border-transparent",
|
||||||
2: "border border-t-0 border-b-0",
|
2: "border border-b-0 border-t-0",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -19,10 +19,10 @@ export const IconButton = forwardRef<HTMLButtonElement, Props>(function IconButt
|
|||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
tabIndex={disabled ? -1 : 0}
|
tabIndex={disabled ? -1 : 0}
|
||||||
className={cn(
|
className={cn(
|
||||||
"h-10 w-10 flex items-center justify-center rounded-lg cursor-default outline-none",
|
"flex h-10 w-10 cursor-default items-center justify-center rounded-lg outline-none",
|
||||||
"text-nb-gray-400 hover:text-nb-gray-300 hover:bg-nb-gray-900",
|
"text-nb-gray-400 hover:bg-nb-gray-900 hover:text-nb-gray-300",
|
||||||
"focus-visible:ring-2 focus-visible:ring-white/60 focus-visible:ring-offset-2 focus-visible:ring-offset-nb-gray-940",
|
"focus-visible:ring-2 focus-visible:ring-white/60 focus-visible:ring-offset-2 focus-visible:ring-offset-nb-gray-940",
|
||||||
"transition-colors duration-150 wails-no-draggable",
|
"wails-no-draggable transition-colors duration-150",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|||||||
@@ -17,12 +17,14 @@ export const ConfirmDialog = forwardRef<HTMLDivElement, ConfirmDialogProps>(func
|
|||||||
open
|
open
|
||||||
aria-label={ariaLabel}
|
aria-label={ariaLabel}
|
||||||
aria-labelledby={ariaLabelledBy}
|
aria-labelledby={ariaLabelledBy}
|
||||||
className={"wails-draggable select-none flex flex-col items-center static bg-transparent text-inherit p-0 m-0 max-w-none max-h-none border-0 w-full"}
|
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"
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex flex-col items-center gap-5 text-center px-8 pt-6 pb-7",
|
"flex flex-col items-center gap-5 px-8 pb-7 pt-6 text-center",
|
||||||
isMacOS() && "pt-10",
|
isMacOS() && "pt-10",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,4 +1,9 @@
|
|||||||
import { forwardRef, type ComponentPropsWithoutRef, type ElementRef, type HTMLAttributes } from "react";
|
import {
|
||||||
|
forwardRef,
|
||||||
|
type ComponentPropsWithoutRef,
|
||||||
|
type ElementRef,
|
||||||
|
type HTMLAttributes,
|
||||||
|
} from "react";
|
||||||
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
||||||
import { VisuallyHidden } from "@radix-ui/react-visually-hidden";
|
import { VisuallyHidden } from "@radix-ui/react-visually-hidden";
|
||||||
import { X } from "lucide-react";
|
import { X } from "lucide-react";
|
||||||
@@ -60,9 +65,9 @@ export const Content = forwardRef<ElementRef<typeof DialogPrimitive.Content>, Co
|
|||||||
<DialogPrimitive.Content
|
<DialogPrimitive.Content
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"mx-auto relative z-[52] w-full outline-none ring-0",
|
"relative z-[52] mx-auto w-full outline-none ring-0",
|
||||||
"focus:outline-none focus-visible:outline-none focus:ring-0 focus-visible:ring-0",
|
"focus:outline-none focus:ring-0 focus-visible:outline-none focus-visible:ring-0",
|
||||||
"border border-nb-gray-900 bg-nb-gray py-7 shadow-2xl rounded-lg",
|
"rounded-lg border border-nb-gray-900 bg-nb-gray py-7 shadow-2xl",
|
||||||
"data-[state=open]:animate-in data-[state=open]:fade-in-0",
|
"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",
|
"data-[state=open]:zoom-in-95 data-[state=open]:slide-in-from-left-1",
|
||||||
exitAnimation &&
|
exitAnimation &&
|
||||||
@@ -129,7 +134,7 @@ export const Description = forwardRef<
|
|||||||
return (
|
return (
|
||||||
<DialogPrimitive.Description
|
<DialogPrimitive.Description
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn("text-sm text-nb-gray-400 mt-2 leading-snug", className)}
|
className={cn("mt-2 text-sm leading-snug text-nb-gray-400", className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@@ -140,7 +145,7 @@ type FooterProps = HTMLAttributes<HTMLDivElement> & {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const Footer = ({ className, separator = true, ...props }: FooterProps) => (
|
export const Footer = ({ className, separator = true, ...props }: FooterProps) => (
|
||||||
<div className={cn(separator && "border-t border-nb-gray-900 mt-6")}>
|
<div className={cn(separator && "mt-6 border-t border-nb-gray-900")}>
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex flex-col-reverse gap-3 sm:flex-row sm:justify-end",
|
"flex flex-col-reverse gap-3 sm:flex-row sm:justify-end",
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ type DialogActionsProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const DialogActions = ({ children, className }: DialogActionsProps) => (
|
export const DialogActions = ({ children, className }: DialogActionsProps) => (
|
||||||
<div className={cn("wails-no-draggable flex flex-col gap-3 w-full mx-auto", className)}>
|
<div className={cn("wails-no-draggable mx-auto flex w-full flex-col gap-3", className)}>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ export const DialogDescription = ({
|
|||||||
className,
|
className,
|
||||||
align = "center",
|
align = "center",
|
||||||
}: DialogDescriptionProps) => (
|
}: DialogDescriptionProps) => (
|
||||||
<p className={cn("w-full text-sm text-nb-gray-300 select-none", alignClass[align], className)}>
|
<p className={cn("w-full select-none text-sm text-nb-gray-300", alignClass[align], className)}>
|
||||||
{children}
|
{children}
|
||||||
</p>
|
</p>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export const DialogHeading = ({
|
|||||||
<h2
|
<h2
|
||||||
id={id}
|
id={id}
|
||||||
className={cn(
|
className={cn(
|
||||||
"w-full text-base font-semibold text-nb-gray-50 select-none",
|
"w-full select-none text-base font-semibold text-nb-gray-50",
|
||||||
alignClass[align],
|
alignClass[align],
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -18,11 +18,15 @@ export const DaemonOutdatedOverlay = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={"fixed inset-0 z-[100] flex items-center justify-center bg-nb-gray-950 backdrop-blur-sm cursor-default select-none wails-draggable"}
|
className={
|
||||||
|
"wails-draggable fixed inset-0 z-[100] flex cursor-default select-none items-center justify-center bg-nb-gray-950 backdrop-blur-sm"
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<div className={"flex flex-col items-center gap-5 px-8 max-w-lg text-center"}>
|
<div className={"flex max-w-lg flex-col items-center gap-5 px-8 text-center"}>
|
||||||
<div
|
<div
|
||||||
className={"h-11 w-11 rounded-xl flex items-center justify-center bg-nb-gray-920 border border-nb-gray-900 text-amber-500"}
|
className={
|
||||||
|
"flex h-11 w-11 items-center justify-center rounded-xl border border-nb-gray-900 bg-nb-gray-920 text-amber-500"
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<AlertTriangleIcon size={20} />
|
<AlertTriangleIcon size={20} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -18,11 +18,15 @@ export const DaemonUnavailableOverlay = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={"fixed inset-0 z-[100] flex items-center justify-center bg-nb-gray-950 backdrop-blur-sm cursor-default select-none wails-draggable"}
|
className={
|
||||||
|
"wails-draggable fixed inset-0 z-[100] flex cursor-default select-none items-center justify-center bg-nb-gray-950 backdrop-blur-sm"
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<div className={"flex flex-col items-center gap-5 px-8 max-w-lg text-center"}>
|
<div className={"flex max-w-lg flex-col items-center gap-5 px-8 text-center"}>
|
||||||
<div
|
<div
|
||||||
className={"h-11 w-11 rounded-xl flex items-center justify-center bg-nb-gray-920 border border-nb-gray-900 text-red-500"}
|
className={
|
||||||
|
"flex h-11 w-11 items-center justify-center rounded-xl border border-nb-gray-900 bg-nb-gray-920 text-red-500"
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<AlertCircleIcon size={20} />
|
<AlertCircleIcon size={20} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -19,11 +19,11 @@ export const EmptyState = ({ icon, title, description, className }: Props) => {
|
|||||||
return (
|
return (
|
||||||
<div className={cn("py-12 text-center", className)}>
|
<div className={cn("py-12 text-center", className)}>
|
||||||
<div
|
<div
|
||||||
className={"flex flex-col items-center justify-start max-w-sm mx-auto relative"}
|
className={"relative mx-auto flex max-w-sm flex-col items-center justify-start"}
|
||||||
style={{ top: contentTop("7.8rem") }}
|
style={{ top: contentTop("7.8rem") }}
|
||||||
>
|
>
|
||||||
<SquareIcon icon={icon} className={"mb-3"} />
|
<SquareIcon icon={icon} className={"mb-3"} />
|
||||||
<p className={"text-[0.95rem] font-medium text-nb-gray-200 mb-1"}>{title}</p>
|
<p className={"mb-1 text-[0.95rem] font-medium text-nb-gray-200"}>{title}</p>
|
||||||
{description && <p className={"text-sm text-nb-gray-350"}>{description}</p>}
|
{description && <p className={"text-sm text-nb-gray-350"}>{description}</p>}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export const NoResults = ({ icon = FunnelXIcon, title, description }: Props) =>
|
|||||||
icon={icon}
|
icon={icon}
|
||||||
title={title ?? t("common.noResults.title")}
|
title={title ?? t("common.noResults.title")}
|
||||||
description={description ?? t("common.noResults.description")}
|
description={description ?? t("common.noResults.description")}
|
||||||
className={"relative -top-[3.8rem] pointer-events-none"}
|
className={"pointer-events-none relative -top-[3.8rem]"}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { EmptyState } from "./EmptyState";
|
|||||||
export const NotConnectedState = () => {
|
export const NotConnectedState = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
return (
|
return (
|
||||||
<div className={"relative w-full top-[3rem]"}>
|
<div className={"relative top-[3rem] w-full"}>
|
||||||
<EmptyState
|
<EmptyState
|
||||||
icon={GlobeOffIcon}
|
icon={GlobeOffIcon}
|
||||||
title={t("notConnected.title")}
|
title={t("notConnected.title")}
|
||||||
|
|||||||
@@ -32,27 +32,27 @@ const inputVariants = cva("", {
|
|||||||
variants: {
|
variants: {
|
||||||
variant: {
|
variant: {
|
||||||
default: [
|
default: [
|
||||||
"dark:bg-nb-gray-900 dark:placeholder:text-neutral-400/70 placeholder:text-neutral-500 border-neutral-200 dark:border-nb-gray-700",
|
"border-neutral-200 placeholder:text-neutral-500 dark:border-nb-gray-700 dark:bg-nb-gray-900 dark:placeholder:text-neutral-400/70",
|
||||||
"ring-offset-neutral-200/20 dark:ring-offset-neutral-950/50 dark:focus-visible:ring-neutral-500/20 focus-visible:ring-neutral-300/10",
|
"ring-offset-neutral-200/20 focus-visible:ring-neutral-300/10 dark:ring-offset-neutral-950/50 dark:focus-visible:ring-neutral-500/20",
|
||||||
],
|
],
|
||||||
darker: [
|
darker: [
|
||||||
"dark:bg-nb-gray-920 dark:placeholder:text-neutral-400/70 placeholder:text-neutral-500 border-neutral-300 dark:border-nb-gray-800",
|
"border-neutral-300 placeholder:text-neutral-500 dark:border-nb-gray-800 dark:bg-nb-gray-920 dark:placeholder:text-neutral-400/70",
|
||||||
"ring-offset-neutral-200/20 dark:ring-offset-neutral-950/50 dark:focus-visible:ring-neutral-500/20 focus-visible:ring-neutral-300/10",
|
"ring-offset-neutral-200/20 focus-visible:ring-neutral-300/10 dark:ring-offset-neutral-950/50 dark:focus-visible:ring-neutral-500/20",
|
||||||
],
|
],
|
||||||
error: [
|
error: [
|
||||||
"dark:bg-nb-gray-900 dark:placeholder:text-neutral-400/70 placeholder:text-neutral-500 border-neutral-200 dark:border-red-500 text-red-500",
|
"border-neutral-200 text-red-500 placeholder:text-neutral-500 dark:border-red-500 dark:bg-nb-gray-900 dark:placeholder:text-neutral-400/70",
|
||||||
"ring-offset-red-500/10 dark:ring-offset-red-500/10 dark:focus-visible:ring-red-500/10 focus-visible:ring-red-500/10",
|
"ring-offset-red-500/10 focus-visible:ring-red-500/10 dark:ring-offset-red-500/10 dark:focus-visible:ring-red-500/10",
|
||||||
],
|
],
|
||||||
warning: [
|
warning: [
|
||||||
"dark:bg-nb-gray-900 dark:placeholder:text-neutral-400/70 placeholder:text-neutral-500 border-neutral-200 dark:border-orange-400 text-orange-400",
|
"border-neutral-200 text-orange-400 placeholder:text-neutral-500 dark:border-orange-400 dark:bg-nb-gray-900 dark:placeholder:text-neutral-400/70",
|
||||||
"ring-offset-orange-400/10 dark:ring-offset-orange-400/10 dark:focus-visible:ring-orange-400/10 focus-visible:ring-orange-400/10",
|
"ring-offset-orange-400/10 focus-visible:ring-orange-400/10 dark:ring-offset-orange-400/10 dark:focus-visible:ring-orange-400/10",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
prefixSuffixVariant: {
|
prefixSuffixVariant: {
|
||||||
default: [
|
default: [
|
||||||
"dark:bg-nb-gray-900 border-neutral-200 dark:border-nb-gray-700 text-nb-gray-300",
|
"border-neutral-200 text-nb-gray-300 dark:border-nb-gray-700 dark:bg-nb-gray-900",
|
||||||
],
|
],
|
||||||
error: ["dark:bg-nb-gray-900 border-red-500 text-nb-gray-300 text-red-500"],
|
error: ["border-red-500 text-nb-gray-300 text-red-500 dark:bg-nb-gray-900"],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -82,17 +82,17 @@ function buildInputClassName(
|
|||||||
): string {
|
): string {
|
||||||
return cn(
|
return cn(
|
||||||
inputVariants({ variant: opts.variant }),
|
inputVariants({ variant: opts.variant }),
|
||||||
"flex h-[40px] w-full rounded-md bg-white px-3 py-2 text-sm select-text",
|
"flex h-[40px] w-full select-text rounded-md bg-white px-3 py-2 text-sm",
|
||||||
"file:bg-transparent file:text-sm file:font-medium file:border-0",
|
"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:outline-none focus-visible:ring-2 focus-visible:ring-offset-2",
|
||||||
"disabled:cursor-not-allowed disabled:opacity-40",
|
"disabled:cursor-not-allowed disabled:opacity-40",
|
||||||
opts.hasCustomPrefix && "!border-l-0 !rounded-l-none",
|
opts.hasCustomPrefix && "!rounded-l-none !border-l-0",
|
||||||
opts.hasSuffix && "!pr-9",
|
opts.hasSuffix && "!pr-9",
|
||||||
opts.hasIcon && "!pl-10",
|
opts.hasIcon && "!pl-10",
|
||||||
"border",
|
"border",
|
||||||
opts.readOnly && "!bg-nb-gray-910 text-nb-gray-350 !border-nb-gray-800",
|
opts.readOnly && "!border-nb-gray-800 !bg-nb-gray-910 text-nb-gray-350",
|
||||||
opts.showStepper &&
|
opts.showStepper &&
|
||||||
"!rounded-r-none [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none [-moz-appearance:textfield]",
|
"!rounded-r-none [-moz-appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none",
|
||||||
opts.className,
|
opts.className,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -108,7 +108,7 @@ function InputAffix({
|
|||||||
className={cn(
|
className={cn(
|
||||||
inputVariants({ prefixSuffixVariant: error ? "error" : "default" }),
|
inputVariants({ prefixSuffixVariant: error ? "error" : "default" }),
|
||||||
"flex h-[40px] w-auto rounded-l-md bg-white px-3 py-2 text-sm",
|
"flex h-[40px] w-auto rounded-l-md bg-white px-3 py-2 text-sm",
|
||||||
"border items-center whitespace-nowrap",
|
"items-center whitespace-nowrap border",
|
||||||
disabled && "opacity-40",
|
disabled && "opacity-40",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
@@ -122,7 +122,7 @@ function InputIconSlot({ icon, disabled }: Readonly<{ icon: ReactNode; disabled?
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"absolute left-0 top-0 h-full flex items-center text-xs dark:text-nb-gray-300 pl-3 leading-[0]",
|
"absolute left-0 top-0 flex h-full items-center pl-3 text-xs leading-[0] dark:text-nb-gray-300",
|
||||||
disabled && "opacity-40",
|
disabled && "opacity-40",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
@@ -138,7 +138,7 @@ function InputSuffixSlot({
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"absolute right-0 top-0 h-full flex items-center text-xs dark:text-nb-gray-300 pr-3 leading-[0] select-none pointer-events-none",
|
"pointer-events-none absolute right-0 top-0 flex h-full select-none items-center pr-3 text-xs leading-[0] dark:text-nb-gray-300",
|
||||||
disabled && "opacity-30",
|
disabled && "opacity-30",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
@@ -156,11 +156,11 @@ function NumberStepper({
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex flex-col h-[40px] shrink-0 overflow-hidden",
|
"flex h-[40px] shrink-0 flex-col overflow-hidden",
|
||||||
"border border-l-0 rounded-r-md",
|
"rounded-r-md border border-l-0",
|
||||||
"border-neutral-200 dark:border-nb-gray-700 dark:bg-nb-gray-900",
|
"border-neutral-200 dark:border-nb-gray-700 dark:bg-nb-gray-900",
|
||||||
error && "dark:border-red-500",
|
error && "dark:border-red-500",
|
||||||
disabled && "opacity-40 pointer-events-none",
|
disabled && "pointer-events-none opacity-40",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
@@ -168,7 +168,9 @@ function NumberStepper({
|
|||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
aria-label={t("common.increase")}
|
aria-label={t("common.increase")}
|
||||||
onClick={() => onStep(1)}
|
onClick={() => onStep(1)}
|
||||||
className={"flex-1 flex items-center justify-center w-9 hover:bg-nb-gray-800 transition-colors text-nb-gray-300 cursor-default"}
|
className={
|
||||||
|
"flex w-9 flex-1 cursor-default items-center justify-center text-nb-gray-300 transition-colors hover:bg-nb-gray-800"
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<ChevronUp size={12} />
|
<ChevronUp size={12} />
|
||||||
</button>
|
</button>
|
||||||
@@ -178,7 +180,7 @@ function NumberStepper({
|
|||||||
aria-label={t("common.decrease")}
|
aria-label={t("common.decrease")}
|
||||||
onClick={() => onStep(-1)}
|
onClick={() => onStep(-1)}
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex-1 flex items-center justify-center w-9 hover:bg-nb-gray-800 transition-colors text-nb-gray-300 cursor-default",
|
"flex w-9 flex-1 cursor-default items-center justify-center text-nb-gray-300 transition-colors hover:bg-nb-gray-800",
|
||||||
"border-t border-neutral-200 dark:border-nb-gray-700",
|
"border-t border-neutral-200 dark:border-nb-gray-700",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
@@ -193,7 +195,7 @@ function FieldMessage({ error, warning }: Readonly<{ error?: string; warning?: s
|
|||||||
return (
|
return (
|
||||||
<span
|
<span
|
||||||
className={cn(
|
className={cn(
|
||||||
"text-xs mt-2 inline-flex items-center gap-1",
|
"mt-2 inline-flex items-center gap-1 text-xs",
|
||||||
error ? "text-red-500" : "text-orange-400",
|
error ? "text-red-500" : "text-orange-400",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
@@ -264,7 +266,7 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(function Input(
|
|||||||
<button
|
<button
|
||||||
type={"button"}
|
type={"button"}
|
||||||
onClick={() => setShowPassword((s) => !s)}
|
onClick={() => setShowPassword((s) => !s)}
|
||||||
className={"hover:text-white transition-all pointer-events-auto"}
|
className={"pointer-events-auto transition-all hover:text-white"}
|
||||||
aria-label={t("common.togglePasswordVisibility")}
|
aria-label={t("common.togglePasswordVisibility")}
|
||||||
>
|
>
|
||||||
{showPassword ? <EyeOff size={18} /> : <Eye size={18} />}
|
{showPassword ? <EyeOff size={18} /> : <Eye size={18} />}
|
||||||
@@ -288,7 +290,7 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(function Input(
|
|||||||
<button
|
<button
|
||||||
type={"button"}
|
type={"button"}
|
||||||
onClick={onCopy}
|
onClick={onCopy}
|
||||||
className={"hover:text-white transition-all pointer-events-auto"}
|
className={"pointer-events-auto transition-all hover:text-white"}
|
||||||
aria-label={t("common.copy")}
|
aria-label={t("common.copy")}
|
||||||
>
|
>
|
||||||
{copied ? <Check size={16} /> : <Copy size={16} />}
|
{copied ? <Check size={16} /> : <Copy size={16} />}
|
||||||
@@ -311,9 +313,9 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(function Input(
|
|||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={"flex flex-col w-full min-w-0"}>
|
<div className={"flex w-full min-w-0 flex-col"}>
|
||||||
{label && <Label htmlFor={inputId}>{label}</Label>}
|
{label && <Label htmlFor={inputId}>{label}</Label>}
|
||||||
<div className={cn("flex relative h-[40px] w-full", maxWidthClass)}>
|
<div className={cn("relative flex h-[40px] w-full", maxWidthClass)}>
|
||||||
{customPrefix && (
|
{customPrefix && (
|
||||||
<InputAffix
|
<InputAffix
|
||||||
content={customPrefix}
|
content={customPrefix}
|
||||||
@@ -325,7 +327,7 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(function Input(
|
|||||||
|
|
||||||
{icon && <InputIconSlot icon={icon} disabled={props.disabled} />}
|
{icon && <InputIconSlot icon={icon} disabled={props.disabled} />}
|
||||||
|
|
||||||
<div className={"relative flex flex-grow min-w-0"}>
|
<div className={"relative flex min-w-0 flex-grow"}>
|
||||||
<input
|
<input
|
||||||
id={inputId}
|
id={inputId}
|
||||||
type={inputType}
|
type={inputType}
|
||||||
|
|||||||
@@ -16,12 +16,12 @@ export const SearchInput = forwardRef<HTMLInputElement, Props>(function SearchIn
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
role={"search"}
|
role={"search"}
|
||||||
className={cn("flex items-center gap-2 px-1 h-10", disabled && "opacity-50")}
|
className={cn("flex h-10 items-center gap-2 px-1", disabled && "opacity-50")}
|
||||||
>
|
>
|
||||||
<SearchIcon
|
<SearchIcon
|
||||||
size={iconSize}
|
size={iconSize}
|
||||||
aria-hidden={"true"}
|
aria-hidden={"true"}
|
||||||
className={"text-nb-gray-300 shrink-0"}
|
className={"shrink-0 text-nb-gray-300"}
|
||||||
/>
|
/>
|
||||||
<input
|
<input
|
||||||
ref={ref}
|
ref={ref}
|
||||||
@@ -31,7 +31,7 @@ export const SearchInput = forwardRef<HTMLInputElement, Props>(function SearchIn
|
|||||||
{...props}
|
{...props}
|
||||||
className={cn(
|
className={cn(
|
||||||
"w-full bg-transparent text-sm text-nb-gray-200 placeholder:text-nb-gray-400",
|
"w-full bg-transparent text-sm text-nb-gray-200 placeholder:text-nb-gray-400",
|
||||||
"outline-none border-none",
|
"border-none outline-none",
|
||||||
disabled && "cursor-not-allowed",
|
disabled && "cursor-not-allowed",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
@@ -42,7 +42,7 @@ export const SearchInput = forwardRef<HTMLInputElement, Props>(function SearchIn
|
|||||||
className={cn(
|
className={cn(
|
||||||
"shrink-0 select-none",
|
"shrink-0 select-none",
|
||||||
"inline-flex items-center justify-center",
|
"inline-flex items-center justify-center",
|
||||||
"h-5 min-w-[20px] px-1.5 rounded",
|
"h-5 min-w-[20px] rounded px-1.5",
|
||||||
"border border-nb-gray-850 bg-nb-gray-920",
|
"border border-nb-gray-850 bg-nb-gray-920",
|
||||||
"text-[10px] font-medium text-nb-gray-400",
|
"text-[10px] font-medium text-nb-gray-400",
|
||||||
"wails-no-draggable",
|
"wails-no-draggable",
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ export default function FancyToggleSwitch({
|
|||||||
role={"status"}
|
role={"status"}
|
||||||
aria-busy={"true"}
|
aria-busy={"true"}
|
||||||
aria-live={"polite"}
|
aria-live={"polite"}
|
||||||
className={cn("inline-block text-left w-full", className)}
|
className={cn("inline-block w-full text-left", className)}
|
||||||
>
|
>
|
||||||
<div className={"flex justify-between gap-10"}>
|
<div className={"flex justify-between gap-10"}>
|
||||||
<div className={cn(textWrapperClassName)}>
|
<div className={cn(textWrapperClassName)}>
|
||||||
@@ -58,7 +58,7 @@ export default function FancyToggleSwitch({
|
|||||||
<div className={"mt-2 pr-1"}>
|
<div className={"mt-2 pr-1"}>
|
||||||
<div
|
<div
|
||||||
aria-hidden={"true"}
|
aria-hidden={"true"}
|
||||||
className={"h-[24px] w-[44px] rounded-full bg-[#25282d] animate-pulse"}
|
className={"h-[24px] w-[44px] animate-pulse rounded-full bg-[#25282d]"}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -70,9 +70,9 @@ export default function FancyToggleSwitch({
|
|||||||
<div
|
<div
|
||||||
{...(disabled ? { inert: "" } : {})}
|
{...(disabled ? { inert: "" } : {})}
|
||||||
className={cn(
|
className={cn(
|
||||||
"cursor-default transition-all duration-300 relative z-[1]",
|
"relative z-[1] cursor-default transition-all duration-300",
|
||||||
"inline-block text-left w-full",
|
"inline-block w-full text-left",
|
||||||
disabled && "opacity-30 pointer-events-none",
|
disabled && "pointer-events-none opacity-30",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -48,8 +48,8 @@ export const SwitchItemGroup = ({
|
|||||||
aria-label={ariaLabel}
|
aria-label={ariaLabel}
|
||||||
aria-labelledby={ariaLabelledBy}
|
aria-labelledby={ariaLabelledBy}
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex shrink-0 rounded-lg border border-nb-gray-850 bg-nb-gray-910 p-1 overflow-hidden",
|
"flex shrink-0 overflow-hidden rounded-lg border border-nb-gray-850 bg-nb-gray-910 p-1",
|
||||||
disabled && "opacity-50 pointer-events-none",
|
disabled && "pointer-events-none opacity-50",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -36,9 +36,9 @@ const switchVariants = cva("", {
|
|||||||
},
|
},
|
||||||
"thumb-size": {
|
"thumb-size": {
|
||||||
default:
|
default:
|
||||||
"h-5 w-5 data-[state=unchecked]:translate-x-0 data-[state=checked]:translate-x-5",
|
"h-5 w-5 data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0",
|
||||||
small: "h-[14px] w-[14px] data-[state=unchecked]:translate-x-0 data-[state=checked]:translate-x-[17px]",
|
small: "h-[14px] w-[14px] data-[state=checked]:translate-x-[17px] data-[state=unchecked]:translate-x-0",
|
||||||
large: "h-[30px] w-[30px] data-[state=unchecked]:translate-x-[1px] data-[state=checked]:translate-x-[31px]",
|
large: "h-[30px] w-[30px] data-[state=checked]:translate-x-[31px] data-[state=unchecked]:translate-x-[1px]",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -11,9 +11,9 @@ type Props = {
|
|||||||
export const HelpText = ({ children, margin = true, className, disabled = false }: Props) => (
|
export const HelpText = ({ children, margin = true, className, disabled = false }: Props) => (
|
||||||
<span
|
<span
|
||||||
className={cn(
|
className={cn(
|
||||||
"text-[.81rem] dark:text-nb-gray-300 block font-light tracking-wide transition-all duration-300",
|
"block text-[.81rem] font-light tracking-wide transition-all duration-300 dark:text-nb-gray-300",
|
||||||
margin && "mb-2",
|
margin && "mb-2",
|
||||||
disabled && "opacity-30 pointer-events-none",
|
disabled && "pointer-events-none opacity-30",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { type ComponentPropsWithoutRef, forwardRef, type Ref } from "react";
|
|||||||
import { cn } from "@/lib/cn";
|
import { cn } from "@/lib/cn";
|
||||||
|
|
||||||
const labelVariants = cva(
|
const labelVariants = cva(
|
||||||
"text-sm font-medium tracking-wider leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 mb-1.5 inline-block dark:text-nb-gray-100 flex items-center gap-2",
|
"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",
|
||||||
);
|
);
|
||||||
|
|
||||||
type LabelProps = ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
|
type LabelProps = ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
|
||||||
@@ -21,7 +21,7 @@ export const Label = forwardRef<HTMLElement, LabelProps>(function Label(
|
|||||||
labelVariants(),
|
labelVariants(),
|
||||||
className,
|
className,
|
||||||
"select-none transition-all duration-300",
|
"select-none transition-all duration-300",
|
||||||
disabled && "opacity-30 pointer-events-none",
|
disabled && "pointer-events-none opacity-30",
|
||||||
);
|
);
|
||||||
|
|
||||||
if (as === "div") {
|
if (as === "div") {
|
||||||
|
|||||||
@@ -216,7 +216,7 @@ export const SettingsProvider = ({ children }: { children: ReactNode }) => {
|
|||||||
|
|
||||||
if (!value) {
|
if (!value) {
|
||||||
return (
|
return (
|
||||||
<div className={"flex-1 min-h-0 overflow-y-auto py-8 px-7"}>
|
<div className={"min-h-0 flex-1 overflow-y-auto px-7 py-8"}>
|
||||||
<SettingsSkeleton />
|
<SettingsSkeleton />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -19,15 +19,15 @@ export const AppRightPanel = ({ children, overlay, overlayOpen = false, classNam
|
|||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"wails-no-draggable relative m-5",
|
"wails-no-draggable relative m-5",
|
||||||
"bg-nb-gray-940 border border-nb-gray-920",
|
"border border-nb-gray-920 bg-nb-gray-940",
|
||||||
"flex-1 min-h-0 min-w-0 flex flex-col rounded-xl rounded-br-2xl overflow-hidden",
|
"flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden rounded-xl rounded-br-2xl",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<motion.div
|
<motion.div
|
||||||
animate={{ x: overlayOpen ? -48 : 0 }}
|
animate={{ x: overlayOpen ? -48 : 0 }}
|
||||||
transition={PANEL_TRANSITION}
|
transition={PANEL_TRANSITION}
|
||||||
className={"flex-1 min-h-0 min-w-0 flex flex-col"}
|
className={"flex min-h-0 min-w-0 flex-1 flex-col"}
|
||||||
style={{ pointerEvents: overlayOpen ? "none" : "auto" }}
|
style={{ pointerEvents: overlayOpen ? "none" : "auto" }}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@@ -17,7 +17,9 @@ export const UpdateBadge = forwardRef<HTMLDivElement, Props>(function UpdateBadg
|
|||||||
{...rest}
|
{...rest}
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
className={"animate-ping absolute inline-flex h-[15px] w-[15px] rounded-full bg-netbird opacity-20 pointer-events-none"}
|
className={
|
||||||
|
"pointer-events-none absolute inline-flex h-[15px] w-[15px] animate-ping rounded-full bg-netbird opacity-20"
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
<ArrowUpCircleIcon size={size} className={"text-netbird"} />
|
<ArrowUpCircleIcon size={size} className={"text-netbird"} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ function Card({ children, className }: Readonly<{ children: ReactNode; className
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"w-full flex items-center justify-between gap-4 rounded-md border border-nb-gray-800 bg-nb-gray-910 px-4 py-3",
|
"flex w-full items-center justify-between gap-4 rounded-md border border-nb-gray-800 bg-nb-gray-910 px-4 py-3",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
@@ -86,7 +86,9 @@ function Link({ url, children }: Readonly<{ url: string; children: ReactNode }>)
|
|||||||
<button
|
<button
|
||||||
type={"button"}
|
type={"button"}
|
||||||
onClick={() => openUrl(url)}
|
onClick={() => openUrl(url)}
|
||||||
className={"text-sm text-netbird hover:underline hover:underline-offset-4 hover:decoration-[0.5px] font-medium"}
|
className={
|
||||||
|
"text-sm font-medium text-netbird hover:underline hover:decoration-[0.5px] hover:underline-offset-4"
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -65,7 +65,9 @@ export default function LoginWaitingForBrowserDialog() {
|
|||||||
type={"button"}
|
type={"button"}
|
||||||
onClick={tryAgain}
|
onClick={tryAgain}
|
||||||
disabled={!uri}
|
disabled={!uri}
|
||||||
className={"wails-no-draggable text-netbird hover:underline disabled:opacity-40 disabled:cursor-not-allowed"}
|
className={
|
||||||
|
"wails-no-draggable text-netbird hover:underline disabled:cursor-not-allowed disabled:opacity-40"
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{t("browserLogin.tryAgain")}
|
{t("browserLogin.tryAgain")}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -220,13 +220,13 @@ export const MainConnectionStatusSwitch = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn("flex flex-col h-full w-full items-center gap-4", "relative")}
|
className={cn("flex h-full w-full flex-col items-center gap-4", "relative")}
|
||||||
style={{ top: contentTop("11.7rem") }}
|
style={{ top: contentTop("11.7rem") }}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
src={netbirdFullLogo}
|
src={netbirdFullLogo}
|
||||||
alt={"NetBird"}
|
alt={"NetBird"}
|
||||||
className={"h-7 w-auto select-none mb-4 wails-no-draggable"}
|
className={"wails-no-draggable mb-4 h-7 w-auto select-none"}
|
||||||
draggable={false}
|
draggable={false}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -246,7 +246,9 @@ export const MainConnectionStatusSwitch = () => {
|
|||||||
id={"nb-connection-status"}
|
id={"nb-connection-status"}
|
||||||
role={"status"}
|
role={"status"}
|
||||||
aria-live={"polite"}
|
aria-live={"polite"}
|
||||||
className={"text-sm font-medium text-nb-gray-200 tracking-wide transition-colors duration-300 select-none wails-no-draggable mb-1"}
|
className={
|
||||||
|
"wails-no-draggable mb-1 select-none text-sm font-medium tracking-wide text-nb-gray-200 transition-colors duration-300"
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{t(STATUS_KEY[connState])}
|
{t(STATUS_KEY[connState])}
|
||||||
</p>
|
</p>
|
||||||
@@ -256,14 +258,16 @@ export const MainConnectionStatusSwitch = () => {
|
|||||||
iconClassName={"-top-px"}
|
iconClassName={"-top-px"}
|
||||||
tabIndex={show && fqdn ? 0 : -1}
|
tabIndex={show && fqdn ? 0 : -1}
|
||||||
className={cn(
|
className={cn(
|
||||||
"min-h-[1em] max-h-[1em] mt-1 transition-opacity duration-300 max-w-full",
|
"mt-1 max-h-[1em] min-h-[1em] max-w-full transition-opacity duration-300",
|
||||||
"relative left-[0.55rem]",
|
"relative left-[0.55rem]",
|
||||||
show && fqdn ? "opacity-100" : "opacity-0 pointer-events-none",
|
show && fqdn ? "opacity-100" : "pointer-events-none opacity-0",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<TruncatedText
|
<TruncatedText
|
||||||
text={shortenDns(fqdn) || " "}
|
text={shortenDns(fqdn) || " "}
|
||||||
className={"block font-mono text-[0.8rem] leading-tight text-nb-gray-300 truncate max-w-[310px] h-[18px]"}
|
className={
|
||||||
|
"block h-[18px] max-w-[310px] truncate font-mono text-[0.8rem] leading-tight text-nb-gray-300"
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</CopyToClipboard>
|
</CopyToClipboard>
|
||||||
<LocalIpLine ip={ip} ipv6={ipv6} show={show} />
|
<LocalIpLine ip={ip} ipv6={ipv6} show={show} />
|
||||||
@@ -284,9 +288,9 @@ const LocalIpLine = ({ ip, ipv6, show }: { ip: string; ipv6: string; show: boole
|
|||||||
variant={"bright"}
|
variant={"bright"}
|
||||||
tabIndex={show && ip ? 0 : -1}
|
tabIndex={show && ip ? 0 : -1}
|
||||||
className={cn(
|
className={cn(
|
||||||
"min-h-[1em] max-h-[1em] mt-1 transition-opacity duration-300",
|
"mt-1 max-h-[1em] min-h-[1em] transition-opacity duration-300",
|
||||||
"relative left-[0.55rem]",
|
"relative left-[0.55rem]",
|
||||||
show && ip ? "opacity-100" : "opacity-0 pointer-events-none",
|
show && ip ? "opacity-100" : "pointer-events-none opacity-0",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<span className={"font-mono text-[0.8rem] leading-tight text-nb-gray-300"}>
|
<span className={"font-mono text-[0.8rem] leading-tight text-nb-gray-300"}>
|
||||||
@@ -299,9 +303,9 @@ const LocalIpLine = ({ ip, ipv6, show }: { ip: string; ipv6: string; show: boole
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"min-h-[1em] transition-opacity duration-300 max-w-full",
|
"min-h-[1em] max-w-full transition-opacity duration-300",
|
||||||
"relative wails-no-draggable",
|
"wails-no-draggable relative",
|
||||||
show && ip ? "opacity-100" : "opacity-0 pointer-events-none",
|
show && ip ? "opacity-100" : "pointer-events-none opacity-0",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Popover.Root open={open} onOpenChange={setOpen}>
|
<Popover.Root open={open} onOpenChange={setOpen}>
|
||||||
@@ -313,7 +317,7 @@ const LocalIpLine = ({ ip, ipv6, show }: { ip: string; ipv6: string; show: boole
|
|||||||
aria-haspopup={"dialog"}
|
aria-haspopup={"dialog"}
|
||||||
aria-expanded={open}
|
aria-expanded={open}
|
||||||
className={cn(
|
className={cn(
|
||||||
"group relative inline-flex items-center outline-none cursor-default rounded-sm",
|
"group relative inline-flex cursor-default items-center rounded-sm outline-none",
|
||||||
"focus-visible:ring-2 focus-visible:ring-white/60 focus-visible:ring-offset-2 focus-visible:ring-offset-nb-gray-940",
|
"focus-visible:ring-2 focus-visible:ring-white/60 focus-visible:ring-offset-2 focus-visible:ring-offset-nb-gray-940",
|
||||||
"transition-colors",
|
"transition-colors",
|
||||||
)}
|
)}
|
||||||
@@ -348,7 +352,7 @@ const LocalIpLine = ({ ip, ipv6, show }: { ip: string; ipv6: string; show: boole
|
|||||||
className={cn(
|
className={cn(
|
||||||
"z-50 min-w-64 max-w-[280px] overflow-hidden",
|
"z-50 min-w-64 max-w-[280px] overflow-hidden",
|
||||||
"rounded-lg border border-nb-gray-900 bg-nb-gray-935",
|
"rounded-lg border border-nb-gray-900 bg-nb-gray-935",
|
||||||
"p-1 shadow-lg outline-none text-nb-gray-200",
|
"p-1 text-nb-gray-200 shadow-lg outline-none",
|
||||||
"flex flex-col",
|
"flex flex-col",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
@@ -385,14 +389,14 @@ const IpRow = ({ value }: { value: string }) => {
|
|||||||
"group/iprow relative flex items-center justify-between gap-3",
|
"group/iprow relative flex items-center justify-between gap-3",
|
||||||
"rounded-md px-2 py-1.5 text-left",
|
"rounded-md px-2 py-1.5 text-left",
|
||||||
"text-nb-gray-200 hover:bg-nb-gray-900 hover:text-nb-gray-50",
|
"text-nb-gray-200 hover:bg-nb-gray-900 hover:text-nb-gray-50",
|
||||||
"transition-colors outline-none cursor-default",
|
"cursor-default outline-none transition-colors",
|
||||||
"focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-white/60",
|
"focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-white/60",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<span className={"font-mono text-[0.75rem] truncate min-w-0"}>{value}</span>
|
<span className={"min-w-0 truncate font-mono text-[0.75rem]"}>{value}</span>
|
||||||
<span
|
<span
|
||||||
aria-hidden={"true"}
|
aria-hidden={"true"}
|
||||||
className={"shrink-0 inline-flex items-center text-nb-gray-200"}
|
className={"inline-flex shrink-0 items-center text-nb-gray-200"}
|
||||||
>
|
>
|
||||||
{copied ? <CheckIcon size={11} /> : <CopyIcon size={11} />}
|
{copied ? <CheckIcon size={11} /> : <CopyIcon size={11} />}
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ export const MainExitNodeSwitcher = () => {
|
|||||||
}}
|
}}
|
||||||
style={{ width: "var(--radix-popover-trigger-width)" }}
|
style={{ width: "var(--radix-popover-trigger-width)" }}
|
||||||
className={cn(
|
className={cn(
|
||||||
"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 wails-no-draggable",
|
"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",
|
"data-[state=open]:animate-in data-[state=closed]:animate-out",
|
||||||
"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
"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-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
|
||||||
@@ -100,7 +100,7 @@ export const MainExitNodeSwitcher = () => {
|
|||||||
<NoneRow isActive={!active} onSelect={() => handleSelect(NONE_VALUE)} />
|
<NoneRow isActive={!active} onSelect={() => handleSelect(NONE_VALUE)} />
|
||||||
{hasAny && <div className={"-mx-1 my-1 h-px bg-nb-gray-910"} />}
|
{hasAny && <div className={"-mx-1 my-1 h-px bg-nb-gray-910"} />}
|
||||||
{hasAny && (
|
{hasAny && (
|
||||||
<ScrollArea.Root type={"auto"} className={"overflow-hidden -mx-1"}>
|
<ScrollArea.Root type={"auto"} className={"-mx-1 overflow-hidden"}>
|
||||||
<ScrollArea.Viewport className={"max-h-72 px-1"}>
|
<ScrollArea.Viewport className={"max-h-72 px-1"}>
|
||||||
{exitNodes.map((n) => (
|
{exitNodes.map((n) => (
|
||||||
<ExitNodeRow
|
<ExitNodeRow
|
||||||
@@ -115,12 +115,14 @@ export const MainExitNodeSwitcher = () => {
|
|||||||
<ScrollArea.Scrollbar
|
<ScrollArea.Scrollbar
|
||||||
orientation={"vertical"}
|
orientation={"vertical"}
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex select-none touch-none transition-colors",
|
"flex touch-none select-none transition-colors",
|
||||||
"w-1.5 bg-transparent",
|
"w-1.5 bg-transparent",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<ScrollArea.Thumb
|
<ScrollArea.Thumb
|
||||||
className={"flex-1 rounded-full bg-nb-gray-800 hover:bg-nb-gray-700 relative"}
|
className={
|
||||||
|
"relative flex-1 rounded-full bg-nb-gray-800 hover:bg-nb-gray-700"
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</ScrollArea.Scrollbar>
|
</ScrollArea.Scrollbar>
|
||||||
</ScrollArea.Root>
|
</ScrollArea.Root>
|
||||||
@@ -151,14 +153,14 @@ const ExitNodeTriggerCard = forwardRef<HTMLButtonElement, TriggerProps>(
|
|||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
className={cn(
|
className={cn(
|
||||||
"w-full flex items-center gap-3 p-2.5 pr-5 rounded-xl outline-none text-left",
|
"flex w-full items-center gap-3 rounded-xl p-2.5 pr-5 text-left outline-none",
|
||||||
"border border-nb-gray-920 bg-nb-gray-940",
|
"border border-nb-gray-920 bg-nb-gray-940",
|
||||||
"transition-colors duration-150",
|
"transition-colors duration-150",
|
||||||
"wails-no-draggable",
|
"wails-no-draggable",
|
||||||
"focus-visible:ring-2 focus-visible:ring-white/60 focus-visible:ring-offset-2 focus-visible:ring-offset-nb-gray-940",
|
"focus-visible:ring-2 focus-visible:ring-white/60 focus-visible:ring-offset-2 focus-visible:ring-offset-nb-gray-940",
|
||||||
disabled
|
disabled
|
||||||
? "opacity-60 cursor-not-allowed"
|
? "cursor-not-allowed opacity-60"
|
||||||
: "cursor-default hover:bg-nb-gray-935 hover:border-nb-gray-900 data-[state=open]:bg-nb-gray-935 data-[state=open]:border-nb-gray-900",
|
: "cursor-default hover:border-nb-gray-900 hover:bg-nb-gray-935 data-[state=open]:border-nb-gray-900 data-[state=open]:bg-nb-gray-935",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
@@ -166,7 +168,7 @@ const ExitNodeTriggerCard = forwardRef<HTMLButtonElement, TriggerProps>(
|
|||||||
<div
|
<div
|
||||||
aria-hidden={"true"}
|
aria-hidden={"true"}
|
||||||
className={cn(
|
className={cn(
|
||||||
"h-9 w-9 rounded-md flex items-center justify-center shrink-0",
|
"flex h-9 w-9 shrink-0 items-center justify-center rounded-md",
|
||||||
active
|
active
|
||||||
? "bg-green-500/25 text-green-400"
|
? "bg-green-500/25 text-green-400"
|
||||||
: "bg-nb-gray-900 text-nb-gray-300",
|
: "bg-nb-gray-900 text-nb-gray-300",
|
||||||
@@ -175,18 +177,20 @@ const ExitNodeTriggerCard = forwardRef<HTMLButtonElement, TriggerProps>(
|
|||||||
<ExitNodeIcon size={14} />
|
<ExitNodeIcon size={14} />
|
||||||
</div>
|
</div>
|
||||||
<div className={"min-w-0 flex-1"}>
|
<div className={"min-w-0 flex-1"}>
|
||||||
<span className={"block font-medium text-sm text-nb-gray-100 truncate"}>
|
<span className={"block truncate text-sm font-medium text-nb-gray-100"}>
|
||||||
{title}
|
{title}
|
||||||
</span>
|
</span>
|
||||||
<TruncatedText
|
<TruncatedText
|
||||||
text={description}
|
text={description}
|
||||||
className={"block text-[0.85rem] font-medium text-nb-gray-400 truncate max-w-full"}
|
className={
|
||||||
|
"block max-w-full truncate text-[0.85rem] font-medium text-nb-gray-400"
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<ChevronsUpDown
|
<ChevronsUpDown
|
||||||
size={16}
|
size={16}
|
||||||
aria-hidden={"true"}
|
aria-hidden={"true"}
|
||||||
className={"text-nb-gray-400 shrink-0"}
|
className={"shrink-0 text-nb-gray-400"}
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
@@ -205,13 +209,15 @@ const NoneRow = ({ isActive, onSelect }: NoneRowProps) => {
|
|||||||
value={NONE_VALUE}
|
value={NONE_VALUE}
|
||||||
onSelect={onSelect}
|
onSelect={onSelect}
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex gap-2 items-center px-2 py-2 pr-3",
|
"flex items-center gap-2 px-2 py-2 pr-3",
|
||||||
"rounded-md outline-none cursor-default text-sm",
|
"cursor-default rounded-md text-sm outline-none",
|
||||||
"data-[selected=true]:bg-nb-gray-900",
|
"data-[selected=true]:bg-nb-gray-900",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<span className={"min-w-0 flex-1 truncate"}>{t("exitNodes.dropdown.noneTitle")}</span>
|
<span className={"min-w-0 flex-1 truncate"}>{t("exitNodes.dropdown.noneTitle")}</span>
|
||||||
{isActive && <Check size={16} aria-hidden={"true"} className={"shrink-0 text-netbird"} />}
|
{isActive && (
|
||||||
|
<Check size={16} aria-hidden={"true"} className={"shrink-0 text-netbird"} />
|
||||||
|
)}
|
||||||
</Command.Item>
|
</Command.Item>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -228,8 +234,8 @@ const ExitNodeRow = ({ id, label, isActive, onSelect }: ExitNodeRowProps) => (
|
|||||||
value={id}
|
value={id}
|
||||||
onSelect={onSelect}
|
onSelect={onSelect}
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex gap-2 items-center px-2 py-2 pr-3",
|
"flex items-center gap-2 px-2 py-2 pr-3",
|
||||||
"rounded-md outline-none cursor-default text-sm",
|
"cursor-default rounded-md text-sm outline-none",
|
||||||
"data-[selected=true]:bg-nb-gray-900",
|
"data-[selected=true]:bg-nb-gray-900",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -82,7 +82,9 @@ export const MainHeader = () => {
|
|||||||
<DropdownMenuContent
|
<DropdownMenuContent
|
||||||
align={"end"}
|
align={"end"}
|
||||||
sideOffset={8}
|
sideOffset={8}
|
||||||
className={"min-w-52 select-none data-[state=closed]:!animate-none data-[state=closed]:!duration-0"}
|
className={
|
||||||
|
"min-w-52 select-none data-[state=closed]:!animate-none data-[state=closed]:!duration-0"
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{updateAvailable && (
|
{updateAvailable && (
|
||||||
<>
|
<>
|
||||||
@@ -102,7 +104,7 @@ export const MainHeader = () => {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<DropdownMenuItem onClick={openSettings}>
|
<DropdownMenuItem onClick={openSettings}>
|
||||||
<div className={"flex items-center gap-2 w-full"}>
|
<div className={"flex w-full items-center gap-2"}>
|
||||||
<Settings size={14} aria-hidden={"true"} />
|
<Settings size={14} aria-hidden={"true"} />
|
||||||
<span className={"flex-1"}>{t("header.menu.settings")}</span>
|
<span className={"flex-1"}>{t("header.menu.settings")}</span>
|
||||||
<DropdownMenuShortcut>
|
<DropdownMenuShortcut>
|
||||||
@@ -132,10 +134,14 @@ export const MainHeader = () => {
|
|||||||
{updateAvailable && (
|
{updateAvailable && (
|
||||||
<span
|
<span
|
||||||
aria-hidden={"true"}
|
aria-hidden={"true"}
|
||||||
className={"pointer-events-none absolute top-1.5 right-1.5 flex h-2.5 w-2.5 items-center justify-center"}
|
className={
|
||||||
|
"pointer-events-none absolute right-1.5 top-1.5 flex h-2.5 w-2.5 items-center justify-center"
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
className={"absolute inset-0 rounded-full bg-netbird opacity-60 animate-ping"}
|
className={
|
||||||
|
"absolute inset-0 animate-ping rounded-full bg-netbird opacity-60"
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
<span className={"relative h-1.5 w-1.5 rounded-full bg-netbird"} />
|
<span className={"relative h-1.5 w-1.5 rounded-full bg-netbird"} />
|
||||||
</span>
|
</span>
|
||||||
@@ -146,20 +152,20 @@ export const MainHeader = () => {
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"shrink-0 cursor-default wails-draggable relative z-10",
|
"wails-draggable relative z-10 shrink-0 cursor-default",
|
||||||
"flex items-center h-12 top-3",
|
"top-3 flex h-12 items-center",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{/* Windows narrower width compensates for the OS frame Wails counts differently than macOS.
|
{/* Windows narrower width compensates for the OS frame Wails counts differently than macOS.
|
||||||
See https://github.com/wailsapp/wails/issues/3260 */}
|
See https://github.com/wailsapp/wails/issues/3260 */}
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"grid grid-cols-3 items-center shrink-0",
|
"grid shrink-0 grid-cols-3 items-center",
|
||||||
isWindows() ? "w-[364px]" : "w-[380px]",
|
isWindows() ? "w-[364px]" : "w-[380px]",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div />
|
<div />
|
||||||
<div className={"flex justify-center ml-4"}>{profileSlot}</div>
|
<div className={"ml-4 flex justify-center"}>{profileSlot}</div>
|
||||||
<div />
|
<div />
|
||||||
</div>
|
</div>
|
||||||
<div className={"absolute right-[1.3rem] top-1/2 -translate-y-1/2"}>{settingsSlot}</div>
|
<div className={"absolute right-[1.3rem] top-1/2 -translate-y-1/2"}>{settingsSlot}</div>
|
||||||
@@ -176,7 +182,7 @@ type ViewModeItemProps = {
|
|||||||
|
|
||||||
const ViewModeItem = ({ icon: Icon, label, selected, onSelect }: ViewModeItemProps) => (
|
const ViewModeItem = ({ icon: Icon, label, selected, onSelect }: ViewModeItemProps) => (
|
||||||
<DropdownMenuItem onClick={onSelect} role={"menuitemradio"} aria-checked={selected}>
|
<DropdownMenuItem onClick={onSelect} role={"menuitemradio"} aria-checked={selected}>
|
||||||
<div className={"flex items-center gap-2 w-full"}>
|
<div className={"flex w-full items-center gap-2"}>
|
||||||
<Icon size={14} aria-hidden={"true"} />
|
<Icon size={14} aria-hidden={"true"} />
|
||||||
<span className={"flex-1"}>{label}</span>
|
<span className={"flex-1"}>{label}</span>
|
||||||
{selected && <Check size={14} className={"text-netbird"} aria-hidden={"true"} />}
|
{selected && <Check size={14} className={"text-netbird"} aria-hidden={"true"} />}
|
||||||
|
|||||||
@@ -44,18 +44,18 @@ const MainBody = () => {
|
|||||||
const isAdvanced = viewMode === "advanced";
|
const isAdvanced = viewMode === "advanced";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={"wails-draggable flex flex-1 min-h-0"}>
|
<div className={"wails-draggable flex min-h-0 flex-1"}>
|
||||||
{/* Windows narrower width compensates for the OS frame Wails counts differently than macOS.
|
{/* Windows narrower width compensates for the OS frame Wails counts differently than macOS.
|
||||||
See https://github.com/wailsapp/wails/issues/3260 */}
|
See https://github.com/wailsapp/wails/issues/3260 */}
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"relative flex flex-col items-center shrink-0 ",
|
"relative flex shrink-0 flex-col items-center",
|
||||||
isWindows() ? "w-[364px]" : "w-[380px]",
|
isWindows() ? "w-[364px]" : "w-[380px]",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<MainConnectionStatusSwitch />
|
<MainConnectionStatusSwitch />
|
||||||
{!features.disableNetworks && (
|
{!features.disableNetworks && (
|
||||||
<div className={"absolute left-5 right-5 bottom-5 wails-no-draggable"}>
|
<div className={"wails-no-draggable absolute bottom-5 left-5 right-5"}>
|
||||||
<MainExitNodeSwitcher />
|
<MainExitNodeSwitcher />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -88,7 +88,7 @@ const AdvancedAppRightPanel = () => {
|
|||||||
else el.setAttribute("inert", "");
|
else el.setAttribute("inert", "");
|
||||||
}}
|
}}
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex-1 min-h-0 min-w-0 flex flex-col",
|
"flex min-h-0 min-w-0 flex-1 flex-col",
|
||||||
!isConnected && "pointer-events-none select-none",
|
!isConnected && "pointer-events-none select-none",
|
||||||
)}
|
)}
|
||||||
aria-hidden={!isConnected}
|
aria-hidden={!isConnected}
|
||||||
@@ -98,14 +98,14 @@ const AdvancedAppRightPanel = () => {
|
|||||||
role={"tabpanel"}
|
role={"tabpanel"}
|
||||||
id={`nb-tabpanel-${section}`}
|
id={`nb-tabpanel-${section}`}
|
||||||
aria-labelledby={`nb-tab-${section}`}
|
aria-labelledby={`nb-tab-${section}`}
|
||||||
className={"flex-1 min-h-0 flex flex-col"}
|
className={"flex min-h-0 flex-1 flex-col"}
|
||||||
>
|
>
|
||||||
{section === "peers" && <Peers />}
|
{section === "peers" && <Peers />}
|
||||||
{section === "networks" && <Networks />}
|
{section === "networks" && <Networks />}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{!isConnected && (
|
{!isConnected && (
|
||||||
<div className={"absolute inset-0 z-20 flex pointer-events-auto bg-nb-gray-940"}>
|
<div className={"pointer-events-auto absolute inset-0 z-20 flex bg-nb-gray-940"}>
|
||||||
<NotConnectedState />
|
<NotConnectedState />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ export const Navigation = () => {
|
|||||||
role={"tablist"}
|
role={"tablist"}
|
||||||
aria-orientation={"horizontal"}
|
aria-orientation={"horizontal"}
|
||||||
aria-label={t("nav.peers.title")}
|
aria-label={t("nav.peers.title")}
|
||||||
className={"wails-no-draggable shrink-0 flex items-stretch "}
|
className={"wails-no-draggable flex shrink-0 items-stretch"}
|
||||||
>
|
>
|
||||||
{tabs.map((tab, index) => {
|
{tabs.map((tab, index) => {
|
||||||
const isActive = tab.value === section;
|
const isActive = tab.value === section;
|
||||||
@@ -110,7 +110,7 @@ export const Navigation = () => {
|
|||||||
isLast && "rounded-tr-xl",
|
isLast && "rounded-tr-xl",
|
||||||
"focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-white/60",
|
"focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-white/60",
|
||||||
isActive ? "text-netbird" : "text-nb-gray-400 hover:text-nb-gray-300",
|
isActive ? "text-netbird" : "text-nb-gray-400 hover:text-nb-gray-300",
|
||||||
isDisabled ? "opacity-50 cursor-not-allowed" : "cursor-default",
|
isDisabled ? "cursor-not-allowed opacity-50" : "cursor-default",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Icon size={14} aria-hidden={"true"} />
|
<Icon size={14} aria-hidden={"true"} />
|
||||||
|
|||||||
@@ -40,11 +40,11 @@ export const NetworkFilters = ({ value, onChange, counts, disabled }: Props) =>
|
|||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
aria-label={t("common.filter")}
|
aria-label={t("common.filter")}
|
||||||
className={cn(
|
className={cn(
|
||||||
"inline-flex items-center gap-1.5 h-9 px-2 rounded-md",
|
"inline-flex h-9 items-center gap-1.5 rounded-md px-2",
|
||||||
"text-sm text-nb-gray-200",
|
"text-sm text-nb-gray-200",
|
||||||
"outline-none hover:bg-nb-gray-900 data-[state=open]:bg-nb-gray-900 transition-colors duration-150",
|
"outline-none transition-colors duration-150 hover:bg-nb-gray-900 data-[state=open]:bg-nb-gray-900",
|
||||||
"focus-visible:ring-2 focus-visible:ring-white/60 focus-visible:ring-offset-2 focus-visible:ring-offset-nb-gray-940",
|
"focus-visible:ring-2 focus-visible:ring-white/60 focus-visible:ring-offset-2 focus-visible:ring-offset-nb-gray-940",
|
||||||
"disabled:opacity-50 disabled:pointer-events-none",
|
"disabled:pointer-events-none disabled:opacity-50",
|
||||||
"wails-no-draggable cursor-default",
|
"wails-no-draggable cursor-default",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
@@ -71,7 +71,7 @@ export const NetworkFilters = ({ value, onChange, counts, disabled }: Props) =>
|
|||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
aria-hidden={"true"}
|
aria-hidden={"true"}
|
||||||
className={"w-4 shrink-0 flex items-center justify-center"}
|
className={"flex w-4 shrink-0 items-center justify-center"}
|
||||||
>
|
>
|
||||||
{checked && <CheckIcon size={14} className={"text-netbird"} />}
|
{checked && <CheckIcon size={14} className={"text-netbird"} />}
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -160,9 +160,9 @@ export const Networks = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={"flex flex-col w-full h-full min-h-0"}>
|
<div className={"flex h-full min-h-0 w-full flex-col"}>
|
||||||
<div className={"flex items-center gap-2 px-6 py-2.5 border-b border-nb-gray-910"}>
|
<div className={"flex items-center gap-2 border-b border-nb-gray-910 px-6 py-2.5"}>
|
||||||
<div className={"flex-1 min-w-0"}>
|
<div className={"min-w-0 flex-1"}>
|
||||||
<SearchInput
|
<SearchInput
|
||||||
ref={searchRef}
|
ref={searchRef}
|
||||||
placeholder={t("networks.search.placeholder")}
|
placeholder={t("networks.search.placeholder")}
|
||||||
@@ -175,7 +175,7 @@ export const Networks = () => {
|
|||||||
{filtered.length === 0 ? (
|
{filtered.length === 0 ? (
|
||||||
<NoResults />
|
<NoResults />
|
||||||
) : (
|
) : (
|
||||||
<ScrollArea.Root type={"auto"} className={"flex-1 min-h-0 overflow-hidden"}>
|
<ScrollArea.Root type={"auto"} className={"min-h-0 flex-1 overflow-hidden"}>
|
||||||
<ScrollArea.Viewport ref={setScrollParent} className={"h-full w-full"}>
|
<ScrollArea.Viewport ref={setScrollParent} className={"h-full w-full"}>
|
||||||
{scrollParent && (
|
{scrollParent && (
|
||||||
<NetworksList
|
<NetworksList
|
||||||
@@ -188,12 +188,14 @@ export const Networks = () => {
|
|||||||
<ScrollArea.Scrollbar
|
<ScrollArea.Scrollbar
|
||||||
orientation={"vertical"}
|
orientation={"vertical"}
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex select-none touch-none transition-colors",
|
"flex touch-none select-none transition-colors",
|
||||||
"w-1.5 bg-transparent py-1",
|
"w-1.5 bg-transparent py-1",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<ScrollArea.Thumb
|
<ScrollArea.Thumb
|
||||||
className={"flex-1 rounded-full bg-nb-gray-800 hover:bg-nb-gray-700 relative"}
|
className={
|
||||||
|
"relative flex-1 rounded-full bg-nb-gray-800 hover:bg-nb-gray-700"
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</ScrollArea.Scrollbar>
|
</ScrollArea.Scrollbar>
|
||||||
</ScrollArea.Root>
|
</ScrollArea.Root>
|
||||||
@@ -205,7 +207,7 @@ export const Networks = () => {
|
|||||||
"border-t border-nb-gray-910",
|
"border-t border-nb-gray-910",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<span className={"flex-1 text-xs font-medium text-nb-gray-300 tabular-nums"}>
|
<span className={"flex-1 text-xs font-medium tabular-nums text-nb-gray-300"}>
|
||||||
{t("networks.bulk.selectionCount", {
|
{t("networks.bulk.selectionCount", {
|
||||||
selected: selectedInView,
|
selected: selectedInView,
|
||||||
total: filtered.length,
|
total: filtered.length,
|
||||||
@@ -217,10 +219,10 @@ export const Networks = () => {
|
|||||||
onClick={onBulkClick}
|
onClick={onBulkClick}
|
||||||
aria-label={t("networks.bulk.label")}
|
aria-label={t("networks.bulk.label")}
|
||||||
className={cn(
|
className={cn(
|
||||||
"inline-flex items-center h-8 px-3 rounded-md",
|
"inline-flex h-8 items-center rounded-md px-3",
|
||||||
"text-xs font-medium text-nb-gray-100",
|
"text-xs font-medium text-nb-gray-100",
|
||||||
"bg-nb-gray-920 hover:bg-nb-gray-910 border border-nb-gray-900 hover:border-nb-gray-850",
|
"border border-nb-gray-900 bg-nb-gray-920 hover:border-nb-gray-850 hover:bg-nb-gray-910",
|
||||||
"transition-colors outline-none wails-no-draggable cursor-pointer",
|
"wails-no-draggable cursor-pointer outline-none transition-colors",
|
||||||
"focus-visible:ring-2 focus-visible:ring-white/60 focus-visible:ring-offset-2 focus-visible:ring-offset-nb-gray-940",
|
"focus-visible:ring-2 focus-visible:ring-white/60 focus-visible:ring-offset-2 focus-visible:ring-offset-nb-gray-940",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
@@ -341,8 +343,8 @@ const NetworkRow = ({ network: n, index, onKeyDown, onToggle, setRowRef }: Netwo
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"group relative flex items-start gap-2.5 pl-6 pr-9 py-3 min-w-0",
|
"group relative flex min-w-0 items-start gap-2.5 py-3 pl-6 pr-9",
|
||||||
"hover:bg-nb-gray-900/40 transition-colors",
|
"transition-colors hover:bg-nb-gray-900/40",
|
||||||
"wails-no-draggable",
|
"wails-no-draggable",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
@@ -361,19 +363,26 @@ const NetworkRow = ({ network: n, index, onKeyDown, onToggle, setRowRef }: Netwo
|
|||||||
/>
|
/>
|
||||||
<ResourceIconBadge type={resourceTypeOf(n)} />
|
<ResourceIconBadge type={resourceTypeOf(n)} />
|
||||||
<div
|
<div
|
||||||
className={"min-w-0 flex-1 flex flex-col leading-tight relative pointer-events-none"}
|
className={
|
||||||
|
"pointer-events-none relative flex min-w-0 flex-1 flex-col leading-tight"
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<CopyToClipboard message={n.id} onKeyDown={handleKey}>
|
<CopyToClipboard message={n.id} onKeyDown={handleKey}>
|
||||||
<TruncatedText
|
<TruncatedText
|
||||||
text={n.id}
|
text={n.id}
|
||||||
className={"block text-[0.81rem] font-medium text-nb-gray-100 truncate max-w-[300px]"}
|
className={
|
||||||
|
"block max-w-[300px] truncate text-[0.81rem] font-medium text-nb-gray-100"
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</CopyToClipboard>
|
</CopyToClipboard>
|
||||||
</div>
|
</div>
|
||||||
<Subtitle network={n} onKeyDown={handleKey} />
|
<Subtitle network={n} onKeyDown={handleKey} />
|
||||||
</div>
|
</div>
|
||||||
<div aria-hidden={"true"} className={"shrink-0 self-center relative pointer-events-none"}>
|
<div
|
||||||
|
aria-hidden={"true"}
|
||||||
|
className={"pointer-events-none relative shrink-0 self-center"}
|
||||||
|
>
|
||||||
<NetworkToggle checked={n.selected} />
|
<NetworkToggle checked={n.selected} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -386,8 +395,8 @@ const ResourceIconBadge = ({ type }: { type: ResourceType }) => {
|
|||||||
<div
|
<div
|
||||||
aria-hidden={"true"}
|
aria-hidden={"true"}
|
||||||
className={cn(
|
className={cn(
|
||||||
"h-9 w-9 shrink-0 rounded-md flex items-center justify-center mt-[0.25rem]",
|
"mt-[0.25rem] flex h-9 w-9 shrink-0 items-center justify-center rounded-md",
|
||||||
"bg-nb-gray-920 border border-nb-gray-900 text-nb-gray-300",
|
"border border-nb-gray-900 bg-nb-gray-920 text-nb-gray-300",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Icon size={14} />
|
<Icon size={14} />
|
||||||
@@ -413,7 +422,9 @@ const Subtitle = ({ network, onKeyDown }: SubtitleProps) => {
|
|||||||
<CopyToClipboard message={network.range} onKeyDown={onKeyDown}>
|
<CopyToClipboard message={network.range} onKeyDown={onKeyDown}>
|
||||||
<TruncatedText
|
<TruncatedText
|
||||||
text={network.range}
|
text={network.range}
|
||||||
className={"block text-xs font-mono text-nb-gray-400 truncate max-w-[300px]"}
|
className={
|
||||||
|
"block max-w-[300px] truncate font-mono text-xs text-nb-gray-400"
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</CopyToClipboard>
|
</CopyToClipboard>
|
||||||
</div>
|
</div>
|
||||||
@@ -431,7 +442,7 @@ type DomainSubtitleProps = {
|
|||||||
|
|
||||||
const DomainSubtitle = ({ domain, ips, onKeyDown }: DomainSubtitleProps) => {
|
const DomainSubtitle = ({ domain, ips, onKeyDown }: DomainSubtitleProps) => {
|
||||||
const span = (
|
const span = (
|
||||||
<span className={"block text-xs font-mono text-nb-gray-400 truncate max-w-[300px]"}>
|
<span className={"block max-w-[300px] truncate font-mono text-xs text-nb-gray-400"}>
|
||||||
{domain}
|
{domain}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
@@ -449,7 +460,7 @@ const DomainSubtitle = ({ domain, ips, onKeyDown }: DomainSubtitleProps) => {
|
|||||||
interactive
|
interactive
|
||||||
keepOpenOnClick
|
keepOpenOnClick
|
||||||
contentClassName={cn(
|
contentClassName={cn(
|
||||||
"max-w-[18rem] max-h-72 overflow-auto",
|
"max-h-72 max-w-[18rem] overflow-auto",
|
||||||
"rounded-lg border border-nb-gray-900 bg-nb-gray-935",
|
"rounded-lg border border-nb-gray-900 bg-nb-gray-935",
|
||||||
"p-2 pr-4",
|
"p-2 pr-4",
|
||||||
)}
|
)}
|
||||||
@@ -476,7 +487,9 @@ const ResolvedIpsTooltip = ({ ips }: { ips: string[] }) => {
|
|||||||
<li key={ip}>
|
<li key={ip}>
|
||||||
<CopyToClipboard message={ip} className={"px-1 py-0.5"}>
|
<CopyToClipboard message={ip} className={"px-1 py-0.5"}>
|
||||||
<span
|
<span
|
||||||
className={"font-mono text-[0.72rem] text-nb-gray-100 whitespace-nowrap"}
|
className={
|
||||||
|
"whitespace-nowrap font-mono text-[0.72rem] text-nb-gray-100"
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{ip}
|
{ip}
|
||||||
</span>
|
</span>
|
||||||
@@ -498,8 +511,8 @@ const NetworkToggle = ({ checked, mixed }: ToggleProps) => {
|
|||||||
return (
|
return (
|
||||||
<span
|
<span
|
||||||
className={cn(
|
className={cn(
|
||||||
"shrink-0 inline-flex h-5 w-9 items-center rounded-full",
|
"inline-flex h-5 w-9 shrink-0 items-center rounded-full",
|
||||||
"transition-colors wails-no-draggable",
|
"wails-no-draggable transition-colors",
|
||||||
checked || mixed ? "bg-netbird" : "bg-nb-gray-700",
|
checked || mixed ? "bg-netbird" : "bg-nb-gray-700",
|
||||||
mixed && "opacity-60",
|
mixed && "opacity-60",
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -40,11 +40,11 @@ export const PeerFilters = ({ value, onChange, counts, disabled }: Props) => {
|
|||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
aria-label={t("common.filter")}
|
aria-label={t("common.filter")}
|
||||||
className={cn(
|
className={cn(
|
||||||
"inline-flex items-center gap-1.5 h-9 px-2 rounded-md",
|
"inline-flex h-9 items-center gap-1.5 rounded-md px-2",
|
||||||
"text-sm text-nb-gray-200",
|
"text-sm text-nb-gray-200",
|
||||||
"outline-none hover:bg-nb-gray-900 data-[state=open]:bg-nb-gray-900 transition-colors duration-150",
|
"outline-none transition-colors duration-150 hover:bg-nb-gray-900 data-[state=open]:bg-nb-gray-900",
|
||||||
"focus-visible:ring-2 focus-visible:ring-white/60 focus-visible:ring-offset-2 focus-visible:ring-offset-nb-gray-940",
|
"focus-visible:ring-2 focus-visible:ring-white/60 focus-visible:ring-offset-2 focus-visible:ring-offset-nb-gray-940",
|
||||||
"disabled:opacity-50 disabled:pointer-events-none",
|
"disabled:pointer-events-none disabled:opacity-50",
|
||||||
"wails-no-draggable cursor-default",
|
"wails-no-draggable cursor-default",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
@@ -71,7 +71,7 @@ export const PeerFilters = ({ value, onChange, counts, disabled }: Props) => {
|
|||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
aria-hidden={"true"}
|
aria-hidden={"true"}
|
||||||
className={"w-4 shrink-0 flex items-center justify-center"}
|
className={"flex w-4 shrink-0 items-center justify-center"}
|
||||||
>
|
>
|
||||||
{checked && <CheckIcon size={14} className={"text-netbird"} />}
|
{checked && <CheckIcon size={14} className={"text-netbird"} />}
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ export const ProfileDropdown = ({ onManageProfiles }: ProfileDropdownProps) => {
|
|||||||
listRef.current?.focus();
|
listRef.current?.focus();
|
||||||
}}
|
}}
|
||||||
className={cn(
|
className={cn(
|
||||||
"z-50 min-w-64 overflow-hidden rounded-lg border border-nb-gray-900 bg-nb-gray-935 p-1 text-nb-gray-200 shadow-lg select-none wails-no-draggable",
|
"wails-no-draggable z-50 min-w-64 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",
|
"data-[state=open]:animate-in data-[state=closed]:animate-out",
|
||||||
"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
"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-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
|
||||||
@@ -111,7 +111,10 @@ export const ProfileDropdown = ({ onManageProfiles }: ProfileDropdownProps) => {
|
|||||||
>
|
>
|
||||||
{sortedProfiles.length > 0 && (
|
{sortedProfiles.length > 0 && (
|
||||||
<>
|
<>
|
||||||
<ScrollArea.Root type={"auto"} className={"overflow-hidden -mx-1"}>
|
<ScrollArea.Root
|
||||||
|
type={"auto"}
|
||||||
|
className={"-mx-1 overflow-hidden"}
|
||||||
|
>
|
||||||
<ScrollArea.Viewport className={"max-h-60 px-1"}>
|
<ScrollArea.Viewport className={"max-h-60 px-1"}>
|
||||||
{sortedProfiles.map((profile) => (
|
{sortedProfiles.map((profile) => (
|
||||||
<ProfileRow
|
<ProfileRow
|
||||||
@@ -125,11 +128,15 @@ export const ProfileDropdown = ({ onManageProfiles }: ProfileDropdownProps) => {
|
|||||||
<ScrollArea.Scrollbar
|
<ScrollArea.Scrollbar
|
||||||
orientation={"vertical"}
|
orientation={"vertical"}
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex select-none touch-none transition-colors",
|
"flex touch-none select-none transition-colors",
|
||||||
"w-1.5 bg-transparent",
|
"w-1.5 bg-transparent",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<ScrollArea.Thumb className={"flex-1 rounded-full bg-nb-gray-800 hover:bg-nb-gray-700 relative"} />
|
<ScrollArea.Thumb
|
||||||
|
className={
|
||||||
|
"relative flex-1 rounded-full bg-nb-gray-800 hover:bg-nb-gray-700"
|
||||||
|
}
|
||||||
|
/>
|
||||||
</ScrollArea.Scrollbar>
|
</ScrollArea.Scrollbar>
|
||||||
</ScrollArea.Root>
|
</ScrollArea.Root>
|
||||||
<div className={"-mx-1 h-px bg-nb-gray-910"} />
|
<div className={"-mx-1 h-px bg-nb-gray-910"} />
|
||||||
@@ -143,13 +150,17 @@ export const ProfileDropdown = ({ onManageProfiles }: ProfileDropdownProps) => {
|
|||||||
disabled={!onManageProfiles}
|
disabled={!onManageProfiles}
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex items-center gap-2 px-2 py-1.5",
|
"flex items-center gap-2 px-2 py-1.5",
|
||||||
"rounded-md outline-none cursor-default text-sm",
|
"cursor-default rounded-md text-sm outline-none",
|
||||||
"data-[selected=true]:bg-nb-gray-900",
|
"data-[selected=true]:bg-nb-gray-900",
|
||||||
"data-[disabled=true]:opacity-50 data-[disabled=true]:pointer-events-none",
|
"data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Settings2 size={14} aria-hidden={"true"} className={"shrink-0"} />
|
<Settings2
|
||||||
<span className={"truncate flex-1"}>
|
size={14}
|
||||||
|
aria-hidden={"true"}
|
||||||
|
className={"shrink-0"}
|
||||||
|
/>
|
||||||
|
<span className={"flex-1 truncate"}>
|
||||||
{t("profile.dropdown.manageProfiles")}
|
{t("profile.dropdown.manageProfiles")}
|
||||||
</span>
|
</span>
|
||||||
</Command.Item>
|
</Command.Item>
|
||||||
@@ -167,13 +178,13 @@ const ProfileTriggerSkeleton = () => (
|
|||||||
role={"status"}
|
role={"status"}
|
||||||
aria-busy={"true"}
|
aria-busy={"true"}
|
||||||
aria-live={"polite"}
|
aria-live={"polite"}
|
||||||
className={"h-10 flex items-center gap-2 px-3 rounded-lg select-none wails-no-draggable"}
|
className={"wails-no-draggable flex h-10 select-none items-center gap-2 rounded-lg px-3"}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
aria-hidden={"true"}
|
aria-hidden={"true"}
|
||||||
className={"size-4 rounded-full bg-nb-gray-900 animate-pulse shrink-0"}
|
className={"size-4 shrink-0 animate-pulse rounded-full bg-nb-gray-900"}
|
||||||
/>
|
/>
|
||||||
<div aria-hidden={"true"} className={"h-4 w-24 rounded bg-nb-gray-900 animate-pulse"} />
|
<div aria-hidden={"true"} className={"h-4 w-24 animate-pulse rounded bg-nb-gray-900"} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -194,12 +205,12 @@ const ProfileTriggerButton = forwardRef<HTMLButtonElement, ProfileTriggerButtonP
|
|||||||
aria-label={t("header.profile.switch")}
|
aria-label={t("header.profile.switch")}
|
||||||
aria-haspopup={"listbox"}
|
aria-haspopup={"listbox"}
|
||||||
className={cn(
|
className={cn(
|
||||||
"h-10 flex items-center gap-2 px-3 rounded-lg outline-none cursor-default select-none wails-no-draggable",
|
"wails-no-draggable flex h-10 cursor-default select-none items-center gap-2 rounded-lg px-3 outline-none",
|
||||||
"text-nb-gray-200 hover:bg-nb-gray-900",
|
"text-nb-gray-200 hover:bg-nb-gray-900",
|
||||||
"data-[state=open]:bg-nb-gray-900",
|
"data-[state=open]:bg-nb-gray-900",
|
||||||
"disabled:opacity-50 disabled:hover:bg-transparent",
|
"disabled:opacity-50 disabled:hover:bg-transparent",
|
||||||
"focus-visible:ring-2 focus-visible:ring-white/60 focus-visible:ring-offset-2 focus-visible:ring-offset-nb-gray-940",
|
"focus-visible:ring-2 focus-visible:ring-white/60 focus-visible:ring-offset-2 focus-visible:ring-offset-nb-gray-940",
|
||||||
"transition-colors duration-150 wails-no-draggable",
|
"wails-no-draggable transition-colors duration-150",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
@@ -207,15 +218,15 @@ const ProfileTriggerButton = forwardRef<HTMLButtonElement, ProfileTriggerButtonP
|
|||||||
<Icon
|
<Icon
|
||||||
size={16}
|
size={16}
|
||||||
aria-hidden={"true"}
|
aria-hidden={"true"}
|
||||||
className={"text-nb-gray-200 shrink-0 wails-no-draggable"}
|
className={"wails-no-draggable shrink-0 text-nb-gray-200"}
|
||||||
/>
|
/>
|
||||||
<span className={"text-sm font-medium truncate max-w-[140px] wails-no-draggable"}>
|
<span className={"wails-no-draggable max-w-[140px] truncate text-sm font-medium"}>
|
||||||
{name}
|
{name}
|
||||||
</span>
|
</span>
|
||||||
<ChevronDown
|
<ChevronDown
|
||||||
size={14}
|
size={14}
|
||||||
aria-hidden={"true"}
|
aria-hidden={"true"}
|
||||||
className={"text-nb-gray-200 shrink-0 wails-no-draggable"}
|
className={"wails-no-draggable shrink-0 text-nb-gray-200"}
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
@@ -235,13 +246,13 @@ const ProfileRow = ({ profile, isActive, onSelect }: ProfileRowProps) => {
|
|||||||
value={profile.id}
|
value={profile.id}
|
||||||
onSelect={() => onSelect(profile.id)}
|
onSelect={() => onSelect(profile.id)}
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex gap-2 px-2 py-2 pr-3 w-auto last:mb-1",
|
"flex w-auto gap-2 px-2 py-2 pr-3 last:mb-1",
|
||||||
"rounded-md outline-none cursor-default text-sm",
|
"cursor-default rounded-md text-sm outline-none",
|
||||||
"data-[selected=true]:bg-nb-gray-900",
|
"data-[selected=true]:bg-nb-gray-900",
|
||||||
showEmail ? "items-start" : "items-center",
|
showEmail ? "items-start" : "items-center",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div className={"flex flex-col min-w-0 flex-1 leading-tight"}>
|
<div className={"flex min-w-0 flex-1 flex-col leading-tight"}>
|
||||||
<span className={"truncate"}>{profile.name}</span>
|
<span className={"truncate"}>{profile.name}</span>
|
||||||
{showEmail && <TruncatedEmail email={profile.email} />}
|
{showEmail && <TruncatedEmail email={profile.email} />}
|
||||||
</div>
|
</div>
|
||||||
@@ -267,7 +278,7 @@ const TruncatedEmail = ({ email }: { email: string }) => {
|
|||||||
}, [email]);
|
}, [email]);
|
||||||
|
|
||||||
const span = (
|
const span = (
|
||||||
<span ref={ref} className={"text-xs mt-0.5 text-nb-gray-300 truncate max-w-[180px]"}>
|
<span ref={ref} className={"mt-0.5 max-w-[180px] truncate text-xs text-nb-gray-300"}>
|
||||||
{email}
|
{email}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ export function ProfilesTab() {
|
|||||||
|
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"bg-nb-gray-930/60 border border-nb-gray-900 rounded-xl overflow-hidden",
|
"overflow-hidden rounded-xl border border-nb-gray-900 bg-nb-gray-930/60",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<ProfilesTable
|
<ProfilesTable
|
||||||
@@ -138,17 +138,19 @@ export function ProfilesTab() {
|
|||||||
|
|
||||||
{loaded && ordered.length === 0 && (
|
{loaded && ordered.length === 0 && (
|
||||||
<div
|
<div
|
||||||
className={"flex flex-col items-center justify-center py-10 text-center"}
|
className={
|
||||||
|
"flex flex-col items-center justify-center py-10 text-center"
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<UserCircle
|
<UserCircle
|
||||||
size={28}
|
size={28}
|
||||||
aria-hidden={"true"}
|
aria-hidden={"true"}
|
||||||
className={"text-nb-gray-500 mb-2"}
|
className={"mb-2 text-nb-gray-500"}
|
||||||
/>
|
/>
|
||||||
<p className={"text-sm font-semibold text-nb-gray-200"}>
|
<p className={"text-sm font-semibold text-nb-gray-200"}>
|
||||||
{t("settings.profiles.emptyTitle")}
|
{t("settings.profiles.emptyTitle")}
|
||||||
</p>
|
</p>
|
||||||
<p className={"mt-1 text-xs text-nb-gray-400 max-w-sm text-balance"}>
|
<p className={"mt-1 max-w-sm text-balance text-xs text-nb-gray-400"}>
|
||||||
{t("settings.profiles.emptyDescription")}
|
{t("settings.profiles.emptyDescription")}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -271,7 +273,7 @@ const ProfilesTable = ({
|
|||||||
return (
|
return (
|
||||||
<table
|
<table
|
||||||
aria-label={t("settings.profiles.section.profiles")}
|
aria-label={t("settings.profiles.section.profiles")}
|
||||||
className={"w-full text-sm border-separate border-spacing-0"}
|
className={"w-full border-separate border-spacing-0 text-sm"}
|
||||||
>
|
>
|
||||||
<tbody className={"flex flex-col"}>
|
<tbody className={"flex flex-col"}>
|
||||||
{ordered.map((profile, index) => (
|
{ordered.map((profile, index) => (
|
||||||
@@ -347,19 +349,21 @@ const ProfileRow = ({
|
|||||||
>
|
>
|
||||||
<td
|
<td
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex gap-2 min-w-0 leading-tight flex-1",
|
"flex min-w-0 flex-1 gap-2 leading-tight",
|
||||||
showEmail ? "items-start" : "items-center",
|
showEmail ? "items-start" : "items-center",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Icon
|
<Icon
|
||||||
size={15}
|
size={15}
|
||||||
aria-hidden={"true"}
|
aria-hidden={"true"}
|
||||||
className={cn("text-nb-gray-200 shrink-0", showEmail ? "mt-0.5" : "")}
|
className={cn("shrink-0 text-nb-gray-200", showEmail ? "mt-0.5" : "")}
|
||||||
/>
|
/>
|
||||||
<div className={"flex flex-col min-w-0 flex-1 leading-tight"}>
|
<div className={"flex min-w-0 flex-1 flex-col leading-tight"}>
|
||||||
<div className={"flex items-center gap-2 min-w-0"}>
|
<div className={"flex min-w-0 items-center gap-2"}>
|
||||||
<span
|
<span
|
||||||
className={"truncate font-medium text-nb-gray-100 select-text cursor-text"}
|
className={
|
||||||
|
"cursor-text select-text truncate font-medium text-nb-gray-100"
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{profile.name}
|
{profile.name}
|
||||||
</span>
|
</span>
|
||||||
@@ -397,7 +401,7 @@ const TruncatedEmail = ({ email }: { email: string }) => {
|
|||||||
const span = (
|
const span = (
|
||||||
<span
|
<span
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={"text-xs text-nb-gray-300 truncate mt-0.5 select-text cursor-text"}
|
className={"mt-0.5 cursor-text select-text truncate text-xs text-nb-gray-300"}
|
||||||
>
|
>
|
||||||
{email}
|
{email}
|
||||||
</span>
|
</span>
|
||||||
@@ -490,15 +494,15 @@ const ActionIconButton = ({
|
|||||||
aria-disabled={disabled || undefined}
|
aria-disabled={disabled || undefined}
|
||||||
tabIndex={hidden || !tabbable ? -1 : 0}
|
tabIndex={hidden || !tabbable ? -1 : 0}
|
||||||
className={cn(
|
className={cn(
|
||||||
"h-9 w-9 inline-flex items-center justify-center rounded-md cursor-default outline-none",
|
"inline-flex h-9 w-9 cursor-default items-center justify-center rounded-md outline-none",
|
||||||
"transition-colors duration-150",
|
"transition-colors duration-150",
|
||||||
"focus-visible:ring-2 focus-visible:ring-white/60 focus-visible:ring-offset-2 focus-visible:ring-offset-nb-gray-940",
|
"focus-visible:ring-2 focus-visible:ring-white/60 focus-visible:ring-offset-2 focus-visible:ring-offset-nb-gray-940",
|
||||||
variant === "danger"
|
variant === "danger"
|
||||||
? "text-nb-gray-400 hover:text-red-500 hover:bg-red-500/10"
|
? "text-nb-gray-400 hover:bg-red-500/10 hover:text-red-500"
|
||||||
: "text-nb-gray-400 hover:text-nb-gray-100 hover:bg-nb-gray-900",
|
: "text-nb-gray-400 hover:bg-nb-gray-900 hover:text-nb-gray-100",
|
||||||
hidden && "opacity-0 pointer-events-none",
|
hidden && "pointer-events-none opacity-0",
|
||||||
disabled &&
|
disabled &&
|
||||||
"opacity-40 cursor-not-allowed hover:!text-nb-gray-400 hover:!bg-transparent",
|
"cursor-not-allowed opacity-40 hover:!bg-transparent hover:!text-nb-gray-400",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Icon size={16} aria-hidden={"true"} />
|
<Icon size={16} aria-hidden={"true"} />
|
||||||
|
|||||||
@@ -167,7 +167,9 @@ export default function SessionExpirationDialog() {
|
|||||||
|
|
||||||
{!expired && (
|
{!expired && (
|
||||||
<div
|
<div
|
||||||
className={"font-mono font-semibold text-2xl tabular-nums text-nb-gray-50 tracking-wider"}
|
className={
|
||||||
|
"font-mono text-2xl font-semibold tabular-nums tracking-wider text-nb-gray-50"
|
||||||
|
}
|
||||||
aria-live={"polite"}
|
aria-live={"polite"}
|
||||||
>
|
>
|
||||||
{formatRemaining(remaining)}
|
{formatRemaining(remaining)}
|
||||||
|
|||||||
@@ -8,14 +8,18 @@ import netbirdFull from "@/assets/logos/netbird-full.svg";
|
|||||||
const GithubIcon = (props: SVGProps<SVGSVGElement>) => (
|
const GithubIcon = (props: SVGProps<SVGSVGElement>) => (
|
||||||
<svg viewBox={"0 0 24 24"} fill={"currentColor"} {...props}>
|
<svg viewBox={"0 0 24 24"} fill={"currentColor"} {...props}>
|
||||||
<path
|
<path
|
||||||
d={"M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"}
|
d={
|
||||||
|
"M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
const SlackIcon = (props: SVGProps<SVGSVGElement>) => (
|
const SlackIcon = (props: SVGProps<SVGSVGElement>) => (
|
||||||
<svg viewBox={"0 0 24 24"} fill={"currentColor"} {...props}>
|
<svg viewBox={"0 0 24 24"} fill={"currentColor"} {...props}>
|
||||||
<path
|
<path
|
||||||
d={"M5.042 15.165a2.528 2.528 0 0 1-2.52 2.523A2.528 2.528 0 0 1 0 15.165a2.527 2.527 0 0 1 2.522-2.52h2.52v2.52zM6.313 15.165a2.527 2.527 0 0 1 2.521-2.52 2.527 2.527 0 0 1 2.521 2.52v6.313A2.528 2.528 0 0 1 8.834 24a2.528 2.528 0 0 1-2.521-2.522v-6.313zM8.834 5.042a2.528 2.528 0 0 1-2.521-2.52A2.528 2.528 0 0 1 8.834 0a2.528 2.528 0 0 1 2.521 2.522v2.52H8.834zM8.834 6.313a2.528 2.528 0 0 1 2.521 2.521 2.528 2.528 0 0 1-2.521 2.521H2.522A2.528 2.528 0 0 1 0 8.834a2.528 2.528 0 0 1 2.522-2.521h6.312zM18.956 8.834a2.528 2.528 0 0 1 2.522-2.521A2.528 2.528 0 0 1 24 8.834a2.528 2.528 0 0 1-2.522 2.521h-2.522V8.834zM17.688 8.834a2.528 2.528 0 0 1-2.523 2.521 2.527 2.527 0 0 1-2.52-2.521V2.522A2.527 2.527 0 0 1 15.165 0a2.528 2.528 0 0 1 2.523 2.522v6.312zM15.165 18.956a2.528 2.528 0 0 1 2.523 2.522A2.528 2.528 0 0 1 15.165 24a2.527 2.527 0 0 1-2.52-2.522v-2.522h2.52zM15.165 17.688a2.527 2.527 0 0 1-2.52-2.523 2.526 2.526 0 0 1 2.52-2.52h6.313A2.527 2.527 0 0 1 24 15.165a2.528 2.528 0 0 1-2.522 2.523h-6.313z"}
|
d={
|
||||||
|
"M5.042 15.165a2.528 2.528 0 0 1-2.52 2.523A2.528 2.528 0 0 1 0 15.165a2.527 2.527 0 0 1 2.522-2.52h2.52v2.52zM6.313 15.165a2.527 2.527 0 0 1 2.521-2.52 2.527 2.527 0 0 1 2.521 2.52v6.313A2.528 2.528 0 0 1 8.834 24a2.528 2.528 0 0 1-2.521-2.522v-6.313zM8.834 5.042a2.528 2.528 0 0 1-2.521-2.52A2.528 2.528 0 0 1 8.834 0a2.528 2.528 0 0 1 2.521 2.522v2.52H8.834zM8.834 6.313a2.528 2.528 0 0 1 2.521 2.521 2.528 2.528 0 0 1-2.521 2.521H2.522A2.528 2.528 0 0 1 0 8.834a2.528 2.528 0 0 1 2.522-2.521h6.312zM18.956 8.834a2.528 2.528 0 0 1 2.522-2.521A2.528 2.528 0 0 1 24 8.834a2.528 2.528 0 0 1-2.522 2.521h-2.522V8.834zM17.688 8.834a2.528 2.528 0 0 1-2.523 2.521 2.527 2.527 0 0 1-2.52-2.521V2.522A2.527 2.527 0 0 1 15.165 0a2.528 2.528 0 0 1 2.523 2.522v6.312zM15.165 18.956a2.528 2.528 0 0 1 2.523 2.522A2.528 2.528 0 0 1 15.165 24a2.527 2.527 0 0 1-2.52-2.522v-2.522h2.52zM15.165 17.688a2.527 2.527 0 0 1-2.52-2.523 2.526 2.526 0 0 1 2.52-2.52h6.313A2.527 2.527 0 0 1 24 15.165a2.528 2.528 0 0 1-2.522 2.523h-6.313z"
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
@@ -82,19 +86,23 @@ export function SettingsAbout() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={"flex flex-col items-center justify-center gap-4 max-w-2xl mx-auto min-h-[calc(100vh-12rem)]"}
|
className={
|
||||||
|
"mx-auto flex min-h-[calc(100vh-12rem)] max-w-2xl flex-col items-center justify-center gap-4"
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<img src={netbirdFull} alt={t("common.netbird")} className={"h-7 w-auto"} />
|
<img src={netbirdFull} alt={t("common.netbird")} className={"h-7 w-auto"} />
|
||||||
<div className={"flex flex-col items-center gap-0.5 text-center"}>
|
<div className={"flex flex-col items-center gap-0.5 text-center"}>
|
||||||
<button
|
<button
|
||||||
type={"button"}
|
type={"button"}
|
||||||
onClick={handleVersionClick}
|
onClick={handleVersionClick}
|
||||||
className={"text-sm font-semibold text-nb-gray-100 cursor-text select-text bg-transparent outline-none"}
|
className={
|
||||||
|
"cursor-text select-text bg-transparent text-sm font-semibold text-nb-gray-100 outline-none"
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{daemonVersion === "development" ? (
|
{daemonVersion === "development" ? (
|
||||||
<span>
|
<span>
|
||||||
{t("settings.about.clientName")}{" "}
|
{t("settings.about.clientName")}{" "}
|
||||||
<span className={" text-yellow-400 font-mono"}>
|
<span className={"font-mono text-yellow-400"}>
|
||||||
{t("settings.about.development")}
|
{t("settings.about.development")}
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
@@ -102,11 +110,11 @@ export function SettingsAbout() {
|
|||||||
t("settings.about.client", { version: daemonVersion })
|
t("settings.about.client", { version: daemonVersion })
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
<p className={"text-sm text-nb-gray-250 cursor-text select-text font-medium"}>
|
<p className={"cursor-text select-text text-sm font-medium text-nb-gray-250"}>
|
||||||
{guiVersion === "development" ? (
|
{guiVersion === "development" ? (
|
||||||
<span>
|
<span>
|
||||||
{t("settings.about.guiName")}{" "}
|
{t("settings.about.guiName")}{" "}
|
||||||
<span className={" text-yellow-400 font-mono"}>
|
<span className={"font-mono text-yellow-400"}>
|
||||||
{t("settings.about.development")}
|
{t("settings.about.development")}
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
@@ -118,7 +126,7 @@ export function SettingsAbout() {
|
|||||||
|
|
||||||
<UpdateVersionCard />
|
<UpdateVersionCard />
|
||||||
|
|
||||||
<p className={"text-sm text-nb-gray-300 text-center mt-2"}>
|
<p className={"mt-2 text-center text-sm text-nb-gray-300"}>
|
||||||
{t("settings.about.copyright", { year: new Date().getFullYear() })}
|
{t("settings.about.copyright", { year: new Date().getFullYear() })}
|
||||||
</p>
|
</p>
|
||||||
<div
|
<div
|
||||||
@@ -130,7 +138,9 @@ export function SettingsAbout() {
|
|||||||
type={"button"}
|
type={"button"}
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
onClick={() => openUrl(url)}
|
onClick={() => openUrl(url)}
|
||||||
className={"inline-flex items-center gap-1.5 rounded-sm outline-none decoration-[0.5px] underline-offset-4 hover:text-nb-gray-100 hover:underline transition focus-visible:ring-2 focus-visible:ring-white/60 focus-visible:ring-offset-2 focus-visible:ring-offset-nb-gray-940"}
|
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-white/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"} />
|
<Icon aria-hidden={"true"} className={iconClassName ?? "h-3.5 w-3.5"} />
|
||||||
<span>{label}</span>
|
<span>{label}</span>
|
||||||
@@ -146,7 +156,9 @@ export function SettingsAbout() {
|
|||||||
type={"button"}
|
type={"button"}
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
onClick={() => openUrl(link.url)}
|
onClick={() => openUrl(link.url)}
|
||||||
className={"rounded-sm outline-none decoration-[0.5px] underline-offset-4 hover:text-nb-gray-100 hover:underline transition focus-visible:ring-2 focus-visible:ring-white/60 focus-visible:ring-offset-2 focus-visible:ring-offset-nb-gray-940"}
|
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-white/60 focus-visible:ring-offset-2 focus-visible:ring-offset-nb-gray-940"
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{link.label}
|
{link.label}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ function Accent({ onDone }: Readonly<{ onDone: () => void }>) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`fixed inset-0 z-50 bg-black/5 transition-opacity duration-500 pointer-events-none ${visible ? "opacity-100" : "opacity-0"}`}
|
className={`pointer-events-none fixed inset-0 z-50 bg-black/5 transition-opacity duration-500 ${visible ? "opacity-100" : "opacity-0"}`}
|
||||||
>
|
>
|
||||||
<canvas ref={canvasRef} className={"block"} />
|
<canvas ref={canvasRef} className={"block"} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ export function SettingsGeneral() {
|
|||||||
<SectionGroup title={t("settings.general.section.connection")}>
|
<SectionGroup title={t("settings.general.section.connection")}>
|
||||||
<div>
|
<div>
|
||||||
<div className={"flex items-start gap-3"}>
|
<div className={"flex items-start gap-3"}>
|
||||||
<div className={"flex-1 min-w-0"}>
|
<div className={"min-w-0 flex-1"}>
|
||||||
<Label htmlFor={managementUrlId}>
|
<Label htmlFor={managementUrlId}>
|
||||||
{t("settings.general.management.label")}
|
{t("settings.general.management.label")}
|
||||||
</Label>
|
</Label>
|
||||||
@@ -72,7 +72,7 @@ export function SettingsGeneral() {
|
|||||||
<ManagementServerSwitch value={mode} onChange={setMode} />
|
<ManagementServerSwitch value={mode} onChange={setMode} />
|
||||||
</div>
|
</div>
|
||||||
{mode === ManagementMode.SelfHosted && (
|
{mode === ManagementMode.SelfHosted && (
|
||||||
<div className={"flex items-start gap-3 mt-2"}>
|
<div className={"mt-2 flex items-start gap-3"}>
|
||||||
<Input
|
<Input
|
||||||
id={managementUrlId}
|
id={managementUrlId}
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ export const SettingsNavigation = () => {
|
|||||||
) : undefined;
|
) : undefined;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={"flex flex-col w-52 shrink-0 items-center select-none"}>
|
<div className={"flex w-52 shrink-0 select-none flex-col items-center"}>
|
||||||
<VerticalTabs.List aria-label={t("settings.nav.label")}>
|
<VerticalTabs.List aria-label={t("settings.nav.label")}>
|
||||||
<VerticalTabs.Trigger
|
<VerticalTabs.Trigger
|
||||||
value={"general"}
|
value={"general"}
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ export const SettingsPage = () => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{isMacOS() ? (
|
{isMacOS() ? (
|
||||||
<div className={"wails-draggable cursor-default select-none h-12 shrink-0"} />
|
<div className={"wails-draggable h-12 shrink-0 cursor-default select-none"} />
|
||||||
) : (
|
) : (
|
||||||
<div className={"h-px shrink-0 bg-nb-gray-920/0"} />
|
<div className={"h-px shrink-0 bg-nb-gray-920/0"} />
|
||||||
)}
|
)}
|
||||||
@@ -95,10 +95,10 @@ export const SettingsPage = () => {
|
|||||||
<ScrollArea.Root
|
<ScrollArea.Root
|
||||||
key={active}
|
key={active}
|
||||||
type={"auto"}
|
type={"auto"}
|
||||||
className={"flex-1 min-h-0 overflow-hidden"}
|
className={"min-h-0 flex-1 overflow-hidden"}
|
||||||
>
|
>
|
||||||
<ScrollArea.Viewport className={"h-full w-full"}>
|
<ScrollArea.Viewport className={"h-full w-full"}>
|
||||||
<div className={"py-6 px-7"}>
|
<div className={"px-7 py-6"}>
|
||||||
{visibleTabs.map((tab) => (
|
{visibleTabs.map((tab) => (
|
||||||
<VerticalTabs.Content key={tab} value={tab}>
|
<VerticalTabs.Content key={tab} value={tab}>
|
||||||
{TAB_CONTENT[tab]}
|
{TAB_CONTENT[tab]}
|
||||||
@@ -109,12 +109,14 @@ export const SettingsPage = () => {
|
|||||||
<ScrollArea.Scrollbar
|
<ScrollArea.Scrollbar
|
||||||
orientation={"vertical"}
|
orientation={"vertical"}
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex select-none touch-none transition-colors",
|
"flex touch-none select-none transition-colors",
|
||||||
"w-1.5 bg-transparent py-1",
|
"w-1.5 bg-transparent py-1",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<ScrollArea.Thumb
|
<ScrollArea.Thumb
|
||||||
className={"flex-1 rounded-full bg-nb-gray-800 hover:bg-nb-gray-700 relative"}
|
className={
|
||||||
|
"relative flex-1 rounded-full bg-nb-gray-800 hover:bg-nb-gray-700"
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</ScrollArea.Scrollbar>
|
</ScrollArea.Scrollbar>
|
||||||
</ScrollArea.Root>
|
</ScrollArea.Root>
|
||||||
|
|||||||
@@ -93,11 +93,11 @@ export function SettingsSSH() {
|
|||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex items-center gap-6 justify-between",
|
"flex items-center justify-between gap-6",
|
||||||
config.disableSshAuth && "opacity-50 pointer-events-none",
|
config.disableSshAuth && "pointer-events-none opacity-50",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div className={"flex-1 max-w-md"}>
|
<div className={"max-w-md flex-1"}>
|
||||||
<Label htmlFor={jwtTtlId}>{t("settings.ssh.jwtTtl.label")}</Label>
|
<Label htmlFor={jwtTtlId}>{t("settings.ssh.jwtTtl.label")}</Label>
|
||||||
<HelpText margin={false}>{t("settings.ssh.jwtTtl.help")}</HelpText>
|
<HelpText margin={false}>{t("settings.ssh.jwtTtl.help")}</HelpText>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -15,12 +15,12 @@ export const SectionGroup = ({
|
|||||||
tabIndex={disabled ? -1 : 0}
|
tabIndex={disabled ? -1 : 0}
|
||||||
{...(disabled ? { inert: "" } : {})}
|
{...(disabled ? { inert: "" } : {})}
|
||||||
className={cn(
|
className={cn(
|
||||||
"mb-8 last:mb-1 px-1 rounded-md outline-none",
|
"mb-8 rounded-md px-1 outline-none last:mb-1",
|
||||||
"focus-visible:ring-2 focus-visible:ring-white/60 focus-visible:ring-offset-2 focus-visible:ring-offset-nb-gray-940",
|
"focus-visible:ring-2 focus-visible:ring-white/60 focus-visible:ring-offset-2 focus-visible:ring-offset-nb-gray-940",
|
||||||
disabled && "opacity-30 pointer-events-none",
|
disabled && "pointer-events-none opacity-30",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<h2 className={"text-xs uppercase tracking-wider text-nb-gray-400 mb-4 font-semibold"}>
|
<h2 className={"mb-4 text-xs font-semibold uppercase tracking-wider text-nb-gray-400"}>
|
||||||
{title}
|
{title}
|
||||||
</h2>
|
</h2>
|
||||||
<div className={"flex flex-col gap-5"}>{children}</div>
|
<div className={"flex flex-col gap-5"}>{children}</div>
|
||||||
@@ -32,7 +32,9 @@ export const SettingsBottomBar = ({ children }: { children: ReactNode }) => (
|
|||||||
<div className={"h-[3.2rem] shrink-0"} aria-hidden={"true"} />
|
<div className={"h-[3.2rem] shrink-0"} aria-hidden={"true"} />
|
||||||
<div className={"absolute bottom-0 left-0 w-full"}>
|
<div className={"absolute bottom-0 left-0 w-full"}>
|
||||||
<div
|
<div
|
||||||
className={"w-full flex justify-end gap-3 px-8 py-5 border-t border-nb-gray-920 bg-nb-gray-940"}
|
className={
|
||||||
|
"flex w-full justify-end gap-3 border-t border-nb-gray-920 bg-nb-gray-940 px-8 py-5"
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import Skeleton from "react-loading-skeleton";
|
|||||||
|
|
||||||
export const SettingsSkeleton = () => {
|
export const SettingsSkeleton = () => {
|
||||||
return (
|
return (
|
||||||
<div className={"gap-6 flex flex-col"}>
|
<div className={"flex flex-col gap-6"}>
|
||||||
<div>
|
<div>
|
||||||
<Skeleton width={100} height={16} className={"mb-4"} />
|
<Skeleton width={100} height={16} className={"mb-4"} />
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -94,10 +94,10 @@ export function SettingsTroubleshooting() {
|
|||||||
disabled={!capture}
|
disabled={!capture}
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
className={"flex items-center gap-6 justify-between"}
|
className={"flex items-center justify-between gap-6"}
|
||||||
{...(capture ? {} : { inert: "" })}
|
{...(capture ? {} : { inert: "" })}
|
||||||
>
|
>
|
||||||
<div className={"flex-1 max-w-md"}>
|
<div className={"max-w-md flex-1"}>
|
||||||
<Label htmlFor={durationId} disabled={!capture}>
|
<Label htmlFor={durationId} disabled={!capture}>
|
||||||
{t("settings.troubleshooting.duration.label")}
|
{t("settings.troubleshooting.duration.label")}
|
||||||
</Label>
|
</Label>
|
||||||
@@ -136,7 +136,9 @@ export function SettingsTroubleshooting() {
|
|||||||
function CenteredPanel({ children }: Readonly<{ children: ReactNode }>) {
|
function CenteredPanel({ children }: Readonly<{ children: ReactNode }>) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={"absolute inset-0 flex flex-col items-center justify-center gap-5 p-8 text-center"}
|
className={
|
||||||
|
"absolute inset-0 flex flex-col items-center justify-center gap-5 p-8 text-center"
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
@@ -153,7 +155,7 @@ function ProgressSection({
|
|||||||
<CenteredPanel>
|
<CenteredPanel>
|
||||||
<SquareIcon icon={Loader2} className={"[&_svg]:animate-spin"} />
|
<SquareIcon icon={Loader2} className={"[&_svg]:animate-spin"} />
|
||||||
|
|
||||||
<div className={"flex flex-col items-center gap-2 max-w-sm"}>
|
<div className={"flex max-w-sm flex-col items-center gap-2"}>
|
||||||
<DialogHeading className={"text-balance"}>{stageLabel(stage, t)}</DialogHeading>
|
<DialogHeading className={"text-balance"}>{stageLabel(stage, t)}</DialogHeading>
|
||||||
<DialogDescription>
|
<DialogDescription>
|
||||||
{t("settings.troubleshooting.progress.description")}
|
{t("settings.troubleshooting.progress.description")}
|
||||||
@@ -162,7 +164,9 @@ function ProgressSection({
|
|||||||
|
|
||||||
{stage.kind === "capturing" && (
|
{stage.kind === "capturing" && (
|
||||||
<div
|
<div
|
||||||
className={"font-mono font-semibold text-2xl tabular-nums text-nb-gray-50 tracking-wider"}
|
className={
|
||||||
|
"font-mono text-2xl font-semibold tabular-nums tracking-wider text-nb-gray-50"
|
||||||
|
}
|
||||||
aria-live={"polite"}
|
aria-live={"polite"}
|
||||||
>
|
>
|
||||||
{formatRemaining(stage.remainingSec)}
|
{formatRemaining(stage.remainingSec)}
|
||||||
@@ -207,7 +211,7 @@ function DoneResult({
|
|||||||
<CenteredPanel>
|
<CenteredPanel>
|
||||||
<SquareIcon icon={CircleCheckBig} className={"[&_svg]:text-green-500"} />
|
<SquareIcon icon={CircleCheckBig} className={"[&_svg]:text-green-500"} />
|
||||||
|
|
||||||
<div className={"flex flex-col items-center gap-2 max-w-sm"}>
|
<div className={"flex max-w-sm flex-col items-center gap-2"}>
|
||||||
<DialogHeading className={"text-balance"}>
|
<DialogHeading className={"text-balance"}>
|
||||||
{showKey
|
{showKey
|
||||||
? t("settings.troubleshooting.done.uploadedTitle")
|
? t("settings.troubleshooting.done.uploadedTitle")
|
||||||
@@ -221,9 +225,7 @@ function DoneResult({
|
|||||||
docs: (
|
docs: (
|
||||||
<a
|
<a
|
||||||
href={SUPPORT_DOCS_URL}
|
href={SUPPORT_DOCS_URL}
|
||||||
aria-label={t(
|
aria-label={t("settings.about.community.documentation")}
|
||||||
"settings.about.community.documentation",
|
|
||||||
)}
|
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
Browser.OpenURL(SUPPORT_DOCS_URL).catch(() =>
|
Browser.OpenURL(SUPPORT_DOCS_URL).catch(() =>
|
||||||
@@ -244,7 +246,7 @@ function DoneResult({
|
|||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={"w-full max-w-sm flex flex-col gap-3"}>
|
<div className={"flex w-full max-w-sm flex-col gap-3"}>
|
||||||
{showKey && <Input value={result.uploadedKey} readOnly copy />}
|
{showKey && <Input value={result.uploadedKey} readOnly copy />}
|
||||||
|
|
||||||
{result.path && !showKey && (
|
{result.path && !showKey && (
|
||||||
@@ -256,7 +258,7 @@ function DoneResult({
|
|||||||
<button
|
<button
|
||||||
type={"button"}
|
type={"button"}
|
||||||
onClick={onRevealPath}
|
onClick={onRevealPath}
|
||||||
className={"pointer-events-auto hover:text-white transition-all"}
|
className={"pointer-events-auto transition-all hover:text-white"}
|
||||||
aria-label={t("settings.troubleshooting.done.openFileLocation")}
|
aria-label={t("settings.troubleshooting.done.openFileLocation")}
|
||||||
>
|
>
|
||||||
<FolderOpen size={16} aria-hidden={"true"} />
|
<FolderOpen size={16} aria-hidden={"true"} />
|
||||||
@@ -268,7 +270,9 @@ function DoneResult({
|
|||||||
{uploadFailed && (
|
{uploadFailed && (
|
||||||
<div
|
<div
|
||||||
role={"alert"}
|
role={"alert"}
|
||||||
className={"rounded-md border border-red-500/30 bg-red-500/10 px-3 py-2 text-xs text-red-300"}
|
className={
|
||||||
|
"rounded-md border border-red-500/30 bg-red-500/10 px-3 py-2 text-xs text-red-300"
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{result.uploadFailureReason
|
{result.uploadFailureReason
|
||||||
? t("settings.troubleshooting.uploadFailedWithReason", {
|
? t("settings.troubleshooting.uploadFailedWithReason", {
|
||||||
|
|||||||
@@ -29,12 +29,12 @@ export function WelcomeStepTray({ onContinue }: Readonly<WelcomeStepTrayProps>)
|
|||||||
<img
|
<img
|
||||||
src={trayScreenshot}
|
src={trayScreenshot}
|
||||||
alt={""}
|
alt={""}
|
||||||
className={"w-full h-auto select-none pointer-events-none rounded-2xl"}
|
className={"pointer-events-none h-auto w-full select-none rounded-2xl"}
|
||||||
draggable={false}
|
draggable={false}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={"flex flex-col w-full gap-1"}>
|
<div className={"flex w-full flex-col gap-1"}>
|
||||||
<DialogHeading id={"nb-welcome-title"} align={"left"}>
|
<DialogHeading id={"nb-welcome-title"} align={"left"}>
|
||||||
{t("welcome.title")}
|
{t("welcome.title")}
|
||||||
</DialogHeading>
|
</DialogHeading>
|
||||||
|
|||||||
Reference in New Issue
Block a user