预览扫描

This commit is contained in:
tianmo committed 2026-09-17 18:47:17 +08:00
1 parent 2a5fe0ce1f
commit 2cb70c49d4
6 files changed
+86 -10

No files matched your search

+9 -4
View File
@@ -507,10 +507,15 @@ function discoverAssets(filePath, options) {
const selected = path.basename(filePath);
const selectedStem = normalizeStem(selected);
// 先扫码“选中 item 文件夹 + 同级所有 .info”(精确、不跨批次)。
const roots = getSearchRoots(filePath);
// 若同级范围无法凑齐骨架+图集(常见于 Eagle 导入瞬间,同伴落在别的 .info),
// 则扩展搜索到整个库的 images/ 目录(全库兜底,靠“同家族 + 同文件夹 + 最近 btime”配对)。
buildRootsWithLibraryRoot(filePath, roots);
let roots;
if (Array.isArray(options.searchRoots) && options.searchRoots.length) {
roots = [...new Set(options.searchRoots.map((root) => path.resolve(root)))];
} else {
roots = getSearchRoots(filePath);
// 兼容旧的非查询调用:扩展到整个库。查看器会传入 Eagle API
// 明确列举的候选 item 目录,避免每次预览扫描整库。
buildRootsWithLibraryRoot(filePath, roots);
}
// 导入时 Eagle 会先创建各个 .info,再异步写 folders。严格模式必须把尚未写入
// folders 的同批资源也纳入候选,之后用 metadata.json 的 birthtime 精确配对。
Binary file not shown.
@@ -507,10 +507,15 @@ function discoverAssets(filePath, options) {
const selected = path.basename(filePath);
const selectedStem = normalizeStem(selected);
// 先扫码“选中 item 文件夹 + 同级所有 .info”(精确、不跨批次)。
const roots = getSearchRoots(filePath);
// 若同级范围无法凑齐骨架+图集(常见于 Eagle 导入瞬间,同伴落在别的 .info),
// 则扩展搜索到整个库的 images/ 目录(全库兜底,靠“同家族 + 同文件夹 + 最近 btime”配对)。
buildRootsWithLibraryRoot(filePath, roots);
let roots;
if (Array.isArray(options.searchRoots) && options.searchRoots.length) {
roots = [...new Set(options.searchRoots.map((root) => path.resolve(root)))];
} else {
roots = getSearchRoots(filePath);
// 兼容旧的非查询调用:扩展到整个库。查看器会传入 Eagle API
// 明确列举的候选 item 目录,避免每次预览扫描整库。
buildRootsWithLibraryRoot(filePath, roots);
}
// 导入时 Eagle 会先创建各个 .info,再异步写 folders。严格模式必须把尚未写入
// folders 的同批资源也纳入候选,之后用 metadata.json 的 birthtime 精确配对。
@@ -545,6 +545,37 @@
return false;
}
async function getPreviewSearchRoots(sourcePath) {
const currentDir = path.dirname(sourcePath);
const libraryRoot = utils.findLibraryRoot(sourcePath);
if (!libraryRoot) return null;
const folders = utils.foldersOfItem(sourcePath);
const params = new URLSearchParams({
limit: '1000',
keyword: utils.normalizeStem(path.basename(sourcePath)),
});
if (folders.length) params.set('folders', folders.join(','));
try {
const response = await fetch('http://localhost:41595/api/item/list?' + params.toString() + '&_=' + Date.now(), {
cache: 'no-store',
});
const result = await response.json();
if (!response.ok || !result || result.status !== 'success' || !Array.isArray(result.data)) return null;
const imagesDir = path.join(libraryRoot, 'images');
const roots = [currentDir];
for (const item of result.data) {
if (!item || !/^[A-Za-z0-9_-]+$/.test(item.id || '')) continue;
if (!utils.extOf(String(item.name || '') + '.' + String(item.ext || ''))) continue;
const itemDir = path.join(imagesDir, item.id + '.info');
if (utils.isEagleItemDir(itemDir)) roots.push(itemDir);
}
return [...new Set(roots.map((root) => path.resolve(root)))];
} catch (error) {
log('Eagle 候选素材查询失败,回退到本地发现: ' + error.message);
return null;
}
}
function currentResourcePaths() {
if (!activeAssets) return [];
const groupStems = new Set([
@@ -1142,7 +1173,8 @@
else showTextPreview(name, parsed === undefined ? text : JSON.stringify(parsed, null, 2));
return;
}
const assets = utils.discoverAssets(filePath);
const previewSearchRoots = await getPreviewSearchRoots(filePath);
const assets = utils.discoverAssets(filePath, previewSearchRoots ? { searchRoots: previewSearchRoots } : undefined);
if (!assets.skeletonPath || !assets.atlasPath) {
if (!assets.skeletonPath) {
+2
View File
@@ -35,6 +35,8 @@ try {
assert.equal(utils.discoverAssets(lateAtlas, { strictStem: true }).skeletonPath, null);
const lateSkeleton = path.join(late, 'hero.skel.bytes'); fs.writeFileSync(lateSkeleton, Buffer.from('\0' + '3.8.1\0'));
assert.equal(utils.discoverAssets(lateAtlas, { strictStem: true, refreshIndex: true }).skeletonPath, lateSkeleton);
const scoped = utils.discoverAssets(lateAtlas, { searchRoots: [late] });
assert.equal(scoped.skeletonPath, lateSkeleton);
for (const [name, json, expected] of [
['spine.json', { skeleton: { spine: '4.2.43' }, bones: [] }, 'spine'],
['empty-version.json', { skeleton: { spine: '' }, bones: [] }, 'other'],
+33 -1
View File
@@ -545,6 +545,37 @@
return false;
}
async function getPreviewSearchRoots(sourcePath) {
const currentDir = path.dirname(sourcePath);
const libraryRoot = utils.findLibraryRoot(sourcePath);
if (!libraryRoot) return null;
const folders = utils.foldersOfItem(sourcePath);
const params = new URLSearchParams({
limit: '1000',
keyword: utils.normalizeStem(path.basename(sourcePath)),
});
if (folders.length) params.set('folders', folders.join(','));
try {
const response = await fetch('http://localhost:41595/api/item/list?' + params.toString() + '&_=' + Date.now(), {
cache: 'no-store',
});
const result = await response.json();
if (!response.ok || !result || result.status !== 'success' || !Array.isArray(result.data)) return null;
const imagesDir = path.join(libraryRoot, 'images');
const roots = [currentDir];
for (const item of result.data) {
if (!item || !/^[A-Za-z0-9_-]+$/.test(item.id || '')) continue;
if (!utils.extOf(String(item.name || '') + '.' + String(item.ext || ''))) continue;
const itemDir = path.join(imagesDir, item.id + '.info');
if (utils.isEagleItemDir(itemDir)) roots.push(itemDir);
}
return [...new Set(roots.map((root) => path.resolve(root)))];
} catch (error) {
log('Eagle 候选素材查询失败,回退到本地发现: ' + error.message);
return null;
}
}
function currentResourcePaths() {
if (!activeAssets) return [];
const groupStems = new Set([
@@ -1142,7 +1173,8 @@
else showTextPreview(name, parsed === undefined ? text : JSON.stringify(parsed, null, 2));
return;
}
const assets = utils.discoverAssets(filePath);
const previewSearchRoots = await getPreviewSearchRoots(filePath);
const assets = utils.discoverAssets(filePath, previewSearchRoots ? { searchRoots: previewSearchRoots } : undefined);
if (!assets.skeletonPath || !assets.atlasPath) {
if (!assets.skeletonPath) {