9ff3223c44
Declare navigation, page metadata, lazy loaders, and keep-alive policy in one registry. Retain primary iframe pages until a continuous inactive TTL expires and expose typed show, hide, and unload events through the Page SDK. Move business-aware components out of the UI primitive directory, migrate views and Host dialogs to variant primitives and local Tailwind classes, and remove the global component style layer. Keep native dialogs inside the overlay flex context so they remain centered. Cover iframe expiration, lifecycle dispatch, UI dependency direction, and global style ownership with focused tests.
115 lines
3.9 KiB
TypeScript
115 lines
3.9 KiB
TypeScript
import { CloudUpload, Download, FileCode2, Search } from "lucide-solid";
|
|
import { createMemo, For, Show } from "solid-js";
|
|
import { EmptyState } from "../../components/feedback/empty-state";
|
|
import { Page } from "../../components/layout/page";
|
|
import { PageHeading } from "../../components/layout/page-heading";
|
|
import { Button, IconLink } from "../../components/ui/button";
|
|
import { Card, CardHeader } from "../../components/ui/card";
|
|
import {
|
|
Table,
|
|
TableBody,
|
|
TableCell,
|
|
TableHead,
|
|
TableHeader,
|
|
TableRow,
|
|
} from "../../components/ui/table";
|
|
import { witPackageDownloadUrl } from "../../lib/api";
|
|
import type { PageProps } from "../types";
|
|
|
|
export default function WitPackagesPage(props: PageProps) {
|
|
const packages = createMemo(() => {
|
|
const state = props.state();
|
|
const query = state?.query.trim().toLowerCase() ?? "";
|
|
return (
|
|
state?.snapshot?.witPackages.filter(
|
|
(item) =>
|
|
!query ||
|
|
item.name.toLowerCase().includes(query) ||
|
|
item.version.toLowerCase().includes(query) ||
|
|
item.sha256.toLowerCase().includes(query),
|
|
) ?? []
|
|
);
|
|
});
|
|
|
|
return (
|
|
<Page>
|
|
<PageHeading
|
|
eyebrow="INTERFACES"
|
|
title="WIT 包"
|
|
description="管理不可变、版本化的 Component 接口依赖。"
|
|
actions={
|
|
<Button variant="primary" onClick={() => props.command({ type: "open-wit-publish" })}>
|
|
<CloudUpload size={16} />
|
|
发布 WIT 包
|
|
</Button>
|
|
}
|
|
/>
|
|
<Card as="section">
|
|
<CardHeader>
|
|
<div>
|
|
<h2>Registry</h2>
|
|
<p>{packages().length} 个匹配包版本</p>
|
|
</div>
|
|
</CardHeader>
|
|
<Show
|
|
when={packages().length > 0}
|
|
fallback={
|
|
<EmptyState icon={Search} title="没有匹配的 WIT 包" detail="发布或调整搜索关键词。" />
|
|
}
|
|
>
|
|
<Table>
|
|
<TableHeader>
|
|
<TableRow>
|
|
<TableHead>包</TableHead>
|
|
<TableHead>版本</TableHead>
|
|
<TableHead>直接依赖</TableHead>
|
|
<TableHead>SHA-256</TableHead>
|
|
<TableHead>
|
|
<span class="sr-only">下载</span>
|
|
</TableHead>
|
|
</TableRow>
|
|
</TableHeader>
|
|
<TableBody>
|
|
<For each={packages()}>
|
|
{(item) => (
|
|
<TableRow>
|
|
<TableCell>
|
|
<span class="flex items-center gap-2">
|
|
<FileCode2 size={15} class="text-cyan-strong" />
|
|
<strong>{item.name}</strong>
|
|
</span>
|
|
</TableCell>
|
|
<TableCell class="font-mono text-xs">{item.version}</TableCell>
|
|
<TableCell class="text-xs text-muted">
|
|
{item.dependencies.length
|
|
? item.dependencies
|
|
.map((dependency) => `${dependency.name}@${dependency.version}`)
|
|
.join(", ")
|
|
: "无"}
|
|
</TableCell>
|
|
<TableCell
|
|
class="max-w-64 truncate font-mono text-xs text-muted"
|
|
title={item.sha256}
|
|
>
|
|
{item.sha256}
|
|
</TableCell>
|
|
<TableCell class="text-right">
|
|
<IconLink
|
|
aria-label={`下载 ${item.name}@${item.version}`}
|
|
title="下载"
|
|
href={witPackageDownloadUrl(props.state()?.apiBase ?? "", item)}
|
|
>
|
|
<Download size={15} />
|
|
</IconLink>
|
|
</TableCell>
|
|
</TableRow>
|
|
)}
|
|
</For>
|
|
</TableBody>
|
|
</Table>
|
|
</Show>
|
|
</Card>
|
|
</Page>
|
|
);
|
|
}
|