Skip to content

Feature/save current work 2026 07 01#456

Open
zhangtianqides wants to merge 65 commits into
pascalorg:mainfrom
lytech2020:feature/save-current-work-2026-07-01
Open

Feature/save current work 2026 07 01#456
zhangtianqides wants to merge 65 commits into
pascalorg:mainfrom
lytech2020:feature/save-current-work-2026-07-01

Conversation

@zhangtianqides

@zhangtianqides zhangtianqides commented Jul 1, 2026

Copy link
Copy Markdown

What does this PR do?

How to test

Screenshots / screen recording

Checklist

  • I've tested this locally with bun dev
  • My code follows the existing code style (run bun check to verify)
  • I've updated relevant documentation (if applicable)
  • This PR targets the main branch

Note

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 in AGENTS.md), with APIs to create jobs, record run outputs (coords / semantic / merged), update status, and persist scenes via buildAndSaveScene (/api/dxf-import-scene, /api/dxf-jobs/*). The client gets ImportDxfTool, /import-dxf, canvas DxfPreview, DxfValidationFeedback, ImportProgress, and ImportSettings, wired from home and scenes. Channel B is a new /api/vision-analyze route (Anthropic vision, markdown prompt file, normalizeSemanticResponse + tests) that can write semantic_*.json per job and degrade gracefully when the API is missing or times out.

A second pipeline POST /api/dxf-jobs/[jobId]/madori calls an external /analyze-dxf service, stores madori_*.xml, runs parseMadori, then saves the scene (with ?rerun=1 to 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 to AI_AGENT_URL with a buffered body to avoid truncated long chat responses.

Catalog (dev/prod flag) adds /api/catalog-items (POST/PATCH/DELETE) and dev-overlay, gated by PASCAL_ALLOW_CATALOG_SOURCE_WRITE / development mode, plus CATALOG_ITEMS.md.

Pic-to-3D adds /pic-to-3d UI 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.example AI/Madori vars, pascal-function-static file 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.

chou-kyoku and others added 30 commits June 3, 2026 11:25
- 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>
…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>
wangdezihao-gaojunfeng and others added 19 commits June 10, 2026 15:30
在 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,
})

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Fix in Cursor Fix in Web

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')

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Fix in Cursor Fix in Web

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'

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit def8fac. Configure here.

Comment thread apps/editor/app/page.tsx
onClose={() => setDxfOpen(false)}
onDone={({ buildingId }) => {
setDxfOpen(false)
router.push(`/_pascal/scene/${buildingId}`)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit def8fac. Configure here.

x2: first.x * scale,
y2: first.y * scale,
})
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit def8fac. Configure here.

method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name, mergeResult, coords, guideImageUrl }),
})

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Fix in Cursor Fix in Web

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)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit def8fac. Configure here.

const s = getRejectSuggestions([])
expect(s.length).toBe(1)
expect(s[0]).toContain('建筑平面图')
})

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit def8fac. Configure here.

Comment thread .mcp.json
]
}
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)
Fix in Cursor Fix in Web

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'

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Fix in Cursor Fix in Web

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))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 97447b1. Configure here.


// Save run output (best-effort)
if (jobId) {
void saveRunOutput(jobId, coordsJSON, mergeResult, semanticFile, semanticJSON === null)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)
Fix in Cursor Fix in Web

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 })
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 97447b1. Configure here.

}}
onRetry={handleReset}
validation={validation}
/>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Fix in Cursor Fix in Web

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Fix in Cursor Fix in Web

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)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)
Fix in Cursor Fix in Web

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 {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit d1e15c5. Configure here.

body: JSON.stringify({ imageDataUrl, ...(jobId ? { jobId } : {}) }),
signal,
})
if (!res.ok) return { semantic: null, semanticFile: null }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit d1e15c5. Configure here.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Fix All in Cursor

❌ 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')

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 2d3c4c8. Configure here.

const vResult = runValidation(parsed, file.size, settings)
setValidation(vResult)

if (!vResult.passed) { setPhase('rejected'); return }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 2d3c4c8. Configure here.

}
return isFinite(minX)
? { minX, minY, maxX, maxY }
: { minX: 0, minY: 0, maxX: 1, maxY: 1 }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Fix in Cursor Fix in Web

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)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 2d3c4c8. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants