first commit
This commit is contained in:
142
apps/miniprogram/utils/themedIcons.js
Normal file
142
apps/miniprogram/utils/themedIcons.js
Normal file
@@ -0,0 +1,142 @@
|
||||
/* global require, module */
|
||||
|
||||
const { toSvgDataUri } = require("./svgDataUri");
|
||||
const { ICON_SVG_SOURCES } = require("./iconSvgSources");
|
||||
|
||||
const DEFAULT_UI_BUTTON_COLOR = "#ADB9CD";
|
||||
const DEFAULT_UI_ACCENT_COLOR = "#5BD2FF";
|
||||
const HEX_COLOR_PATTERN = /^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/;
|
||||
const BUTTON_ICON_NAMES = Object.freeze([
|
||||
"about",
|
||||
"add",
|
||||
"ai",
|
||||
"back",
|
||||
"cancel",
|
||||
"clear",
|
||||
"codex",
|
||||
"config",
|
||||
"connect",
|
||||
"copy",
|
||||
"create",
|
||||
"delete",
|
||||
"log",
|
||||
"plugins",
|
||||
"right",
|
||||
"recordmanager",
|
||||
"save",
|
||||
"search",
|
||||
"selectall",
|
||||
"share",
|
||||
"shell",
|
||||
"serverlist"
|
||||
]);
|
||||
const buttonIconCache = Object.create(null);
|
||||
const activeButtonIconCache = Object.create(null);
|
||||
const accentButtonIconCache = Object.create(null);
|
||||
|
||||
function normalizeThemeColor(value, fallback) {
|
||||
const normalized = String(value || "")
|
||||
.trim()
|
||||
.toUpperCase();
|
||||
return HEX_COLOR_PATTERN.test(normalized) ? normalized : fallback;
|
||||
}
|
||||
|
||||
function toCamelIconName(name) {
|
||||
return String(name || "").replace(/-([a-zA-Z0-9])/g, (_, segment) => segment.toUpperCase());
|
||||
}
|
||||
|
||||
/**
|
||||
* 按钮图标在 `image` 节点里渲染时,真正可见的是 SVG 自身的 fill/stroke。
|
||||
* 这里只替换十六进制色值,保留 `fill="none"`、opacity、规则属性等结构不变。
|
||||
*/
|
||||
function tintSvgMarkup(svg, color) {
|
||||
return String(svg || "").replace(
|
||||
/\b(fill|stroke)="#[0-9a-fA-F]{3,8}"/g,
|
||||
(_match, attribute) => `${attribute}="${color}"`
|
||||
);
|
||||
}
|
||||
|
||||
function buildIconVariantMap(names, color, cache) {
|
||||
const cacheKey = normalizeThemeColor(color, DEFAULT_UI_BUTTON_COLOR);
|
||||
if (cache[cacheKey]) {
|
||||
return cache[cacheKey];
|
||||
}
|
||||
|
||||
const iconMap = {};
|
||||
names.forEach((name) => {
|
||||
const source = ICON_SVG_SOURCES[name];
|
||||
if (!source) return;
|
||||
const dataUri = toSvgDataUri(tintSvgMarkup(source, cacheKey));
|
||||
iconMap[name] = dataUri;
|
||||
const camelName = toCamelIconName(name);
|
||||
if (camelName !== name) {
|
||||
iconMap[camelName] = dataUri;
|
||||
}
|
||||
});
|
||||
cache[cacheKey] = iconMap;
|
||||
return iconMap;
|
||||
}
|
||||
|
||||
function buildButtonIconMap(settings) {
|
||||
const source = settings && typeof settings === "object" ? settings : {};
|
||||
return buildIconVariantMap(BUTTON_ICON_NAMES, source.uiBtnColor, buttonIconCache);
|
||||
}
|
||||
|
||||
/**
|
||||
* 连接态按钮需要落在高饱和背景上:
|
||||
* 1. 图标本体改用界面前景色,避免继续沿用按钮灰而发闷;
|
||||
* 2. 目前只给 connect / shell 等“连接态按钮”消费,但实现保持通用,后续可复用。
|
||||
*/
|
||||
function buildActiveButtonIconMap(settings) {
|
||||
const source = settings && typeof settings === "object" ? settings : {};
|
||||
return buildIconVariantMap(BUTTON_ICON_NAMES, source.uiTextColor, activeButtonIconCache);
|
||||
}
|
||||
|
||||
/**
|
||||
* 强调态按钮图标用于触摸反馈:
|
||||
* 1. 直接把 SVG 的 fill/stroke 换成强调色,避免只能靠背景变化;
|
||||
* 2. 优先使用 UI 强调色,没有时退回默认强调蓝。
|
||||
*/
|
||||
function buildAccentButtonIconMap(settings) {
|
||||
const source = settings && typeof settings === "object" ? settings : {};
|
||||
return buildIconVariantMap(
|
||||
BUTTON_ICON_NAMES,
|
||||
source.uiAccentColor || source.shellAccentColor || DEFAULT_UI_ACCENT_COLOR,
|
||||
accentButtonIconCache
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 页面通常会同时消费常态 / 激活态 / 强调态三组按钮图标:
|
||||
* 1. 统一在这里聚合,避免每个页面各自重复构造;
|
||||
* 2. 返回值字段名直接对齐页面 data,便于 `setData` 复用。
|
||||
*/
|
||||
function buildButtonIconThemeMaps(settings) {
|
||||
return {
|
||||
icons: buildButtonIconMap(settings),
|
||||
activeIcons: buildActiveButtonIconMap(settings),
|
||||
accentIcons: buildAccentButtonIconMap(settings)
|
||||
};
|
||||
}
|
||||
|
||||
function extractIconName(pathLike) {
|
||||
const raw = String(pathLike || "");
|
||||
const match = raw.match(/\/assets\/icons\/([a-zA-Z0-9-]+)\.svg/);
|
||||
return match ? match[1] : "";
|
||||
}
|
||||
|
||||
function resolveButtonIcon(pathLike, iconMap) {
|
||||
const name = extractIconName(pathLike);
|
||||
if (!name) return pathLike;
|
||||
if (!iconMap) return pathLike;
|
||||
return iconMap[name] || iconMap[toCamelIconName(name)] || pathLike;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
buildActiveButtonIconMap,
|
||||
buildAccentButtonIconMap,
|
||||
buildButtonIconMap,
|
||||
buildButtonIconThemeMaps,
|
||||
resolveButtonIcon,
|
||||
tintSvgMarkup
|
||||
};
|
||||
Reference in New Issue
Block a user