Fix group_elements and ungroup_elements operations

This commit is contained in:
yctimlin
2025-10-18 22:23:40 +08:00
parent 4215fffb78
commit cb70a5cc6d
2 changed files with 81 additions and 26 deletions
+80 -25
View File
@@ -137,6 +137,27 @@ async function batchCreateElementsOnCanvas(elementsData: ServerElement[]): Promi
return result?.elements || elementsData;
}
// Helper to fetch element from canvas
async function getElementFromCanvas(elementId: string): Promise<ServerElement | null> {
if (!ENABLE_CANVAS_SYNC) {
logger.debug('Canvas sync disabled, skipping fetch');
return null;
}
try {
const response = await fetch(`${EXPRESS_SERVER_URL}/api/elements/${elementId}`);
if (!response.ok) {
logger.warn(`Failed to fetch element ${elementId}: ${response.status}`);
return null;
}
const data = await response.json() as { element?: ServerElement };
return data.element || null;
} catch (error) {
logger.error('Error fetching element from canvas:', error);
return null;
}
}
// In-memory storage for scene state
interface SceneState {
theme: string;
@@ -642,43 +663,77 @@ server.setRequestHandler(CallToolRequestSchema, async (request: CallToolRequest)
case 'group_elements': {
const params = ElementIdsSchema.parse(args);
const { elementIds } = params;
const groupId = generateId();
sceneState.groups.set(groupId, elementIds);
for (const id of elementIds) {
await updateElementOnCanvas({ id, groupIds: [groupId] });
try {
const groupId = generateId();
sceneState.groups.set(groupId, elementIds);
// Update elements on canvas with proper error handling
const updatePromises = elementIds.map(async (id) => {
return await updateElementOnCanvas({ id, groupIds: [groupId] });
});
const results = await Promise.all(updatePromises);
const successCount = results.filter(result => result).length;
if (successCount === 0) {
sceneState.groups.delete(groupId); // Rollback local state
throw new Error('Failed to group any elements: HTTP server unavailable');
}
logger.info('Grouping elements', { elementIds, groupId, successCount });
const result = { groupId, elementIds, successCount };
return {
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }]
};
} catch (error) {
throw new Error(`Failed to group elements: ${(error as Error).message}`);
}
logger.info('Grouping elements', { elementIds, groupId });
const result = { groupId, elementIds };
return {
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }]
};
}
case 'ungroup_elements': {
const params = GroupIdSchema.parse(args);
const { groupId } = params;
if (!sceneState.groups.has(groupId)) {
throw new Error(`Group ${groupId} not found`);
}
const elementIds = sceneState.groups.get(groupId);
sceneState.groups.delete(groupId);
for (const id of elementIds ?? []) {
await updateElementOnCanvas({ id, groupIds: [] });
try {
const elementIds = sceneState.groups.get(groupId);
sceneState.groups.delete(groupId);
// Update elements on canvas, removing only this specific groupId
const updatePromises = (elementIds ?? []).map(async (id) => {
// Fetch current element to get existing groupIds
const element = await getElementFromCanvas(id);
if (!element) {
logger.warn(`Element ${id} not found on canvas, skipping ungroup`);
return null;
}
// Remove only the specific groupId, preserve others
const updatedGroupIds = (element.groupIds || []).filter(gid => gid !== groupId);
return await updateElementOnCanvas({ id, groupIds: updatedGroupIds });
});
const results = await Promise.all(updatePromises);
const successCount = results.filter(result => result !== null).length;
if (successCount === 0) {
logger.warn('Failed to ungroup any elements: HTTP server unavailable or elements not found');
}
logger.info('Ungrouping elements', { groupId, elementIds, successCount });
const result = { groupId, ungrouped: true, elementIds, successCount };
return {
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }]
};
} catch (error) {
throw new Error(`Failed to ungroup elements: ${(error as Error).message}`);
}
logger.info('Ungrouping elements', { groupId, elementIds });
const result = { groupId, ungrouped: true, elementIds };
return {
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }]
};
}
case 'align_elements': {
+1 -1
View File
@@ -11,7 +11,7 @@ const logger: winston.Logger = winston.createLogger({
const extra = info.metadata && Object.keys(info.metadata).length
? ` ${JSON.stringify(info.metadata)}`
: '';
return `${info.timestamp} [${info.level}] ${info.message} ${extra}`
return `${info.timestamp} [${info.level}] ${info.message}${extra}`
})
),