The Preferences page had its own inline accent color section that wasn't
updated with the custom picker. Now shows:
- 5 preset swatches + separator + custom swatch with pipette icon
- Color picker + hex input appear when custom is selected
- Larger swatches (w-8 h-8) matching the page's spacious layout
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Added "Mat Override" column to the review table
- Each line has its own dropdown (per-line takes priority over global)
- Default shows global override if set, otherwise "No override"
- "Clear" option to explicitly remove override on a line when global is set
- Amber background when override is active
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Dropdown in Step 3 review to set a single material override for all lines
- Override is passed to each OrderLine.material_override at creation time
- Amber background when override is active
- Library materials loaded on Step 3 entry
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- POST /orders/{id}/batch-material-override endpoint
- Dropdown above the lines table: "Apply to all lines…"
- Options: clear all overrides, or select a library material
- Updates all order lines in one request
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add `material_override` nullable column on OrderLine (DB migration)
- Line override takes priority over OutputType override
- PATCH /orders/{id}/lines/{id} endpoint to update material_override
- Inline dropdown on each order line in the OrderDetail page
- Amber background when override is active
- Same output type, different material per line — no need to create a new output type
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Excel preview detects when a product already has a different STEP file linked
- Excel preview detects intra-Excel conflicts (same product, different CAD model names)
- Product STEP upload warns when replacing an existing file and shows render count
- All warnings are non-blocking (amber badges, toast warnings)
- LEARNINGS.md: all open items resolved
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Per-render-position focal_length_mm/sensor_width_mm (DB → pipeline → Blender)
- FOV-based camera distance with min clamp fix for wide-angle lenses
- Unmapped materials blocking dialog on "Dispatch Renders" with batch alias creation
- Material check endpoint (GET /orders/{id}/check-materials)
- Batch alias endpoint (POST /materials/batch-aliases)
- Quick-map "No alias" badges on Materials page
- Full product hard-delete with storage cleanup (MinIO + disk files + orphaned CadFile)
- Delete button on ProductDetail page with confirmation
- Clickable product names in Media Browser (links to product page)
- Single-line render dispatch/retry (POST /orders/{id}/lines/{id}/dispatch-render)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New endpoint POST /admin/settings/regenerate-all-canonical-scenes
queues GLB + USD master export for ALL completed CAD files, replacing
existing assets. Used after pipeline changes that affect tessellation
or normals.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extract Base Color, Metallic, Roughness, Transmission, IOR from Blender
asset library materials via catalog_assets.py. Store in catalog JSON and
serve via /api/asset-libraries/pbr-map endpoint. Frontend viewers apply
PBR properties to Three.js MeshStandardMaterial using hex color strings
(avoiding Three.js ColorManagement sRGB/linear issues).
Key fixes:
- RLS bypass for material alias lookup in pbr-map endpoint
- pbrMap empty guard prevents premature grey fallback in viewers
- Cache-Control: no-cache on pbr-map requests to avoid stale data
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The queue handles far more than thumbnails: OCC tessellation, USD master
generation, GLB production, order line renders, and workflow renders.
asset_pipeline better reflects its role as the render-worker's primary queue.
Updated all references in: task decorators, celery_app.py, beat_tasks.py,
docker-compose.yml worker command, worker.py MONITORED_QUEUES, admin.py,
CLAUDE.md, LEARNINGS.md, Dockerfile, helpTexts.ts, test files,
and all .claude/commands/*.md skill files.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- beat_tasks.py: import app.models at module level so SQLAlchemy can
resolve relationship("Template") and relationship("User") when domain
models are imported in isolation inside task functions. Fixes all
beat tasks (batch_render_notifications, recover_stuck_cad_files) that
crashed every 60s with mapper initialization error.
- _blender_materials.py: build_mat_map_lower() now adds a slug-normalized
key variant (re.sub([^a-z0-9]+, _, kl)) for each mat_map entry. OCC
part names like 'F-802007_TR4-D1-H122AG' → slug 'f_802007_tr4_d1_h122ag'
now matches USD-imported Blender objects. Existing prefix fallback
(key.startswith(part_key)) catches AF-suffix variants.
- Orders.tsx: kanban drag-to-reject implemented. submitted/processing
cards are draggable (cursor-grab). Rejected column highlights with
red ring on drag-over. Drop opens reject reason modal via createPortal.
Confirm calls rejectOrder() mutation + invalidates orders cache.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Introduces GMSH as an alternative to OCC BRepMesh for STEP→GLB tessellation.
GMSH produces conforming meshes that eliminate fan triangles at cylinder seam
edges — a structural limitation of OCC BRepMesh that cannot be fixed via
deflection parameters.
Changes:
- render-worker/Dockerfile: install gmsh>=4.15.0 + libglu1-mesa + libxft2
- export_step_to_gltf.py: --tessellation_engine occ|gmsh CLI arg +
_tessellate_with_gmsh() using BRep→GMSH→Poly_Triangulation write-back
- admin.py: tessellation_engine setting (SETTINGS_DEFAULTS, SettingsOut,
SettingsUpdate, validation)
- export_glb.py: pass tessellation_engine to export_step_to_gltf.py CLI in
both geometry and production GLB tasks
- Admin.tsx: radio button UI for OCC vs GMSH selection
Tested: 121 faces meshed, 0 BRepMesh fallback, 649K triangles on sample part.
Clean seam edges for UV unwrap — GMSH respects B-rep periodic face boundaries.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Render pipeline:
- Replace per-object _apply_smooth() loop with _apply_smooth_batch(): selects
all 175 parts, calls shade_smooth_by_angle() ONCE in C → reduces 16s to ~0.2s
- Remove 175 per-part "assigned material to part" log lines (replace with summary)
- Add TIMING_SUMMARY log line at end of every render showing all step durations
- _lap() helper records split times for: template_load, glb_import, rotation,
smooth_shading, material_assign, pre_render_setup, gpu_render
Frontend role checks:
- Add global_admin + tenant_admin to User role type in auth store
- Add isAdmin() and isPrivileged() helper functions
- Fix Admin.tsx, Layout.tsx, Notifications.tsx, OrderDetail.tsx, ProductDetail.tsx,
CostOverviewWidget.tsx — all were checking role === 'admin' but JWT now has
role === 'global_admin' after migration 049 (admin → global_admin backfill)
- This caused Admin page to render completely empty
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Per-tenant Azure AI config stored in tenants.tenant_config JSONB
- GET/PUT /api/tenants/{id}/ai-config + POST .../test connection
- api_key never returned to frontend (has_api_key: bool pattern)
- azure_ai.py resolves creds from tenant config when ai_enabled=True
- ai_tasks.py loads tenant config and passes it to validate_thumbnail
- Admin GPU Status section: probe button + status badge + last-checked time
- Notifications: _BELL_CHANNELS filter (notification+alert only in bell)
- Tenants.tsx: per-row Azure AI Config modal with URL auto-parse helper
- Remove duplicate in-memory /gpu-probe endpoints (kept DB-backed /probe/gpu)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- GET /api/workflows/pipeline-steps: returns all StepName enum values
with category (input|processing|rendering|output) + descriptions;
registered before /{workflow_id} to avoid path collision
- frontend/src/api/workflows.ts: getPipelineSteps(), PipelineStep
and PipelineStepsResponse interfaces
- WorkflowEditor: PipelineStepsPanel showing steps grouped by category
with collapsible accordion sections
- ConfigSidepanel: "Pipeline Step" select dropdown binds any node to a
StepName; selected step description shown below dropdown
- Active workflow indicator: green dot next to is_active=true entries
- Improved empty state with descriptive copy
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Phase 5.1 — MATERIAL_PALETTE removal:
- Remove MATERIAL_PALETTE + _material_to_color() from step_processor.py
- build_part_colors() now returns {part→material_name} for Blender resolver
Phase 6 — Notification Center Refactor:
- Migration 051: add channel (activity|notification|alert) to audit_log,
add frequency (immediate|daily|never) to notification_configs
- Three notification channels: activity (per-render), notification (batch
order summaries), alert (admin infrastructure)
- Per-render emit_notification_sync calls demoted to channel=activity
- New emit_batch_render_notification_sync(): single summary notification
when all order lines reach terminal state ("47/50 succeeded, 3 failed")
- Beat task batch_render_notifications every 60s: safety-net for missed
batch notifications after order completion
- GET /notifications: defaults to channel IN (notification, alert);
accepts ?channel=activity for activity feed
- Unread count badge counts only notification+alert channels
- Notifications.tsx: three tabs (Notifications | Activity | Alerts)
- NotificationSettings.tsx: frequency dropdown per event type
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- GPU: fix Cycles device activation order — set compute_device_type
BEFORE engine init, re-set AFTER open_mainfile wipes preferences
- GPU: remove _mark_sharp_and_seams edit-mode loop (redundant with
Blender 5.0 shade_smooth_by_angle), saves ~200s/render on 175 parts
- Material: fix _AFN suffix mismatch — build AF-stripped mat_map keys
and add prefix fallback in _apply_material_library (blender_render.py)
- Material: production GLB now uses get_material_library_path() which
checks active AssetLibrary instead of empty legacy system setting
- Admin: RenderTemplateTable multi-select output types (M2M frontend)
- Admin: MaterialLibraryPanel replaced with link to Asset Libraries
- UX: move Toaster to top-left to avoid dispatch button overlap
- SQLAlchemy: add .unique() to all RenderTemplate M2M collection queries
- Logging: flush=True on all Blender progress prints, stdout reconfigure
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Bug A: Media Library thumbnails were gray because <img src> cannot send
JWT auth headers. Added useAuthBlob() hook (fetch + createObjectURL) in
MediaBrowser.tsx. Also fixed publish_asset Celery task to populate
product_id + cad_file_id on MediaAsset for thumbnail fallback resolution.
Bug B: Product dimensions now shown in Product Details card with Ruler
icon and "from CAD" label when cad_mesh_attributes.dimensions_mm exists.
Bug C: Replaced 128×128 CAD thumbnail with InlineCadViewer component.
Queries gltf_geometry MediaAssets, fetches GLB via auth fetch → blob URL
→ Three.js Canvas with OrbitControls. Falls back to thumbnail + "Load 3D
Model" button. Polling when GLB generation is in progress.
Bug D: trimesh was in [cad] optional extra but Dockerfile only installed
[dev]. Changed to pip install -e ".[dev,cad]" — trimesh now available in
backend container, GLB + Colors export works.
Also added bbox extraction (STL-first numpy parsing) in render_step_thumbnail
and admin "Re-extract CAD Metadata" bulk endpoint.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove max-w-* constraints from all data/table pages so content fills available width after sidebar (Billing, MediaBrowser, OrderDetail, WorkerManagement, WorkerActivity, Materials, Tenants, AssetLibrary, NewProductOrder, ProductDetail)
- Narrow form/settings pages keep their max-width (NotificationSettings, Preferences, NewOrder, Notifications)
- render_order_line_task: create MediaAsset record on render success so results immediately appear in Media Browser without requiring the retroactive import button
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Single chronological list sorted by updated_at (newest first)
- Type badges distinguish CAD processing (FileCode2) from Render jobs (Image)
- Render job rows now link directly to the order (/orders/:id)
- Remove separate "Render Jobs" and "CAD File Processing" sections
- Stat cards simplified to 4: CAD active/failed + Render active/failed
- Backend: add order_id to RenderJobEntry response
- Frontend: add order_id to RenderJobEntry interface, remove flamenco_job_id
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
frontend/src/pages/WorkflowEditor.tsx: full React Flow editor with
custom nodes (Input/Convert/Render/FFmpeg/Output), config sidepanel,
node palette with drag-drop, new workflow dialog.
frontend/src/api/workflows.ts: workflow CRUD API client.
@xyflow/react added to package.json dependencies.
Route /workflows + sidebar link for admin+pm.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>