v3: added customizable site emoji (logo), small visual tweaks
All checks were successful
Build and Publish Docker Image / build-and-push (push) Successful in 41s
All checks were successful
Build and Publish Docker Image / build-and-push (push) Successful in 41s
This commit is contained in:
@@ -1,56 +1,19 @@
|
||||
import { defineConfig, type Plugin } from "vite";
|
||||
import { defineConfig } from "vite";
|
||||
import react from "@vitejs/plugin-react-swc";
|
||||
import { lingui } from "@lingui/vite-plugin";
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
const SITE_NAME = process.env.GERBEUR_SITE_NAME || "gerbeur";
|
||||
const SITE_EMOJI = process.env.GERBEUR_SITE_EMOJI || "🚚";
|
||||
|
||||
function manifestPlugin(): Plugin {
|
||||
const cssPath = path.resolve("src/index.css");
|
||||
const outPath = path.resolve("public/manifest.webmanifest");
|
||||
|
||||
function cssVar(rootBlock: string, name: string): string | undefined {
|
||||
return rootBlock.match(
|
||||
new RegExp(`${name.replace("-", "\\-")}:\\s*(#[0-9a-fA-F]{3,8})`),
|
||||
)?.[1];
|
||||
}
|
||||
|
||||
function generate() {
|
||||
const css = fs.readFileSync(cssPath, "utf-8");
|
||||
// Only read the first :root block — dark-mode defaults, before any @media overrides
|
||||
const rootBlock = css.match(/:root\s*\{([^}]+)\}/)?.[1] ?? "";
|
||||
const bgColor = cssVar(rootBlock, "--color-bg") ?? "#111827";
|
||||
|
||||
const manifest = {
|
||||
name: SITE_NAME,
|
||||
short_name: SITE_NAME,
|
||||
start_url: "/",
|
||||
display: "standalone",
|
||||
background_color: bgColor,
|
||||
theme_color: bgColor,
|
||||
icons: [{ src: "/favicon.svg", type: "image/svg+xml", sizes: "any" }],
|
||||
share_target: {
|
||||
action: "/",
|
||||
method: "GET",
|
||||
params: { url: "share_url", title: "share_title", text: "share_text" },
|
||||
},
|
||||
};
|
||||
|
||||
fs.writeFileSync(outPath, JSON.stringify(manifest, null, 2) + "\n");
|
||||
}
|
||||
|
||||
return {
|
||||
name: "generate-manifest",
|
||||
buildStart: generate,
|
||||
configureServer(server) {
|
||||
generate();
|
||||
server.watcher.on("change", (file) => {
|
||||
if (path.resolve(file) === cssPath) generate();
|
||||
});
|
||||
},
|
||||
};
|
||||
// Cache-busting token for the favicon URLs (see index.html). Changes when the
|
||||
// emoji changes, so browsers don't keep serving a stale cached icon. Mirrors
|
||||
// Twemoji's filename convention (kept trivial here to avoid the frontend build
|
||||
// depending on backend code; the canonical version lives in api/lib/site-icons.ts).
|
||||
function emojiToCodepoint(emoji: string): string {
|
||||
const cleaned = emoji.includes("") ? emoji : emoji.replace(/️/g, "");
|
||||
return [...cleaned].map((ch) => ch.codePointAt(0)!.toString(16)).join("-");
|
||||
}
|
||||
const ICON_VERSION = emojiToCodepoint(SITE_EMOJI);
|
||||
|
||||
export default defineConfig({
|
||||
server: {
|
||||
@@ -74,16 +37,18 @@ export default defineConfig({
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
manifestPlugin(),
|
||||
{
|
||||
// In dev the API server isn't serving index.html, so replace the placeholder here.
|
||||
// In dev the API server isn't serving index.html, so replace the placeholders here.
|
||||
// In production the Oak server does the replacement at runtime (see api/lib/static.ts).
|
||||
name: "inject-site-name",
|
||||
transformIndexHtml: {
|
||||
order: "pre",
|
||||
handler: (html, ctx) =>
|
||||
ctx.server
|
||||
? html.replaceAll("__SITE_NAME__", SITE_NAME)
|
||||
? html
|
||||
.replaceAll("__SITE_NAME__", SITE_NAME)
|
||||
.replaceAll("__SITE_EMOJI__", SITE_EMOJI)
|
||||
.replaceAll("__ICON_VERSION__", ICON_VERSION)
|
||||
: html,
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user