(feat): add license page

(fix): noise suppression error
This commit is contained in:
Alois 2026-04-20 21:59:36 +02:00
commit efee2e87cc
5 changed files with 232 additions and 33 deletions

View file

@ -0,0 +1,144 @@
import {
Card,
CardContent,
CardTitle,
CardHeader,
CardDescription,
CardFooter,
Button,
Badge,
Dialog,
DialogContent,
DialogTrigger,
} from "@tensamin/ui";
import {
packages,
packageCount,
generatedAt,
} from "../../../../../licenses/third-party-credits.json";
const licenseTexts = import.meta.glob("../../../../../licenses/**/*", {
eager: true,
import: "default",
query: "?raw",
}) as Record<string, string>;
function getLicenseFiles(licensePackage: (typeof packages)[number]) {
return licensePackage.files.map((fileName) => {
const path =
"../../../../../" + licensePackage.licenseFolder + "/" + fileName;
return {
fileName,
text: licenseTexts[path],
};
});
}
export default function Page() {
return (
<div className="flex h-full min-h-0 flex-col gap-7">
<div className="flex flex-col">
<p>Last generated: {generatedAt}</p>
<p>Package Count: {packageCount}</p>
</div>
<div className="min-h-0 flex-1 max-h-[calc(100vh-180px)] overflow-auto pr-2">
<div className="flex flex-col gap-5">
{packages.map((licensePackage) => (
<Card
key={licensePackage.name + licensePackage.version}
id={licensePackage.name + licensePackage.version}
>
<CardHeader>
<CardTitle className="flex gap-2 items-center">
<Badge>{licensePackage.license}</Badge> {licensePackage.name}{" "}
{licensePackage.version}
</CardTitle>
</CardHeader>
{licensePackage.description && (
<CardContent>
<CardDescription>
{licensePackage.description}
</CardDescription>
</CardContent>
)}
<CardFooter className="gap-2">
<LicenseDialog licensePackage={licensePackage} />
{licensePackage.repository ? (
<a
target="_blank"
rel="noreferrer"
href={licensePackage.repository
?.replace("git+", "")
.replace(".git", "")}
>
<Button variant="outline" className="cursor-pointer">
Open Repository
</Button>
</a>
) : (
<Button disabled variant="outline" className="cursor-pointer">
Open Repository
</Button>
)}
{licensePackage.homepage ? (
<a
target="_blank"
rel="noreferrer"
href={licensePackage.homepage}
>
<Button variant="outline" className="cursor-pointer">
Open Homepage
</Button>
</a>
) : (
<Button disabled variant="outline" className="cursor-pointer">
Open Homepage
</Button>
)}
</CardFooter>
</Card>
))}
</div>
</div>
</div>
);
}
function LicenseDialog({
licensePackage,
}: {
licensePackage: (typeof packages)[number];
}) {
const licenseFiles = getLicenseFiles(licensePackage);
const hasLicenseText = licenseFiles.some(({ text }) => text);
return (
<Dialog>
<DialogTrigger
render={
<Button disabled={!hasLicenseText} className="cursor-pointer">
Open License
</Button>
}
/>
<DialogContent className="flex max-h-[85vh] min-h-0 flex-col overflow-hidden sm:max-w-3xl">
<div className="min-h-0 flex-1 overflow-y-auto overscroll-contain pr-2">
{licenseFiles.map(({ fileName, text }, index) => (
<section key={fileName} className="border-b last:border-b-0">
<h3
className={`border-b pb-2 text-sm font-medium ${index >= 1 && "pt-2"}`}
>
{fileName}
</h3>
<pre className="pt-2 whitespace-pre-wrap wrap-break-word text-xs leading-relaxed">
{text ||
"License text could not be loaded. Please contact support@tensamin.net"}
</pre>
</section>
))}
</div>
</DialogContent>
</Dialog>
);
}

View file

@ -1,8 +1,9 @@
import { realpathSync } from "node:fs";
import { createReadStream, realpathSync, statSync } from "node:fs";
import type { IncomingMessage, ServerResponse } from "node:http";
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { defineConfig } from "vite";
import { defineConfig, type Plugin } from "vite";
import react from "@vitejs/plugin-react";
import tsconfigPaths from "vite-tsconfig-paths";
import tailwindcss from "@tailwindcss/vite";
@ -15,6 +16,41 @@ function resolveMarkdownDependency(packageName: string): string {
return realpathSync(resolve(markdownPackageDir, "node_modules", packageName));
}
function deepFilterAssetHeaders(rootDir: string): Plugin {
const serveModel = (
req: IncomingMessage,
res: ServerResponse,
next: () => void,
) => {
const url = req.url ? new URL(req.url, "http://localhost") : null;
if (url?.pathname !== "/assets/v2/models/DeepFilterNet3_onnx.tar.gz") {
next();
return;
}
const modelPath = resolve(
rootDir,
"assets/v2/models/DeepFilterNet3_onnx.tar.gz",
);
const stat = statSync(modelPath);
res.statusCode = 200;
res.setHeader("Content-Type", "application/gzip");
res.setHeader("Content-Length", stat.size.toString());
res.setHeader("Cache-Control", "no-cache");
createReadStream(modelPath).pipe(res);
};
return {
name: "deepfilter-asset-headers",
configureServer(server) {
server.middlewares.use(serveModel);
},
configurePreviewServer(server) {
server.middlewares.use(serveModel);
},
};
}
export default defineConfig({
clearScreen: false,
resolve: {
@ -63,7 +99,12 @@ export default defineConfig({
minify: !process.env.TAURI_ENV_DEBUG ? "esbuild" : false,
sourcemap: !!process.env.TAURI_ENV_DEBUG,
},
plugins: [react(), tsconfigPaths(), tailwindcss()],
plugins: [
deepFilterAssetHeaders(resolve(appDir, "public")),
react(),
tsconfigPaths(),
tailwindcss(),
],
worker: {
format: "es",
},