feat(canvas): revive Themelings color tracking (raw/semantic) for canvas branch
This commit is contained in:
parent
3fa53ae111
commit
7bb5e3364c
6 changed files with 136 additions and 38 deletions
34
bun.lock
34
bun.lock
|
|
@ -5,14 +5,12 @@
|
|||
"": {
|
||||
"name": "themelings",
|
||||
"dependencies": {
|
||||
"@napi-rs/canvas": "^0.1.97",
|
||||
"color": "^5.0.3",
|
||||
"color": "^5.0.2",
|
||||
"discord-api-types": "^0.38.46",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "^2.4.11",
|
||||
"@types/bun": "^1.3.12",
|
||||
"@types/color": "^4.2.1",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^6.0.2",
|
||||
|
|
@ -41,38 +39,8 @@
|
|||
|
||||
"@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.4.11", "", { "os": "win32", "cpu": "x64" }, "sha512-A8D3JM/00C2KQgUV3oj8Ba15EHEYwebAGCy5Sf9GAjr5Y3+kJIYOiESoqRDeuRZueuMdCsbLZIUqmPhpYXJE9A=="],
|
||||
|
||||
"@napi-rs/canvas": ["@napi-rs/canvas@0.1.97", "", { "optionalDependencies": { "@napi-rs/canvas-android-arm64": "0.1.97", "@napi-rs/canvas-darwin-arm64": "0.1.97", "@napi-rs/canvas-darwin-x64": "0.1.97", "@napi-rs/canvas-linux-arm-gnueabihf": "0.1.97", "@napi-rs/canvas-linux-arm64-gnu": "0.1.97", "@napi-rs/canvas-linux-arm64-musl": "0.1.97", "@napi-rs/canvas-linux-riscv64-gnu": "0.1.97", "@napi-rs/canvas-linux-x64-gnu": "0.1.97", "@napi-rs/canvas-linux-x64-musl": "0.1.97", "@napi-rs/canvas-win32-arm64-msvc": "0.1.97", "@napi-rs/canvas-win32-x64-msvc": "0.1.97" } }, "sha512-8cFniXvrIEnVwuNSRCW9wirRZbHvrD3JVujdS2P5n5xiJZNZMOZcfOvJ1pb66c7jXMKHHglJEDVJGbm8XWFcXQ=="],
|
||||
|
||||
"@napi-rs/canvas-android-arm64": ["@napi-rs/canvas-android-arm64@0.1.97", "", { "os": "android", "cpu": "arm64" }, "sha512-V1c/WVw+NzH8vk7ZK/O8/nyBSCQimU8sfMsB/9qeSvdkGKNU7+mxy/bIF0gTgeBFmHpj30S4E9WHMSrxXGQuVQ=="],
|
||||
|
||||
"@napi-rs/canvas-darwin-arm64": ["@napi-rs/canvas-darwin-arm64@0.1.97", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ok+SCEF4YejcxuJ9Rm+WWunHHpf2HmiPxfz6z1a/NFQECGXtsY7A4B8XocK1LmT1D7P174MzwPF9Wy3AUAwEPw=="],
|
||||
|
||||
"@napi-rs/canvas-darwin-x64": ["@napi-rs/canvas-darwin-x64@0.1.97", "", { "os": "darwin", "cpu": "x64" }, "sha512-PUP6e6/UGlclUvAQNnuXCcnkpdUou6VYZfQOQxExLp86epOylmiwLkqXIvpFmjoTEDmPmXrI+coL/9EFU1gKPA=="],
|
||||
|
||||
"@napi-rs/canvas-linux-arm-gnueabihf": ["@napi-rs/canvas-linux-arm-gnueabihf@0.1.97", "", { "os": "linux", "cpu": "arm" }, "sha512-XyXH2L/cic8eTNtbrXCcvqHtMX/nEOxN18+7rMrAM2XtLYC/EB5s0wnO1FsLMWmK+04ZSLN9FBGipo7kpIkcOw=="],
|
||||
|
||||
"@napi-rs/canvas-linux-arm64-gnu": ["@napi-rs/canvas-linux-arm64-gnu@0.1.97", "", { "os": "linux", "cpu": "arm64" }, "sha512-Kuq/M3djq0K8ktgz6nPlK7Ne5d4uWeDxPpyKWOjWDK2RIOhHVtLtyLiJw2fuldw7Vn4mhw05EZXCEr4Q76rs9w=="],
|
||||
|
||||
"@napi-rs/canvas-linux-arm64-musl": ["@napi-rs/canvas-linux-arm64-musl@0.1.97", "", { "os": "linux", "cpu": "arm64" }, "sha512-kKmSkQVnWeqg7qdsiXvYxKhAFuHz3tkBjW/zyQv5YKUPhotpaVhpBGv5LqCngzyuRV85SXoe+OFj+Tv0a0QXkQ=="],
|
||||
|
||||
"@napi-rs/canvas-linux-riscv64-gnu": ["@napi-rs/canvas-linux-riscv64-gnu@0.1.97", "", { "os": "linux", "cpu": "none" }, "sha512-Jc7I3A51jnEOIAXeLsN/M/+Z28LUeakcsXs07FLq9prXc0eYOtVwsDEv913Gr+06IRo34gJJVgT0TXvmz+N2VA=="],
|
||||
|
||||
"@napi-rs/canvas-linux-x64-gnu": ["@napi-rs/canvas-linux-x64-gnu@0.1.97", "", { "os": "linux", "cpu": "x64" }, "sha512-iDUBe7AilfuBSRbSa8/IGX38Mf+iCSBqoVKLSQ5XaY2JLOaqz1TVyPFEyIck7wT6mRQhQt5sN6ogfjIDfi74tg=="],
|
||||
|
||||
"@napi-rs/canvas-linux-x64-musl": ["@napi-rs/canvas-linux-x64-musl@0.1.97", "", { "os": "linux", "cpu": "x64" }, "sha512-AKLFd/v0Z5fvgqBDqhvqtAdx+fHMJ5t9JcUNKq4FIZ5WH+iegGm8HPdj00NFlCSnm83Fp3Ln8I2f7uq1aIiWaA=="],
|
||||
|
||||
"@napi-rs/canvas-win32-arm64-msvc": ["@napi-rs/canvas-win32-arm64-msvc@0.1.97", "", { "os": "win32", "cpu": "arm64" }, "sha512-u883Yr6A6fO7Vpsy9YE4FVCIxzzo5sO+7pIUjjoDLjS3vQaNMkVzx5bdIpEL+ob+gU88WDK4VcxYMZ6nmnoX9A=="],
|
||||
|
||||
"@napi-rs/canvas-win32-x64-msvc": ["@napi-rs/canvas-win32-x64-msvc@0.1.97", "", { "os": "win32", "cpu": "x64" }, "sha512-sWtD2EE3fV0IzN+iiQUqr/Q1SwqWhs2O1FKItFlxtdDkikpEj5g7DKQpY3x55H/MAOnL8iomnlk3mcEeGiUMoQ=="],
|
||||
|
||||
"@types/bun": ["@types/bun@1.3.12", "", { "dependencies": { "bun-types": "1.3.12" } }, "sha512-DBv81elK+/VSwXHDlnH3Qduw+KxkTIWi7TXkAeh24zpi5l0B2kUg9Ga3tb4nJaPcOFswflgi/yAvMVBPrxMB+A=="],
|
||||
|
||||
"@types/color": ["@types/color@4.2.1", "", { "dependencies": { "@types/color-convert": "2" } }, "sha512-ResWeDLy1vozIMbD6JLRKuNBbIcIlBkjTIxVHHd5Cqtm77T+ahH3BWE/PWv1OhFd1HAwcn8no4ig2uTaRXpYQQ=="],
|
||||
|
||||
"@types/color-convert": ["@types/color-convert@2.0.3", "", { "dependencies": { "@types/color-name": "*" } }, "sha512-2Q6wzrNiuEvYxVQqhh7sXM2mhIhvZR/Paq4FdsQkOMgWsCIkKvSGj8Le1/XalulrmgOzPMqNa0ix+ePY4hTrfg=="],
|
||||
|
||||
"@types/color-name": ["@types/color-name@1.1.4", "", {}, "sha512-hulKeREDdLFesGQjl96+4aoJSHY5b2GRjagzzcqCfIrWhe5vkCqIvrLbqzBaI1q94Vg8DNJZZqTR5ocdWmWclg=="],
|
||||
|
||||
"@types/node": ["@types/node@20.12.14", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-scnD59RpYD91xngrQQLGkE+6UrHUPzeKZWhhjBSa3HSkwjbQc38+q3RoIVEwxQGRw3M+j5hpNAM+lgV3cVormg=="],
|
||||
|
||||
"bun-types": ["bun-types@1.3.12", "", { "dependencies": { "@types/node": "*" } }, "sha512-HqOLj5PoFajAQciOMRiIZGNoKxDJSr6qigAttOX40vJuSp6DN/CxWp9s3C1Xwm4oH7ybueITwiaOcWXoYVoRkA=="],
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
"format": "biome check --fix ."
|
||||
},
|
||||
"dependencies": {
|
||||
"color": "^5.0.2",
|
||||
"discord-api-types": "^0.38.46"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
|||
16
src/types.ts
16
src/types.ts
|
|
@ -16,6 +16,22 @@ export type CodeDiff =
|
|||
size: number;
|
||||
};
|
||||
|
||||
export type RawColors = Record<string, string>;
|
||||
|
||||
export type Icons = Record<
|
||||
string,
|
||||
{
|
||||
file: string;
|
||||
hash: string;
|
||||
scales: number[];
|
||||
width: number | null;
|
||||
height: number | null;
|
||||
}
|
||||
>;
|
||||
|
||||
export type Semantic = Record<string, [string, { raw: string; opacity: number }]>;
|
||||
export type SemanticColors = Record<string, Semantic>;
|
||||
|
||||
export type Differs = {
|
||||
code: Map<string, CodeDiff> | undefined;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ export async function fetchGitChanged() {
|
|||
|
||||
const gitQueue: Promise<void>[] = [];
|
||||
|
||||
export async function commit(files: string[], message: string) {
|
||||
export async function commit(files: string[], message: string, cwd = "../data") {
|
||||
while (gitQueue[0]) {
|
||||
await gitQueue[0];
|
||||
}
|
||||
|
|
@ -27,15 +27,15 @@ export async function commit(files: string[], message: string) {
|
|||
if (process.env.NODE_ENV === "test" && !commitAnyway) {
|
||||
await fetchGitChanged();
|
||||
} else {
|
||||
await Bun.$`git restore --staged .`.cwd("../data").nothrow().quiet().then(handleShellErr);
|
||||
await fetchGitChanged();
|
||||
await Bun.$`git restore --staged .`.cwd(cwd).nothrow().quiet().then(handleShellErr);
|
||||
if (cwd === "../data") await fetchGitChanged();
|
||||
await Bun.$`git add ${{ raw: files.map((x) => Bun.$.escape(x)).join(" ") }}`
|
||||
.cwd("../data")
|
||||
.cwd(cwd)
|
||||
.nothrow()
|
||||
.quiet()
|
||||
.then(handleShellErr);
|
||||
await Bun.$`git commit -m ${message}`
|
||||
.cwd("../data")
|
||||
.cwd(cwd)
|
||||
.nothrow()
|
||||
.quiet()
|
||||
.then((e) => void e);
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import { commit } from "./git";
|
|||
import { log, makeProgress, wrapPromise } from "./progress";
|
||||
import { apksFolder, codePath, cuteVersion, isMock, isQuiet, oprevFiles, prevFiles, version } from "./shared";
|
||||
import codeTask from "./tasks/code";
|
||||
import colorsTask from "./tasks/colors";
|
||||
import decompile from "./tasks/decompile";
|
||||
import diffs from "./tasks/diffs";
|
||||
import { formatError, handleShellErr, join } from "./utils";
|
||||
|
|
@ -24,6 +25,9 @@ export async function runTasks() {
|
|||
code_getting: "Finding file imports",
|
||||
code_remaking: "Recreating code",
|
||||
code_pushing: "Committing source",
|
||||
colors: "Colors",
|
||||
colors_getting: "Extracting colors",
|
||||
colors_pushing: "Committing colors",
|
||||
diff: "Diffs",
|
||||
diff_code: "Diffing code",
|
||||
},
|
||||
|
|
@ -38,8 +42,12 @@ export async function runTasks() {
|
|||
progress.start("preinit_discard");
|
||||
await Bun.$`git reset --hard`.cwd("../data").nothrow().quiet().then(handleShellErr);
|
||||
await Bun.write("../data/version.txt", version);
|
||||
// canvas branch may not exist in test
|
||||
await Bun.$`git reset --hard`.cwd("../canvas").nothrow().quiet().then(() => {});
|
||||
await Bun.write("../canvas/version.txt", version).catch(() => {});
|
||||
|
||||
await Bun.$`git restore --staged .`.cwd("../data").nothrow().quiet().then(handleShellErr);
|
||||
await Bun.$`git restore --staged .`.cwd("../canvas").nothrow().quiet().then(() => {});
|
||||
|
||||
progress.update("preinit_discard", true);
|
||||
|
||||
|
|
@ -71,6 +79,15 @@ export async function runTasks() {
|
|||
if (progress.someFailed("code"))
|
||||
throw new Error(`Failed at parser tasks!\n${progress.prettyErrors("code")}`);
|
||||
|
||||
// colors (canvas) - uses same code array
|
||||
try {
|
||||
await wrapPromise(colorsTask(code), progress, "colors");
|
||||
} catch (e: any) {
|
||||
progress.update("colors", false, formatError(e));
|
||||
// don't fail whole run if colors fails, just log
|
||||
console.error("Colors task failed:", formatError(e));
|
||||
}
|
||||
|
||||
while (!progress.isFinished("decompile_gzip")) {
|
||||
await Bun.sleep(1000);
|
||||
}
|
||||
|
|
@ -101,4 +118,8 @@ export async function runTasks() {
|
|||
}
|
||||
|
||||
await commit(["version.txt"], `chore: bump app version to ${cuteVersion}`);
|
||||
// canvas version bump (if canvas checkout exists)
|
||||
try {
|
||||
await commit(["version.txt"], `chore: bump app version to ${cuteVersion}`, "../canvas");
|
||||
} catch {}
|
||||
}
|
||||
|
|
|
|||
92
src/update/tasks/colors.ts
Normal file
92
src/update/tasks/colors.ts
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
import { runInNewContext } from "node:vm";
|
||||
import Color, { type ColorInstance } from "color";
|
||||
import type { RawColors, SemanticColors } from "../../types";
|
||||
import { commit } from "../git";
|
||||
import { cuteVersion } from "../shared";
|
||||
import { sortObj } from "../utils";
|
||||
|
||||
function hex(color: ColorInstance) {
|
||||
return (color.alpha() === 1 ? color.hex() : color.hexa()).toLowerCase();
|
||||
}
|
||||
|
||||
// Support both old python decompiler (rX[rY] bug) and new Rust
|
||||
function fixHermesDecIndex(code: string) {
|
||||
return code.replace(/r(\d+)\[(\d+)\] =/g, "r$1[r$2] =");
|
||||
}
|
||||
|
||||
function findModuleSnippet(code: string[], importPath: string): string | null {
|
||||
const text = code.join("\n");
|
||||
const parts = text.split(/(?=\/\/ === Module \d+:)/);
|
||||
for (const part of parts) if (part.includes(importPath)) return part;
|
||||
return null;
|
||||
}
|
||||
|
||||
function evalRawModule(snippet: string): any {
|
||||
let code = snippet;
|
||||
code = code.replace(/^import set from "set".*$/m, "const __importSet = { fileFinishedImporting: () => {} };");
|
||||
code = code.replace(/^import.*$/gm, "");
|
||||
code = code.replace(/set\.fileFinishedImporting/g, "__importSet.fileFinishedImporting");
|
||||
code = code.replace(/export\s+const\s+_private/, "const _private");
|
||||
code = code + "\n; _private;";
|
||||
return runInNewContext(code, {});
|
||||
}
|
||||
|
||||
function evalSemanticModule(snippet: string): any {
|
||||
let code = snippet;
|
||||
code = code.replace(/^import set from "set".*$/m, "const __importSet = { fileFinishedImporting: () => {} };");
|
||||
code = code.replace(/^import items from "items".*$/m, "const items = { _private: { Themes: new Proxy({}, { get(_, k){ return k.toString().toLowerCase(); } }) } };");
|
||||
code = code.replace(/^import.*$/gm, "");
|
||||
code = code.replace(/set\.fileFinishedImporting/g, "__importSet.fileFinishedImporting");
|
||||
code = code.replace(/export\s+const\s+_private/, "const _private");
|
||||
code = code + "\n; _private;";
|
||||
code = fixHermesDecIndex(code);
|
||||
return runInNewContext(code, {});
|
||||
}
|
||||
|
||||
export function getInternalRawColors(code: string[]) {
|
||||
const snippet = findModuleSnippet(code, "raw-color-definitions.tsx");
|
||||
if (!snippet) throw new Error("Cannot find raw-color-definitions module");
|
||||
const mod = evalRawModule(snippet);
|
||||
const raw: RawColors = {};
|
||||
for (const [key, color] of Object.entries(mod.RawColors as Record<string, string>)) {
|
||||
raw[key] = hex(Color(color as string));
|
||||
}
|
||||
return raw;
|
||||
}
|
||||
|
||||
export function getInternalSemanticColors(code: string[], raw: Record<string, string>) {
|
||||
const snippet = findModuleSnippet(code, "native/generated-definitions");
|
||||
if (!snippet) throw new Error("Cannot find semantic definitions module");
|
||||
const mod = evalSemanticModule(snippet);
|
||||
const semantic: SemanticColors = {};
|
||||
for (const [key, colors] of Object.entries(mod.SemanticColors as Record<string, Record<string, { raw: string; opacity: number }>>)) {
|
||||
const color: SemanticColors[string] = {};
|
||||
for (const [theme, val] of Object.entries(colors)) {
|
||||
if (typeof val !== "object" || !("raw" in val)) continue;
|
||||
color[theme] = [hex(Color(raw[val.raw]).alpha(val.opacity)), { raw: val.raw, opacity: val.opacity }];
|
||||
}
|
||||
semantic[key] = color;
|
||||
}
|
||||
return semantic;
|
||||
}
|
||||
|
||||
export function convertSimpleSemantic(semantic: SemanticColors) {
|
||||
const simpleSemantic: Record<string, Record<string, string>> = {};
|
||||
for (const [key, colors] of Object.entries(semantic)) {
|
||||
const color: Record<string, string> = {};
|
||||
for (const [theme, [clr]] of Object.entries(colors)) color[theme] = clr;
|
||||
simpleSemantic[key] = color;
|
||||
}
|
||||
return simpleSemantic;
|
||||
}
|
||||
|
||||
export default async function colors(code: string[]) {
|
||||
const raw = getInternalRawColors(code);
|
||||
await Bun.write("../canvas/raw.json", JSON.stringify(sortObj(raw), null, 4));
|
||||
|
||||
const semantic = getInternalSemanticColors(code, raw);
|
||||
await Bun.write("../canvas/semantic.json", JSON.stringify(sortObj(semantic), null, 4));
|
||||
await Bun.write("../canvas/semantic_simple.json", JSON.stringify(sortObj(convertSimpleSemantic(semantic)), null, 4));
|
||||
|
||||
await commit(["raw.json", "semantic.json", "semantic_simple.json"], `chore: update colors for ${cuteVersion}`, "../canvas");
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue