Feature/save current work 2026 07 01#456
Conversation
- Add ImportDxfButton to CommunityViewerToolbarLeft; opens ImportDxfTool in a fixed overlay (click backdrop to dismiss) - Add optional `badge` slot to TreeNodeWrapper rendered between label and actions - WallTreeNode reads metadata.needsReview and shows an amber AlertCircle with a tooltip when the DXF merge engine flagged a position mismatch Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…d, add furniture to MergeResult fixture
…print, mm units, command palette entry - Fix $INSUNITS from 6 (m) to 4 (mm); all coordinates ×1000 - Break walls at door/window openings (solidSegments algorithm) - Export Item nodes as rotated rectangle footprints (PASCAL_FURNITURE layer) - Improve door: sill line + 90° swing arc oriented to wall angle - Improve window: two face lines + jamb lines (proper gap rectangle) - Add "Export AutoCAD (DXF)" to Ctrl+K command palette (Export & Share group) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…es on export Pascal stores wall Y = -DXF_Y (same flip applied during DXF import in madori-xml-parser.ts). Without the inverse flip on export the floor plan was mirrored vertically. Fix: - Negate Y in point2d() and dxfPolyline() vertex loop - Swap and negate arc start/end angles in dxfArc() (CCW [s→e] → CCW [-e→-s]) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
AutoCAD rejected the file with "group code 90 undefined at line 20" because: 1. LWPOLYLINE's vertex-count (code 90) requires a preceding "100 AcDbPolyline" subclass marker — without it AutoCAD can't identify the code 2. No $ACADVER header → AutoCAD fell back to R12 (AC1009) which predates LWPOLYLINE 3. Missing TABLES / BLOCKS / OBJECTS sections required by AC1015 structure Fix: - Add $ACADVER=AC1015 to HEADER section - Add 100-subclass markers to all entity types (AcDbEntity + AcDbLine / AcDbPolyline / AcDbText / AcDbCircle + AcDbArc), with layer (code 8) placed after AcDbEntity per spec - Add empty TABLES, BLOCKS, OBJECTS sections to complete the 6-section layout Online viewers were unaffected because they use lenient parsers. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
DXF import: - Expand SiteNode polygon to building bbox + 2m margin (fixes 30m default square) - Hide import warnings from UI, log to console only - Remove auto-created slab (was not requested) DXF export (AutoCAD compatibility): - Add AC1015 version, handles, AcDbSymbolTable markers, TABLES/BLOCKS structure - Fix Y-axis mirror (negate Y on output, swap arc angles) - Wall breaks at door/window openings - Furniture footprint export - Add to Ctrl+K command palette Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Three separate counters (th for tables, bh for blocks, nextH for entities) could generate overlapping handles, causing AutoCAD's eHandleInUse error. Fix: - Replace makeHandleGen(start) with makeHandleGen() starting at 1 - Use the single nextH() counter for tables, blocks, and entities - Pre-compute APPID table/entry handles before building the string to avoid the evaluation-order bug where body args run before tbl() assigns the TABLE handle (so owner references were pointing to wrong entities) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
在 Center Layout 下方新增 Mirror Layout,支持将整个场景沿 Z=0 平面镜像。 Co-authored-by: Cursor <cursoragent@cursor.com>
# Conflicts: # apps/editor/app/page.tsx # apps/editor/components/scene-loader.tsx # apps/editor/components/viewer-toolbar.tsx # bun.lock # packages/editor/src/components/editor/export-manager.tsx # packages/editor/src/components/tools/item/use-placement-coordinator.tsx # packages/editor/src/components/ui/item-catalog/item-catalog.tsx # packages/editor/src/components/ui/sidebar/tab-bar.tsx # packages/editor/src/index.tsx # packages/editor/src/store/use-editor.tsx
移除 catalog-dev 中的测试用自定义物品,恢复为空列表。 Co-authored-by: Cursor <cursoragent@cursor.com>
支持从场景图导出 IFC4,包含墙体、楼板、门窗、家具与房间空间;楼板优先使用 slab 节点,无 slab 时从封闭墙体推算地面。 Co-authored-by: Cursor <cursoragent@cursor.com>
使用 IFCPOLYLINE 生成可被 BIM 查看器识别的地板轮廓,并从 slab 推导与 zone 缺口填充导出地板。 Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Load catalog GLBs with Draco/Meshopt support and prefer meshes from the live scene so furnishings export as triangulated geometry instead of placeholder boxes. Co-authored-by: Cursor <cursoragent@cursor.com>
| y1: l.start.y * scale, | ||
| x2: l.end.x * scale, | ||
| y2: l.end.y * scale, | ||
| }) |
There was a problem hiding this comment.
Validator skips LINE vertices
High Severity
The extractSegs function doesn't use the lineStart/lineEnd helpers for LINE entities. This means it misses endpoints provided as vertices[0] and vertices[1] by dxf-parser v1.1.2. Consequently, many LINE entities are dropped, leading to incomplete segment data and potential false rejections from wall, parallel-pair, and closable-region checks.
Reviewed by Cursor Bugbot for commit def8fac. Configure here.
| return NextResponse.json({ error: 'not_found' }, { status: 404 }) | ||
| } | ||
|
|
||
| const root = path.resolve(process.cwd(), '..', '..', 'pascal-function-statuc') |
There was a problem hiding this comment.
Static files wrong directory name
High Severity
The static file serving API incorrectly resolves its root directory due to a typo (pascal-function-statuc instead of pascal-function-static). This causes panorama and walkthrough media assets to consistently return 404s.
Reviewed by Cursor Bugbot for commit def8fac. Configure here.
| { params }: { params: Promise<{ jobId: string }> }, | ||
| ): Promise<NextResponse> { | ||
| const { jobId } = await params | ||
| const rerun = request.nextUrl.searchParams.get('rerun') === '1' |
There was a problem hiding this comment.
Unguarded Madori import API
High Severity
The new /api/dxf-jobs and /api/dxf-jobs/[jobId]/madori routes are missing authentication. This allows unauthorized clients to create jobs, trigger Madori processing (including external API calls), write data to the job store, and persist new scenes.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit def8fac. Configure here.
| onClose={() => setDxfOpen(false)} | ||
| onDone={({ buildingId }) => { | ||
| setDxfOpen(false) | ||
| router.push(`/_pascal/scene/${buildingId}`) |
There was a problem hiding this comment.
Post-import URL needs proxy prefix
Medium Severity
After a DXF import, the app navigates to /_pascal/scene/{id}. This path is only valid when a reverse proxy strips the /_pascal prefix. Running bun dev directly expects /scene/[id], causing a 404 error after a successful import.
Reviewed by Cursor Bugbot for commit def8fac. Configure here.
| x2: first.x * scale, | ||
| y2: first.y * scale, | ||
| }) | ||
| } |
There was a problem hiding this comment.
Validator ignores closed polyline shape
Medium Severity
Closed LWPOLYLINE handling checks only closed, while dxf-parser marks closed polylines with shape. Missing the closing edge drops segments and can trigger false “no walls” or “no closed region” rejections for otherwise valid plans.
Reviewed by Cursor Bugbot for commit def8fac. Configure here.
| method: 'POST', | ||
| headers: { 'Content-Type': 'application/json' }, | ||
| body: JSON.stringify({ name, mergeResult, coords, guideImageUrl }), | ||
| }) |
There was a problem hiding this comment.
DXF import missing scene token
Medium Severity
The geo+AI path posts to /api/dxf-import-scene with only Content-Type, but that route uses guardSceneApiRequest, which requires PASCAL_SCENE_API_TOKEN when configured. Import completes through merge then fails at scene save with 401/503.
Reviewed by Cursor Bugbot for commit def8fac. Configure here.
| // Read from env; falls back to localhost for local dev. | ||
| const MADORI_API_URL = process.env['MADORI_API_URL'] ?? 'http://localhost:8000' | ||
| const MADORI_API_KEY = process.env['MADORI_API_KEY'] ?? '' | ||
| const MADORI_ANALYZE_DXF_URL = resolveAnalyzeDxfUrl(MADORI_API_URL) |
There was a problem hiding this comment.
Wrong default Madori API URL
Medium Severity
When MADORI_API_URL is unset, the server defaults to http://localhost:8000, which is the reverse proxy port in the runbook—not the Madori analyze service. Default pipeline madori in the UI then fails with 502 unless env is set.
Reviewed by Cursor Bugbot for commit def8fac. Configure here.
| const s = getRejectSuggestions([]) | ||
| expect(s.length).toBe(1) | ||
| expect(s[0]).toContain('建筑平面图') | ||
| }) |
There was a problem hiding this comment.
Suggestion tests mismatch validator
Low Severity
getRejectSuggestions tests use Chinese reject-reason fixtures and expect Chinese hints, but validateDxf and getRejectSuggestions emit English strings keyed on English substrings, so most cases only get the generic English fallback.
Reviewed by Cursor Bugbot for commit def8fac. Configure here.
| ] | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Committed local MCP paths
Low Severity
.mcp.json and .codex/config.toml hardcode /Users/user/github/pascal-editor/... for the MCP server binary. Other machines cannot run MCP without editing committed config.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit def8fac. Configure here.
- Implemented `evaluate-run.ts` with pure functions for determining success, checking bedroom counts, and validating room types. - Created `evaluate-run.test.ts` to cover unit tests for all evaluation functions. - Developed `run-eval.ts` as the main evaluation harness to execute test cases against the `PascalAiAgent`, including support for dry runs and detailed reporting. - Added logic for handling dependencies between cases and ensuring proper scene resolution. - Included comprehensive error handling and logging for evaluation results.
| import { randomUUID } from 'node:crypto' | ||
|
|
||
| /** ComfyUI on LAN - default matches pic-to-3D/run_pic2three.py */ | ||
| export const COMFYUI_BASE_URL = 'http://192.168.100.250:8188' |
There was a problem hiding this comment.
ComfyUI URL hardcoded
Medium Severity
The COMFYUI_BASE_URL is hardcoded to a specific LAN IP (http://192.168.100.250:8188) without an environment override. This causes all Pic-to-3D ComfyUI interactions (upload, queue, status, download) to fail when running on other networks.
Reviewed by Cursor Bugbot for commit 97447b1. Configure here.
| const l = e as unknown as { start?: { x: number; y: number }; end?: { x: number; y: number } } | ||
| if (!l.start || !l.end) continue | ||
| ctx.moveTo(tx(l.start.x), ty(l.start.y)) | ||
| ctx.lineTo(tx(l.end.x), ty(l.end.y)) |
There was a problem hiding this comment.
Preview skips LINE vertices
Medium Severity
Canvas preview drawing for LINE entities requires start and end and skips lines that only expose vertices, so many real DXF uploads show an empty or misleading preview and a bad vision screenshot even when geometry exists on other layers.
Reviewed by Cursor Bugbot for commit 97447b1. Configure here.
|
|
||
| // Save run output (best-effort) | ||
| if (jobId) { | ||
| void saveRunOutput(jobId, coordsJSON, mergeResult, semanticFile, semanticJSON === null) |
There was a problem hiding this comment.
Channel B skip flag wrong
Medium Severity
Job run metadata sets channelBSkipped from semanticJSON === null, but /api/vision-analyze often returns HTTP 200 with valid: false on timeout or fallback paths. The client still records Channel B as not skipped while mergeDxf correctly ignores invalid semantics.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 97447b1. Configure here.
| } catch (err) { | ||
| console.error('[dxf-jobs] createJob failed:', err) | ||
| return NextResponse.json({ error: 'Failed to create job' }, { status: 500 }) | ||
| } |
There was a problem hiding this comment.
DXF job APIs unauthenticated
High Severity
New DXF job endpoints create on-disk jobs, invoke external Madori analyze-dxf, and persist scenes via buildAndSaveScene, but unlike POST /api/dxf-import-scene they never call guardSceneApiRequest. Any client that can reach the editor can trigger those side effects when scene API auth is disabled or LAN-trusted.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 97447b1. Configure here.
| }} | ||
| onRetry={handleReset} | ||
| validation={validation} | ||
| /> |
There was a problem hiding this comment.
Adjust settings shows nothing
Medium Severity
On the rejected phase, “Adjust settings” sets settingsOpen and calls revalidate, but ImportSettings is only rendered in idle and preview. If validation still fails, the UI stays on DxfValidationFeedback with no way to change unit scale or wall thickness.
Reviewed by Cursor Bugbot for commit 0627aa2. Configure here.
| const timeout = setTimeout(() => timeoutCtrl.abort(), 10_000) | ||
| const combined = typeof AbortSignal.any === 'function' | ||
| ? AbortSignal.any([abort.signal, timeoutCtrl.signal]) | ||
| : timeoutCtrl.signal |
There was a problem hiding this comment.
Cancel may not abort vision
Low Severity
When AbortSignal.any is unavailable, Channel B’s vision fetch uses only the 10s timeout signal, not the user’s import abort controller. Closing or resetting the dialog may leave the vision request running until timeout.
Reviewed by Cursor Bugbot for commit 0627aa2. Configure here.
| // Read from env; falls back to localhost for local dev. | ||
| const MADORI_API_URL = process.env['MADORI_API_URL'] ?? 'http://localhost:8000' | ||
| const MADORI_API_KEY = process.env['MADORI_API_KEY'] ?? '' | ||
| const MADORI_ANALYZE_DXF_URL = resolveAnalyzeDxfUrl(MADORI_API_URL) |
There was a problem hiding this comment.
Madori default API URL wrong
Medium Severity
External API integrations for Madori DXF import and Pic-to-3D generation use hardcoded or default local/LAN URLs. This prevents these features from working correctly outside of a specific development environment where the external services are not available at the configured addresses.
Additional Locations (2)
Reviewed by Cursor Bugbot for commit d1e15c5. Configure here.
| async function proxy(request: NextRequest, context: { params: Promise<{ path: string[] }> }) { | ||
| const { path } = await context.params | ||
| const target = `${AI_AGENT_URL}/${path.map(encodeURIComponent).join('/')}` | ||
| try { |
There was a problem hiding this comment.
AI proxy drops query string
Medium Severity
The AI reverse proxy builds the upstream URL from path segments only and does not append the incoming request’s query string. GET or DELETE calls to /api/ai/...?... lose parameters the agent expects.
Reviewed by Cursor Bugbot for commit d1e15c5. Configure here.
| body: JSON.stringify({ imageDataUrl, ...(jobId ? { jobId } : {}) }), | ||
| signal, | ||
| }) | ||
| if (!res.ok) return { semantic: null, semanticFile: null } |
There was a problem hiding this comment.
Channel B ignores error JSON body
Low Severity
runChannelB returns { semantic: null } whenever !res.ok, without reading the response. Vision errors that use channelBFallback with HTTP 502/504 still include a parseable SemanticJSON body, so the client treats Channel B as wholly missing instead of recording the fallback reason or semanticFile.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit d1e15c5. Configure here.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 4 potential issues.
There are 22 total unresolved issues (including 18 from previous reviews).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 2d3c4c8. Configure here.
| const [settings, setSettings] = useState<ImportSettingsValue>(DEFAULT_SETTINGS) | ||
| const [importStats, setImportStats] = useState<ImportStats | null>(null) | ||
| const [settingsOpen, setSettingsOpen] = useState(false) | ||
| const [pipeline, setPipeline] = useState<PipelineMode>('madori') |
There was a problem hiding this comment.
Madori default without service
Medium Severity
ImportDxfTool defaults pipeline to madori, which POSTs /api/dxf-jobs/{id}/madori and calls external MADORI_API_URL. Without that service configured, import fails even when geo+ai (worker + optional vision) could run locally.
Reviewed by Cursor Bugbot for commit 2d3c4c8. Configure here.
| const vResult = runValidation(parsed, file.size, settings) | ||
| setValidation(vResult) | ||
|
|
||
| if (!vResult.passed) { setPhase('rejected'); return } |
There was a problem hiding this comment.
Madori still geo-validated
Medium Severity
The default import pipeline is Madori, but upload still runs validateDxf with parallel-wall and architectural-scale rules. Many CAD files meant for Madori are rejected in preview before the Madori API is ever called.
Reviewed by Cursor Bugbot for commit 2d3c4c8. Configure here.
| } | ||
| return isFinite(minX) | ||
| ? { minX, minY, maxX, maxY } | ||
| : { minX: 0, minY: 0, maxX: 1, maxY: 1 } |
There was a problem hiding this comment.
Preview bbox ignores entities
Medium Severity
Client-side entityBbox only considers LINE and LWPOLYLINE. When no such entities exist, it falls back to a 1×1 box, so validateDxf can mis-estimate scale and wrongly reject or accept files whose extent comes from arcs, circles, or other types.
Reviewed by Cursor Bugbot for commit 2d3c4c8. Configure here.
| items = CATALOG_ITEMS.filter((item) => item.tags?.includes('custom')).map(normalizeCatalogEntry) | ||
| if (items.length > 0) { | ||
| await writeDevCatalogOverlay(items) | ||
| } |
There was a problem hiding this comment.
Catalog GET mutates disk
Medium Severity
GET /api/catalog-items/dev-overlay writes the dev overlay file when the overlay is empty and PASCAL_ALLOW_CATALOG_SOURCE_WRITE is enabled. A read request can mutate server disk in production configs described in the Windows runbook.
Reviewed by Cursor Bugbot for commit 2d3c4c8. Configure here.


