diff options
-rw-r--r-- | FrontEnd/.eslintrc.cjs | 2 | ||||
-rw-r--r-- | FrontEnd/package.json | 2 | ||||
-rw-r--r-- | FrontEnd/pnpm-lock.yaml | 107 | ||||
-rw-r--r-- | FrontEnd/tools/palette.ts | 271 | ||||
-rw-r--r-- | FrontEnd/tools/tsconfig.json | 21 |
5 files changed, 396 insertions, 7 deletions
diff --git a/FrontEnd/.eslintrc.cjs b/FrontEnd/.eslintrc.cjs index a9cd8e03..6fcccd3e 100644 --- a/FrontEnd/.eslintrc.cjs +++ b/FrontEnd/.eslintrc.cjs @@ -13,7 +13,7 @@ module.exports = { plugins: ["@typescript-eslint", "prettier", "react", "react-hooks"],
parser: "@typescript-eslint/parser",
parserOptions: {
- project: true,
+ project: ["./tsconfig.json", "tools/tsconfig.json"],
tsconfigRootDir: __dirname
},
settings: {
diff --git a/FrontEnd/package.json b/FrontEnd/package.json index 7f438f0e..850543ee 100644 --- a/FrontEnd/package.json +++ b/FrontEnd/package.json @@ -43,7 +43,6 @@ "devDependencies": { "@parcel/packager-raw-url": "2.9.3", "@parcel/transformer-webmanifest": "2.9.3", - "@tsconfig/vite-react": "^2.0.0", "@types/color": "^3.0.3", "@types/lodash": "^4.14.195", "@types/marked": "^5.0.0", @@ -65,6 +64,7 @@ "parcel": "latest", "prettier": "^3.0.0", "process": "^0.11.10", + "ts-node": "latest", "typescript": "^5.1.6" } }
\ No newline at end of file diff --git a/FrontEnd/pnpm-lock.yaml b/FrontEnd/pnpm-lock.yaml index d4a2962c..0ee3d598 100644 --- a/FrontEnd/pnpm-lock.yaml +++ b/FrontEnd/pnpm-lock.yaml @@ -88,9 +88,6 @@ devDependencies: '@parcel/transformer-webmanifest': specifier: 2.9.3 version: 2.9.3(@parcel/core@2.9.3) - '@tsconfig/vite-react': - specifier: ^2.0.0 - version: 2.0.0 '@types/color': specifier: ^3.0.3 version: 3.0.3 @@ -154,6 +151,9 @@ devDependencies: process: specifier: ^0.11.10 version: 0.11.10 + ts-node: + specifier: latest + version: 10.9.1(@types/node@20.4.1)(typescript@5.1.6) typescript: specifier: ^5.1.6 version: 5.1.6 @@ -201,6 +201,13 @@ packages: regenerator-runtime: 0.13.11 dev: false + /@cspotcode/source-map-support@0.8.1: + resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} + engines: {node: '>=12'} + dependencies: + '@jridgewell/trace-mapping': 0.3.9 + dev: true + /@eslint-community/eslint-utils@4.4.0(eslint@8.44.0): resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -266,6 +273,22 @@ packages: react: 18.2.0 dev: false + /@jridgewell/resolve-uri@3.1.1: + resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==} + engines: {node: '>=6.0.0'} + dev: true + + /@jridgewell/sourcemap-codec@1.4.15: + resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + dev: true + + /@jridgewell/trace-mapping@0.3.9: + resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + dependencies: + '@jridgewell/resolve-uri': 3.1.1 + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + /@lezer/common@0.15.12: resolution: {integrity: sha512-edfwCxNLnzq5pBA/yaIhwJ3U3Kz8VAUOTRg0hhxaizaI1N+qxV7EXDv/kLCkLeq2RzSFvxexlaj5Mzfn2kY0Ig==} dev: true @@ -1357,8 +1380,20 @@ packages: engines: {node: '>=10.13.0'} dev: true - /@tsconfig/vite-react@2.0.0: - resolution: {integrity: sha512-erT+k9yzjRYnqRn6Fmvz+Y8+AtE+/YE954frGGwwit2ifsoWzRzYaOTlGj9/z0xJyYiaKNnNiFhid312QdC4rw==} + /@tsconfig/node10@1.0.9: + resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==} + dev: true + + /@tsconfig/node12@1.0.11: + resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} + dev: true + + /@tsconfig/node14@1.0.3: + resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} + dev: true + + /@tsconfig/node16@1.0.4: + resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} dev: true /@types/color-convert@2.0.0: @@ -1597,6 +1632,11 @@ packages: acorn: 8.10.0 dev: true + /acorn-walk@8.2.0: + resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==} + engines: {node: '>=0.4.0'} + dev: true + /acorn@8.10.0: resolution: {integrity: sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==} engines: {node: '>=0.4.0'} @@ -1631,6 +1671,10 @@ packages: color-convert: 2.0.1 dev: true + /arg@4.1.3: + resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} + dev: true + /argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} dev: true @@ -1886,6 +1930,10 @@ packages: path-type: 4.0.0 dev: true + /create-require@1.1.1: + resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} + dev: true + /cross-spawn@7.0.3: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} @@ -1967,6 +2015,11 @@ packages: hasBin: true dev: true + /diff@4.0.2: + resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} + engines: {node: '>=0.3.1'} + dev: true + /dir-glob@3.0.1: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} @@ -2989,6 +3042,10 @@ packages: yallist: 4.0.0 dev: true + /make-error@1.3.6: + resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + dev: true + /marked@5.1.1: resolution: {integrity: sha512-bTmmGdEINWmOMDjnPWDxGPQ4qkDLeYorpYbEtFOXzOruTwUE671q4Guiuchn4N8h/v6NGd7916kXsm3Iz4iUSg==} engines: {node: '>= 18'} @@ -3789,6 +3846,37 @@ packages: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} dev: false + /ts-node@10.9.1(@types/node@20.4.1)(typescript@5.1.6): + resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} + hasBin: true + peerDependencies: + '@swc/core': '>=1.2.50' + '@swc/wasm': '>=1.2.50' + '@types/node': '*' + typescript: '>=2.7' + peerDependenciesMeta: + '@swc/core': + optional: true + '@swc/wasm': + optional: true + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.9 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.4 + '@types/node': 20.4.1 + acorn: 8.10.0 + acorn-walk: 8.2.0 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.2 + make-error: 1.3.6 + typescript: 5.1.6 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + dev: true + /tslib@1.14.1: resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} dev: true @@ -3875,6 +3963,10 @@ packages: engines: {node: '>= 4'} dev: true + /v8-compile-cache-lib@3.0.1: + resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} + dev: true + /void-elements@3.1.0: resolution: {integrity: sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==} engines: {node: '>=0.10.0'} @@ -3962,6 +4054,11 @@ packages: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} dev: true + /yn@3.1.1: + resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} + engines: {node: '>=6'} + dev: true + /yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} diff --git a/FrontEnd/tools/palette.ts b/FrontEnd/tools/palette.ts new file mode 100644 index 00000000..7b047ce1 --- /dev/null +++ b/FrontEnd/tools/palette.ts @@ -0,0 +1,271 @@ +#!/usr/bin/env ts-node + +/** + * Color variable name scheme: + * has variant: --[prefix]-[name]-[variant]-color: [color]; + * no variant: --[prefix]-[name]-color: [color]; + * Variant scheme: + * [variant-prefix][level] + * eg. --cru-primary-color: [color]; --cru-primary-l1-color: [color]; + */ + +class HslColor { + constructor( + public h: number, + public s: number, + public l: number, + ) {} + + lighter(level: number): HslColor { + return new HslColor(this.h, this.s, this.l + level * 10); + } + + darker(level: number): HslColor { + return new HslColor(this.h, this.s, this.l - level * 10); + } + + toString(): string { + return `hsl(${this.h} ${this.s}% ${this.l}%)`; + } +} + +class CssVarColor { + constructor( + public name: string, + public cssVar: string, + ) {} + + toString(): string { + return `var(--${this.cssVar})`; + } +} + +class VariantColor { + constructor( + public color: HslColor, + public variant?: string | null, + ) {} + + toCssString(prefix: string, name: string): string { + const variantPart = this.variant == null ? "" : `-${this.variant}`; + return `--${prefix}-${name}${variantPart}-color: ${this.color.toString()};`; + } +} + +type LightnessVariantType = "lighter" | "darker"; + +interface LightnessVariant { + prefix: string; + type: LightnessVariantType; +} + +function generateLightnessVariantColors( + baseColor: HslColor, + lightnessVariant: LightnessVariant, + levels: number, +): VariantColor[] { + const result: VariantColor[] = []; + + for (let i = 1; i <= levels; i++) { + const color = + lightnessVariant.type === "lighter" + ? baseColor.lighter(i) + : baseColor.darker(i); + const colorVariant = `${lightnessVariant.prefix}${i}`; + result.push(new VariantColor(color, colorVariant)); + } + + return result; +} + +type ColorMode = "light" | "dark"; + +const themeVariantPrefixes = ["l", "d", "f", "b"] as const; + +type LightnessVariants = { + prefix: (typeof themeVariantPrefixes)[number]; + type: LightnessVariantType; +}[]; + +function generateThemeColorLightnessVariants( + mode: ColorMode, +): LightnessVariants { + return [ + { + prefix: "l", + type: "lighter", + }, + { + prefix: "d", + type: "darker", + }, + { + prefix: "f", + type: mode === "light" ? "lighter" : "darker", + }, + { + prefix: "b", + type: mode === "light" ? "darker" : "lighter", + }, + ]; +} + +class ColorGroup { + constructor( + public name: string, + public baseColor: HslColor, + ) {} + + generateVariantColors( + lightnessVariants: LightnessVariant[], + levels = 3, + ): VariantColor[] { + const result: VariantColor[] = [new VariantColor(this.baseColor)]; + + for (const lightnessVariant of lightnessVariants) { + result.push( + ...generateLightnessVariantColors( + this.baseColor, + lightnessVariant, + levels, + ), + ); + } + + return result; + } +} + +class ThemeColorGroup extends ColorGroup { + constructor(name: string, baseColor: HslColor) { + super(name, baseColor); + } + + generateColors(mode: ColorMode): VariantColor[] { + return super.generateVariantColors( + generateThemeColorLightnessVariants(mode), + ); + } + + generateCss(prefix: string, mode: ColorMode): string { + return this.generateColors(mode) + .map((c) => c.toCssString(prefix, this.name)) + .join("\n"); + } +} + +class VarColorGroup { + constructor( + public name: string, + public varName: string, + ) {} + + generateCss( + prefix: string, + variantPrefixes = themeVariantPrefixes, + levels = 3, + ): string { + const vs: string[] = []; + for (const v of variantPrefixes) { + for (let l = 1; l <= levels; l++) { + vs.push(`${v}${l}`); + } + } + let result = vs + .map( + (v) => + `--${prefix}-${this.name}-${v}-color: var(--${prefix}-${this.varName}-${v}-color);`, + ) + .join("\n"); + result = `--${prefix}-${this.name}-color: var(--${prefix}-${this.varName}-color);\n${result}`; + return result; + } +} + +const themeColorNames = [ + "primary", + "secondary", + "tertiary", + "danger", + "success", +] as const; + +type ThemeColorNames = (typeof themeColorNames)[number]; + +type ThemeColors = { + [key in ThemeColorNames]: HslColor; +}; + +// Config region begin +const prefix = "cru"; + +const themeColors: ThemeColors = { + primary: new HslColor(210, 100, 50), + secondary: new HslColor(40, 100, 50), + tertiary: new HslColor(160, 100, 50), + danger: new HslColor(0, 100, 50), + success: new HslColor(120, 100, 50), +}; + +// Config region end + +let output = ""; + +function indentText( + text: string, + level: number, + indentWidth = 2, + appendNewlines = 1, +): string { + const lines = text.split("\n"); + const indent = " ".repeat(level * indentWidth); + return ( + lines + .map((line) => (line.length === 0 ? "" : `${indent}${line}`)) + .join("\n") + "\n".repeat(appendNewlines) + ); +} + +function print(text: string, indent = 0, appendNewlines = 1) { + output += indentText(text, indent, 2, appendNewlines); +} + +function generateThemeColorCss(mode: ColorMode): string { + let output = ""; + for (const name of themeColorNames) { + const colorGroup = new ThemeColorGroup(name, themeColors[name]); + output += colorGroup.generateCss(prefix, mode); + } + return output; +} + +function generateThemeColorAliasCss(): string { + let result = ""; + for (const name of themeColorNames) { + const varColorGroup = new VarColorGroup("theme", name); + result += `.${prefix}-${name} {\n${indentText( + varColorGroup.generateCss(prefix), + 1, + )}\n}\n`; + } + return result; +} + +function main() { + print("/* Generated by palette.ts */\n"); + + print(":root {"); + print(generateThemeColorCss("light"), 1); + print("}\n"); + + print("@media (prefers-color-scheme: dark) {"); + print(":root {", 1); + print(generateThemeColorCss("dark"), 2); + print("}", 1); + print("}\n"); + + print(generateThemeColorAliasCss()); +} + +main(); +process.stdout.write(output); diff --git a/FrontEnd/tools/tsconfig.json b/FrontEnd/tools/tsconfig.json new file mode 100644 index 00000000..8c9b3c47 --- /dev/null +++ b/FrontEnd/tools/tsconfig.json @@ -0,0 +1,21 @@ +{ + // This is an alias to @tsconfig/node16: https://github.com/tsconfig/bases + "extends": "ts-node/node16/tsconfig.json", + // Most ts-node options can be specified here using their programmatic names. + "ts-node": { + // It is faster to skip typechecking. + // Remove if you want ts-node to do typechecking. + "transpileOnly": true, + "compilerOptions": { + // compilerOptions specified here will override those declared below, + // but *only* in ts-node. Useful if you want ts-node and tsc to use + // different options with a single tsconfig.json. + } + }, + "compilerOptions": { + // typescript options here + }, + "include": [ + "**/*" + ] +}
\ No newline at end of file |