feat: move revision tracking to ExportRecord, add perceptual hash comparison, cut list modal, and auto-start API

- Move Revision from Drawing to ExportRecord so each export captures its own revision snapshot
- Add Hamming distance comparison for perceptual hashes (tolerance of 10 bits) to avoid false revision bumps
- Replace CoenM.ImageHash with inline DifferenceHash impl (compatible with ImageSharp 3.x)
- Increase PDF render DPI from 72 to 150 for better hash fidelity
- Add download-dxfs-by-drawing endpoint for cross-export DXF zip downloads
- Prefix DXF filenames with equipment number when no drawing number is present
- Pass original filename to storage service for standalone part exports
- Auto-start FabWorks.Api from ExportDXF client if not already running
- Add cut list modal with copy-to-clipboard in the web UI
- Update PDF hash on existing export records after upload
- Bump static asset cache versions to v3

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-25 15:48:28 -05:00
parent f6cd91f1b5
commit b9e84de7c0
20 changed files with 803 additions and 50 deletions
+8 -1
View File
@@ -213,6 +213,9 @@ const pages = {
allBom = [...bomByItem.values()];
}
// Store for cut list modal
window._currentBom = allBom;
const bomRows = allBom.map((b, i) => {
const hasDetails = b.cutTemplate || b.formProgram;
const toggleId = `dbom-${b.id}`;
@@ -265,8 +268,12 @@ const pages = {
${bomHeader}
<span class="badge badge-count">${allBom.length} items</span>
<span style="margin-left:auto;display:flex;gap:6px">
${dxfCount > 0 ? `<button class="btn btn-sm" onclick="showCutListModal(window._currentBom)">${icons.clipboard} Cut List</button>` : ''}
${pdfHash ? `<a class="btn btn-amber btn-sm" href="/api/filebrowser/download?hash=${encodeURIComponent(pdfHash)}&ext=pdf&name=${pdfName}">${icons.download} PDF</a>` : ''}
${dxfCount > 0 ? `<a class="btn btn-cyan btn-sm" href="/api/exports/${activeExport.id}/download-dxfs">${icons.download} All DXFs</a>` : ''}
${dxfCount > 0 ? (singleExport
? `<a class="btn btn-cyan btn-sm" href="/api/exports/${activeExport.id}/download-dxfs">${icons.download} All DXFs</a>`
: `<a class="btn btn-cyan btn-sm" href="/api/exports/download-dxfs?drawingNumber=${encodeURIComponent(drawingNumber)}">${icons.download} All DXFs</a>`
) : ''}
</span>
</div>
${allBom.length ? `