Filerobot Image Editor Integration

Date: 2026-03-25 Status: Draft

Summary

Integrate react-filerobot-image-editor as a lazy-loaded image editing modal, mirroring the FreeCut video editing pattern. Users can crop, rotate, filter, annotate, and adjust images directly in the browser — 0 credits, pure client-side processing.

Scope

Nodes with Edit Button

All image-producing nodes:

Primary image nodes:

Entity nodes (generate reference images):

Credits

0 credits — client-side processing only, no backend/AI involvement. Same precedent as FreeCut.

Architecture

Store State

Add to use-workflow-store.ts, mirroring freecutEdit:

imageEdit: {
  nodeId: string
  imageUrl: string
  designStateUrl?: string  // R2 URL to saved Filerobot designState JSON
} | null

openImageEdit: (nodeId: string, imageUrl: string, designStateUrl?: string) => void
closeImageEdit: () => void

GeneratedResult Extension

Add filerobotDesignStateUrl to GeneratedResult in types/nodes.ts:

export interface GeneratedResult {
  readonly url: string
  readonly thumbnailUrl?: string
  readonly timestamp: string
  readonly jobId: string
  readonly freecutProjectUrl?: string
  readonly filerobotDesignStateUrl?: string  // NEW
}

File: frontend/src/components/editor/filerobot-editor-modal.tsx

Save Flow

  1. onSave fires with editedImageObject (contains imageBase64) + designState
  2. Convert base64 → File blob (filerobot-edit.png, type image/png)
  3. Upload image via uploadFile(file, userId) → R2 URL (may throw StorageExceededError)
  4. Upload designState JSON via POST /v1/upload-json → R2 URL (reuses existing endpoint)
  5. Create GeneratedResult:
    {
      url,
      jobId: `filerobot-edit-${Date.now()}`,
      timestamp: new Date().toISOString(),
      filerobotDesignStateUrl: designStateUrl,
    }
    
  6. Append to node’s generatedResults, set as active, update legacy URL field per node type:
    • Primary image nodes (generate-image, image-to-image, edit-image): update generatedImageUrl
    • Entity nodes (character, face, object, location): update sourceImageUrl
    • Upload image: update generatedImageUrl (newly added, see below)
  7. Show brief “Saved” feedback, then close modal

Image Node Integration

Add a Pencil icon button to the hover overlay on all 8 image nodes:

In workflow-editor-main.tsx, alongside FreeCut:

{isImageEditOpen && (
  <Suspense fallback={null}>
    <FilerobotEditorModal
      imageUrl={imageEditUrl}
      designStateUrl={imageEditDesignStateUrl}
      onSaveComplete={handleImageEditSave}
      onClose={handleImageEditClose}
    />
  </Suspense>
)}

Theming

Reactive dark/light theme using useTheme().resolvedTheme from next-themes. Colors match the app’s CSS vars from globals.css.

Dark mode:

Key Value Source
bg-primary #121212 App bg
bg-secondary #1E1E1E App card
accent-primary #ff0073 Brand pink
accent-primary-active #e0005f Hover state
icons-primary #E2E8F0 App text
icons-secondary #94A3B8 Muted text
borders-primary #2D2D2D App border
borders-secondary #3D3D3D Secondary border
text-primary #E2E8F0 App text
text-secondary #94A3B8 Muted text

Light mode:

Key Value Source
bg-primary #F8FAFC App bg
bg-secondary #FFFFFF App card
accent-primary #ff0073 Brand pink
accent-primary-active #e0005f Hover state
icons-primary #1E293B App text
borders-primary #E2E8F0 App border
text-primary #1E293B App text
text-secondary #64748B Muted text

Typography: fontFamily: "inherit" to match the app’s font stack.

Dependencies

New npm packages:

All lazy-loaded — zero impact on initial bundle size.

Files Changed

File Change
frontend/package.json Add 3 dependencies
frontend/src/types/nodes.ts Add filerobotDesignStateUrl to GeneratedResult + add generatedResults/activeResultIndex to UploadImageData
frontend/src/hooks/use-workflow-store.ts Add imageEdit state, openImageEdit, closeImageEdit
frontend/src/components/editor/filerobot-editor-modal.tsx NEW — modal component
frontend/src/components/editor/workflow-editor/workflow-editor-main.tsx Mount modal with Suspense
frontend/src/components/nodes/generate-image-node.tsx Add Pencil edit button
frontend/src/components/nodes/image-to-image-node.tsx Add Pencil edit button
frontend/src/components/nodes/edit-image-node.tsx Add Pencil edit button
frontend/src/components/nodes/upload-image-node.tsx Add generatedResults/activeResultIndex support + Pencil edit button
frontend/src/components/nodes/character-node.tsx Add Pencil edit button
frontend/src/components/nodes/face-node.tsx Add Pencil edit button
frontend/src/components/nodes/object-node.tsx Add Pencil edit button
frontend/src/components/nodes/location-node.tsx Add Pencil edit button
frontend/src/components/editor/workflow-editor/execution-graph.ts Update upload-image output extraction to use generatedResults[activeIndex] with url fallback
backend/src/services/workflow-engine/output-extractor.ts Add upload-image to IMAGE_RESULT_TYPES set so backend orchestrator extracts from generatedResults

Upload Image Node Upgrade

upload-image-node currently uses a flat data model (url, r2Url, assetId) without generatedResults/activeResultIndex. To support the edit button consistently, add generatedResults + activeResultIndex to this node:

Error Handling

Backend Changes (Minimal)