From 9cd009dfb93a1eb17063ea85b7eaffc4d251a3d9 Mon Sep 17 00:00:00 2001 From: liquidliang Date: Tue, 13 Jan 2026 20:33:13 +0800 Subject: [PATCH] =?UTF-8?q?fix(minimap):=20=E4=BF=AE=E5=A4=8D=E9=A1=B5?= =?UTF-8?q?=E9=9D=A2=E5=8A=A0=E8=BD=BD=E6=97=B6=E5=B0=8F=E5=9C=B0=E5=9B=BE?= =?UTF-8?q?=E8=87=AA=E5=8A=A8=E5=B1=95=E5=BC=80=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加初始化标志跳过首次 viewport 变化检测,避免页面加载时误触发 - 优化 AIInputBar 提交冷却时间从 3 秒改为 1 秒 - 修复 SW 执行路径提前 return 导致冷却定时器未启动的问题 - 在 postProcessingCompleted 事件中立即重置 isSubmitting 状态 - 注释掉多个调试日志减少控制台噪音 - 更新 CLAUDE.md 添加分页加载与虚拟滚动功能文档 - 更新 CODEBUDDY.md 添加多分支函数共享逻辑的最佳实践 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .codebuddy/commands/update-claude-md.md | 12 +- CLAUDE.md | 104 +++++++++++++++++- CODEBUDDY.md | 62 +++++++++++ .../components/ai-input-bar/AIInputBar.tsx | 42 +++++-- .../src/components/minimap/Minimap.tsx | 9 ++ packages/drawnix/src/plugins/with-workzone.ts | 4 +- .../src/services/media-result-handler.ts | 2 + .../src/services/sw-capabilities/handler.ts | 70 ++++++------ .../services/workflow-completion-service.ts | 1 + 9 files changed, 251 insertions(+), 55 deletions(-) diff --git a/.codebuddy/commands/update-claude-md.md b/.codebuddy/commands/update-claude-md.md index 431b80f1..75d000c9 100644 --- a/.codebuddy/commands/update-claude-md.md +++ b/.codebuddy/commands/update-claude-md.md @@ -1,10 +1,10 @@ -# Update CODEBUDDY.md +# Update CLAUDE.md -分析代码审查反馈或开发过程中发现的问题,提取通用规则并更新 CODEBUDDY.md。 +分析代码审查反馈或开发过程中发现的问题,提取通用规则并更新 CLAUDE.md。 ## 说明 -这个命令实现了 Boris Cherny 第 5 条技巧:Code Review 自动更新规则到 CODEBUDDY.md,让 AI 越来越懂你的项目。形成知识沉淀的飞轮效应。 +这个命令实现了 Boris Cherny 第 5 条技巧:Code Review 自动更新规则到 CLAUDE.md,让 AI 越来越懂你的项目。形成知识沉淀的飞轮效应。 ## 参数 @@ -55,7 +55,7 @@ ### 4. 定位更新位置 -读取 CODEBUDDY.md,确定规则应该添加到哪个章节: +读取 CLAUDE.md,确定规则应该添加到哪个章节: - 开发规范相关 → `### Development Rules` - 编码标准相关 → `### Coding Standards` @@ -76,7 +76,7 @@ ### 6. 更新文件 -使用 Edit 工具更新 CODEBUDDY.md: +使用 Edit 工具更新 CLAUDE.md: - 保持现有格式 - 在适当位置插入新规则 - 如果需要,创建新章节 @@ -85,7 +85,7 @@ 显示更新内容摘要: ```markdown -## 已更新 CODEBUDDY.md +## 已更新 CLAUDE.md ### 新增规则 diff --git a/CLAUDE.md b/CLAUDE.md index e2bc3e17..fd3cb741 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -10,7 +10,7 @@ **项目信息:** - **名称**: Aitu (爱图) - AI 图片与视频创作工具 -- **版本**: 0.4.0 +- **版本**: 0.5.0 - **许可证**: MIT - **标语**: 爱上图片,爱上创作 - **官网**: https://aitu.tu-zi.com @@ -125,7 +125,7 @@ packages/drawnix/ │ ├── components/ # UI 组件 (43个子目录) │ ├── services/ # 业务逻辑服务 (32个服务) │ ├── plugins/ # 功能插件 (15个插件) -│ ├── hooks/ # React Hooks (24个) +│ ├── hooks/ # React Hooks (27个) │ ├── useWorkflowSubmission.ts # 工作流提交(核心) │ ├── useAutoInsertToCanvas.ts # 自动插入画布 │ ├── utils/ # 工具函数 (33个模块) @@ -177,6 +177,9 @@ components/ ├── settings-dialog/ # 设置对话框 ├── project-drawer/ # 项目抽屉 ├── task-queue/ # 任务队列 UI +│ └── VirtualTaskList.tsx # 虚拟任务列表(支持分页和虚拟滚动) +├── lazy-image/ # 懒加载图片组件 +│ └── LazyImage.tsx # 基于 IntersectionObserver 的图片懒加载 ├── video-frame-selector/ # 视频帧选择器 ├── generation-history/ # 生成历史 ├── minimap/ # 小地图 @@ -254,6 +257,9 @@ hooks/ ├── useMention.ts # @提及功能 ├── useMediaCache.ts # 媒体缓存 ├── useViewportScale.ts # 视口缩放 +├── useInfinitePagination.ts # 无限滚动分页 +├── useVirtualList.ts # 虚拟列表(封装 @tanstack/react-virtual) +├── useImageLazyLoad.ts # 图片懒加载 └── ...其他 Hooks ``` @@ -971,6 +977,100 @@ const result = await backupRestoreService.importFromZip(file, onProgress); - 虚拟 URL(`/asset-library/`)从 Cache API 获取 - 导入时区分本地素材和 AI 生成素材,存储位置不同 +### 分页加载与虚拟滚动 + +支持任务队列和素材库的分页加载与虚拟滚动,优化大数据量场景下的性能。 + +**核心文件**: +- `hooks/useInfinitePagination.ts` - 无限滚动分页 Hook +- `hooks/useVirtualList.ts` - 虚拟列表 Hook(封装 @tanstack/react-virtual) +- `hooks/useImageLazyLoad.ts` - 图片懒加载 Hook +- `components/lazy-image/LazyImage.tsx` - 懒加载图片组件 +- `components/task-queue/VirtualTaskList.tsx` - 虚拟任务列表组件 +- `components/media-library/VirtualAssetGrid.tsx` - 虚拟素材网格组件 +- `apps/web/src/sw/task-queue/storage.ts` - IndexedDB 游标分页查询 + +**功能特点**: +- IndexedDB 游标分页:支持大数据量的高效分页查询 +- 无限滚动:滚动到底部自动加载更多数据 +- 虚拟滚动:只渲染可见区域的元素,大幅减少 DOM 节点 +- 图片懒加载:基于 IntersectionObserver,进入视口才加载图片 +- 实时更新:支持 `prependItems`、`updateItem`、`removeItem` 操作 + +**使用示例**: +```typescript +// 无限滚动分页 +const { + items, + isLoading, + isLoadingMore, + hasMore, + loadMore, + reset, +} = useInfinitePagination({ + fetcher: async ({ offset, limit }) => { + const result = await swTaskQueueClient.requestPaginatedTasks({ + offset, + limit, + status: filterStatus, + }); + return { + items: result.tasks, + total: result.total, + hasMore: result.hasMore, + }; + }, + pageSize: 50, + getItemKey: (task) => task.id, + deps: [filterStatus], +}); + +// 虚拟列表 +const { parentRef, virtualItems, totalSize, getItem } = useVirtualList({ + items: tasks, + estimateSize: 200, + overscan: 3, +}); +``` + +**IndexedDB 分页查询**: +```typescript +// Service Worker 中的游标分页实现 +async getPaginatedTasks(params: PaginationParams): Promise { + const { offset = 0, limit = 50, status } = params; + const db = await this.getDB(); + const tx = db.transaction('tasks', 'readonly'); + const store = tx.objectStore('tasks'); + const index = store.index('by-createdAt'); + + let cursor = await index.openCursor(null, 'prev'); + let skipped = 0; + const items: Task[] = []; + + while (cursor && items.length < limit) { + if (status && cursor.value.status !== status) { + cursor = await cursor.continue(); + continue; + } + if (skipped < offset) { + skipped++; + cursor = await cursor.continue(); + continue; + } + items.push(cursor.value); + cursor = await cursor.continue(); + } + + return { items, total, hasMore: offset + items.length < total }; +} +``` + +**性能优化**: +- 默认每页 50 条数据 +- 虚拟列表 overscan 设置为 3-5 个元素 +- 图片懒加载 rootMargin 设置为 200px(提前加载) +- 使用 `getItemKey` 进行去重,避免重复数据 + --- ## 开发规范 diff --git a/CODEBUDDY.md b/CODEBUDDY.md index e0b3722b..8bd13891 100644 --- a/CODEBUDDY.md +++ b/CODEBUDDY.md @@ -314,6 +314,68 @@ useEffect(() => { **原因**: 事件处理中的清理操作(如删除 UI 元素)通常通过 ref 引用,不依赖于事件携带的数据。如果在检查事件数据有效性时过早 `return`,会导致清理逻辑被跳过,造成 UI 残留(如 WorkZone 面板未关闭)。应该将独立的操作分离,确保关键清理逻辑总能执行。 +#### 多分支函数中提前 return 导致共享逻辑未执行 +**场景**: 异步函数有多个执行路径(如 SW 执行和主线程 fallback),每个分支都需要执行某些共享逻辑(如重置状态、启动定时器) + +❌ **错误示例**: +```typescript +const handleGenerate = async () => { + setIsSubmitting(true); + + try { + const { usedSW } = await submitWorkflowToSW(params); + if (usedSW) { + // SW 执行成功 + setPrompt(''); + return; // ❌ 提前返回,跳过了冷却定时器 + } + } catch (error) { + // fallback to main thread + } + + // 主线程执行... + await executeInMainThread(); + + // 启动冷却定时器(只有主线程路径能执行到这里) + submitCooldownRef.current = setTimeout(() => { + setIsSubmitting(false); // ❌ SW 路径永远不会重置! + }, 1000); +}; +``` + +✅ **正确示例**: +```typescript +const handleGenerate = async () => { + setIsSubmitting(true); + + try { + const { usedSW } = await submitWorkflowToSW(params); + if (usedSW) { + // SW 执行成功 + setPrompt(''); + + // ✅ 在 return 前执行共享逻辑 + submitCooldownRef.current = setTimeout(() => { + setIsSubmitting(false); + }, 1000); + return; + } + } catch (error) { + // fallback to main thread + } + + // 主线程执行... + await executeInMainThread(); + + // 主线程路径也执行相同的冷却逻辑 + submitCooldownRef.current = setTimeout(() => { + setIsSubmitting(false); + }, 1000); +}; +``` + +**原因**: 当函数有多个执行路径时,每个路径都可能需要执行某些共享逻辑(如重置状态)。如果在某个分支中提前 `return` 而忘记执行共享逻辑,会导致状态不一致(如 `isSubmitting` 永远为 `true`,输入框被禁用)。解决方案:1) 在每个 `return` 前确保执行共享逻辑;2) 或将共享逻辑提取到 `finally` 块中。 + #### CSS/SCSS Guidelines - Use BEM naming convention - Prefer design system CSS variables (see Brand Guidelines below) diff --git a/packages/drawnix/src/components/ai-input-bar/AIInputBar.tsx b/packages/drawnix/src/components/ai-input-bar/AIInputBar.tsx index 7369f192..94b27d6b 100644 --- a/packages/drawnix/src/components/ai-input-bar/AIInputBar.tsx +++ b/packages/drawnix/src/components/ai-input-bar/AIInputBar.tsx @@ -522,6 +522,14 @@ export const AIInputBar: React.FC = React.memo(({ className, is if (event.type === 'postProcessingCompleted') { const position = event.result.firstElementPosition; + // 立即重置提交状态,允许用户继续输入 + // console.log('[AIInputBar] postProcessingCompleted: resetting isSubmitting'); + if (submitCooldownRef.current) { + clearTimeout(submitCooldownRef.current); + submitCooldownRef.current = null; + } + setIsSubmitting(false); + // 关闭 ChatDrawer(如果是由 AIInputBar 触发的对话) // 注意:这里使用 setTimeout 确保消息更新后再关闭 setTimeout(() => { @@ -829,8 +837,12 @@ export const AIInputBar: React.FC = React.memo(({ className, is // Handle generation const handleGenerate = useCallback(async () => { if (!prompt.trim() && allContent.length === 0) return; - if (isSubmitting) return; // 仅防止快速重复点击 + if (isSubmitting) { + // console.log('[AIInputBar] handleGenerate blocked: isSubmitting=true'); + return; // 仅防止快速重复点击 + } + // console.log('[AIInputBar] handleGenerate: setting isSubmitting=true'); setIsSubmitting(true); try { @@ -1056,12 +1068,22 @@ export const AIInputBar: React.FC = React.memo(({ className, is const hasSelection = allContent.length > 0; addPromptHistory(prompt.trim(), hasSelection); } - // 清空输入并关闭面板 + // 清空输入,保持面板打开以便用户继续创作 setPrompt(''); setSelectedContent([]); setUploadedContent([]); - setIsFocused(false); - inputRef.current?.blur(); + + // 启动 1 秒冷却定时器,之后允许用户继续输入 + if (submitCooldownRef.current) { + clearTimeout(submitCooldownRef.current); + } + console.log('[AIInputBar] SW execution success, starting 1s cooldown timer'); + submitCooldownRef.current = setTimeout(() => { + console.log('[AIInputBar] 1s cooldown expired: setting isSubmitting=false'); + setIsSubmitting(false); + submitCooldownRef.current = null; + }, 1000); + return; // 提前返回 } } catch (swError) { @@ -1294,13 +1316,11 @@ export const AIInputBar: React.FC = React.memo(({ className, is } } - // 清空输入并关闭面板 + // 清空输入,保持面板打开以便用户继续创作 setPrompt(''); setSelectedContent([]); setUploadedContent([]); // 同时清空用户上传内容 - setIsFocused(false); - // 让输入框失去焦点 - inputRef.current?.blur(); + // 不关闭面板,让用户可以继续输入 } catch (error) { console.error('Failed to create generation task:', error); // 中止工作流 @@ -1308,15 +1328,17 @@ export const AIInputBar: React.FC = React.memo(({ className, is // 出错时立即允许重试 setIsSubmitting(false); } - // 成功提交后,3秒内不允许重复提交(防止误操作双击) + // 成功提交后,1秒内不允许重复提交(防止误操作双击) // 清除之前的定时器 if (submitCooldownRef.current) { clearTimeout(submitCooldownRef.current); } + console.log('[AIInputBar] Starting 1s cooldown timer'); submitCooldownRef.current = setTimeout(() => { + console.log('[AIInputBar] 1s cooldown expired: setting isSubmitting=false'); setIsSubmitting(false); submitCooldownRef.current = null; - }, 3000); + }, 1000); }, [prompt, allContent, isSubmitting, selectedModel, workflowControl, submitWorkflowToSW, addPromptHistory, selectedSize]); // 处理工作流重试(从指定步骤开始) diff --git a/packages/drawnix/src/components/minimap/Minimap.tsx b/packages/drawnix/src/components/minimap/Minimap.tsx index 1888c094..612f3ca3 100644 --- a/packages/drawnix/src/components/minimap/Minimap.tsx +++ b/packages/drawnix/src/components/minimap/Minimap.tsx @@ -77,6 +77,8 @@ export const Minimap: React.FC = ({ offsetX: board.viewport.offsetX, offsetY: board.viewport.offsetY, }); + // 是否已完成初始化(跳过首次 viewport 变化检测,避免页面加载时误触发) + const initializedRef = useRef(false); /** * 获取所有元素的边界 @@ -179,6 +181,13 @@ export const Minimap: React.FC = ({ offsetX: current.offsetX, offsetY: current.offsetY, }; + + // 首次检测到变化时,只更新 ref,不展开小地图(跳过初始化阶段的 viewport 变化) + if (!initializedRef.current) { + initializedRef.current = true; + return; + } + lastInteractionRef.current = Date.now(); // 有交互时自动展开小地图 diff --git a/packages/drawnix/src/plugins/with-workzone.ts b/packages/drawnix/src/plugins/with-workzone.ts index 89c25205..d0168861 100644 --- a/packages/drawnix/src/plugins/with-workzone.ts +++ b/packages/drawnix/src/plugins/with-workzone.ts @@ -322,7 +322,7 @@ export const WorkZoneTransforms = { Transforms.insertNode(board, workzoneElement, [board.children.length]); }); - console.log('[WorkZone] Inserted (without history):', workzoneElement.id); + // console.log('[WorkZone] Inserted (without history):', workzoneElement.id); return workzoneElement; }, @@ -352,7 +352,7 @@ export const WorkZoneTransforms = { PlaitHistoryBoard.withoutSaving(board, () => { Transforms.removeNode(board, [index]); }); - console.log('[WorkZone] Removed (without history):', elementId); + // console.log('[WorkZone] Removed (without history):', elementId); } }, diff --git a/packages/drawnix/src/services/media-result-handler.ts b/packages/drawnix/src/services/media-result-handler.ts index 783b9e3b..2b3111b7 100644 --- a/packages/drawnix/src/services/media-result-handler.ts +++ b/packages/drawnix/src/services/media-result-handler.ts @@ -116,6 +116,7 @@ export async function handleSplitAndInsertTask( const insertionPoint = config.insertionPoint || getDefaultInsertionPoint(); if (result.success) { + console.log(`[MediaResultHandler] Split success, calling completePostProcessing for task ${taskId}`); workflowCompletionService.completePostProcessing( taskId, result.count, @@ -128,6 +129,7 @@ export async function handleSplitAndInsertTask( const insertResult = await insertImageGroup([url], insertionPoint); if (insertResult.success) { + console.log(`[MediaResultHandler] Fallback insert success, calling completePostProcessing for task ${taskId}`); workflowCompletionService.completePostProcessing(taskId, 1, insertionPoint); return { success: true, count: 1 }; } else { diff --git a/packages/drawnix/src/services/sw-capabilities/handler.ts b/packages/drawnix/src/services/sw-capabilities/handler.ts index 158fed81..c103246c 100644 --- a/packages/drawnix/src/services/sw-capabilities/handler.ts +++ b/packages/drawnix/src/services/sw-capabilities/handler.ts @@ -108,8 +108,8 @@ export class SWCapabilitiesHandler { */ private findWorkZoneForMCP(board: PlaitBoard, mcpTool: string): PlaitWorkZone | null { const allWorkZones = WorkZoneTransforms.getAllWorkZones(board); - console.log('[SWCapabilities] findWorkZoneForMCP:', mcpTool, 'total WorkZones:', allWorkZones.length); - + // console.log('[SWCapabilities] findWorkZoneForMCP:', mcpTool, 'total WorkZones:', allWorkZones.length); + if (allWorkZones.length === 0) { return null; } @@ -118,14 +118,14 @@ export class SWCapabilitiesHandler { for (const workzone of allWorkZones) { const hasMatchingStep = workzone.workflow.steps?.some(step => step.mcp === mcpTool); if (hasMatchingStep) { - console.log('[SWCapabilities] Found WorkZone with matching MCP step:', workzone.id); + // console.log('[SWCapabilities] Found WorkZone with matching MCP step:', workzone.id); return workzone; } } // Fallback: return the most recent WorkZone const sortedWorkZones = allWorkZones.sort((a, b) => (b.createdAt || 0) - (a.createdAt || 0)); - console.log('[SWCapabilities] Using most recent WorkZone:', sortedWorkZones[0]?.id); + // console.log('[SWCapabilities] Using most recent WorkZone:', sortedWorkZones[0]?.id); return sortedWorkZones[0] || null; } @@ -209,7 +209,7 @@ export class SWCapabilitiesHandler { return { success: false, error: '缺少 mermaid 参数', type: 'error' }; } - console.log('[SWCapabilities] handleMermaid called, mermaid length:', mermaid.length); + // console.log('[SWCapabilities] handleMermaid called, mermaid length:', mermaid.length); try { // Dynamic import mermaid-to-drawnix @@ -217,7 +217,7 @@ export class SWCapabilitiesHandler { // Extract mermaid code from markdown code block if present const mermaidCode = this.extractCodeBlock(mermaid, 'mermaid'); - console.log('[SWCapabilities] Extracted mermaid code length:', mermaidCode.length); + // console.log('[SWCapabilities] Extracted mermaid code length:', mermaidCode.length); // Parse mermaid to drawnix elements let result; @@ -235,15 +235,15 @@ export class SWCapabilitiesHandler { // Find WorkZone associated with this mermaid operation const targetWorkZone = this.findWorkZoneForMCP(board, 'insert_mermaid'); - console.log('[SWCapabilities] Target WorkZone:', targetWorkZone?.id, 'expectedInsertPosition:', targetWorkZone?.expectedInsertPosition); + // console.log('[SWCapabilities] Target WorkZone:', targetWorkZone?.id, 'expectedInsertPosition:', targetWorkZone?.expectedInsertPosition); // Get viewport center as insertion point for better centering const viewportCenter = this.getViewportCenter(board); - console.log('[SWCapabilities] Viewport center:', viewportCenter); + // console.log('[SWCapabilities] Viewport center:', viewportCenter); // Insert elements to canvas at viewport center const insertResult = this.insertElementsToCanvasAtPoint(board, elements, viewportCenter); - console.log('[SWCapabilities] Insert result:', insertResult); + // console.log('[SWCapabilities] Insert result:', insertResult); if (insertResult.success) { // Center the inserted elements in viewport after a short delay @@ -253,10 +253,10 @@ export class SWCapabilitiesHandler { if (targetWorkZone) { // Remove the WorkZone after successful insertion - console.log('[SWCapabilities] Removing WorkZone:', targetWorkZone.id); + // console.log('[SWCapabilities] Removing WorkZone:', targetWorkZone.id); setTimeout(() => { WorkZoneTransforms.removeWorkZone(board, targetWorkZone!.id); - console.log('[SWCapabilities] WorkZone removed successfully'); + // console.log('[SWCapabilities] WorkZone removed successfully'); // Dispatch event to notify AI input bar that generation is complete window.dispatchEvent(new CustomEvent('ai-generation-complete', { @@ -296,7 +296,7 @@ export class SWCapabilitiesHandler { return { success: false, error: '缺少 markdown 参数', type: 'error' }; } - console.log('[SWCapabilities] handleMindmap called, markdown length:', markdown.length); + // console.log('[SWCapabilities] handleMindmap called, markdown length:', markdown.length); try { // Dynamic import markdown-to-drawnix @@ -304,7 +304,7 @@ export class SWCapabilitiesHandler { // Extract markdown code const markdownCode = this.extractCodeBlock(markdown, 'markdown'); - console.log('[SWCapabilities] Extracted markdown code length:', markdownCode.length); + // console.log('[SWCapabilities] Extracted markdown code length:', markdownCode.length); // Parse markdown to mindmap element let mindElement; @@ -323,15 +323,15 @@ export class SWCapabilitiesHandler { // Find WorkZone associated with this mindmap operation const targetWorkZone = this.findWorkZoneForMCP(board, 'insert_mindmap'); - console.log('[SWCapabilities] Target WorkZone:', targetWorkZone?.id, 'expectedInsertPosition:', targetWorkZone?.expectedInsertPosition); + // console.log('[SWCapabilities] Target WorkZone:', targetWorkZone?.id, 'expectedInsertPosition:', targetWorkZone?.expectedInsertPosition); // Get viewport center as insertion point for better centering const viewportCenter = this.getViewportCenter(board); - console.log('[SWCapabilities] Viewport center:', viewportCenter); + // console.log('[SWCapabilities] Viewport center:', viewportCenter); // Insert to canvas at viewport center const insertResult = this.insertElementsToCanvasAtPoint(board, [mindElement], viewportCenter); - console.log('[SWCapabilities] Insert result:', insertResult); + // console.log('[SWCapabilities] Insert result:', insertResult); if (insertResult.success) { // Center the inserted mindmap in viewport after a short delay @@ -341,10 +341,10 @@ export class SWCapabilitiesHandler { if (targetWorkZone) { // Remove the WorkZone after successful insertion - console.log('[SWCapabilities] Removing WorkZone:', targetWorkZone.id); + // console.log('[SWCapabilities] Removing WorkZone:', targetWorkZone.id); setTimeout(() => { WorkZoneTransforms.removeWorkZone(board, targetWorkZone!.id); - console.log('[SWCapabilities] WorkZone removed successfully'); + // console.log('[SWCapabilities] WorkZone removed successfully'); // Dispatch event to notify AI input bar that generation is complete window.dispatchEvent(new CustomEvent('ai-generation-complete', { @@ -744,7 +744,7 @@ export class SWCapabilitiesHandler { insertPoint: Point ): { success: boolean; elementsCount?: number; error?: string } { try { - console.log('[SWCapabilities] insertElementsToCanvasAtPoint called, point:', insertPoint, 'elements count:', elements.length); + // console.log('[SWCapabilities] insertElementsToCanvasAtPoint called, point:', insertPoint, 'elements count:', elements.length); // Use board.insertFragment to correctly insert elements // This handles element positioning and ID generation properly @@ -759,7 +759,7 @@ export class SWCapabilitiesHandler { scrollToPointIfNeeded(board, insertPoint); }); - console.log('[SWCapabilities] Elements inserted successfully at point:', insertPoint); + // console.log('[SWCapabilities] Elements inserted successfully at point:', insertPoint); return { success: true, elementsCount: elements.length }; } catch (error: any) { console.error('[SWCapabilities] insertElementsToCanvasAtPoint failed:', error); @@ -786,7 +786,7 @@ export class SWCapabilitiesHandler { const centerX = origination[0] + containerRect.width / (2 * zoom); const centerY = origination[1] + containerRect.height / (2 * zoom); - console.log('[SWCapabilities] getViewportCenter:', { centerX, centerY, zoom, origination }); + // console.log('[SWCapabilities] getViewportCenter:', { centerX, centerY, zoom, origination }); return [centerX, centerY]; } catch (error) { console.warn('[SWCapabilities] Error getting viewport center:', error); @@ -811,7 +811,7 @@ export class SWCapabilitiesHandler { // Calculate the bounding rectangle of all inserted elements const boundingRect = getRectangleByElements(board, insertedElements as any[], false); - console.log('[SWCapabilities] Inserted elements bounding rect:', boundingRect); + // console.log('[SWCapabilities] Inserted elements bounding rect:', boundingRect); // Get viewport dimensions const boardContainer = PlaitBoardUtils.getBoardContainer(board); @@ -831,14 +831,14 @@ export class SWCapabilitiesHandler { // Don't zoom in too much if elements are small optimalZoom = Math.min(Math.max(optimalZoom, 0.2), 1.5); - console.log('[SWCapabilities] Calculated optimal zoom:', { - boundingRect, - availableWidth, - availableHeight, - zoomX, - zoomY, - optimalZoom, - }); + // console.log('[SWCapabilities] Calculated optimal zoom:', { + // boundingRect, + // availableWidth, + // availableHeight, + // zoomX, + // zoomY, + // optimalZoom, + // }); // Calculate the center of the inserted elements const elementsCenterX = boundingRect.x + boundingRect.width / 2; @@ -848,11 +848,11 @@ export class SWCapabilitiesHandler { const newOriginationX = elementsCenterX - containerRect.width / (2 * optimalZoom); const newOriginationY = elementsCenterY - containerRect.height / (2 * optimalZoom); - console.log('[SWCapabilities] Centering and fitting elements in viewport:', { - elementsCenter: [elementsCenterX, elementsCenterY], - newOrigination: [newOriginationX, newOriginationY], - optimalZoom, - }); + // console.log('[SWCapabilities] Centering and fitting elements in viewport:', { + // elementsCenter: [elementsCenterX, elementsCenterY], + // newOrigination: [newOriginationX, newOriginationY], + // optimalZoom, + // }); // Update viewport with optimal zoom and centered position BoardTransforms.updateViewport(board, [newOriginationX, newOriginationY], optimalZoom); diff --git a/packages/drawnix/src/services/workflow-completion-service.ts b/packages/drawnix/src/services/workflow-completion-service.ts index 640536af..2eb50bde 100644 --- a/packages/drawnix/src/services/workflow-completion-service.ts +++ b/packages/drawnix/src/services/workflow-completion-service.ts @@ -163,6 +163,7 @@ class WorkflowCompletionService { this.postProcessingResults.set(taskId, result); + console.log(`[WorkflowCompletion] Emitting postProcessingCompleted event for task ${taskId}`); this.completionEvents$.next({ taskId, batchId: this.taskToBatch.get(taskId),