1import type { Plugin } from "vite";
2import { writeFileSync, readFileSync, existsSync, copyFileSync } from "fs";
3import { resolve } from "path";
4
5interface PageConfig {
6 /** Page name (used for filename and script name) */
7 name: string;
8 /** Page title */
9 title: string;
10 /** Script filename (e.g., 'homepage.min.js') */
11 script: string;
12 /** Optional CSS filename */
13 css?: string;
14 /** Target filename (defaults to name + '.html') */
15 filename?: string;
16}
17
18// Load the favicon SVG from assets
19function getFaviconSVG(): string {
20 const logoPath = resolve(__dirname, "..", "assets", "logo.svg");
21 return readFileSync(logoPath, "utf-8");
22}
23
24// Generate HTML content for a page
25function generatePageHTML(config: PageConfig): string {
26 const cssLink = config.css
27 ? `<link rel="stylesheet" href="./${config.css}">`
28 : "";
29
30 return `<!DOCTYPE html>
31<html lang="en">
32<head>
33 <meta charset="UTF-8">
34 <meta name="viewport" content="width=device-width, initial-scale=1.0">
35 <title>${config.title}</title>
36 <link rel="icon" type="image/x-icon" href="dumb://homepage/favicon.ico">
37 ${cssLink}
38</head>
39<body>
40 <script src="./${config.script}"></script>
41</body>
42</html>`;
43}
44
45/**
46 * Vite plugin to generate dumb:// protocol pages
47 * Supports multiple pages like homepage, config, about, etc.
48 */
49export function pageGenerator(pages: PageConfig[]): Plugin {
50 return {
51 name: "dumb-page-generator",
52 writeBundle() {
53 const assetsDir = resolve(__dirname, "..", "assets", "webui");
54
55 try {
56 // Generate HTML files for each page
57 for (const page of pages) {
58 const htmlContent = generatePageHTML(page);
59 const filename = page.filename || `${page.name}.html`;
60 writeFileSync(resolve(assetsDir, filename), htmlContent);
61 console.log(`✓ Generated ${filename} for dumb://${page.name}`);
62 }
63
64 // Generate favicon files for scheme handler lookups
65 const faviconContent = getFaviconSVG();
66 writeFileSync(resolve(assetsDir, "favicon.svg"), faviconContent);
67 console.log("✓ Generated favicon.svg");
68
69 // Copy pre-generated PNG and ICO favicons if they exist
70 const sourceDir = resolve(__dirname, "..", "assets", "webui");
71 try {
72 const pngPath = resolve(sourceDir, "favicon.png");
73 const icoPath = resolve(sourceDir, "favicon.ico");
74
75 if (existsSync(pngPath)) {
76 copyFileSync(pngPath, resolve(assetsDir, "favicon.png"));
77 console.log("✓ Copied favicon.png");
78 }
79
80 if (existsSync(icoPath)) {
81 copyFileSync(icoPath, resolve(assetsDir, "favicon.ico"));
82 console.log("✓ Copied favicon.ico");
83 }
84 } catch (err) {
85 console.warn("⚠ Could not copy favicon PNG/ICO files:", err);
86 }
87 } catch (error) {
88 console.error("✗ Failed to generate page files:", error);
89 throw error;
90 }
91 },
92 };
93}
94
95/**
96 * Convenience function for homepage generation
97 * @deprecated Use pageGenerator with homepage config instead
98 */
99export function homepageGenerator(): Plugin {
100 return pageGenerator([
101 {
102 name: "homepage",
103 title: "Dumber Browser",
104 script: "homepage.min.js",
105 filename: "index.html", // Special case: homepage uses index.html
106 },
107 ]);
108}