Make the brand CTA visible and fix two more v3 variable classes

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UytviPMJbXzD8P84nLvXcq
This commit is contained in:
2026-08-25 18:33:28 +02:00
co-authored by Claude Opus 5
parent df22475f54
commit 476dc6a1e9
4 changed files with 35 additions and 13 deletions
+1 -1
View File
@@ -131,7 +131,7 @@ function SuggestInput({
</PopoverAnchor> </PopoverAnchor>
<PopoverContent <PopoverContent
align="start" align="start"
className="w-[--radix-popover-trigger-width] p-0" className="w-[var(--radix-popover-trigger-width)] p-0"
onOpenAutoFocus={(event) => event.preventDefault()} onOpenAutoFocus={(event) => event.preventDefault()}
> >
<Command shouldFilter={false}> <Command shouldFilter={false}>
@@ -103,7 +103,7 @@ export function ValuePreview({
> >
<ChevronRight <ChevronRight
className={cn( className={cn(
"size-3 shrink-0 transition-transform duration-[--duration-fast]", "size-3 shrink-0 transition-transform duration-[var(--duration-fast)]",
open && "rotate-90", open && "rotate-90",
)} )}
/> />
@@ -205,7 +205,8 @@ export function RemoteAccess() {
</div> </div>
<div> <div>
<Button <Button
className="rounded-full bg-[--brand-secondary] text-white hover:opacity-90" variant="brand"
className="rounded-full"
disabled={!code.trim() || !portalUrl.trim() || connect.isPending} disabled={!code.trim() || !portalUrl.trim() || connect.isPending}
onClick={() => connect.mutate()} onClick={() => connect.mutate()}
> >
+31 -10
View File
@@ -1,8 +1,11 @@
import { useMutation, useQuery } from "@tanstack/react-query" import { useMutation, useQuery } from "@tanstack/react-query"
import { createFileRoute } from "@tanstack/react-router" import { createFileRoute } from "@tanstack/react-router"
import { Check, Copy } from "lucide-react"
import { useEffect } from "react" import { useEffect } from "react"
import { ApiError, PanelsService } from "@/client" import { ApiError, PanelsService } from "@/client"
import { Button } from "@/components/ui/button"
import { useCopyToClipboard } from "@/hooks/useCopyToClipboard"
import { appPath, isPortal, openPortalSession } from "@/lib/portal" import { appPath, isPortal, openPortalSession } from "@/lib/portal"
/** /**
@@ -27,6 +30,8 @@ export const Route = createFileRoute("/panel/")({
}) })
function PairPanel() { function PairPanel() {
const [copied, copy] = useCopyToClipboard()
// A code lives ten minutes. Asking for one is the mount, and asking again is // A code lives ten minutes. Asking for one is the mount, and asking again is
// what happens when this one is no longer recognised. // what happens when this one is no longer recognised.
const start = useMutation({ const start = useMutation({
@@ -98,16 +103,32 @@ function PairPanel() {
</p> </p>
</div> </div>
<p className="font-mono text-6xl font-medium tracking-[0.2em]"> <div className="flex items-center justify-center gap-4">
<span data-testid="pairing-code" aria-hidden="true"> <p className="font-mono text-6xl font-medium tracking-[0.2em]">
{started?.code ?? "······"} <span data-testid="pairing-code" aria-hidden="true">
</span> {started?.code ?? "······"}
{/* Spelled out, so a reader says the characters rather than trying </span>
to pronounce them as a word. */} {/* Spelled out, so a reader says the characters rather than trying
<span className="sr-only"> to pronounce them as a word. */}
{(started?.code ?? "").split("").join(" ")} <span className="sr-only">
</span> {(started?.code ?? "").split("").join(" ")}
</p> </span>
</p>
{/* Only where it would work: a panel served over plain http on the
LAN has no clipboard, and a button that silently does nothing is
worse than none. */}
{started?.code && navigator.clipboard ? (
<Button
variant="ghost"
size="icon"
className="size-11 shrink-0 text-muted-foreground md:size-8"
aria-label="Copy the code"
onClick={() => copy(started.code)}
>
{copied === started.code ? <Check /> : <Copy />}
</Button>
) : null}
</div>
<p className="text-sm text-muted-foreground"> <p className="text-sm text-muted-foreground">
The code lasts ten minutes; this screen replaces it when it runs out. The code lasts ten minutes; this screen replaces it when it runs out.