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