round24: Favicon-Groesse und Wort-Erkennung verbessert, ausgeblendet-Badge entfernt, Proxmox-Port-Bug gefixt, Suche um Geraete und Spaeter-lesen erweitert

This commit is contained in:
2026-07-24 00:58:28 +02:00
parent d87885f16f
commit 6a2bf24498
10 changed files with 331 additions and 94 deletions

View File

@@ -4,7 +4,7 @@ import { Link } from "@tanstack/react-router";
import { SearchInput, StatusBadge, ResultsList, FavoritesBar, Favicon, Button } from "@launchpad/ui";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faGear, faPlus } from "@fortawesome/free-solid-svg-icons";
import { rankServices, type SearchResult } from "@launchpad/shared";
import { rankSearchResults, type SearchResult } from "@launchpad/shared";
import { useServices } from "../hooks/useServices.js";
import { useBookmarks } from "../hooks/useBookmarks.js";
import { useDevices } from "../hooks/useDevices.js";
@@ -133,8 +133,9 @@ export function HomePage() {
const isLoading = servicesLoading || bookmarksLoading;
function openItem(item: SearchResult | { url: string; id?: string; kind?: "service" | "bookmark" }) {
if ("kind" in item && item.kind === "device") return; // Geräte haben keine eigene URL, nicht anklickbar
window.open(item.url, "_blank", "noopener,noreferrer");
if ("kind" in item && item.kind && item.id) {
if ("kind" in item && (item.kind === "service" || item.kind === "bookmark") && item.id) {
recordVisit(item.kind, item.id).then(() => {
queryClient.invalidateQueries({ queryKey: ["recent-visits"] });
});
@@ -167,24 +168,57 @@ export function HomePage() {
});
// Ausgeblendete Dienste (z. B. Fehlerseiten/nicht erreichbare Scan-Treffer,
// siehe Admin -> Dienste) tauchen in der Suche nicht auf.
// siehe Admin -> Dienste) tauchen in der Suche nicht auf. Geräte und
// Später-lesen-Einträge sind jetzt ebenfalls durchsuchbar (siehe
// rankSearchResults: Dienste stehen dabei immer zuerst, Geräte immer
// zuletzt - Geräte haben keine eigene URL zum Öffnen, siehe ResultsList).
const allItems: SearchResult[] = useMemo(() => {
const deviceHostnameById = new Map((devices ?? []).map((d) => [d.id, d.hostname]));
const deviceOnlineById = new Map((devices ?? []).map((d) => [d.id, d.online]));
const visibleServices: SearchResult[] = (services ?? [])
.filter((s) => s.visible)
.map((s) => ({
...s,
kind: "service" as const,
deviceHostname: deviceHostnameById.get(s.deviceId) ?? null,
deviceOnline: deviceOnlineById.get(s.deviceId) ?? false,
}));
const bookmarkItems: SearchResult[] = (bookmarks ?? []).map((b) => ({
...b,
kind: "bookmark" as const,
}));
return [...visibleServices, ...bookmarkItems];
}, [services, bookmarks, devices]);
const deviceItems: SearchResult[] = (devices ?? []).map((d) => ({
kind: "device" as const,
id: d.id,
displayName: d.hostname,
hostname: d.hostname,
ip: d.ip,
mac: d.mac,
online: d.online,
favicon: null,
category: null,
favorite: false,
order: 0,
alias: [d.ip, d.mac].filter((v): v is string => !!v),
description: d.ip,
}));
const readLaterSearchItems: SearchResult[] = (readLaterItems ?? []).map((r) => ({
kind: "readlater" as const,
id: r.id,
displayName: r.displayName,
hostname: "",
url: r.url,
favicon: r.favicon,
category: null,
favorite: false,
order: 0,
alias: [],
description: null,
}));
return [...visibleServices, ...bookmarkItems, ...deviceItems, ...readLaterSearchItems];
}, [services, bookmarks, devices, readLaterItems]);
const results = useMemo(() => rankServices(allItems, query), [allItems, query]);
const results = useMemo(() => rankSearchResults(allItems, query), [allItems, query]);
const favoriteServices = useMemo(
() =>
@@ -390,7 +424,7 @@ export function HomePage() {
>
<Favicon src={item.favicon} fallbackLetter={item.displayName} size="md" />
</span>
<span className="w-full truncate text-center text-[10px] leading-tight text-black/55 dark:text-white/55">
<span className="line-clamp-2 w-full text-center text-[10px] leading-tight text-black/55 dark:text-white/55">
{item.displayName}
</span>
</button>