143 lines
4.1 KiB
JavaScript
143 lines
4.1 KiB
JavaScript
/* 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
|
||
};
|