feat: ship minecraft theme updates and platform workflow improvements

这个提交包含在:
Codex CLI
2026-02-15 17:36:56 +08:00
父节点 cd7540ab9d
当前提交 37266bb846
修改 32 个文件,包含 5297 行新增119 行删除

查看文件

@@ -3,9 +3,9 @@ export const API_BASE =
(process.env.NODE_ENV === "development" ? "http://localhost:8080" : "/admin139");
function uiText(zhText: string, enText: string): string {
if (typeof window === "undefined") return enText;
if (typeof window === "undefined") return zhText;
const lang = window.localStorage.getItem("csp.ui.language");
return lang === "zh" ? zhText : enText;
return lang === "en" ? enText : zhText;
}
type ApiEnvelope<T> =

查看文件

@@ -186,21 +186,5 @@ export function analyzeCpp14Policy(code: string): Cpp14PolicyIssue[] {
);
}
if (!/\bfreopen\s*\(/.test(text) && /\bint\s+main\s*\(/.test(text)) {
const idx = text.search(/\bint\s+main\s*\(/);
const pos = offsetToPosition(lineStarts, Math.max(0, idx));
pushIssue(
issues,
"freopen-tip",
"hint",
"未检测到 freopen福建二轮常见文件读写要求",
"若考场题面要求 *.in/*.out,请按官方文件名补上 freopen。",
pos.line,
pos.column,
pos.line,
pos.column + 3
);
}
return issues;
}

查看文件

@@ -1,15 +1,20 @@
"use client";
import { useCallback } from "react";
import { useUiPreferences } from "@/components/ui-preference-provider";
export function readUiLanguage(): "en" | "zh" {
if (typeof window === "undefined") return "en";
return window.localStorage.getItem("csp.ui.language") === "zh" ? "zh" : "en";
if (typeof window === "undefined") return "zh";
return window.localStorage.getItem("csp.ui.language") === "en" ? "en" : "zh";
}
export function useI18nText() {
const { language } = useUiPreferences();
const isZh = language === "zh";
const tx = (zhText: string, enText: string) => (isZh ? zhText : enText);
const tx = useCallback(
(zhText: string, enText: string) => (isZh ? zhText : enText),
[isZh]
);
return { language, isZh, tx };
}

查看文件

@@ -0,0 +1,74 @@
function hashSeed(seed: string): number {
let h = 2166136261 >>> 0;
for (let i = 0; i < seed.length; i += 1) {
h ^= seed.charCodeAt(i);
h = Math.imul(h, 16777619) >>> 0;
}
return h >>> 0;
}
function mulberry32(seed: number): () => number {
let t = seed >>> 0;
return () => {
t += 0x6d2b79f5;
let x = Math.imul(t ^ (t >>> 15), t | 1);
x ^= x + Math.imul(x ^ (x >>> 7), x | 61);
return ((x ^ (x >>> 14)) >>> 0) / 4294967296;
};
}
const SKIN_PALETTE = ["#f6d3b3", "#e6be95", "#d7a980", "#f8dec6", "#c99264"];
const HAIR_PALETTE = ["#2f1b12", "#4a2f1d", "#6d4c41", "#212121", "#4e342e", "#1b5e20"];
const ACCENT_PALETTE = ["#7cb342", "#00b0d6", "#ffb300", "#8d6e63", "#ab47bc", "#ef5350"];
export function buildAvatarSeed(...parts: Array<string | number | null | undefined>): string {
const cleaned = parts
.map((part) => String(part ?? "").trim())
.filter((part) => part.length > 0);
return cleaned.length > 0 ? cleaned.join("|") : "guest|pixel";
}
export function generatePixelAvatarDataUri(seedInput: string, pixelSize = 10): string {
const seed = buildAvatarSeed(seedInput);
const rng = mulberry32(hashSeed(seed));
const skin = SKIN_PALETTE[Math.floor(rng() * SKIN_PALETTE.length)] ?? SKIN_PALETTE[0];
const hair = HAIR_PALETTE[Math.floor(rng() * HAIR_PALETTE.length)] ?? HAIR_PALETTE[0];
const accent = ACCENT_PALETTE[Math.floor(rng() * ACCENT_PALETTE.length)] ?? ACCENT_PALETTE[0];
const bg = rng() > 0.5 ? "#1f1f1f" : "#2b2b2b";
const border = rng() > 0.5 ? "#000000" : "#3e2723";
const width = 8 * pixelSize;
const height = 8 * pixelSize;
const rects: string[] = [
`<rect width="${width}" height="${height}" fill="${bg}"/>`,
`<rect x="0" y="0" width="${width}" height="${pixelSize}" fill="${accent}" opacity="0.4"/>`,
];
for (let y = 0; y < 8; y += 1) {
for (let x = 0; x < 4; x += 1) {
const fillFace = y >= 1 && y <= 6 && x >= 1;
const threshold = fillFace ? 0.2 : 0.52;
if (rng() < threshold) continue;
const color = y <= 1 ? hair : y >= 6 ? accent : skin;
const leftX = x * pixelSize;
const rightX = (7 - x) * pixelSize;
const yPos = y * pixelSize;
rects.push(`<rect x="${leftX}" y="${yPos}" width="${pixelSize}" height="${pixelSize}" fill="${color}"/>`);
if (rightX !== leftX) {
rects.push(`<rect x="${rightX}" y="${yPos}" width="${pixelSize}" height="${pixelSize}" fill="${color}"/>`);
}
}
}
const eyeY = 3 * pixelSize;
rects.push(`<rect x="${2 * pixelSize}" y="${eyeY}" width="${pixelSize}" height="${pixelSize}" fill="#111"/>`);
rects.push(`<rect x="${5 * pixelSize}" y="${eyeY}" width="${pixelSize}" height="${pixelSize}" fill="#111"/>`);
const mouthY = 5 * pixelSize;
rects.push(`<rect x="${3 * pixelSize}" y="${mouthY}" width="${2 * pixelSize}" height="${pixelSize}" fill="${hair}" opacity="0.8"/>`);
rects.push(`<rect x="${0}" y="${0}" width="${width}" height="${height}" fill="none" stroke="${border}" stroke-width="${Math.max(2, Math.floor(pixelSize / 4))}"/>`);
const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}" shape-rendering="crispEdges">${rects.join("")}</svg>`;
return `data:image/svg+xml;utf8,${encodeURIComponent(svg)}`;
}