Add TypeScript theme generator for doki-master-theme palettes
Generates one Gitea-compatible CSS theme file per doki-master-theme definition (88 themes). Each file contains a gitea-theme-meta-info block and a :root block with the full set of CSS custom properties derived from the character's colour palette. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
node_modules/
|
||||
dist/
|
||||
output/
|
||||
.vscode/
|
||||
@@ -0,0 +1,3 @@
|
||||
[submodule "doki-master-theme"]
|
||||
path = doki-master-theme
|
||||
url = https://github.com/doki-theme/doki-master-theme.git
|
||||
Submodule
+1
Submodule doki-master-theme added at 4467240b45
+53
@@ -0,0 +1,53 @@
|
||||
import { readdir, writeFile, mkdir } from 'node:fs/promises';
|
||||
import { join, resolve } from 'node:path';
|
||||
import { loadTheme } from './src/palette.js';
|
||||
import { buildVariables } from './src/themeBuilder.js';
|
||||
import { renderThemeCSS } from './src/cssWriter.js';
|
||||
|
||||
const DOKI_REPO = resolve(process.env['DOKI_REPO'] ?? './doki-master-theme');
|
||||
const OUTPUT_DIR = resolve(process.env['OUTPUT_DIR'] ?? './output');
|
||||
|
||||
async function findDefinitionFiles(dir: string): Promise<string[]> {
|
||||
const entries = await readdir(dir, { recursive: true });
|
||||
return entries
|
||||
.filter((f) => f.endsWith('.master.definition.json'))
|
||||
.map((f) => join(dir, f));
|
||||
}
|
||||
|
||||
async function main(): Promise<void> {
|
||||
await mkdir(OUTPUT_DIR, { recursive: true });
|
||||
|
||||
const definitionsDir = join(DOKI_REPO, 'definitions');
|
||||
const definitionFiles = await findDefinitionFiles(definitionsDir);
|
||||
|
||||
if (definitionFiles.length === 0) {
|
||||
console.error(`No definition files found in ${definitionsDir}`);
|
||||
console.error('Make sure the doki-master-theme submodule is initialised:');
|
||||
console.error(' git submodule update --init');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
let success = 0;
|
||||
let errors = 0;
|
||||
|
||||
for (const defPath of definitionFiles) {
|
||||
try {
|
||||
const theme = loadTheme(defPath, DOKI_REPO);
|
||||
const variables = buildVariables(theme);
|
||||
const css = renderThemeCSS(theme, variables);
|
||||
|
||||
const outPath = join(OUTPUT_DIR, `theme-${theme.internalName}.css`);
|
||||
await writeFile(outPath, css, 'utf8');
|
||||
|
||||
console.log(`✓ ${theme.internalName}`);
|
||||
success++;
|
||||
} catch (err) {
|
||||
console.error(`✗ ${defPath}: ${err}`);
|
||||
errors++;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`\nGenerated ${success} theme${success !== 1 ? 's' : ''}. Errors: ${errors}.`);
|
||||
}
|
||||
|
||||
main();
|
||||
Generated
+566
@@ -0,0 +1,566 @@
|
||||
{
|
||||
"name": "gitea-doki-theme-generator",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "gitea-doki-theme-generator",
|
||||
"version": "1.0.0",
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.15.21",
|
||||
"tsx": "^4.19.4",
|
||||
"typescript": "^5.8.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.0.tgz",
|
||||
"integrity": "sha512-lhRUCeuOyJQURhTxl4WkpFTjIsbDayJHih5kZC1giwE+MhIzAb7mEsQMqMf18rHLsrb5qI1tafG20mLxEWcWlA==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"aix"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.0.tgz",
|
||||
"integrity": "sha512-wqh0ByljabXLKHeWXYLqoJ5jKC4XBaw6Hk08OfMrCRd2nP2ZQ5eleDZC41XHyCNgktBGYMbqnrJKq/K/lzPMSQ==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm64": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.0.tgz",
|
||||
"integrity": "sha512-+WzIXQOSaGs33tLEgYPYe/yQHf0WTU0X42Jca3y8NWMbUVhp7rUnw+vAsRC/QiDrdD31IszMrZy+qwPOPjd+rw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-x64": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.0.tgz",
|
||||
"integrity": "sha512-+VJggoaKhk2VNNqVL7f6S189UzShHC/mR9EE8rDdSkdpN0KflSwWY/gWjDrNxxisg8Fp1ZCD9jLMo4m0OUfeUA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/darwin-arm64": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.0.tgz",
|
||||
"integrity": "sha512-0T+A9WZm+bZ84nZBtk1ckYsOvyA3x7e2Acj1KdVfV4/2tdG4fzUp91YHx+GArWLtwqp77pBXVCPn2We7Letr0Q==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/darwin-x64": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.0.tgz",
|
||||
"integrity": "sha512-fyzLm/DLDl/84OCfp2f/XQ4flmORsjU7VKt8HLjvIXChJoFFOIL6pLJPH4Yhd1n1gGFF9mPwtlN5Wf82DZs+LQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/freebsd-arm64": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.0.tgz",
|
||||
"integrity": "sha512-l9GeW5UZBT9k9brBYI+0WDffcRxgHQD8ShN2Ur4xWq/NFzUKm3k5lsH4PdaRgb2w7mI9u61nr2gI2mLI27Nh3Q==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/freebsd-x64": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.0.tgz",
|
||||
"integrity": "sha512-BXoQai/A0wPO6Es3yFJ7APCiKGc1tdAEOgeTNy3SsB491S3aHn4S4r3e976eUnPdU+NbdtmBuLncYir2tMU9Nw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.0.tgz",
|
||||
"integrity": "sha512-CjaaREJagqJp7iTaNQjjidaNbCKYcd4IDkzbwwxtSvjI7NZm79qiHc8HqciMddQ6CKvJT6aBd8lO9kN/ZudLlw==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm64": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.0.tgz",
|
||||
"integrity": "sha512-RVyzfb3FWsGA55n6WY0MEIEPURL1FcbhFE6BffZEMEekfCzCIMtB5yyDcFnVbTnwk+CLAgTujmV/Lgvih56W+A==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ia32": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.0.tgz",
|
||||
"integrity": "sha512-KBnSTt1kxl9x70q+ydterVdl+Cn0H18ngRMRCEQfrbqdUuntQQ0LoMZv47uB97NljZFzY6HcfqEZ2SAyIUTQBQ==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-loong64": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.0.tgz",
|
||||
"integrity": "sha512-zpSlUce1mnxzgBADvxKXX5sl8aYQHo2ezvMNI8I0lbblJtp8V4odlm3Yzlj7gPyt3T8ReksE6bK+pT3WD+aJRg==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-mips64el": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.0.tgz",
|
||||
"integrity": "sha512-2jIfP6mmjkdmeTlsX/9vmdmhBmKADrWqN7zcdtHIeNSCH1SqIoNI63cYsjQR8J+wGa4Y5izRcSHSm8K3QWmk3w==",
|
||||
"cpu": [
|
||||
"mips64el"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ppc64": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.0.tgz",
|
||||
"integrity": "sha512-bc0FE9wWeC0WBm49IQMPSPILRocGTQt3j5KPCA8os6VprfuJ7KD+5PzESSrJ6GmPIPJK965ZJHTUlSA6GNYEhg==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-riscv64": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.0.tgz",
|
||||
"integrity": "sha512-SQPZOwoTTT/HXFXQJG/vBX8sOFagGqvZyXcgLA3NhIqcBv1BJU1d46c0rGcrij2B56Z2rNiSLaZOYW5cUk7yLQ==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-s390x": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.0.tgz",
|
||||
"integrity": "sha512-SCfR0HN8CEEjnYnySJTd2cw0k9OHB/YFzt5zgJEwa+wL/T/raGWYMBqwDNAC6dqFKmJYZoQBRfHjgwLHGSrn3Q==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-x64": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.0.tgz",
|
||||
"integrity": "sha512-us0dSb9iFxIi8srnpl931Nvs65it/Jd2a2K3qs7fz2WfGPHqzfzZTfec7oxZJRNPXPnNYZtanmRc4AL/JwVzHQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/netbsd-arm64": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.0.tgz",
|
||||
"integrity": "sha512-CR/RYotgtCKwtftMwJlUU7xCVNg3lMYZ0RzTmAHSfLCXw3NtZtNpswLEj/Kkf6kEL3Gw+BpOekRX0BYCtklhUw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"netbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/netbsd-x64": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.0.tgz",
|
||||
"integrity": "sha512-nU1yhmYutL+fQ71Kxnhg8uEOdC0pwEW9entHykTgEbna2pw2dkbFSMeqjjyHZoCmt8SBkOSvV+yNmm94aUrrqw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"netbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-arm64": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.0.tgz",
|
||||
"integrity": "sha512-cXb5vApOsRsxsEl4mcZ1XY3D4DzcoMxR/nnc4IyqYs0rTI8ZKmW6kyyg+11Z8yvgMfAEldKzP7AdP64HnSC/6g==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-x64": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.0.tgz",
|
||||
"integrity": "sha512-8wZM2qqtv9UP3mzy7HiGYNH/zjTA355mpeuA+859TyR+e+Tc08IHYpLJuMsfpDJwoLo1ikIJI8jC3GFjnRClzA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openharmony-arm64": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.0.tgz",
|
||||
"integrity": "sha512-FLGfyizszcef5C3YtoyQDACyg95+dndv79i2EekILBofh5wpCa1KuBqOWKrEHZg3zrL3t5ouE5jgr94vA+Wb2w==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openharmony"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/sunos-x64": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.0.tgz",
|
||||
"integrity": "sha512-1ZgjUoEdHZZl/YlV76TSCz9Hqj9h9YmMGAgAPYd+q4SicWNX3G5GCyx9uhQWSLcbvPW8Ni7lj4gDa1T40akdlw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"sunos"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-arm64": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.0.tgz",
|
||||
"integrity": "sha512-Q9StnDmQ/enxnpxCCLSg0oo4+34B9TdXpuyPeTedN/6+iXBJ4J+zwfQI28u/Jl40nOYAxGoNi7mFP40RUtkmUA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-ia32": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.0.tgz",
|
||||
"integrity": "sha512-zF3ag/gfiCe6U2iczcRzSYJKH1DCI+ByzSENHlM2FcDbEeo5Zd2C86Aq0tKUYAJJ1obRP84ymxIAksZUcdztHA==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-x64": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.0.tgz",
|
||||
"integrity": "sha512-pEl1bO9mfAmIC+tW5btTmrKaujg3zGtUmWNdCw/xs70FBjwAL3o9OEKNHvNmnyylD6ubxUERiEhdsL0xBQ9efw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "22.19.19",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.19.tgz",
|
||||
"integrity": "sha512-dyh/xO2Fh5bYrfWaaqGrRQQGkNdmYw6AmaAUvYeUMNTWQtvb796ikLdmTchRmOlOiIJ1TDXfWgVx1QkUlQ6Hew==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~6.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.0.tgz",
|
||||
"integrity": "sha512-sNR9MHpXSUV/XB4zmsFKN+QgVG82Cc7+/aaxJ8Adi8hyOac+EXptIp45QBPaVyX3N70664wRbTcLTOemCAnyqw==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"esbuild": "bin/esbuild"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@esbuild/aix-ppc64": "0.28.0",
|
||||
"@esbuild/android-arm": "0.28.0",
|
||||
"@esbuild/android-arm64": "0.28.0",
|
||||
"@esbuild/android-x64": "0.28.0",
|
||||
"@esbuild/darwin-arm64": "0.28.0",
|
||||
"@esbuild/darwin-x64": "0.28.0",
|
||||
"@esbuild/freebsd-arm64": "0.28.0",
|
||||
"@esbuild/freebsd-x64": "0.28.0",
|
||||
"@esbuild/linux-arm": "0.28.0",
|
||||
"@esbuild/linux-arm64": "0.28.0",
|
||||
"@esbuild/linux-ia32": "0.28.0",
|
||||
"@esbuild/linux-loong64": "0.28.0",
|
||||
"@esbuild/linux-mips64el": "0.28.0",
|
||||
"@esbuild/linux-ppc64": "0.28.0",
|
||||
"@esbuild/linux-riscv64": "0.28.0",
|
||||
"@esbuild/linux-s390x": "0.28.0",
|
||||
"@esbuild/linux-x64": "0.28.0",
|
||||
"@esbuild/netbsd-arm64": "0.28.0",
|
||||
"@esbuild/netbsd-x64": "0.28.0",
|
||||
"@esbuild/openbsd-arm64": "0.28.0",
|
||||
"@esbuild/openbsd-x64": "0.28.0",
|
||||
"@esbuild/openharmony-arm64": "0.28.0",
|
||||
"@esbuild/sunos-x64": "0.28.0",
|
||||
"@esbuild/win32-arm64": "0.28.0",
|
||||
"@esbuild/win32-ia32": "0.28.0",
|
||||
"@esbuild/win32-x64": "0.28.0"
|
||||
}
|
||||
},
|
||||
"node_modules/fsevents": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/tsx": {
|
||||
"version": "4.22.3",
|
||||
"resolved": "https://registry.npmjs.org/tsx/-/tsx-4.22.3.tgz",
|
||||
"integrity": "sha512-mdoNxBC/cSQObGGVQ5Bpn5i+yv7j68gk3Nfm3wFjcJg3Z0Mix9jzAFfP12prmm5eVGmDKtp0yyArrs0Q+8gZHg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"esbuild": "~0.28.0"
|
||||
},
|
||||
"bin": {
|
||||
"tsx": "dist/cli.mjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "~2.3.3"
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.9.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "6.21.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
|
||||
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "gitea-doki-theme-generator",
|
||||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"generate": "tsx generate.ts",
|
||||
"generate:watch": "tsx watch generate.ts"
|
||||
},
|
||||
"devDependencies": {
|
||||
"tsx": "^4.19.4",
|
||||
"typescript": "^5.8.3",
|
||||
"@types/node": "^22.15.21"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
interface RGB { r: number; g: number; b: number; }
|
||||
interface HSL { h: number; s: number; l: number; }
|
||||
|
||||
function parseHex(hex: string): RGB {
|
||||
const h = hex.replace('#', '').slice(0, 6);
|
||||
return {
|
||||
r: parseInt(h.slice(0, 2), 16),
|
||||
g: parseInt(h.slice(2, 4), 16),
|
||||
b: parseInt(h.slice(4, 6), 16),
|
||||
};
|
||||
}
|
||||
|
||||
function toHex(rgb: RGB): string {
|
||||
const clamp = (n: number) => Math.max(0, Math.min(255, Math.round(n)));
|
||||
const hex = (n: number) => clamp(n).toString(16).padStart(2, '0');
|
||||
return `#${hex(rgb.r)}${hex(rgb.g)}${hex(rgb.b)}`.toUpperCase();
|
||||
}
|
||||
|
||||
function rgbToHsl(rgb: RGB): HSL {
|
||||
const r = rgb.r / 255;
|
||||
const g = rgb.g / 255;
|
||||
const b = rgb.b / 255;
|
||||
const max = Math.max(r, g, b);
|
||||
const min = Math.min(r, g, b);
|
||||
const l = (max + min) / 2;
|
||||
|
||||
if (max === min) return { h: 0, s: 0, l };
|
||||
|
||||
const d = max - min;
|
||||
const s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
||||
|
||||
let h: number;
|
||||
if (max === r) {
|
||||
h = ((g - b) / d + (g < b ? 6 : 0)) / 6;
|
||||
} else if (max === g) {
|
||||
h = ((b - r) / d + 2) / 6;
|
||||
} else {
|
||||
h = ((r - g) / d + 4) / 6;
|
||||
}
|
||||
|
||||
return { h, s, l };
|
||||
}
|
||||
|
||||
function hue2rgb(p: number, q: number, t: number): number {
|
||||
if (t < 0) t += 1;
|
||||
if (t > 1) t -= 1;
|
||||
if (t < 1 / 6) return p + (q - p) * 6 * t;
|
||||
if (t < 1 / 2) return q;
|
||||
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
|
||||
return p;
|
||||
}
|
||||
|
||||
function hslToRgb(hsl: HSL): RGB {
|
||||
const { h, s, l } = hsl;
|
||||
if (s === 0) {
|
||||
const v = Math.round(l * 255);
|
||||
return { r: v, g: v, b: v };
|
||||
}
|
||||
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
||||
const p = 2 * l - q;
|
||||
return {
|
||||
r: Math.round(hue2rgb(p, q, h + 1 / 3) * 255),
|
||||
g: Math.round(hue2rgb(p, q, h) * 255),
|
||||
b: Math.round(hue2rgb(p, q, h - 1 / 3) * 255),
|
||||
};
|
||||
}
|
||||
|
||||
export function stripAlpha(hex: string): string {
|
||||
return '#' + hex.replace('#', '').slice(0, 6);
|
||||
}
|
||||
|
||||
export function normalizeHex(hex: string): string {
|
||||
let h = hex.replace('#', '');
|
||||
if (h.length === 3) h = h[0] + h[0] + h[1] + h[1] + h[2] + h[2];
|
||||
return '#' + h.slice(0, 6).toUpperCase();
|
||||
}
|
||||
|
||||
export function scaleLightness(hex: string, delta: number): string {
|
||||
const rgb = parseHex(stripAlpha(hex));
|
||||
const hsl = rgbToHsl(rgb);
|
||||
hsl.l = Math.max(0, Math.min(1, hsl.l + delta));
|
||||
return toHex(hslToRgb(hsl));
|
||||
}
|
||||
|
||||
export function withAlpha(hex: string, alpha: number): string {
|
||||
const base = normalizeHex(hex).replace('#', '');
|
||||
const a = Math.max(0, Math.min(255, Math.round(alpha)));
|
||||
return '#' + base + a.toString(16).padStart(2, '0').toUpperCase();
|
||||
}
|
||||
|
||||
export function mix(a: string, b: string, weight: number): string {
|
||||
const ra = parseHex(stripAlpha(a));
|
||||
const rb = parseHex(stripAlpha(b));
|
||||
return toHex({
|
||||
r: ra.r + (rb.r - ra.r) * weight,
|
||||
g: ra.g + (rb.g - ra.g) * weight,
|
||||
b: ra.b + (rb.b - ra.b) * weight,
|
||||
});
|
||||
}
|
||||
|
||||
export function desaturate(hex: string): string {
|
||||
const rgb = parseHex(stripAlpha(hex));
|
||||
const hsl = rgbToHsl(rgb);
|
||||
hsl.s = 0;
|
||||
return toHex(hslToRgb(hsl));
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import type { ThemeConfig } from './types.js';
|
||||
|
||||
export function renderThemeCSS(theme: ThemeConfig, variables: Record<string, string>): string {
|
||||
const isDark = theme.colorScheme === 'dark';
|
||||
const lines: string[] = [];
|
||||
|
||||
lines.push('gitea-theme-meta-info {');
|
||||
lines.push(` --theme-display-name: "${theme.displayName}";`);
|
||||
lines.push(` --theme-color-scheme: "${theme.colorScheme}";`);
|
||||
lines.push('}');
|
||||
lines.push('');
|
||||
lines.push(':root {');
|
||||
|
||||
for (const [name, value] of Object.entries(variables)) {
|
||||
lines.push(` ${name}: ${value};`);
|
||||
}
|
||||
|
||||
// Native CSS properties at the end (not custom properties)
|
||||
lines.push(` accent-color: var(--color-accent);`);
|
||||
lines.push(` color-scheme: ${isDark ? 'dark' : 'light'};`);
|
||||
|
||||
lines.push('}');
|
||||
lines.push('');
|
||||
|
||||
return lines.join('\n');
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
import type { DokiDefinition, DokiColors, ColorTemplate, ThemeConfig } from './types.js';
|
||||
|
||||
function loadJson<T>(path: string): T {
|
||||
return JSON.parse(readFileSync(path, 'utf8')) as T;
|
||||
}
|
||||
|
||||
export function resolvePalette(definitionPath: string, dokiRepoRoot: string): DokiColors {
|
||||
const templatesDir = join(dokiRepoRoot, 'templates');
|
||||
const base = loadJson<ColorTemplate>(join(templatesDir, 'base.colors.template.json'));
|
||||
const def = loadJson<DokiDefinition>(definitionPath);
|
||||
|
||||
const variantTemplate = loadJson<ColorTemplate>(
|
||||
join(templatesDir, def.dark ? 'dark.colors.template.json' : 'light.colors.template.json')
|
||||
);
|
||||
|
||||
const overrides = def.overrides?.editorScheme?.colors ?? {};
|
||||
|
||||
const resolved: DokiColors = {
|
||||
...base.colors,
|
||||
...variantTemplate.colors,
|
||||
...def.colors,
|
||||
...overrides,
|
||||
} as DokiColors;
|
||||
|
||||
return resolved;
|
||||
}
|
||||
|
||||
function sanitize(s: string): string {
|
||||
return s.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
|
||||
}
|
||||
|
||||
export function loadTheme(definitionPath: string, dokiRepoRoot: string): ThemeConfig {
|
||||
const def = loadJson<DokiDefinition>(definitionPath);
|
||||
const palette = resolvePalette(definitionPath, dokiRepoRoot);
|
||||
|
||||
// Normalise path separators so we can split reliably
|
||||
const normPath = definitionPath.replace(/\\/g, '/');
|
||||
const parts = normPath.split('/');
|
||||
const filename = parts[parts.length - 1];
|
||||
|
||||
// Strip .master.definition.json then split on dots
|
||||
const stem = filename.replace('.master.definition.json', '');
|
||||
const nameParts = stem.split('.');
|
||||
const hasVariant = nameParts.length > 1;
|
||||
|
||||
const variant = hasVariant ? nameParts[nameParts.length - 1] : null;
|
||||
|
||||
// Series directory is 4 levels up when a variant dir exists, 2 levels up otherwise
|
||||
const series = hasVariant ? parts[parts.length - 4] : parts[parts.length - 2];
|
||||
|
||||
const internalName = ['doki', sanitize(series), sanitize(def.displayName), variant ? sanitize(variant) : null]
|
||||
.filter(Boolean)
|
||||
.join('-');
|
||||
|
||||
const displayName = variant
|
||||
? `${def.group}: ${def.displayName} (${variant})`
|
||||
: `${def.group}: ${def.displayName}`;
|
||||
|
||||
return {
|
||||
internalName,
|
||||
displayName,
|
||||
colorScheme: def.dark ? 'dark' : 'light',
|
||||
palette,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,431 @@
|
||||
import { normalizeHex, scaleLightness, withAlpha, mix, desaturate } from './colorMath.js';
|
||||
import type { ThemeConfig, DokiColors } from './types.js';
|
||||
|
||||
const FALLBACK = '#FF00FF'; // magenta — visually obvious if a key is missing
|
||||
|
||||
function get(p: DokiColors, key: string, fallback = FALLBACK): string {
|
||||
const v = p[key];
|
||||
if (!v) {
|
||||
console.warn(` warning: missing palette key "${key}", using fallback`);
|
||||
return fallback;
|
||||
}
|
||||
return normalizeHex(v);
|
||||
}
|
||||
|
||||
// Primary scale deltas (dark theme: dark-N lighter, light-N darker; light theme: reversed)
|
||||
const PRIMARY_DARK_DELTAS = [0.10, 0.17, 0.24, 0.30, 0.40, 0.52, 0.62];
|
||||
const PRIMARY_LIGHT_DELTAS = [0.10, 0.17, 0.24, 0.30, 0.40, 0.52, 0.62];
|
||||
|
||||
// Secondary scale deltas
|
||||
const SECONDARY_DARK_DELTAS = [0.04, 0.08, 0.14, 0.20, 0.26, 0.31, 0.36, 0.40, 0.43, 0.46, 0.50, 0.53, 0.55];
|
||||
const SECONDARY_LIGHT_DELTAS = [0.04, 0.08, 0.14, 0.20];
|
||||
|
||||
const ALPHA_STEPS = [
|
||||
[10, 0x19], [20, 0x33], [30, 0x4b], [40, 0x66], [50, 0x80],
|
||||
[60, 0x99], [70, 0xb3], [80, 0xcc], [90, 0xe1],
|
||||
] as const;
|
||||
|
||||
const DARK_NAMED_COLORS = {
|
||||
red: '#CC4848', redLight: '#D15A5A', redDark1: '#C23636', redDark2: '#AD3030',
|
||||
orange: '#CC580C', orangeLight: '#F6A066', orangeDark1: '#F38236', orangeDark2: '#F16E17',
|
||||
yellow: '#CC9903', yellowLight: '#EAAF03', yellowDark1: '#B88A03', yellowDark2: '#A37A02',
|
||||
olive: '#91A313', oliveLight: '#ABC016', oliveDark1: '#839311', oliveDark2: '#74820F',
|
||||
green: '#87AB63', greenLight: '#93B373', greenDark1: '#7A9E55', greenDark2: '#6C8C4C',
|
||||
teal: '#00918A', tealLight: '#00B6AD', tealDark1: '#00837C', tealDark2: '#00746E',
|
||||
blue: '#3A8AC6', blueLight: '#4E96CC', blueDark1: '#347CB3', blueDark2: '#2E6E9F',
|
||||
violet: '#906AE1', violetLight: '#9B79E4', violetDark1: '#7B4EDB', violetDark2: '#6733D6',
|
||||
purple: '#B259D0', purpleLight: '#BA6AD5', purpleDark1: '#A742C9', purpleDark2: '#9834B9',
|
||||
pink: '#D22E8B', pinkLight: '#D74397', pinkDark1: '#BE297D', pinkDark2: '#A9246F',
|
||||
brown: '#A47252', brownLight: '#B08061', brownDark1: '#94674A', brownDark2: '#835B42',
|
||||
black: '#202225', blackLight: '#45484E', blackDark1: '#2E3033', blackDark2: '#292B2E',
|
||||
};
|
||||
|
||||
const LIGHT_NAMED_COLORS = {
|
||||
red: '#DB2828', redLight: '#E45E5E', redDark1: '#C82121', redDark2: '#B11E1E',
|
||||
orange: '#F2711C', orangeLight: '#F59555', orangeDark1: '#E6630D', orangeDark2: '#CC580C',
|
||||
yellow: '#FBBD08', yellowLight: '#FCCE46', yellowDark1: '#E5AC04', yellowDark2: '#CC9903',
|
||||
olive: '#B5CC18', oliveLight: '#D3E942', oliveDark1: '#A3B816', oliveDark2: '#91A313',
|
||||
green: '#21BA45', greenLight: '#46DE6A', greenDark1: '#1EA73E', greenDark2: '#1A9537',
|
||||
teal: '#00B5AD', tealLight: '#08FFF4', tealDark1: '#00A39C', tealDark2: '#00918A',
|
||||
blue: '#2185D0', blueLight: '#51A5E3', blueDark1: '#1E78BB', blueDark2: '#1A6AA6',
|
||||
violet: '#6435C9', violetLight: '#8B67D7', violetDark1: '#5A30B5', violetDark2: '#502AA1',
|
||||
purple: '#A333C8', purpleLight: '#BB64D8', purpleDark1: '#932EB4', purpleDark2: '#8229A0',
|
||||
pink: '#E03997', pinkLight: '#E86BB1', pinkDark1: '#DB228A', pinkDark2: '#C21E7B',
|
||||
brown: '#A5673F', brownLight: '#C58B66', brownDark1: '#955D39', brownDark2: '#845232',
|
||||
black: '#1D2328', blackLight: '#4B5B68', blackDark1: '#2C3339', blackDark2: '#131619',
|
||||
};
|
||||
|
||||
const SERIES_16 = {
|
||||
'--color-series-16-0': '#7DB233',
|
||||
'--color-series-16-1': '#499A37',
|
||||
'--color-series-16-2': '#CE4751',
|
||||
'--color-series-16-3': '#8F9121',
|
||||
'--color-series-16-4': '#AC32A6',
|
||||
'--color-series-16-5': '#7445E9',
|
||||
'--color-series-16-6': '#C67D28',
|
||||
'--color-series-16-7': '#4DB392',
|
||||
'--color-series-16-8': '#AA4D30',
|
||||
'--color-series-16-9': '#2A6F84',
|
||||
'--color-series-16-10': '#C45327',
|
||||
'--color-series-16-11': '#3D965C',
|
||||
'--color-series-16-12': '#792A93',
|
||||
'--color-series-16-13': '#439D73',
|
||||
'--color-series-16-14': '#103AAD',
|
||||
'--color-series-16-15': '#982E85',
|
||||
};
|
||||
|
||||
export function buildVariables(theme: ThemeConfig): Record<string, string> {
|
||||
const p = theme.palette;
|
||||
const isDark = theme.colorScheme === 'dark';
|
||||
|
||||
const primary = get(p, 'accentColor');
|
||||
const secondary = get(p, 'borderColor', get(p, 'secondaryBackground'));
|
||||
const body = get(p, 'baseBackground');
|
||||
const fg = get(p, 'foregroundColor');
|
||||
const headerColor = get(p, 'headerColor');
|
||||
const contrastColor = get(p, 'contrastColor');
|
||||
const inactiveBackground = get(p, 'inactiveBackground', get(p, 'secondaryBackground'));
|
||||
|
||||
const named = isDark ? DARK_NAMED_COLORS : LIGHT_NAMED_COLORS;
|
||||
const sign = isDark ? 1 : -1;
|
||||
|
||||
const vars: Record<string, string> = {};
|
||||
|
||||
// ─── Section 1: is-dark-theme ────────────────────────────────────────────────
|
||||
vars['--is-dark-theme'] = isDark ? 'true' : 'false';
|
||||
|
||||
// ─── Section 2: Primary color system ─────────────────────────────────────────
|
||||
vars['--color-primary'] = primary;
|
||||
vars['--color-primary-contrast'] = get(p, 'accentContrastColor', '#FFFFFF');
|
||||
|
||||
PRIMARY_DARK_DELTAS.forEach((d, i) => {
|
||||
vars[`--color-primary-dark-${i + 1}`] = scaleLightness(primary, sign * d);
|
||||
});
|
||||
PRIMARY_LIGHT_DELTAS.forEach((d, i) => {
|
||||
vars[`--color-primary-light-${i + 1}`] = scaleLightness(primary, -sign * d);
|
||||
});
|
||||
|
||||
ALPHA_STEPS.forEach(([pct, alpha]) => {
|
||||
vars[`--color-primary-alpha-${pct}`] = withAlpha(primary, alpha);
|
||||
});
|
||||
|
||||
vars['--color-primary-hover'] = 'var(--color-primary-dark-1)';
|
||||
vars['--color-primary-active'] = 'var(--color-primary-dark-2)';
|
||||
|
||||
// ─── Section 3: Secondary color system ───────────────────────────────────────
|
||||
vars['--color-secondary'] = secondary;
|
||||
|
||||
SECONDARY_DARK_DELTAS.forEach((d, i) => {
|
||||
vars[`--color-secondary-dark-${i + 1}`] = scaleLightness(secondary, sign * d);
|
||||
});
|
||||
SECONDARY_LIGHT_DELTAS.forEach((d, i) => {
|
||||
vars[`--color-secondary-light-${i + 1}`] = scaleLightness(secondary, -sign * d);
|
||||
});
|
||||
|
||||
ALPHA_STEPS.forEach(([pct, alpha]) => {
|
||||
vars[`--color-secondary-alpha-${pct}`] = withAlpha(secondary, alpha);
|
||||
});
|
||||
|
||||
vars['--color-secondary-button'] = 'var(--color-secondary-dark-4)';
|
||||
vars['--color-secondary-hover'] = isDark ? 'var(--color-secondary-dark-3)' : 'var(--color-secondary-dark-5)';
|
||||
vars['--color-secondary-active'] = isDark ? 'var(--color-secondary-dark-2)' : 'var(--color-secondary-dark-6)';
|
||||
|
||||
// ─── Section 4: Console colors ────────────────────────────────────────────────
|
||||
vars['--color-console-fg'] = fg;
|
||||
vars['--color-console-fg-subtle'] = get(p, 'lineNumberColor');
|
||||
vars['--color-console-bg'] = contrastColor;
|
||||
vars['--color-console-border'] = secondary;
|
||||
vars['--color-console-hover-bg'] = scaleLightness(contrastColor, isDark ? 0.05 : -0.05);
|
||||
vars['--color-console-active-bg'] = scaleLightness(contrastColor, isDark ? 0.10 : -0.10);
|
||||
vars['--color-console-menu-bg'] = mix(contrastColor, body, 0.5);
|
||||
vars['--color-console-menu-border'] = secondary;
|
||||
vars['--color-console-link'] = get(p, 'lineNumberColor');
|
||||
|
||||
// ─── Section 5: Named semantic colors (12 hues) ───────────────────────────────
|
||||
vars['--color-red'] = named.red;
|
||||
vars['--color-red-light'] = named.redLight;
|
||||
vars['--color-red-dark-1'] = named.redDark1;
|
||||
vars['--color-red-dark-2'] = named.redDark2;
|
||||
|
||||
vars['--color-orange'] = named.orange;
|
||||
vars['--color-orange-light'] = named.orangeLight;
|
||||
vars['--color-orange-dark-1'] = named.orangeDark1;
|
||||
vars['--color-orange-dark-2'] = named.orangeDark2;
|
||||
|
||||
vars['--color-yellow'] = named.yellow;
|
||||
vars['--color-yellow-light'] = named.yellowLight;
|
||||
vars['--color-yellow-dark-1'] = named.yellowDark1;
|
||||
vars['--color-yellow-dark-2'] = named.yellowDark2;
|
||||
|
||||
vars['--color-olive'] = named.olive;
|
||||
vars['--color-olive-light'] = named.oliveLight;
|
||||
vars['--color-olive-dark-1'] = named.oliveDark1;
|
||||
vars['--color-olive-dark-2'] = named.oliveDark2;
|
||||
|
||||
vars['--color-green'] = named.green;
|
||||
vars['--color-green-light'] = named.greenLight;
|
||||
vars['--color-green-dark-1'] = named.greenDark1;
|
||||
vars['--color-green-dark-2'] = named.greenDark2;
|
||||
|
||||
vars['--color-teal'] = named.teal;
|
||||
vars['--color-teal-light'] = named.tealLight;
|
||||
vars['--color-teal-dark-1'] = named.tealDark1;
|
||||
vars['--color-teal-dark-2'] = named.tealDark2;
|
||||
|
||||
vars['--color-blue'] = named.blue;
|
||||
vars['--color-blue-light'] = named.blueLight;
|
||||
vars['--color-blue-dark-1'] = named.blueDark1;
|
||||
vars['--color-blue-dark-2'] = named.blueDark2;
|
||||
|
||||
vars['--color-violet'] = named.violet;
|
||||
vars['--color-violet-light'] = named.violetLight;
|
||||
vars['--color-violet-dark-1'] = named.violetDark1;
|
||||
vars['--color-violet-dark-2'] = named.violetDark2;
|
||||
|
||||
vars['--color-purple'] = named.purple;
|
||||
vars['--color-purple-light'] = named.purpleLight;
|
||||
vars['--color-purple-dark-1'] = named.purpleDark1;
|
||||
vars['--color-purple-dark-2'] = named.purpleDark2;
|
||||
|
||||
vars['--color-pink'] = named.pink;
|
||||
vars['--color-pink-light'] = named.pinkLight;
|
||||
vars['--color-pink-dark-1'] = named.pinkDark1;
|
||||
vars['--color-pink-dark-2'] = named.pinkDark2;
|
||||
|
||||
vars['--color-brown'] = named.brown;
|
||||
vars['--color-brown-light'] = named.brownLight;
|
||||
vars['--color-brown-dark-1'] = named.brownDark1;
|
||||
vars['--color-brown-dark-2'] = named.brownDark2;
|
||||
|
||||
vars['--color-black'] = named.black;
|
||||
vars['--color-black-light'] = named.blackLight;
|
||||
vars['--color-black-dark-1'] = named.blackDark1;
|
||||
vars['--color-black-dark-2'] = named.blackDark2;
|
||||
|
||||
// ─── Section 6: ANSI colors ───────────────────────────────────────────────────
|
||||
const ansiBlack = contrastColor;
|
||||
const ansiRed = get(p, 'terminal.ansiRed', named.red);
|
||||
const ansiGreen = get(p, 'terminal.ansiGreen', named.green);
|
||||
const ansiYellow = get(p, 'terminal.ansiYellow', named.yellow);
|
||||
const ansiBlue = get(p, 'terminal.ansiBlue', named.blue);
|
||||
const ansiMagenta = get(p, 'terminal.ansiMagenta', get(p, 'accentColor'));
|
||||
const ansiCyan = get(p, 'terminal.ansiCyan', named.teal);
|
||||
|
||||
vars['--color-ansi-black'] = ansiBlack;
|
||||
vars['--color-ansi-red'] = ansiRed;
|
||||
vars['--color-ansi-green'] = ansiGreen;
|
||||
vars['--color-ansi-yellow'] = ansiYellow;
|
||||
vars['--color-ansi-blue'] = ansiBlue;
|
||||
vars['--color-ansi-magenta'] = ansiMagenta;
|
||||
vars['--color-ansi-cyan'] = ansiCyan;
|
||||
vars['--color-ansi-white'] = 'var(--color-console-fg-subtle)';
|
||||
vars['--color-ansi-bright-black'] = scaleLightness(ansiBlack, 0.12);
|
||||
vars['--color-ansi-bright-red'] = scaleLightness(ansiRed, 0.08);
|
||||
vars['--color-ansi-bright-green'] = scaleLightness(ansiGreen, 0.08);
|
||||
vars['--color-ansi-bright-yellow'] = scaleLightness(ansiYellow, 0.08);
|
||||
vars['--color-ansi-bright-blue'] = scaleLightness(ansiBlue, 0.08);
|
||||
vars['--color-ansi-bright-magenta'] = scaleLightness(ansiMagenta, 0.08);
|
||||
vars['--color-ansi-bright-cyan'] = scaleLightness(ansiCyan, 0.08);
|
||||
vars['--color-ansi-bright-white'] = 'var(--color-console-fg)';
|
||||
|
||||
// ─── Section 7: Chart/series colors (fixed) ───────────────────────────────────
|
||||
Object.assign(vars, SERIES_16);
|
||||
|
||||
// ─── Section 8: Utility colors ────────────────────────────────────────────────
|
||||
const grey = desaturate(secondary);
|
||||
vars['--color-grey'] = grey;
|
||||
vars['--color-grey-light'] = scaleLightness(grey, 0.08);
|
||||
vars['--color-gold'] = isDark ? '#B1983B' : '#A1882B';
|
||||
vars['--color-white'] = '#FFFFFF';
|
||||
|
||||
// ─── Section 9: Diff colors ───────────────────────────────────────────────────
|
||||
const diffInserted = get(p, 'diff.inserted', '#1B3B1C');
|
||||
const diffDeleted = get(p, 'diff.deleted', '#565656');
|
||||
const diffModified = get(p, 'diff.modified', '#203952');
|
||||
|
||||
// Mix diff color at ~80% alpha over body background
|
||||
const diffAddedRowBg = mix(body, diffInserted, 0.8);
|
||||
const diffRemovedRowBg = mix(body, diffDeleted, 0.8);
|
||||
const diffMovedRowBg = mix(body, diffModified, 0.8);
|
||||
|
||||
vars['--color-diff-added-fg'] = ansiGreen;
|
||||
vars['--color-diff-added-linenum-bg'] = scaleLightness(diffAddedRowBg, -0.05);
|
||||
vars['--color-diff-added-row-bg'] = diffAddedRowBg;
|
||||
vars['--color-diff-added-row-border'] = scaleLightness(diffAddedRowBg, -0.10);
|
||||
vars['--color-diff-added-word-bg'] = scaleLightness(diffAddedRowBg, -0.15);
|
||||
|
||||
vars['--color-diff-removed-fg'] = named.red;
|
||||
vars['--color-diff-removed-linenum-bg'] = scaleLightness(diffRemovedRowBg, -0.05);
|
||||
vars['--color-diff-removed-row-bg'] = diffRemovedRowBg;
|
||||
vars['--color-diff-removed-row-border'] = scaleLightness(diffRemovedRowBg, -0.10);
|
||||
vars['--color-diff-removed-word-bg'] = scaleLightness(diffRemovedRowBg, -0.15);
|
||||
|
||||
vars['--color-diff-moved-row-bg'] = diffMovedRowBg;
|
||||
vars['--color-diff-moved-row-border'] = scaleLightness(diffMovedRowBg, 0.15);
|
||||
vars['--color-diff-inactive'] = scaleLightness(body, isDark ? -0.05 : 0.05);
|
||||
|
||||
// ─── Section 10: Status/alert colors ─────────────────────────────────────────
|
||||
const errorText = get(p, 'errorColor', get(p, 'stopColor', '#FF5555'));
|
||||
vars['--color-error-border'] = withAlpha(errorText, 0x66);
|
||||
vars['--color-error-bg'] = withAlpha(errorText, 0x1a);
|
||||
vars['--color-error-bg-active'] = withAlpha(errorText, 0x33);
|
||||
vars['--color-error-bg-hover'] = withAlpha(errorText, 0x26);
|
||||
vars['--color-error-text'] = errorText;
|
||||
|
||||
const successText = ansiGreen;
|
||||
vars['--color-success-border'] = withAlpha(successText, 0x66);
|
||||
vars['--color-success-bg'] = withAlpha(successText, 0x1a);
|
||||
vars['--color-success-text'] = successText;
|
||||
|
||||
const warningText = ansiYellow;
|
||||
vars['--color-warning-border'] = withAlpha(warningText, 0x66);
|
||||
vars['--color-warning-bg'] = withAlpha(warningText, 0x1a);
|
||||
vars['--color-warning-text'] = warningText;
|
||||
|
||||
const infoText = ansiBlue;
|
||||
vars['--color-info-border'] = withAlpha(infoText, 0x66);
|
||||
vars['--color-info-bg'] = withAlpha(infoText, 0x1a);
|
||||
vars['--color-info-text'] = infoText;
|
||||
|
||||
const priorityText = named.violet;
|
||||
vars['--color-priority-border'] = withAlpha(priorityText, 0x66);
|
||||
vars['--color-priority-bg'] = withAlpha(priorityText, 0x1a);
|
||||
vars['--color-priority-text'] = priorityText;
|
||||
|
||||
// ─── Section 11: Badge colors ─────────────────────────────────────────────────
|
||||
vars['--color-red-badge'] = named.red;
|
||||
vars['--color-red-badge-bg'] = withAlpha(named.red, 0x1a);
|
||||
vars['--color-red-badge-hover-bg'] = withAlpha(named.red, 0x4d);
|
||||
|
||||
vars['--color-green-badge'] = named.green;
|
||||
vars['--color-green-badge-bg'] = withAlpha(named.green, 0x1a);
|
||||
vars['--color-green-badge-hover-bg'] = withAlpha(named.green, 0x4d);
|
||||
|
||||
vars['--color-yellow-badge'] = named.yellow;
|
||||
vars['--color-yellow-badge-bg'] = withAlpha(named.yellow, 0x1a);
|
||||
vars['--color-yellow-badge-hover-bg'] = withAlpha(named.yellow, 0x4d);
|
||||
|
||||
vars['--color-orange-badge'] = named.orange;
|
||||
vars['--color-orange-badge-bg'] = withAlpha(named.orange, 0x1a);
|
||||
vars['--color-orange-badge-hover-bg'] = withAlpha(named.orange, 0x4d);
|
||||
|
||||
// ─── Section 12: Brand colors (fixed) ─────────────────────────────────────────
|
||||
vars['--color-git'] = '#F05133';
|
||||
vars['--color-logo'] = '#609926';
|
||||
|
||||
// ─── Section 13: Semantic / target-based colors ───────────────────────────────
|
||||
const boxBody = contrastColor;
|
||||
const boxBodyHighlight = isDark
|
||||
? scaleLightness(boxBody, 0.04)
|
||||
: withAlpha(primary, 0x10);
|
||||
|
||||
vars['--color-body'] = body;
|
||||
vars['--color-box-header'] = isDark ? contrastColor : headerColor;
|
||||
vars['--color-box-body'] = contrastColor;
|
||||
vars['--color-box-body-highlight'] = boxBodyHighlight;
|
||||
vars['--color-text-dark'] = isDark ? get(p, 'selectionForeground') : fg;
|
||||
vars['--color-text'] = fg;
|
||||
vars['--color-text-light'] = scaleLightness(fg, isDark ? -0.08 : 0.08);
|
||||
vars['--color-text-light-1'] = get(p, 'buttonFont');
|
||||
vars['--color-text-light-2'] = get(p, 'lineNumberColor');
|
||||
vars['--color-text-light-3'] = get(p, 'disabledColor');
|
||||
vars['--color-footer'] = 'var(--color-nav-bg)';
|
||||
vars['--color-timeline'] = secondary;
|
||||
vars['--color-input-text'] = 'var(--color-text-dark)';
|
||||
vars['--color-input-background'] = get(p, 'textEditorBackground');
|
||||
vars['--color-input-toggle-background'] = secondary;
|
||||
vars['--color-input-border'] = 'var(--color-secondary-dark-1)';
|
||||
// Subtle overlay tint — fixed neutral values that work on any background
|
||||
vars['--color-light'] = isDark ? '#F3F3F406' : '#00001706';
|
||||
vars['--color-light-border'] = isDark ? '#F3F3F428' : '#0000171D';
|
||||
vars['--color-hover'] = isDark ? withAlpha(fg, 0x19) : '#00001708';
|
||||
vars['--color-hover-opaque'] = scaleLightness(body, isDark ? 0.06 : -0.05);
|
||||
vars['--color-active'] = isDark ? withAlpha(fg, 0x24) : '#00001714';
|
||||
vars['--color-menu'] = inactiveBackground;
|
||||
vars['--color-card'] = inactiveBackground;
|
||||
vars['--color-markup-table-row'] = isDark ? withAlpha(fg, 0x0f) : withAlpha(primary, 0x0a);
|
||||
vars['--color-markup-code-block'] = isDark ? withAlpha(fg, 0x12) : withAlpha(primary, 0x10);
|
||||
vars['--color-markup-code-inline'] = isDark ? withAlpha(fg, 0x28) : withAlpha(primary, 0x12);
|
||||
vars['--color-button'] = get(p, 'buttonColor', contrastColor);
|
||||
vars['--color-code-bg'] = get(p, 'codeBlock');
|
||||
vars['--color-shadow'] = isDark ? withAlpha(body, 0x58) : '#00001726';
|
||||
vars['--color-shadow-opaque'] = isDark ? contrastColor : scaleLightness(body, -0.12);
|
||||
vars['--color-secondary-bg'] = get(p, 'secondaryBackground');
|
||||
vars['--color-expand-button'] = get(p, 'highlightColor');
|
||||
vars['--color-placeholder-text'] = 'var(--color-text-light-3)';
|
||||
vars['--color-editor-line-highlight'] = isDark ? 'var(--color-secondary-alpha-40)' : 'var(--color-secondary-alpha-30)';
|
||||
vars['--color-editor-selection'] = isDark ? 'var(--color-primary-alpha-50)' : 'var(--color-primary-alpha-30)';
|
||||
vars['--color-project-column-bg'] = isDark ? 'var(--color-secondary-light-2)' : 'var(--color-secondary-light-4)';
|
||||
vars['--color-caret'] = isDark ? 'var(--color-text)' : 'var(--color-text-dark)';
|
||||
vars['--color-reaction-bg'] = isDark ? withAlpha(fg, 0x12) : '#0000170A';
|
||||
vars['--color-reaction-hover-bg'] = isDark ? 'var(--color-primary-light-4)' : 'var(--color-primary-light-5)';
|
||||
vars['--color-reaction-active-bg'] = isDark ? 'var(--color-primary-light-5)' : 'var(--color-primary-light-6)';
|
||||
vars['--color-tooltip-text'] = isDark ? '#FAFAFA' : '#FBFDFF';
|
||||
vars['--color-tooltip-bg'] = isDark ? '#0B0B0CF0' : '#000017F0';
|
||||
vars['--color-nav-bg'] = headerColor;
|
||||
vars['--color-nav-hover-bg'] = 'var(--color-secondary-light-1)';
|
||||
vars['--color-nav-text'] = 'var(--color-text)';
|
||||
vars['--color-secondary-nav-bg'] = scaleLightness(headerColor, isDark ? 0.02 : -0.02);
|
||||
vars['--color-label-text'] = 'var(--color-text)';
|
||||
vars['--color-label-bg'] = withAlpha(secondary, 0x4b);
|
||||
vars['--color-label-hover-bg'] = withAlpha(secondary, 0xa0);
|
||||
vars['--color-label-active-bg'] = withAlpha(secondary, 0xff);
|
||||
vars['--color-accent'] = 'var(--color-primary-light-1)';
|
||||
vars['--color-small-accent'] = isDark ? 'var(--color-primary-light-5)' : 'var(--color-primary-light-6)';
|
||||
vars['--color-highlight-fg'] = get(p, 'searchForeground', get(p, 'selectionForeground'));
|
||||
vars['--color-highlight-bg'] = get(p, 'searchBackground', withAlpha(primary, 0x66));
|
||||
vars['--color-overlay-backdrop'] = '#080808C0';
|
||||
vars['--color-danger'] = 'var(--color-red)';
|
||||
vars['--color-transparency-grid-light'] = isDark ? '#2A2A2A' : '#FAFAFA';
|
||||
vars['--color-transparency-grid-dark'] = isDark ? '#1A1A1A' : '#E2E2E2';
|
||||
vars['--color-workflow-edge-hover'] = scaleLightness(secondary, isDark ? 0.2 : -0.2);
|
||||
|
||||
// ─── Section 14: Syntax highlighting ──────────────────────────────────────────
|
||||
const syntaxComment = get(p, 'comments');
|
||||
const diffRemovedSolid = normalizeHex(diffRemovedRowBg);
|
||||
const diffAddedSolid = normalizeHex(diffAddedRowBg);
|
||||
|
||||
vars['--color-syntax-keyword'] = get(p, 'keywordColor');
|
||||
vars['--color-syntax-bool'] = ansiCyan;
|
||||
vars['--color-syntax-control'] = ansiYellow;
|
||||
vars['--color-syntax-name'] = ansiYellow;
|
||||
vars['--color-syntax-type'] = get(p, 'classNameColor');
|
||||
vars['--color-syntax-number'] = get(p, 'constantColor', get(p, 'keyColor'));
|
||||
vars['--color-syntax-operator'] = get(p, 'keywordColor');
|
||||
vars['--color-syntax-regexp'] = ansiMagenta;
|
||||
vars['--color-syntax-string'] = get(p, 'stringColor');
|
||||
vars['--color-syntax-comment'] = syntaxComment;
|
||||
vars['--color-syntax-invalid'] = errorText;
|
||||
vars['--color-syntax-link'] = 'var(--color-primary)';
|
||||
vars['--color-syntax-tag'] = get(p, 'htmlTagColor', get(p, 'keywordColor'));
|
||||
vars['--color-syntax-attribute'] = ansiMagenta;
|
||||
vars['--color-syntax-property'] = get(p, 'keyColor');
|
||||
vars['--color-syntax-variable'] = ansiYellow;
|
||||
vars['--color-syntax-string-special'] = ansiYellow;
|
||||
vars['--color-syntax-escape'] = ansiYellow;
|
||||
vars['--color-syntax-entity'] = ansiMagenta;
|
||||
vars['--color-syntax-preproc'] = ansiGreen;
|
||||
vars['--color-syntax-preproc-file'] = get(p, 'constantColor', get(p, 'keyColor'));
|
||||
vars['--color-syntax-decorator'] = ansiGreen;
|
||||
vars['--color-syntax-namespace'] = fg;
|
||||
vars['--color-syntax-name-pseudo'] = ansiMagenta;
|
||||
vars['--color-syntax-comment-special'] = scaleLightness(syntaxComment, 0.08);
|
||||
vars['--color-syntax-text'] = isDark ? fg : 'inherit';
|
||||
vars['--color-syntax-text-alt'] = scaleLightness(fg, -0.10);
|
||||
vars['--color-syntax-punctuation'] = isDark ? fg : 'inherit';
|
||||
vars['--color-syntax-whitespace'] = get(p, 'disabledColor');
|
||||
vars['--color-syntax-diff-fg'] = isDark ? '#FFFFFF' : '#000000';
|
||||
vars['--color-syntax-deleted-bg'] = diffRemovedSolid;
|
||||
vars['--color-syntax-inserted-bg'] = diffAddedSolid;
|
||||
vars['--color-syntax-emph'] = ansiYellow;
|
||||
vars['--color-syntax-strong'] = ansiYellow;
|
||||
vars['--color-syntax-heading'] = ansiYellow;
|
||||
vars['--color-syntax-subheading'] = get(p, 'stringColor');
|
||||
vars['--color-syntax-output'] = syntaxComment;
|
||||
vars['--color-syntax-prompt'] = ansiYellow;
|
||||
vars['--color-syntax-traceback'] = errorText;
|
||||
vars['--color-syntax-matching-bracket-bg'] = withAlpha(named.teal, 0x48);
|
||||
vars['--color-syntax-nonmatching-bracket-bg'] = withAlpha(named.red, 0x48);
|
||||
|
||||
return vars;
|
||||
}
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
export interface DokiDefinition {
|
||||
id: string;
|
||||
name: string;
|
||||
displayName: string;
|
||||
dark: boolean;
|
||||
author: string;
|
||||
group: string;
|
||||
stickers?: unknown;
|
||||
overrides?: {
|
||||
editorScheme?: {
|
||||
colors?: Partial<DokiColors>;
|
||||
};
|
||||
};
|
||||
colors: DokiColors;
|
||||
characterId: string;
|
||||
}
|
||||
|
||||
export interface DokiColors {
|
||||
// Backgrounds
|
||||
baseBackground: string;
|
||||
textEditorBackground: string;
|
||||
secondaryBackground: string;
|
||||
headerColor: string;
|
||||
contrastColor: string;
|
||||
codeBlock: string;
|
||||
foldedTextBackground?: string;
|
||||
inactiveBackground?: string;
|
||||
inactiveBackgroundDarker?: string;
|
||||
lightEditorColor?: string;
|
||||
nonProjectFileScopeColor?: string;
|
||||
testScopeColor?: string;
|
||||
breakpointColor?: string;
|
||||
breakpointActiveColor?: string;
|
||||
highlightColor: string;
|
||||
caretRow: string;
|
||||
|
||||
// Foreground / text
|
||||
foregroundColor: string;
|
||||
buttonFont: string;
|
||||
selectionForeground: string;
|
||||
lineNumberColor: string;
|
||||
disabledColor: string;
|
||||
unusedColor?: string;
|
||||
infoForeground?: string;
|
||||
|
||||
// Selection / states
|
||||
selectionBackground: string;
|
||||
selectionBackgroundTransparent?: string;
|
||||
selectionInactive?: string;
|
||||
identifierHighlight?: string;
|
||||
searchBackground?: string;
|
||||
searchForeground?: string;
|
||||
popupMask?: string;
|
||||
|
||||
// Accent / brand
|
||||
accentColor: string;
|
||||
accentColorTransparent?: string;
|
||||
accentColorLessTransparent?: string;
|
||||
accentColorMoreTransparent?: string;
|
||||
accentContrastColor?: string;
|
||||
accentColorOverride?: string;
|
||||
startColor?: string;
|
||||
stopColor?: string;
|
||||
borderColor: string;
|
||||
buttonColor?: string;
|
||||
|
||||
// Syntax
|
||||
comments: string;
|
||||
stringColor: string;
|
||||
keywordColor: string;
|
||||
classNameColor: string;
|
||||
htmlTagColor?: string;
|
||||
keyColor: string;
|
||||
constantColor?: string;
|
||||
errorColor?: string;
|
||||
|
||||
// Diff
|
||||
"diff.inserted"?: string;
|
||||
"diff.deleted"?: string;
|
||||
"diff.modified"?: string;
|
||||
"diff.conflict"?: string;
|
||||
|
||||
// Terminal / ANSI
|
||||
"terminal.ansiRed"?: string;
|
||||
"terminal.ansiGreen"?: string;
|
||||
"terminal.ansiYellow"?: string;
|
||||
"terminal.ansiBlue"?: string;
|
||||
"terminal.ansiMagenta"?: string;
|
||||
"terminal.ansiCyan"?: string;
|
||||
|
||||
// Index signature for icon colors and other optional keys
|
||||
[key: string]: string | undefined;
|
||||
}
|
||||
|
||||
export interface ColorTemplate {
|
||||
type: "COLOR";
|
||||
name: string;
|
||||
extends?: string;
|
||||
colors: Partial<DokiColors>;
|
||||
}
|
||||
|
||||
export interface ThemeConfig {
|
||||
internalName: string;
|
||||
displayName: string;
|
||||
colorScheme: "dark" | "light";
|
||||
palette: DokiColors;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"module": "NodeNext",
|
||||
"moduleResolution": "NodeNext",
|
||||
"strict": true,
|
||||
"outDir": "./dist"
|
||||
},
|
||||
"include": ["generate.ts", "src/**/*.ts"]
|
||||
}
|
||||
Reference in New Issue
Block a user