mirror of
https://github.com/VibedByKaKi/t3-code-android-nightly.git
synced 2026-10-09 20:01:15 +02:00
Co-authored-by: maria-rcks <maria@kuuro.net> Co-authored-by: Bilal Bakr <62337003+Bil0000@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: Vitalii Yehorov <vitalyiegorov@gmail.com> Co-authored-by: Jake Leventhal <jakeleventhal@me.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-authored-by: Alex Southwell <saphid@gmail.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Nicholas Wasmiller <derped@mineperial.com> Co-authored-by: PB <poilmb@gmail.com> Co-authored-by: Exotic <118054752+extoci@users.noreply.github.com> Co-authored-by: Yash Singh <saiansh2525@gmail.com> Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: Theo Browne <me@t3.gg> Co-authored-by: Gabriel De Andrade <30420087+gabrielelpidio@users.noreply.github.com> Co-authored-by: Dara Adedeji <76637177+SunkenInTime@users.noreply.github.com> Co-authored-by: scratchyone <scratchywon@gmail.com> Co-authored-by: Dominic Roy <dominic@sdko.org> Co-authored-by: chukfinley <chuk@chuk.dev> Co-authored-by: Primož Ajdišek <bigpod@bigpod.si> Co-authored-by: benthecarman <benthecarman@live.com> Co-authored-by: NaveDanan <nave0712@gmail.com> Co-authored-by: aaditagrawal <103925638+aaditagrawal@users.noreply.github.com> Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: Nick Anisimov <n.anisimov.23@gmail.com> Co-authored-by: MacKinley Smith <smithmackinley@gmail.com> Co-authored-by: Yordis Prieto <yordis.prieto@gmail.com> Co-authored-by: t3-code[bot] <269035359+t3-code[bot]@users.noreply.github.com> Co-authored-by: AKolenda <akole779@mtroyal.ca> Co-authored-by: Guillermo Casanova <75276669+Gigioxx@users.noreply.github.com> Co-authored-by: Otavio Salvador <otavio@ossystems.com.br> Co-authored-by: Shirish Pothi <183252392+shirishpothi@users.noreply.github.com> Co-authored-by: Ishaan Kothari <ishaanko.mail@gmail.com> Co-authored-by: Bob Fowler <bob@rjf.ca> Co-authored-by: Anton Bezdenezhnykh <gamer392@yandex.ru> Co-authored-by: ValeraZSD <48602572+ValeraZSD@users.noreply.github.com> Co-authored-by: Ephraim <ephraim39hr14m@gmail.com> Co-authored-by: Ryan Ilano <ryanilano@users.noreply.github.com> Co-authored-by: Alex <me@pixp.cc> Co-authored-by: maco <gosarmarcel7@gmail.com> Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Tristan Knight <admin@snappeh.com> Co-authored-by: PR Batch Tester <agent@local.test> Co-authored-by: oliver <97427849+flamboh@users.noreply.github.com> Co-authored-by: kamkm <99585688+Kamkmgamer@users.noreply.github.com> Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
300 lines
13 KiB
TypeScript
300 lines
13 KiB
TypeScript
#!/usr/bin/env node
|
|
|
|
import * as NodeFS from "node:fs";
|
|
import * as NodePath from "node:path";
|
|
import tailwindColors from "tailwindcss/colors";
|
|
import { BUILT_IN_THEME_IDS, type BuiltInThemeId } from "@t3tools/shared/themePalettes";
|
|
|
|
import {
|
|
createMobileThemeVariables,
|
|
getMobileThemeColors,
|
|
getMobileThemeVariables,
|
|
DEFAULT_MOBILE_THEME_ID,
|
|
type MobileThemeAppearance,
|
|
} from "../src/lib/mobileTheme.ts";
|
|
|
|
const APPEARANCES = ["light", "dark"] as const;
|
|
const GENERATED_CSS_PATH = NodePath.resolve(import.meta.dirname, "../generated-uniwind-themes.css");
|
|
const GENERATED_NAMES_PATH = NodePath.resolve(
|
|
import.meta.dirname,
|
|
"../generated-uniwind-theme-names.json",
|
|
);
|
|
const GENERATED_DEFAULT_VARIABLES_PATH = NodePath.resolve(
|
|
import.meta.dirname,
|
|
"../generated-uniwind-default-theme-variables.json",
|
|
);
|
|
const GENERATED_CLERK_THEME_PATH = NodePath.resolve(import.meta.dirname, "../clerk-theme.json");
|
|
|
|
type TailwindColorFamily = keyof typeof tailwindColors;
|
|
type TailwindColorShade = 50 | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 | 950;
|
|
|
|
const color = (family: TailwindColorFamily, shade?: TailwindColorShade, opacity = 1): string => {
|
|
const familyColors = tailwindColors[family];
|
|
const value =
|
|
typeof familyColors === "string"
|
|
? shade === undefined
|
|
? familyColors
|
|
: undefined
|
|
: shade === undefined
|
|
? undefined
|
|
: familyColors[String(shade) as keyof typeof familyColors];
|
|
if (value === undefined) {
|
|
throw new Error(`Unknown Tailwind color ${family}${shade === undefined ? "" : `-${shade}`}.`);
|
|
}
|
|
if (opacity === 1) return value;
|
|
|
|
const percentage = Number((opacity * 100).toFixed(4));
|
|
const oklch = /^oklch\((.*)\)$/.exec(value);
|
|
if (oklch) return `oklch(${oklch[1]} / ${percentage}%)`;
|
|
if (value === "#fff") return `rgb(255 255 255 / ${percentage}%)`;
|
|
if (value === "#000") return `rgb(0 0 0 / ${percentage}%)`;
|
|
return `color-mix(in srgb, ${value} ${percentage}%, transparent)`;
|
|
};
|
|
|
|
// These replace the remaining dark:* utility pairs. A registered palette theme is
|
|
// neither literally `light` nor `dark`, so appearance-sensitive values must also be
|
|
// represented as semantic variables for custom themes.
|
|
const ADAPTIVE_COLORS: Readonly<Record<string, readonly [light: string, dark: string]>> = {
|
|
"--color-adaptive-amber-50-950-a40": [color("amber", 50), color("amber", 950, 0.4)],
|
|
"--color-adaptive-amber-200-900-a60": [color("amber", 200), color("amber", 900, 0.6)],
|
|
"--color-adaptive-amber-500-a12-a16": [color("amber", 500, 0.12), color("amber", 500, 0.16)],
|
|
"--color-adaptive-amber-500-a10-400-a10": [color("amber", 500, 0.1), color("amber", 400, 0.1)],
|
|
"--color-adaptive-amber-500-a25-400-a25": [color("amber", 500, 0.25), color("amber", 400, 0.25)],
|
|
"--color-adaptive-amber-700-300": [color("amber", 700), color("amber", 300)],
|
|
"--color-adaptive-amber-700-400": [color("amber", 700), color("amber", 400)],
|
|
"--color-adaptive-amber-800-200": [color("amber", 800), color("amber", 200)],
|
|
"--color-adaptive-blue-50-blue-400-a14": [color("blue", 50), color("blue", 400, 0.14)],
|
|
"--color-adaptive-blue-300-a50-blue-400-a28": [color("blue", 300, 0.5), color("blue", 400, 0.28)],
|
|
"--color-adaptive-blue-500-a20-blue-400-a15": [color("blue", 500, 0.2), color("blue", 400, 0.15)],
|
|
"--color-adaptive-blue-500-400": [color("blue", 500), color("blue", 400)],
|
|
"--color-adaptive-blue-600-400": [color("blue", 600), color("blue", 400)],
|
|
"--color-adaptive-black-a10-a25": [
|
|
color("black", undefined, 0.1),
|
|
color("black", undefined, 0.25),
|
|
],
|
|
"--color-adaptive-black-a15-a35": [
|
|
color("black", undefined, 0.15),
|
|
color("black", undefined, 0.35),
|
|
],
|
|
"--color-adaptive-black-a2p5-white-a2p5": [
|
|
color("black", undefined, 0.025),
|
|
color("white", undefined, 0.025),
|
|
],
|
|
"--color-adaptive-emerald-500-a12-a16": [
|
|
color("emerald", 500, 0.12),
|
|
color("emerald", 500, 0.16),
|
|
],
|
|
"--color-adaptive-emerald-600-400": [color("emerald", 600), color("emerald", 400)],
|
|
"--color-adaptive-emerald-700-300": [color("emerald", 700), color("emerald", 300)],
|
|
"--color-adaptive-indigo-500-a12-a16": [color("indigo", 500, 0.12), color("indigo", 500, 0.16)],
|
|
"--color-adaptive-indigo-600-300": [color("indigo", 600), color("indigo", 300)],
|
|
"--color-adaptive-indigo-700-300": [color("indigo", 700), color("indigo", 300)],
|
|
"--color-adaptive-neutral-100-900": [color("neutral", 100), color("neutral", 900)],
|
|
"--color-adaptive-neutral-200-700-a60": [color("neutral", 200), color("neutral", 700, 0.6)],
|
|
"--color-adaptive-neutral-200-800": [color("neutral", 200), color("neutral", 800)],
|
|
"--color-adaptive-neutral-200-a70-white-a8": [
|
|
color("neutral", 200, 0.7),
|
|
color("white", undefined, 0.08),
|
|
],
|
|
"--color-adaptive-neutral-200-white-a6": [color("neutral", 200), color("white", undefined, 0.06)],
|
|
"--color-adaptive-neutral-200-white-a8": [color("neutral", 200), color("white", undefined, 0.08)],
|
|
"--color-adaptive-neutral-200-a80-white-a8": [
|
|
color("neutral", 200, 0.8),
|
|
color("white", undefined, 0.08),
|
|
],
|
|
"--color-adaptive-neutral-300-a60-white-a12": [
|
|
color("neutral", 300, 0.6),
|
|
color("white", undefined, 0.12),
|
|
],
|
|
"--color-adaptive-neutral-400-500": [color("neutral", 400), color("neutral", 500)],
|
|
"--color-adaptive-neutral-400-a60-500-a60": [
|
|
color("neutral", 400, 0.6),
|
|
color("neutral", 500, 0.6),
|
|
],
|
|
"--color-adaptive-neutral-400-a80-500-a80": [
|
|
color("neutral", 400, 0.8),
|
|
color("neutral", 500, 0.8),
|
|
],
|
|
"--color-adaptive-neutral-500-a10-a16": [color("neutral", 500, 0.1), color("neutral", 500, 0.16)],
|
|
"--color-adaptive-neutral-500-400": [color("neutral", 500), color("neutral", 400)],
|
|
"--color-adaptive-neutral-500-500": [color("neutral", 500), color("neutral", 500)],
|
|
"--color-adaptive-neutral-600-300": [color("neutral", 600), color("neutral", 300)],
|
|
"--color-adaptive-neutral-600-400": [color("neutral", 600), color("neutral", 400)],
|
|
"--color-adaptive-neutral-950-50": [color("neutral", 950), color("neutral", 50)],
|
|
"--color-adaptive-neutral-950-a5-white-a5": [
|
|
color("neutral", 950, 0.05),
|
|
color("white", undefined, 0.05),
|
|
],
|
|
"--color-adaptive-neutral-950-a10-white-a10": [
|
|
color("neutral", 950, 0.1),
|
|
color("white", undefined, 0.1),
|
|
],
|
|
"--color-adaptive-red-50-950-a80": [color("red", 50), color("red", 950, 0.8)],
|
|
"--color-adaptive-red-200-800": [color("red", 200), color("red", 800)],
|
|
"--color-adaptive-red-600-a80-400-a80": [color("red", 600, 0.8), color("red", 400, 0.8)],
|
|
"--color-adaptive-red-700-300": [color("red", 700), color("red", 300)],
|
|
"--color-adaptive-rose-100-500-a18": [color("rose", 100), color("rose", 500, 0.18)],
|
|
"--color-adaptive-rose-100-a80-500-a12": [color("rose", 100, 0.8), color("rose", 500, 0.12)],
|
|
"--color-adaptive-rose-300-a70-400-a28": [color("rose", 300, 0.7), color("rose", 400, 0.28)],
|
|
"--color-adaptive-rose-500-a12-a16": [color("rose", 500, 0.12), color("rose", 500, 0.16)],
|
|
"--color-adaptive-rose-500-400": [color("rose", 500), color("rose", 400)],
|
|
"--color-adaptive-rose-600-400": [color("rose", 600), color("rose", 400)],
|
|
"--color-adaptive-rose-700-300": [color("rose", 700), color("rose", 300)],
|
|
"--color-adaptive-sky-500-a12-a16": [color("sky", 500, 0.12), color("sky", 500, 0.16)],
|
|
"--color-adaptive-sky-500-a10-400-a10": [color("sky", 500, 0.1), color("sky", 400, 0.1)],
|
|
"--color-adaptive-sky-500-a25-400-a25": [color("sky", 500, 0.25), color("sky", 400, 0.25)],
|
|
"--color-adaptive-sky-600-400": [color("sky", 600), color("sky", 400)],
|
|
"--color-adaptive-sky-700-300": [color("sky", 700), color("sky", 300)],
|
|
"--color-adaptive-violet-500-a12-a16": [color("violet", 500, 0.12), color("violet", 500, 0.16)],
|
|
"--color-adaptive-violet-600-400": [color("violet", 600), color("violet", 400)],
|
|
"--color-adaptive-violet-700-300": [color("violet", 700), color("violet", 300)],
|
|
"--color-adaptive-white-neutral-950-a70": [color("white"), color("neutral", 950, 0.7)],
|
|
"--color-adaptive-zinc-500-a12-a16": [color("zinc", 500, 0.12), color("zinc", 500, 0.16)],
|
|
"--color-adaptive-zinc-500-400": [color("zinc", 500), color("zinc", 400)],
|
|
"--color-adaptive-zinc-600-300": [color("zinc", 600), color("zinc", 300)],
|
|
};
|
|
|
|
export const customThemeNames = BUILT_IN_THEME_IDS.flatMap((themeId) =>
|
|
APPEARANCES.map((appearance) => `${themeId}-${appearance}`),
|
|
);
|
|
|
|
const adaptiveVariablesFor = (appearance: MobileThemeAppearance) =>
|
|
Object.fromEntries(
|
|
Object.entries(ADAPTIVE_COLORS).map(([name, [light, dark]]) => [
|
|
name,
|
|
appearance === "light" ? light : dark,
|
|
]),
|
|
);
|
|
|
|
const variablesFor = (themeId: BuiltInThemeId, appearance: MobileThemeAppearance) => ({
|
|
...getMobileThemeVariables(themeId, appearance),
|
|
...adaptiveVariablesFor(appearance),
|
|
...clerkVariablesFor(appearance),
|
|
});
|
|
|
|
// Clerk's native screens use one build-time palette per appearance. Custom
|
|
// profile pages must match it even when the rest of the app uses a named theme.
|
|
const clerkColorsFor = (appearance: MobileThemeAppearance) => {
|
|
// Native authentication uses plain cards, rather than tonal settings groups.
|
|
const variables = createMobileThemeVariables(
|
|
getMobileThemeColors(DEFAULT_MOBILE_THEME_ID, appearance),
|
|
appearance,
|
|
);
|
|
return {
|
|
primary: variables["--color-primary"],
|
|
background: variables["--color-sheet-solid"],
|
|
input: variables["--color-input"],
|
|
danger: variables["--color-danger-foreground"],
|
|
success: appearance === "dark" ? "#34d399" : "#059669",
|
|
warning: variables["--color-warning-foreground"],
|
|
foreground: variables["--color-foreground"],
|
|
mutedForeground: variables["--color-foreground-muted"],
|
|
primaryForeground: variables["--color-primary-foreground"],
|
|
inputForeground: variables["--color-foreground"],
|
|
neutral: variables["--color-secondary"],
|
|
border: variables["--color-border"],
|
|
ring: variables["--color-focus"],
|
|
muted: variables["--color-subtle"],
|
|
shadow: variables["--color-primary-shadow"],
|
|
};
|
|
};
|
|
|
|
const clerkVariablesFor = (appearance: MobileThemeAppearance) => {
|
|
const colors = clerkColorsFor(appearance);
|
|
return {
|
|
"--color-clerk-page": colors.background.toLowerCase(),
|
|
"--color-clerk-foreground": colors.foreground.toLowerCase(),
|
|
"--color-clerk-foreground-muted": colors.mutedForeground.toLowerCase(),
|
|
"--color-clerk-border": colors.border,
|
|
"--color-clerk-danger": colors.danger.toLowerCase(),
|
|
};
|
|
};
|
|
|
|
const renderVariant = (name: string, variables: Readonly<Record<string, string>>) => {
|
|
const declarations = Object.entries(variables)
|
|
.map(([variable, value]) => ` ${variable}: ${value};`)
|
|
.join("\n");
|
|
return ` @variant ${name} {\n${declarations}\n }`;
|
|
};
|
|
|
|
export const renderUniwindThemesCSS = () => {
|
|
const variants = [
|
|
...APPEARANCES.map((appearance) =>
|
|
renderVariant(appearance, {
|
|
...getMobileThemeVariables(DEFAULT_MOBILE_THEME_ID, appearance),
|
|
...adaptiveVariablesFor(appearance),
|
|
...clerkVariablesFor(appearance),
|
|
}),
|
|
),
|
|
...BUILT_IN_THEME_IDS.flatMap((themeId) =>
|
|
APPEARANCES.map((appearance) =>
|
|
renderVariant(`${themeId}-${appearance}`, variablesFor(themeId, appearance)),
|
|
),
|
|
),
|
|
];
|
|
return [
|
|
"/* Generated by scripts/generate-uniwind-themes.mts. Do not edit manually. */",
|
|
"@layer theme {",
|
|
" :root {",
|
|
variants.join("\n\n"),
|
|
" }",
|
|
"}",
|
|
"",
|
|
].join("\n");
|
|
};
|
|
|
|
export const renderDefaultThemeVariablesJSON = () =>
|
|
`${JSON.stringify(
|
|
Object.fromEntries(
|
|
APPEARANCES.map((appearance) => [
|
|
appearance,
|
|
getMobileThemeVariables(DEFAULT_MOBILE_THEME_ID, appearance),
|
|
]),
|
|
),
|
|
null,
|
|
2,
|
|
)}\n`;
|
|
|
|
export const getGeneratedUniwindThemeOutputs = (): ReadonlyArray<
|
|
readonly [filename: string, contents: string]
|
|
> => [
|
|
[GENERATED_CSS_PATH, renderUniwindThemesCSS()],
|
|
[GENERATED_NAMES_PATH, `${JSON.stringify(customThemeNames, null, 2)}\n`],
|
|
[GENERATED_DEFAULT_VARIABLES_PATH, renderDefaultThemeVariablesJSON()],
|
|
[
|
|
GENERATED_CLERK_THEME_PATH,
|
|
`${JSON.stringify({ colors: clerkColorsFor("light"), darkColors: clerkColorsFor("dark"), design: { borderRadius: 18 } }, null, 2)}\n`,
|
|
],
|
|
];
|
|
|
|
const writeFileAtomically = (filename: string, contents: string) => {
|
|
const current = NodeFS.existsSync(filename) ? NodeFS.readFileSync(filename, "utf8") : null;
|
|
if (current === contents) return;
|
|
|
|
const temporaryFilename = `${filename}.${process.pid}.tmp`;
|
|
try {
|
|
NodeFS.writeFileSync(temporaryFilename, contents);
|
|
NodeFS.renameSync(temporaryFilename, filename);
|
|
} finally {
|
|
if (NodeFS.existsSync(temporaryFilename)) NodeFS.unlinkSync(temporaryFilename);
|
|
}
|
|
};
|
|
|
|
if (import.meta.main) {
|
|
const checkOnly = process.argv.includes("--check");
|
|
for (const [filename, contents] of getGeneratedUniwindThemeOutputs()) {
|
|
if (checkOnly) {
|
|
const current = NodeFS.existsSync(filename) ? NodeFS.readFileSync(filename, "utf8") : null;
|
|
if (current !== contents) {
|
|
console.error(
|
|
`${NodePath.relative(process.cwd(), filename)} is stale. Run vp run --filter @t3tools/mobile generate.`,
|
|
);
|
|
process.exitCode = 1;
|
|
}
|
|
continue;
|
|
}
|
|
// Metro watches the generated CSS. Replacing a complete temporary file keeps
|
|
// Tailwind from compiling a partially rewritten theme file.
|
|
writeFileAtomically(filename, contents);
|
|
}
|
|
}
|