polish(web): favicon, Punktfunk casing, display-save toast, idle stop button
- Favicon: the brand-mark lens SVG at /favicon.svg, linked from the root head. - Branding: user-facing "punktfunk" → "Punktfunk" (tab title, app_name, pairing prose, the logo aria-labels/SVG titles) — protocol identifiers (punktfunk/1, _punktfunk._udp, binary/module names) stay lowercase. - The display policy auto-saves on every preset pick / field edit with no signal; mount @unom/ui's sonner Toaster in the root and fire a success toast from the save funnel's onSuccess (new i18n key, en + de). First toast usage in the console — errors stay inline as before. - The stop-session button rendered destructive-red even with nothing to stop: variant switches to the muted `secondary` while no session is active. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -48,7 +48,7 @@ export function AppShell({ children }: { children: ReactNode }) {
|
||||
<aside className="sticky top-0 hidden h-dvh w-60 shrink-0 flex-col overflow-y-auto border-r bg-card/40 p-4 sm:flex">
|
||||
<Link
|
||||
to="/"
|
||||
aria-label="punktfunk"
|
||||
aria-label="Punktfunk"
|
||||
className="mb-7 flex items-center gap-2 px-2 pt-1"
|
||||
>
|
||||
<BrandMark className="size-7 drop-shadow-[0_2px_12px_rgba(108,91,243,0.45)]" />
|
||||
|
||||
@@ -5,13 +5,13 @@
|
||||
export function BrandMark({ className }: { className?: string }) {
|
||||
return (
|
||||
<svg
|
||||
aria-label="punktfunk"
|
||||
aria-label="Punktfunk"
|
||||
role="img"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 1000 1000"
|
||||
className={className}
|
||||
>
|
||||
<title>punktfunk</title>
|
||||
<title>Punktfunk</title>
|
||||
<path
|
||||
d="M403.037,791.672c107.586,0 194.41,-86.824 194.41,-194.41c0,-107.586 -86.824,-194.41 -194.41,-194.41c-107.586,0 -194.41,86.824 -194.41,194.41c0,107.586 86.824,194.41 194.41,194.41Z"
|
||||
fill="#a79ff8"
|
||||
|
||||
@@ -8,13 +8,13 @@ export function Wordmark({ className }: { className?: string }) {
|
||||
return (
|
||||
<svg
|
||||
role="img"
|
||||
aria-label="punktfunk"
|
||||
aria-label="Punktfunk"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 579 136"
|
||||
fill="currentColor"
|
||||
className={cn("w-auto text-highlight", className)}
|
||||
>
|
||||
<title>punktfunk</title>
|
||||
<title>Punktfunk</title>
|
||||
<path d="M16.782,16.051l0,102.687l31.253,0l0,-35.563l73.436,0l0,-23.555l-73.436,0l0,-19.398l77.285,0l0,-24.171l-108.537,0Z" />
|
||||
<path d="M131.785,16.051l0,47.264c0.154,16.627 0.154,16.627 0.308,20.014c0.77,15.087 2.463,21.4 7.544,26.634c7.698,8.16 20.014,10.315 59.272,10.315c23.863,0 34.178,-0.616 43.415,-2.463c11.7,-2.463 19.552,-10.623 21.246,-22.323c0.924,-7.236 1.078,-8.929 1.54,-32.176l0,-47.264l-31.253,0l0,47.264c0,2.155 -0.154,7.082 -0.308,10.623c-0.462,9.699 -1.232,12.47 -3.695,15.087c-3.387,3.695 -9.853,4.619 -31.407,4.619c-26.634,0 -32.638,-1.693 -34.332,-9.853c-0.77,-4.157 -0.77,-4.311 -1.078,-20.476l0,-47.264l-31.253,0Z" />
|
||||
<path d="M271.575,15.943l0,102.687l31.868,0l-0.77,-76.669l3.387,0l54.038,76.669l54.346,0l0,-102.687l-31.868,0l0.77,76.515l-3.233,0l-53.73,-76.515l-54.808,0Z" />
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
useRouterState,
|
||||
} from "@tanstack/react-router";
|
||||
import "@fontsource-variable/geist";
|
||||
import { Toaster } from "@unom/ui/toast";
|
||||
import { AppShell } from "@/components/app-shell";
|
||||
import appCss from "@/styles.css?url";
|
||||
|
||||
@@ -22,9 +23,12 @@ export const Route = createRootRouteWithContext<RouterContext>()({
|
||||
{ charSet: "utf-8" },
|
||||
{ name: "viewport", content: "width=device-width, initial-scale=1" },
|
||||
{ name: "color-scheme", content: "dark light" },
|
||||
{ title: "punktfunk" },
|
||||
{ title: "Punktfunk" },
|
||||
],
|
||||
links: [
|
||||
{ rel: "stylesheet", href: appCss },
|
||||
{ rel: "icon", type: "image/svg+xml", href: "/favicon.svg" },
|
||||
],
|
||||
links: [{ rel: "stylesheet", href: appCss }],
|
||||
}),
|
||||
component: RootComponent,
|
||||
});
|
||||
@@ -47,6 +51,8 @@ function RootComponent() {
|
||||
<Outlet />
|
||||
</AppShell>
|
||||
)}
|
||||
{/* Sonner toaster (lazy client-side) — success feedback for auto-saved settings. */}
|
||||
<Toaster />
|
||||
<Scripts />
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -78,7 +78,7 @@ export const DashboardView: FC<{
|
||||
{m.action_request_idr()}
|
||||
</Button>
|
||||
<Button
|
||||
variant="destructive"
|
||||
variant={s.session ? "destructive" : "secondary"}
|
||||
size="sm"
|
||||
disabled={!s.session || isStopping}
|
||||
onClick={onStopSession}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { Button } from "@unom/ui/button";
|
||||
import { toast } from "@unom/ui/toast";
|
||||
import { Pencil, Plus, RefreshCw, Trash2 } from "lucide-react";
|
||||
import { type FC, type MouseEvent, type ReactNode, useEffect, useState } from "react";
|
||||
import {
|
||||
@@ -62,6 +63,9 @@ export const DisplaySection: FC = () => {
|
||||
onSuccess: (res) => {
|
||||
setDraft(res.settings);
|
||||
qc.invalidateQueries({ queryKey: getGetDisplaySettingsQueryKey() });
|
||||
// The policy auto-saves on every preset pick / field edit — without a signal
|
||||
// users kept looking for a Save button. Errors stay inline (apiErrorMessage).
|
||||
toast.success(m.display_settings_saved());
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user