diff --git a/src/app/os/files/page.tsx b/src/app/os/files/page.tsx index c412c25..0f31552 100644 --- a/src/app/os/files/page.tsx +++ b/src/app/os/files/page.tsx @@ -3,7 +3,7 @@ import { useState, useEffect, useMemo } from "react"; import { useSearchParams } from "next/navigation"; import { useDebounce } from "use-debounce"; -import { Search, X } from "lucide-react"; +import { Search, X, ChevronsUpDown, ChevronsDownUp } from "lucide-react"; import { Input } from "@/components/ui/input"; import { Button } from "@/components/ui/button"; @@ -47,7 +47,7 @@ export default function Files() { return (
-
+
)}
-
- {!loading && files.length > 0 && ( -
- - -
- )} - {!loading && ( -
- {isFiltering ? ( - <> - {filtered.length} of {files.length} paths - - ) : ( - <>{files.length} paths - )} -
- )} -
+ {!loading && files.length > 0 && ( +
+ +
+ +
+ )} + {!loading && ( +
+ {isFiltering ? ( + <> + {filtered.length} of {files.length} paths + + ) : ( + <>{files.length} paths + )} +
+ )}
diff --git a/src/app/os/find/page.tsx b/src/app/os/find/page.tsx index fc70fae..89d9253 100644 --- a/src/app/os/find/page.tsx +++ b/src/app/os/find/page.tsx @@ -2,7 +2,7 @@ import { useState, useEffect, useMemo } from "react"; import { redirect, useSearchParams } from "next/navigation"; -import { Search, X } from "lucide-react"; +import { Search, X, ChevronsUpDown, ChevronsDownUp } from "lucide-react"; import { Input } from "@/components/ui/input"; import { Button } from "@/components/ui/button"; @@ -68,18 +68,14 @@ export default function FindByKey() { }, [isFiltering]); return ( -
-
-

- Binaries that have the following entitlement: -

-

- {key} -

-
- -
-
+
+
+
+ Binaries with + {key} +
+
+
)}
-
- {!loading && paths.length > 0 && ( -
- - -
- )} - {!loading && ( -
- {isFiltering ? ( - <> - {filtered.length} of {paths.length} paths - - ) : ( - <>{paths.length} paths - )} -
- )} + {!loading && paths.length > 0 && ( +
+ +
+ +
+ )} + {!loading && ( +
+ {isFiltering ? ( + <> + {filtered.length} of {paths.length} paths + + ) : ( + <>{paths.length} paths + )} +
+ )}
- {loading ? ( -
- {Array.from({ length: 6 }).map((_, i) => ( -
-
-
-
- ))} -
- ) : filtered.length === 0 ? ( -
- {paths.length === 0 ? ( -

No binaries found with this entitlement.

- ) : ( -

No paths match "{keyword}"

- )} -
- ) : ( - - )} +
+ {loading ? ( +
+ {Array.from({ length: 6 }).map((_, i) => ( +
+
+
+
+ ))} +
+ ) : filtered.length === 0 ? ( +
+ {paths.length === 0 ? ( +

No binaries found with this entitlement.

+ ) : ( +

No paths match "{keyword}"

+ )} +
+ ) : ( + + )} +
); } diff --git a/src/app/os/keys/page.tsx b/src/app/os/keys/page.tsx index be34aa8..cc9abbb 100644 --- a/src/app/os/keys/page.tsx +++ b/src/app/os/keys/page.tsx @@ -1,152 +1,15 @@ "use client"; -import { useState, useEffect, useMemo, useCallback, memo } from "react"; +import { useState, useEffect, useMemo } from "react"; import { useSearchParams } from "next/navigation"; import Link from "next/link"; -import { Search, X, ChevronRight, ChevronDown } from "lucide-react"; +import { Search, X } from "lucide-react"; import { Input } from "@/components/ui/input"; import { Button } from "@/components/ui/button"; - -function useColumnCount() { - const [cols, setCols] = useState(3); - useEffect(() => { - let timeout: ReturnType; - const update = () => { - const w = window.innerWidth; - setCols(w < 640 ? 1 : w < 1024 ? 2 : 3); - }; - const debouncedUpdate = () => { - clearTimeout(timeout); - timeout = setTimeout(update, 100); - }; - update(); - window.addEventListener("resize", debouncedUpdate); - return () => { - clearTimeout(timeout); - window.removeEventListener("resize", debouncedUpdate); - }; - }, []); - return cols; -} - +import { Skeleton } from "@/components/ui/skeleton"; import { createEngine } from "@/lib/engine"; -interface GroupedKeys { - [prefix: string]: string[]; -} - -function groupKeysByPrefix(keys: string[]): GroupedKeys { - const groups: GroupedKeys = {}; - - for (const key of keys) { - const parts = key.split("."); - let prefix: string; - if (parts.length >= 3 && parts[0] === "com" && parts[1] === "apple") { - prefix = `${parts[0]}.${parts[1]}.${parts[2]}`; - } else if (parts.length >= 2) { - prefix = `${parts[0]}.${parts[1]}`; - } else { - prefix = key; - } - - if (!groups[prefix]) { - groups[prefix] = []; - } - groups[prefix].push(key); - } - - return groups; -} - -const KeyBadge = memo(function KeyBadge({ - keyName, - prefix, - os, -}: { - keyName: string; - prefix: string; - os: string; -}) { - const suffix = keyName.startsWith(prefix + ".") - ? keyName.slice(prefix.length) - : keyName === prefix - ? "" - : keyName; - - return ( - - {suffix ? ( - <> - - {prefix} - - {suffix} - - ) : ( - {keyName} - )} - - ); -}); - -const KeyGroup = memo(function KeyGroup({ - prefix, - keys, - os, - cols, - isOpen, - onToggle, - isFiltering, -}: { - prefix: string; - keys: string[]; - os: string; - cols: number; - isOpen: boolean; - onToggle: () => void; - isFiltering: boolean; -}) { - const autoExpand = keys.length <= 8 || isFiltering; - const expanded = isOpen || autoExpand; - - return ( -
- - {expanded && ( -
- {keys.map((key) => ( - - ))} -
- )} -
- ); -}); - export default function Keys() { const params = useSearchParams(); const os = params.get("os") as string; @@ -156,9 +19,6 @@ export default function Keys() { const [keys, setKeys] = useState([]); const [keyword, setKeyword] = useState(""); const [debouncedKeyword, setDebouncedKeyword] = useState(""); - const [openGroups, setOpenGroups] = useState>(new Set()); - - const cols = useColumnCount(); useEffect(() => { const timer = setTimeout(() => { @@ -187,51 +47,8 @@ export default function Keys() { [debouncedKeyword, keys] ); - const grouped = useMemo(() => groupKeysByPrefix(filtered), [filtered]); - const sortedPrefixes = useMemo( - () => Object.keys(grouped).sort((a, b) => a.localeCompare(b)), - [grouped] - ); - const isFiltering = debouncedKeyword.length > 0; - const toggleGroup = useCallback((prefix: string) => { - setOpenGroups((prev) => { - const next = new Set(prev); - if (next.has(prefix)) { - next.delete(prefix); - } else { - next.add(prefix); - } - return next; - }); - }, []); - - const handleExpandAll = useCallback(() => { - setOpenGroups(new Set(sortedPrefixes)); - }, [sortedPrefixes]); - - const handleCollapseAll = useCallback(() => { - setOpenGroups(new Set()); - }, []); - - // Separate single keys from groups - const { groups, singles } = useMemo(() => { - const groups: { prefix: string; keys: string[] }[] = []; - const singles: string[] = []; - - for (const prefix of sortedPrefixes) { - const prefixKeys = grouped[prefix]; - if (prefixKeys.length === 1 && prefixKeys[0] === prefix) { - singles.push(prefixKeys[0]); - } else { - groups.push({ prefix, keys: prefixKeys }); - } - } - - return { groups, singles }; - }, [sortedPrefixes, grouped]); - return (
@@ -255,80 +72,27 @@ export default function Keys() { )}
-
- {!loading && sortedPrefixes.length > 0 && ( -
- - -
- )} - {!loading && ( -
- {isFiltering ? ( - <> - {filtered.length} of {keys.length} keys - - ) : ( - <>{keys.length} entitlement keys - )} -
- )} -
+ {!loading && ( +
+ {isFiltering ? ( + <> + {filtered.length} of {keys.length} keys + + ) : ( + <>{keys.length} entitlement keys + )} +
+ )}
{loading ? ( -
- {[ - { prefix: 180, items: [140, 160, 120] }, - { prefix: 220, items: [180, 140, 200, 160] }, - { prefix: 160, items: [120, 180] }, - { prefix: 200, items: [160, 140, 180, 120, 200] }, - { prefix: 140, items: [100, 140, 120] }, - { prefix: 240, items: [180, 160, 200, 140] }, - ].map((group, index) => ( -
-
-
-
-
-
-
- {group.items.map((width, i) => ( -
- ))} -
-
+
+ {Array.from({ length: 30 }).map((_, i) => ( + ))}
) : filtered.length === 0 ? ( @@ -341,29 +105,18 @@ export default function Keys() {
) : (
- {singles.length > 0 && ( -
- {singles.map((key) => ( - - ))} -
- )} - - {groups.map(({ prefix, keys: groupKeys }) => ( - toggleGroup(prefix)} - isFiltering={isFiltering} - /> - ))} +
+ {filtered.map((key) => ( + + {key} + + ))} +
)}
diff --git a/src/app/os/layout.tsx b/src/app/os/layout.tsx index 45cb8ac..b3e84dc 100644 --- a/src/app/os/layout.tsx +++ b/src/app/os/layout.tsx @@ -1,20 +1,7 @@ "use client"; import { useSearchParams } from "next/navigation"; -import Link from "next/link"; - -import { - Breadcrumb, - BreadcrumbItem, - BreadcrumbLink, - BreadcrumbList, - BreadcrumbSeparator, -} from "@/components/ui/breadcrumb"; -import { VersionSwitcher } from "@/components/version-switcher"; - -import { withBase } from "@/lib/env"; import { useEffect } from "react"; -import { usePathname } from "next/navigation"; export default function OSDetailLayout({ children, @@ -22,69 +9,14 @@ export default function OSDetailLayout({ children: React.ReactNode; }) { const params = useSearchParams(); - const pathname = usePathname(); const os = params.get("os") || ""; - const binaryPath = params.get("path") || ""; - const binaryName = binaryPath ? binaryPath.split("/").pop() : ""; - - const currentPage = pathname.includes("/files") - ? "files" - : pathname.includes("/bin") - ? "bin" - : pathname.includes("/find") - ? "find" - : "keys"; useEffect(() => { - if (os) document.title = `${os || ""} - Entitlement Database`; + if (os) document.title = `${os} - Entitlement Database`; }, [os]); return ( -
-
-
- - - - Home - - - -
- {os?.split("/")[0]} - -
-
- {currentPage === "bin" && binaryName && ( - <> - - - - {binaryName} - - - - )} -
-
-
- - -
- +
{children}
); diff --git a/src/components/navtop.tsx b/src/components/navtop.tsx index 5d342ce..b6730bd 100644 --- a/src/components/navtop.tsx +++ b/src/components/navtop.tsx @@ -2,8 +2,10 @@ import Link from "next/link"; import { useTheme } from "next-themes"; -import { Moon, Sun } from "lucide-react"; +import { Moon, Sun, Key, Folder } from "lucide-react"; import { useEffect, useState } from "react"; +import { usePathname, useSearchParams } from "next/navigation"; +import { VersionSwitcher } from "./version-switcher"; function ThemeToggle() { const { theme, setTheme } = useTheme(); @@ -20,7 +22,7 @@ function ThemeToggle() { return ( - -
- - - )} +
- {groups.map((group) => { - const majorGroups = groupByMajor(group.list); + {filteredGroups.map((group) => { + const majorGroups = groupByMajor(group.list); - return ( -
-

{group.name}

+ return ( +
+

+ {group.name} + +

- {showLess ? ( -
    - {group.list - .filter((os) => highlights.has(os.build)) - .map((os, index) => ( -
  • - -
    -

    {group.name === "iOS" ? os.version : os.name}

    -
    - {group.name !== "iOS" &&
    {os.version}
    } -
    {os.build}
    -
    -
    - -
  • - ))} -
- ) : ( -
- {majorGroups.map((majorGroup) => ( -
-

- {group.name === "iOS" ? "iOS" : group.name === "mac" ? "macOS" : "OS X"} {majorGroup.major} -

-
    - {majorGroup.versions.map((os, index) => ( + {!showAll ? ( +
      + {group.list + .filter((os) => highlights.has(os.build)) + .map((os, index) => (
    • -
      -

      {group.name === "iOS" ? os.version : os.name}

      -
      - {group.name !== "iOS" &&
      {os.version}
      } -
      {os.build}
      -
      +
      + {os.version} + + {os.build} +
    • ))} -
    +
+ ) : ( +
+ {majorGroups.map((majorGroup) => ( +
+

+ {group.name} {majorGroup.major} +

+
    + {majorGroup.versions.map((os, index) => ( +
  • + +
    + {os.version} + + {os.build} + +
    + +
  • + ))} +
+
+ ))}
- ))} -
- )} -
- ); - })} + )} +
+ ); + })} + + )}
); }