Skip to content
Merged
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ New here? Look for issues labeled

1. **Fork** the repo and create your branch **from `dev`** (never `main` — `main` only
receives release merges from `dev`).
2. **Set up:** Node.js 20+ (CI runs on Node 24) and pnpm. The pnpm version is pinned
2. **Set up:** Node.js 24+ (same as CI) and pnpm. The pnpm version is pinned
via the `packageManager` field, so the easiest path is:

```bash
Expand Down
6 changes: 4 additions & 2 deletions frontend/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,10 @@ first-party tunnel `/api/monitoring` to beat ad blockers.
**shadcn-vue first.** Check `components/ui/`, then the shadcn-vue docs for
something to copy in; hand-rolled Tailwind only when neither fits. Keep when
syncing upstream: `Spinner` + `ToolLoadingSkeleton` (project-specific),
`toggle` / `toggle-group`'s deliberate `primary` pressed pair, and the
overlay roots' shortcut suspension.
`toggle` / `toggle-group`'s deliberate `primary` pressed pair, the overlay
roots' shortcut suspension, and `select`'s trigger geometry — `py-1` plus a
flex (not `-webkit-box` line-clamp) value span, because Safari shifts button
content up ~2px once it overflows the trigger's content box.

### Design tokens

Expand Down
10 changes: 5 additions & 5 deletions frontend/components/ConnectivityTest.vue
Original file line number Diff line number Diff line change
Expand Up @@ -51,20 +51,20 @@
<span class="text-base font-medium truncate">{{ test.name }}</span>
</div>
<!-- Status + ms. Multi mode pins these to the best round (see checkConnectivityHandler). -->
<div class="flex items-center justify-between gap-2">
<span class="flex items-center gap-1.5 text-base min-w-0">
<div class="flex items-center justify-between gap-2 text-sm md:text-base">
<span class="flex items-center gap-1.5 min-w-0">
<span v-if="toneOf(test) === 'wait'" class="relative flex shrink-0">
<span class="absolute inline-flex size-2 rounded-full bg-info opacity-75 animate-ping"></span>
<span class="relative inline-flex size-2 rounded-full" :class="dotClass(toneOf(test))"></span>
</span>
<component v-else-if="statusFaceIcon(test)" :is="statusFaceIcon(test)" class="size-4 shrink-0"
:class="textClass(toneOf(test))" />
<span :class="textClass(toneOf(test))" class="font-mono whitespace-nowrap min-w-0">{{ test.status
<span :class="textClass(toneOf(test))" class="min-w-0 truncate" :title="test.status">{{ test.status
}}</span>
</span>
<span v-if="test.time !== 0" class="text-base font-mono tabular-nums text-muted-foreground"
<span v-if="test.time !== 0" class="shrink-0 font-mono tabular-nums text-muted-foreground"
:title="t('connectivity.minTestTime') + test.mintime + ' ms'">
{{ test.time }}<span class="ml-0.5 text-sm">ms</span>
{{ test.time }}<span class="ml-0.5 text-xs md:text-sm">ms</span>
</span>
</div>
<!-- Multi-test per-round latency bars, all on one absolute scale
Expand Down
8 changes: 4 additions & 4 deletions frontend/components/Nav.vue
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,12 @@
</NavigationMenuTrigger>
<NavigationMenuContent class="z-50">
<!-- Two-column grid on PC -->
<ul class="relative grid grid-cols-2 gap-x-4 gap-y-0.5 min-w-[24rem]">
<ul class="relative grid grid-cols-2 gap-x-4 gap-y-0.5 min-w-[28rem]">
<span aria-hidden="true"
class="pointer-events-none absolute inset-y-1 left-1/2 w-px -translate-x-1/2 bg-border"></span>
<li v-for="tool in advancedTools" :key="tool.slug">
<NavigationMenuLink as-child class="cursor-pointer">
<button type="button" class="w-full truncate text-left" @click="openTool(tool.slug)">
<button type="button" class="w-full text-left leading-snug" @click="openTool(tool.slug)">
{{ t(tool.titleKey) }}
</button>
</NavigationMenuLink>
Expand Down Expand Up @@ -203,7 +203,7 @@
<!-- Mobile navigation drawer. Flex column so the link list scrolls instead
of clipping on short screens when Advanced Tools is expanded. -->
<Sheet v-if="isMobile" :open="isNavMenuOpen" @update:open="onNavMenuChange">
<SheetContent side="left" class="w-72 p-0 flex flex-col gap-0" :title="t('nav.Navigation')">
<SheetContent side="left" class="w-80 p-0 flex flex-col gap-0" :title="t('nav.Navigation')">
<div class="flex shrink-0 items-center justify-between border-b px-4 py-3">
<h5 class="m-0 text-base font-semibold">{{ t('nav.Navigation') }}</h5>
<SheetClose />
Expand All @@ -224,7 +224,7 @@
<CollapsibleContent>
<div class="my-0.5 ml-3 flex flex-col gap-0.5 border-l pl-3">
<button v-for="tool in advancedTools" :key="tool.slug" type="button"
class="block w-full truncate rounded-md px-3 py-1.5 text-left text-sm text-muted-foreground transition-colors hover:bg-accent/50 hover:text-foreground"
class="block w-full rounded-md px-3 py-1.5 text-left text-sm leading-snug text-muted-foreground transition-colors hover:bg-accent/50 hover:text-foreground"
@click="openTool(tool.slug)">
{{ t(tool.titleKey) }}
</button>
Expand Down
2 changes: 1 addition & 1 deletion frontend/components/ui/select/SelectTrigger.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ defineProps({ class: { type: [String, Array, Object], default: undefined } });

<template>
<SelectTrigger
:class="cn('flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1', $props.class)"
:class="cn('flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&>span]:flex [&>span]:min-w-0 [&>span]:items-center [&>span]:overflow-hidden [&>span]:whitespace-nowrap', $props.class)"
>
<slot />
<SelectIcon as-child>
Expand Down
8 changes: 6 additions & 2 deletions frontend/components/ui/select/SelectValue.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ defineProps({ placeholder: { type: String, default: undefined } });
</script>

<template>
<SelectValue :placeholder="placeholder">
<slot />
<!-- The trigger lays this span out as a flex row, so the default text needs
an element of its own to truncate against. -->
<SelectValue v-slot="{ selectedLabel, modelValue }" :placeholder="placeholder">
<slot :selected-label="selectedLabel" :model-value="modelValue">
<span class="truncate">{{ selectedLabel.length ? selectedLabel.join(', ') : placeholder }}</span>
</slot>
</SelectValue>
</template>
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
"version": "7.4.0",
"type": "module",
"packageManager": "pnpm@11.22.0",
"engines": {
"node": ">=24"
},
"scripts": {
"dev": "concurrently \"vite\" \"nodemon --import ./sentry-instrument.js backend-server.js\"",
"build": "vite build",
Expand Down
Loading