60 lines
1.7 KiB
TypeScript
60 lines
1.7 KiB
TypeScript
export type QuickLinkMap = Record<string, string>;
|
|
|
|
export type NekoBrowserLink = {
|
|
id: number;
|
|
key: string;
|
|
label: string;
|
|
url: string;
|
|
port: string;
|
|
};
|
|
|
|
const NEKO_KEY_RE = /^neko_browser(?:_(\d+))?$/i;
|
|
|
|
function parsePort(url: string): string {
|
|
try {
|
|
return new URL(url, "http://127.0.0.1").port || "";
|
|
} catch {
|
|
return "";
|
|
}
|
|
}
|
|
|
|
export function extractNekoBrowserLinks(quickLinks?: QuickLinkMap | null): NekoBrowserLink[] {
|
|
const picked = new Map<number, NekoBrowserLink>();
|
|
for (const [key, rawUrl] of Object.entries(quickLinks ?? {})) {
|
|
const match = key.match(NEKO_KEY_RE);
|
|
const url = rawUrl.trim();
|
|
if (!match || !url) continue;
|
|
const id = key === "neko_browser" ? 1 : Number(match[1] || 1);
|
|
const next: NekoBrowserLink = {
|
|
id,
|
|
key,
|
|
label: `Neko ${id}`,
|
|
url,
|
|
port: parsePort(url),
|
|
};
|
|
const current = picked.get(id);
|
|
const preferNext = key !== "neko_browser";
|
|
if (!current || preferNext) picked.set(id, next);
|
|
}
|
|
return Array.from(picked.values()).sort((a, b) => a.id - b.id);
|
|
}
|
|
|
|
export function resolveNekoBrowserLink(
|
|
links: NekoBrowserLink[],
|
|
preferredIndex: number,
|
|
): { link: NekoBrowserLink | null; fallback: boolean } {
|
|
if (!links.length) return { link: null, fallback: false };
|
|
if (!Number.isInteger(preferredIndex) || preferredIndex < 0) {
|
|
return { link: links[0], fallback: false };
|
|
}
|
|
if (preferredIndex < links.length) {
|
|
return { link: links[preferredIndex], fallback: false };
|
|
}
|
|
return { link: links[0], fallback: true };
|
|
}
|
|
|
|
export function formatNekoPorts(links: NekoBrowserLink[]): string {
|
|
const ports = links.map((item) => item.port).filter(Boolean);
|
|
return ports.length ? ports.map((port) => `:${port}`).join(" / ") : "";
|
|
}
|