feat: 初始化画个标仓库 - AI驱动设计系统(BYOK模式) - 224源/371504图标+25海报源+130技能

This commit is contained in:
sgp
2026-10-03 21:44:18 +08:00
commit 9e222877ea
497 changed files with 43611 additions and 0 deletions
+137
View File
@@ -0,0 +1,137 @@
/**
* 画个标 · 内置素材库加载器(懒加载)
* 素材库由 scripts/build-icons.js 构建拆分为:
* - js/data/builtin-icons.js 轻量索引(sources 元数据 + AI iconIndex,无全量 svg)
* - js/data/sources/{id}.js 每源一个数据文件(HGB_Source_{id} = {icons:[...]})
* 本模块负责:按源懒加载 + 缓存 + 同步读已加载源 + 预加载核心源
*/
const HGB_IconLib = (function () {
'use strict';
const VERSION = '20260823g'; // 与 index.html 中 builtin-icons.js?v= 版本联动
const cache = {}; // sid -> {icons:[...]}
const pending = {}; // sid -> Promise
function fileURL(sid) {
return 'js/data/sources/' + sid + '.js?v=' + VERSION;
}
function loadScript(url) {
return new Promise((resolve, reject) => {
const s = document.createElement('script');
s.src = url;
s.onload = resolve;
s.onerror = function () { reject(new Error('素材源加载失败: ' + url)); };
document.head.appendChild(s);
});
}
/** 加载单个源(缓存 + 并发去重) */
function loadSource(sid) {
if (cache[sid]) return Promise.resolve(cache[sid]);
if (pending[sid]) return pending[sid];
pending[sid] = loadScript(fileURL(sid)).then(() => {
const data = window['HGB_Source_' + sid];
if (!data || !data.icons) throw new Error('素材源不存在: ' + sid);
cache[sid] = data;
delete pending[sid];
return data;
}).catch((e) => {
delete pending[sid];
throw e;
});
return pending[sid];
}
/** 并行加载多个源(全部成功才 resolve) */
function loadSources(ids) {
return Promise.all((ids || []).map(loadSource));
}
/** 并行加载多个源(容忍单个失败,返回已成功加载的) */
function preload(ids) {
return Promise.allSettled((ids || []).map(loadSource)).then((rs) =>
rs.filter(r => r.status === 'fulfilled').map(r => r.value));
}
/** 加载全部源(搜索全部/导出等场景) */
function loadAll() {
const ids = Object.keys((typeof HGB_BuiltinIcons !== 'undefined' ? HGB_BuiltinIcons.sources : {}) || {});
return preload(ids);
}
/** 同步读取已加载源(未加载返回 null) */
function getSource(sid) {
return cache[sid] || null;
}
/** 是否已加载 */
function loaded(sid) {
return !!cache[sid];
}
/** 已加载的全部源 id */
function loadedIds() {
return Object.keys(cache);
}
/** 从已加载源按 名称/id 找图标(返回 {id,name,svg,...} 或 null) */
function findIcon(ref) {
if (!ref || typeof ref !== 'string') return null;
const name = ref.trim();
const parts = name.split(':');
if (parts.length === 2) {
const src = cache[parts[0]];
const hit = src && (src.icons || []).find(i => i.name === parts[1] || i.id === name);
if (hit) return hit;
}
const lower = name.toLowerCase();
for (const sid of Object.keys(cache)) {
const hit = (cache[sid].icons || []).find(i =>
i.id === name || i.name === name ||
String(i.name).toLowerCase() === lower ||
String(i.name).toLowerCase().includes(lower));
if (hit) return hit;
}
return null;
}
/** 搜索已加载源(词根包含匹配),返回完整图标对象 */
function searchLoaded(keyword, limit) {
const kw = String(keyword || '').toLowerCase();
const out = [];
for (const sid of Object.keys(cache)) {
for (const ic of cache[sid].icons || []) {
if (ic.name && ic.name.toLowerCase().includes(kw)) {
out.push({ id: sid + ':' + ic.name, name: ic.name, svg: ic.svg, source: sid, type: ic.type });
if (out.length >= (limit || 60)) return out;
}
}
}
return out;
}
/** 搜索索引(全量 id/name,无 svg,用于技能/白名单展示) */
function searchIndex(keyword, limit) {
const kw = String(keyword || '').toLowerCase();
const out = [];
const idx = (typeof HGB_BuiltinIcons !== 'undefined' && HGB_BuiltinIcons.iconIndex) || [];
for (const ic of idx) {
if (ic.name && ic.name.toLowerCase().includes(kw)) {
out.push({ id: ic.id, name: ic.name, source: String(ic.id).split(':')[0], category: ic.category });
if (out.length >= (limit || 20)) return out;
}
}
return out;
}
/** 按 id 反查来源 id */
function sourceOf(fullId) {
return String(fullId || '').split(':')[0];
}
return {
VERSION, loadSource, loadSources, preload, loadAll,
getSource, loaded, loadedIds, findIcon, searchLoaded, searchIndex, sourceOf
};
})();