What does this PR do?
How to test
Screenshots / screen recording
Checklist
bun devbun checkto verify)mainbranchNote
High Risk
Large new surface area touching scene persistence, on-disk job data, optional writes to catalog source, and multiple external services (Anthropic vision, Madori analyze-dxf, ComfyUI, AI agent proxy)—misconfiguration or API failures can block imports or expose keys if env vars leak to the client.
Overview
This PR turns the editor into a broader floor-plan and asset workflow hub: DXF → scene import, optional AI vision semantics, an alternate 3dMadori path, plus image-to-3D and dev-time catalog tooling.
DXF import introduces a file-system job queue under
PASCAL_DATA_DIR/dxf-imports/(documented inAGENTS.md), with APIs to create jobs, record run outputs (coords/semantic/merged), update status, and persist scenes viabuildAndSaveScene(/api/dxf-import-scene,/api/dxf-jobs/*). The client getsImportDxfTool,/import-dxf, canvasDxfPreview,DxfValidationFeedback,ImportProgress, andImportSettings, wired from home and scenes. Channel B is a new/api/vision-analyzeroute (Anthropic vision, markdown prompt file,normalizeSemanticResponse+ tests) that can writesemantic_*.jsonper job and degrade gracefully when the API is missing or times out.A second pipeline
POST /api/dxf-jobs/[jobId]/madoricalls an external/analyze-dxfservice, storesmadori_*.xml, runsparseMadori, then saves the scene (with?rerun=1to reuse cached XML).Editor product changes add sidebar tabs (AI assistant, Add catalog, 360 panorama, walkthrough), links to pic-to-3d, and a MeasureNavi light theme (Inter / JetBrains Mono,
measurenavi-editor-theme.css)./api/ai/[...path]proxies toAI_AGENT_URLwith a buffered body to avoid truncated long chat responses.Catalog (dev/prod flag) adds
/api/catalog-items(POST/PATCH/DELETE) anddev-overlay, gated byPASCAL_ALLOW_CATALOG_SOURCE_WRITE/ development mode, plusCATALOG_ITEMS.md.Pic-to-3D adds
/pic-to-3dUI and/api/pic-to-3d/*routes talking to ComfyUI (generate, poll status, download GLB).Also included:
.agents/skills/dxf-import,SAVE_BUILD_JSON.md,WINDOWS_DEPLOY_RUNBOOK.md, MCP wiring (.mcp.json, Codex config),.env.exampleAI/Madori vars,pascal-function-staticfile serving, and gitignore entries for pascal-reverse-proxy build artifacts.Reviewed by Cursor Bugbot for commit 2d3c4c8. Bugbot is set up for automated code reviews on this repo. Configure here.