refactor: 通用化 slide 识别和游离节点收集
- findSlides(): 不靠命名,靠结构识别(面积/children/已知模式) - collectOrphans(): 用 slide 子树 id 集合判断,不靠名字前缀 - 先递归子节点,子节点有 slide 则父节点是容器 - 7月汇报(6 slide) 和 A2A(7 slide) 都正确识别
This commit is contained in:
+54
-14
@@ -318,23 +318,58 @@ function convert(input) {
|
||||
}
|
||||
return results;
|
||||
}
|
||||
// ===== 通用 slide 识别 =====
|
||||
// 收集 slide 容器子树的所有 id(用于游离节点判断)
|
||||
function collectIds(node, set) {
|
||||
if (!node) return;
|
||||
if (node.id) set.add(node.id);
|
||||
if (node.children) for (const c of node.children) collectIds(c, set);
|
||||
}
|
||||
|
||||
// 找到所有 slide 容器(name 为 slide-N 或 page 的节点)
|
||||
function findSlides(node) {
|
||||
if (!node || !node.name) return [];
|
||||
// 已知 slide 命名模式
|
||||
const SLIDE_NAME_RE = /^(slide[- ]?|page)/i;
|
||||
// 结构性节点(不应作为 slide 容器)
|
||||
const STRUCTURAL_TAGS = new Set(['HTML', 'BODY', 'HEAD', 'CANVAS']);
|
||||
|
||||
function isSlideCandidate(node, canvasArea) {
|
||||
if (!node || node.type !== 'FRAME') return false;
|
||||
if (!node.rect) return false;
|
||||
// 排除截图底图
|
||||
if (node.layerGroup === 'comparison') return false;
|
||||
// 排除结构性节点
|
||||
if (STRUCTURAL_TAGS.has(node.tag)) return false;
|
||||
// 必须有 children
|
||||
if (!node.children || node.children.length === 0) return false;
|
||||
// 已知命名模式
|
||||
if (node.name && SLIDE_NAME_RE.test(node.name.toLowerCase())) return true;
|
||||
// 结构特征:面积 > 50% canvas
|
||||
const rw = node.rect.width ?? node.rect.w ?? 0;
|
||||
const rh = node.rect.height ?? node.rect.h ?? 0;
|
||||
const area = rw * rh;
|
||||
if (canvasArea > 0 && area > canvasArea * 0.5) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
function findSlides(node, canvasArea) {
|
||||
if (!node) return [];
|
||||
const results = [];
|
||||
if (/^slide-?\d*$/.test(node.name) || node.name === 'page' || node.name.startsWith('slide ')) {
|
||||
results.push(node);
|
||||
}
|
||||
// 先递归收集子节点中的 slide candidates
|
||||
if (node.children && Array.isArray(node.children)) {
|
||||
for (const child of node.children) {
|
||||
results.push(...findSlides(child));
|
||||
results.push(...findSlides(child, canvasArea));
|
||||
}
|
||||
}
|
||||
// 如果子节点中已有 slide candidates,当前节点是容器,不是 slide
|
||||
if (results.length > 0) return results;
|
||||
// 当前节点是 slide candidate
|
||||
if (isSlideCandidate(node, canvasArea)) {
|
||||
results.push(node);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
const slideContainers = findSlides(inputRoot);
|
||||
const canvasArea = (canvas.width || 0) * (canvas.height || 0);
|
||||
const slideContainers = findSlides(inputRoot, canvasArea);
|
||||
console.log(`找到 ${slideContainers.length} 个 slide 容器`);
|
||||
|
||||
const slideGroups = slideContainers.map(s => ({
|
||||
@@ -352,16 +387,21 @@ function convert(input) {
|
||||
|
||||
slideGroups.forEach(sg => console.log(` ${sg.name}: ${sg.children.length} 个元素`));
|
||||
|
||||
// ===== fallback: 收集不在 slide 子树内但坐标在 slide 范围内的节点 =====
|
||||
// 从根节点收集所有非 slide 节点
|
||||
// ===== 通用游离节点收集 =====
|
||||
// 构建 slide 容器子树的 id 集合
|
||||
const slideSubtreeIds = new Set();
|
||||
for (const sc of slideContainers) {
|
||||
collectIds(sc, slideSubtreeIds);
|
||||
}
|
||||
|
||||
function collectOrphans(node) {
|
||||
if (!node || !node.rect) return [];
|
||||
const results = [];
|
||||
const nr = node.rect;
|
||||
const rw = nr.width ?? nr.w;
|
||||
const rh = nr.height ?? nr.h;
|
||||
// 跳过 slide 容器本身和 raster
|
||||
if (!node.name?.startsWith('slide-') && !node.name?.startsWith('slide ') && node.type !== 'RECTANGLE' && node.layerGroup !== 'comparison') {
|
||||
// 用 id 集合判断:不在任何 slide 子树内的节点才是游离节点
|
||||
if (!slideSubtreeIds.has(node.id) && node.type !== 'RECTANGLE' && node.layerGroup !== 'comparison') {
|
||||
results.push({
|
||||
id: node.id, type: node.type, name: node.name, tag: node.tag,
|
||||
rect: { x: nr.x, y: nr.y, w: rw, h: rh },
|
||||
@@ -370,8 +410,8 @@ function convert(input) {
|
||||
});
|
||||
}
|
||||
if (node.children) for (const c of node.children) {
|
||||
// 跳过 slide 容器的子树(它们已经被 collectSlideChildren 处理了)
|
||||
if (c.name?.startsWith('slide-') || c.name?.startsWith('slide ') || c.name === 'page') continue;
|
||||
// 跳过已在 slide 子树内的节点
|
||||
if (slideSubtreeIds.has(c.id)) continue;
|
||||
results.push(...collectOrphans(c));
|
||||
}
|
||||
return results;
|
||||
|
||||
Reference in New Issue
Block a user