Skip to content

Image Studio: cover crop + unified gallery + konva annotation + avatar crop + logo upload - #146

Merged
important-new merged 29 commits into
InspectorHub:mainfrom
important-new:feat/image-studio
Jun 15, 2026
Merged

Image Studio: cover crop + unified gallery + konva annotation + avatar crop + logo upload#146
important-new merged 29 commits into
InspectorHub:mainfrom
important-new:feat/image-studio

Conversation

@important-new

Copy link
Copy Markdown
Contributor

Unified image module (app/components/image-studio/) sharing crop/preview/bake primitives across the app.

Features

  • Report cover cropCoverCropper (react-easy-crop): preset ratios (3:2/16:9/1.91:1/4:3) + landscape↔portrait swap + zoom + rule-of-thirds + live preview. Client bakes a 2048px JPEG → POST /api/inspections/:id/cover → stores cover_image_key + re-editable cover_crop transform (migration 0006, additive). Report/OG/PDF read the baked key first, falling back to the uncropped source.
  • Unified photo gallery — new SideRail Photos tab: cross-inspection grid (react-photo-album) + fullscreen lightbox (yet-another-react-lightbox) over the existing /media endpoint, with Set-as-cover / Annotate actions in the lightbox toolbar. Thin isolation wrappers around both single-maintainer libs.
  • Konva annotationPhotoAnnotator (react-konva) replaces the hand-rolled SVG PhotoStudio render layer; circle/arrow/freehand/label/measure + zoom; now actually persists (composite PNG + node-tree JSON via the existing saveAnnotation endpoint — previously onSave discarded annotations).
  • Avatar cropAvatarCropper (round 512² crop) on the inspector profile photo, reusing the shared bake util.
  • Logo upload — fixes the previously-dead Company Logo file input; LogoUploader with fit-preview, transparent PNG/SVG passthrough (no crop/bake), + backend size/MIME guard.

Notes

  • New deps: react-easy-crop, react-photo-album, yet-another-react-lightbox, react-konva (all MIT, React-18 compatible).
  • Migration 0006 is additive (ADD COLUMN cover_crop, cover_image_key) — no FK/recreate.
  • Verified on production saas (cover crop, gallery, avatar end-to-end; annotator renders + persists).
  • Full gate green: 1685 API + 394 web unit tests, type-check, lint, build.

🤖 Generated with Claude Code

important-new and others added 29 commits June 14, 2026 21:59
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…sk 4)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Renumber 0005_bent_gamma_corps -> 0006 to avoid collision with the
  pre-existing 0005_email_identity_and_signature migration from PR InspectorHub#145.
  Journal entry idx 5 / tag 0006_bent_gamma_corps; snapshot renamed to
  0006_snapshot.json (prevId already chains off email-identity snapshot).
- Wrap JSON.parse of the crop form field in try/catch so malformed JSON
  returns 400 BadRequest instead of an unhandled 500.
- resolveCoverUrl: use ?? instead of || for coverImageKey fallback.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…r action (Task 8)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rds)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
File input now opens the round AvatarCropper instead of uploading the raw
file; the baked 512² JPEG is submitted through the existing photoFetcher /
photo-upload intent unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Logos keep their original format (transparent PNG/SVG): no crop, no bake.
Just upload + object-contain fit preview. Part of the Image Studio effort.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Branch on intent before the conform parse (mirrors settings-profile's
photo-upload). Relays the multipart logo to POST /api/admin/branding/logo
via api.adminBranding.branding.logo.$post.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replaces the dead file input with LogoUploader driven by a useFetcher.
Optimistically reflects the new logoUrl on successful upload.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Reject logos over 2MB or outside png/svg/jpeg/webp before storing to R2.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ered by overlay)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@important-new
important-new merged commit 22a7a95 into InspectorHub:main Jun 15, 2026
4 checks passed
@important-new
important-new deleted the feat/image-studio branch June 15, 2026 00:15
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.

1 participant