(feat): add license page
(fix): noise suppression error
This commit is contained in:
parent
dc1c11219b
commit
efee2e87cc
5 changed files with 232 additions and 33 deletions
144
apps/web/src/routes/settings/licenses.tsx
Normal file
144
apps/web/src/routes/settings/licenses.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
|
|
@ -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",
|
||||
},
|
||||
|
|
|
|||
Loading…
Reference in a new issue