feat(nuxt): nuxt3 -> nuxt4

This commit is contained in:
2026-02-21 16:22:51 +01:00
parent 6a74e8c6ea
commit c4c165edab
22 changed files with 6600 additions and 9049 deletions

View File

@@ -0,0 +1,10 @@
export const capitalize = (str: string) =>
str.charAt(0).toUpperCase() + str.slice(1);
export const upperCase = (str: string) => str.toUpperCase();
export const arrayToUnion = (array: string[] | readonly string[]) =>
array.map((x) => `"${x}"`).join(" | ");
export const unreadonly = <T extends string>(arr: readonly T[]): string[] =>
[...arr] as string[];

53
shared/utils/renderer.ts Normal file
View File

@@ -0,0 +1,53 @@
import { Canvas, loadImage } from "skia-canvas";
import QRCode from "qrcode";
const CANVAS_SIZE = 1000;
const LOGO_PADDING = 1;
export const renderQRCodeToCanvas = async (
content: string,
logoUrl: string | undefined,
) => {
const canvas = new Canvas(CANVAS_SIZE, CANVAS_SIZE);
const errorCorrectionLevel: QRCode.QRCodeErrorCorrectionLevel = logoUrl
? "H"
: "M";
await QRCode.toCanvas(canvas, content, {
errorCorrectionLevel,
width: CANVAS_SIZE,
margin: 1,
});
if (!logoUrl) return canvas;
const qrCode = QRCode.create(content, { errorCorrectionLevel });
const moduleCount = qrCode.modules.size + 2;
const logoImage = await loadImage(logoUrl);
const moduleSize = CANVAS_SIZE / moduleCount;
let logoModules = Math.floor(moduleCount * 0.3);
if (logoModules % 2 !== moduleCount % 2) {
logoModules += 1;
}
const backgroundSize = logoModules * moduleSize + 1;
const backgroundPosition = (moduleSize * (moduleCount - logoModules)) / 2;
const logoSize = backgroundSize - LOGO_PADDING * 2;
const logoPosition = backgroundPosition + LOGO_PADDING;
const ctx = canvas.getContext("2d");
ctx.fillStyle = "white";
ctx.fillRect(
backgroundPosition,
backgroundPosition,
backgroundSize,
backgroundSize,
);
ctx.drawImage(logoImage, logoPosition, logoPosition, logoSize, logoSize);
return canvas;
};

61
shared/utils/settings.ts Normal file
View File

@@ -0,0 +1,61 @@
import { z } from "zod";
// TODO: might be better to load these dynamically lol
export const LOGOS = [
"bereal",
"bitcoin",
"buymeacoffee",
"diaspora",
"discord",
"dropbox",
"ello",
"facebook",
"flickr",
"github",
"googlemaps",
"googlemeet",
"googlemessages",
"imessage",
"instagram",
"kik",
"line",
"linkedin",
"litecoin",
"mastodon",
"medium",
"messenger",
"monero",
"onlyfans",
"patreon",
"paypal",
"peertube",
"pinterest",
"reddit",
"session",
"signal",
"snapchat",
"spotify",
"substack",
"telegram",
"threema",
"twitch",
"venmo",
"viber",
"wechat",
"whatsapp",
"x",
"youtube",
"zoom",
] as const;
export const IMAGE_FORMATS = ["png", "jpeg", "webp"] as const;
export type ImageFormat = (typeof IMAGE_FORMATS)[number];
export const settingsSchema = z.object({
format: z.enum(IMAGE_FORMATS).default("png"),
logo: z.enum(LOGOS).optional(),
content: z.string().min(1, "Required"),
});
export type Settings = z.infer<typeof settingsSchema>;