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

@@ -59,6 +59,11 @@ export interface Service {
* keine DNS-Auflösung möglich war.
*/
deviceHostname?: string | null;
/**
* Ebenfalls nur frontend-seitig ergänzt - Online-Status des zugehörigen
* Geräts, für den kleinen Online/Offline-Punkt in der Trefferliste.
*/
deviceOnline?: boolean;
}
export interface HealthStatus {
@@ -125,13 +130,53 @@ export interface Rankable {
}
/**
* Vereinheitlichte Suchtreffer-Form für die Trefferliste: Dienste und
* Lesezeichen zusammen, aber unterscheidbar über `kind` (siehe
* "getrennt von Diensten" bei Favoriten).
* Synthetische Rankable-Form für Geräte in der Suche - Geräte haben keine
* eigene URL zum Öffnen (siehe DeviceSearchResult.kind==="device" in
* ResultsList: wird ausgegraut und nicht anklickbar dargestellt), sollen
* aber trotzdem über Name/IP/MAC auffindbar sein (IP/MAC landen in `alias`,
* dort durchsucht rankService bereits).
*/
export interface DeviceSearchResult {
kind: "device";
id: string;
displayName: string;
hostname: string;
ip: string;
mac: string | null;
online: boolean;
favicon: null;
category: null;
favorite: false;
order: number;
alias: string[];
description: string | null;
}
/** Synthetische Rankable-Form für Später-lesen-Einträge in der Suche. */
export interface ReadLaterSearchResult {
kind: "readlater";
id: string;
displayName: string;
hostname: string;
url: string;
favicon: string | null;
category: null;
favorite: false;
order: number;
alias: string[];
description: string | null;
}
/**
* Vereinheitlichte Suchtreffer-Form für die Trefferliste: Dienste,
* Lesezeichen, Geräte und Später-lesen-Einträge zusammen, aber
* unterscheidbar über `kind`.
*/
export type SearchResult =
| (Service & { kind: "service" })
| (Bookmark & { kind: "bookmark" });
| (Bookmark & { kind: "bookmark" })
| DeviceSearchResult
| ReadLaterSearchResult;
/**
* Ranking-Stufen für die Suche, gemäß Spezifikation:
@@ -194,6 +239,37 @@ export function rankServices<T extends Rankable>(items: T[], query: string): T[]
.map((entry) => entry.item);
}
/**
* Wie rankServices, aber für die Startseiten-Suche, die mehrere Arten von
* Treffern mischt (Dienste, Lesezeichen, Später-lesen, Geräte). Dienste
* stehen dabei IMMER vor allen anderen Arten, unabhängig von der
* Treffergüte - sie sind die eigentlichen "Ziele" der App. Geräte stehen
* immer zuletzt (sie sind nur informativ, nicht direkt anklickbar, siehe
* DeviceSearchResult). Lesezeichen/Später-lesen liegen dazwischen.
*/
export function rankSearchResults(items: SearchResult[], query: string): SearchResult[] {
const q = query.trim();
if (q.length === 0) return [];
function kindPriority(kind: SearchResult["kind"]): number {
if (kind === "service") return 0;
if (kind === "device") return 2;
return 1; // bookmark, readlater
}
return items
.map((item) => ({ item, rank: rankService(item, q) }))
.filter((entry): entry is { item: SearchResult; rank: number } => entry.rank !== null)
.sort((a, b) => {
const kindDiff = kindPriority(a.item.kind) - kindPriority(b.item.kind);
if (kindDiff !== 0) return kindDiff;
if (a.rank !== b.rank) return a.rank - b.rank;
if (a.item.favorite !== b.item.favorite) return a.item.favorite ? -1 : 1;
return a.item.order - b.item.order;
})
.map((entry) => entry.item);
}
/**
* Bekannte Domain-Fragmente -> naheliegende Kategorie, für den Kategorie-
* Vorschlag beim Anlegen eines Lesezeichens (siehe BookmarksPage.tsx). Rein