Skip to content

[pro-web] feat: implement logo generation wizard with multi-step creation flow#601

Merged
AndlerRL merged 36 commits intodevelopfrom
feat/logo-generator
Feb 24, 2026
Merged

[pro-web] feat: implement logo generation wizard with multi-step creation flow#601
AndlerRL merged 36 commits intodevelopfrom
feat/logo-generator

Conversation

@derianrddev
Copy link
Contributor

@derianrddev derianrddev commented Jan 16, 2026

🎬 Video Demo

https://www.loom.com/share/ec78833eff904f8c9a440c49a08d475f

Summary by Sourcery

Introduce a multi-step AI-assisted logo generation flow integrated with the Brand Kit, including a completed wordmark style step, foundational direction/refinement/confirmation steps, and supporting typography and asset infrastructure.

New Features:

  • Add a dedicated Logo Generation wizard with a four-step flow (style, direction, refine, confirm) accessible from the Brand Kit dialog.
  • Implement a wordmark logo style designer with live preview, typography presets, casing/tracking/weight controls, and font role selection.
  • Enable logo refinement using preloaded brand kit reference assets, prompt generation helpers, and image generation wiring.
  • Add symbol and wordmark style selection screens backed by typed style enums and preset configurations.
  • Provide an image preview modal and logo reference slots for managing and previewing logo reference imagery.

Enhancements:

  • Extend the shared wizard stepper with a logo-generation variant, decorative checkpoint step, and improved responsive layout.
  • Augment workspace media state and actions to track logo generation choices, wordmark presets, and reference assets, and re-export font weight helpers for consumers.
  • Refactor Google Fonts configuration into a brand-kit–specific module with expanded weight support and utilities for mapping semantic wordmark weights.
  • Define strong brand-kit types for symbol/wordmark styles, casing, font weights, and presets, plus wordmark preset and casing option constants.
  • Improve Brand Kit dialog navigation to support switching between the Brand Kit wizard and the Logo Generation wizard entry point.

Build:

  • Add @radix-ui/react-radio-group as a UI dependency for font selection controls.

Summary by CodeRabbit

  • New Features

    • Brand Kit flow: multi-step generator (vibes, fonts, palettes) and logo generator with previews, refinement, download (ZIP) and save.
    • Per-scope image generation: separate media/wordmark/symbol instances with undo/redo, retry, initial-image and scope-aware prompts.
    • New workspace sheet modal and Pro chat controls for improved workspace interactions.
  • Improvements

    • Reference image management, selectable carousels, and snap slider controls.
    • Wordmark live preview (auto-scaling) and improved color-contrast/readability handling.
    • Consistent responsive sizing and UI refinements.

@derianrddev derianrddev self-assigned this Jan 16, 2026
@vercel
Copy link

vercel bot commented Jan 16, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
masterbots-pro Ready Ready Preview, Comment Feb 18, 2026 2:52pm
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
masterbots Skipped Skipped Feb 18, 2026 2:52pm

Request Review

@sourcery-ai
Copy link

sourcery-ai bot commented Jan 16, 2026

Reviewer's Guide

Implements a new multi-step Logo Generation wizard integrated with the existing Brand Kit flow, adds a full wordmark/logo-typography system (presets, casing, weights, auto-scaling preview), extends workspace media state/actions for logo data, and introduces precomputed Google Fonts and reference-asset helpers for AI logo generation.

Sequence diagram for Brand Kit checkpoint to Logo Generation wizard transition

sequenceDiagram
  actor User
  participant BrandKitDialog
  participant BrandKitWizard
  participant CheckpointStep
  participant WorkspaceMedia
  participant LogoGenerationWizard

  User->>BrandKitDialog: open Brand_Kit_dialog
  BrandKitDialog->>BrandKitWizard: render view brandKit

  User->>BrandKitWizard: complete previous_steps
  BrandKitWizard->>CheckpointStep: render CheckpointStep

  User->>CheckpointStep: click Generate_Logo
  CheckpointStep->>WorkspaceMedia: buildBrandKitData
  alt activeOrganization_exists
    CheckpointStep->>WorkspaceMedia: saveBrandKit
    WorkspaceMedia-->>CheckpointStep: save_success
    CheckpointStep->>WorkspaceMedia: clearBrandKitStep
  else no_activeOrganization
    Note over CheckpointStep,WorkspaceMedia: Skip_saving_brand_kit
  end

  CheckpointStep->>BrandKitWizard: onGenerateLogo_callback
  BrandKitWizard->>BrandKitDialog: onGenerateLogo
  BrandKitDialog->>BrandKitDialog: setCurrentView logoGeneration
  BrandKitDialog->>LogoGenerationWizard: render LogoGenerationWizard
  LogoGenerationWizard->>LogoGenerationWizard: initialize steps Logo_Style,Direction,Refine,Confirm
Loading

Sequence diagram for Refine Logo step AI generation with reference assets

sequenceDiagram
  actor User
  participant RefineLogoStep
  participant WorkspaceMedia
  participant LogoPromptHelper as logo-prompt
  participant LogoRefAssetsHelper as logo-reference-assets
  participant ImageGen as useImageGeneration

  RefineLogoStep->>WorkspaceMedia: get selectedLogoStyle
  RefineLogoStep->>WorkspaceMedia: get selectedSymbolStyle
  RefineLogoStep->>WorkspaceMedia: get selectedWordmarkStyle
  RefineLogoStep->>WorkspaceMedia: get wordmarkName
  RefineLogoStep->>WorkspaceMedia: get selectedVibes
  RefineLogoStep->>WorkspaceMedia: get selectedPalette
  RefineLogoStep->>WorkspaceMedia: get selectedFonts

  RefineLogoStep->>LogoRefAssetsHelper: preloadLogoReferenceAssets selectedFonts,selectedPalette,selectedLogoStyle,selectedWordmarkStyle,selectedSymbolStyle
  LogoRefAssetsHelper-->>RefineLogoStep: logoReferenceAssets[4]
  RefineLogoStep->>WorkspaceMedia: setLogoReferenceAssets logoReferenceAssets

  RefineLogoStep->>LogoPromptHelper: buildLogoPrompt selectedLogoStyle,selectedSymbolStyle,selectedWordmarkStyle,wordmarkName,selectedVibes,selectedPalette,selectedFonts
  LogoPromptHelper-->>RefineLogoStep: generatedPrompt
  RefineLogoStep->>RefineLogoStep: set prompt_if_not_dirty

  User->>RefineLogoStep: edit_prompt_optional
  User->>RefineLogoStep: click Generate_logo

  RefineLogoStep->>LogoRefAssetsHelper: getLogoReferenceChecksums selectedFonts,selectedPalette,selectedLogoStyle,selectedWordmarkStyle,selectedSymbolStyle
  LogoRefAssetsHelper-->>RefineLogoStep: referenceImageChecksums[]

  RefineLogoStep->>ImageGen: generateImage prompt,gemini-3-pro-image-preview,referenceImageChecksums,aspectRatio_1_1
  ImageGen-->>RefineLogoStep: generatedImage_or_error
  RefineLogoStep->>WorkspaceMedia: handlePromptSubmit prompt
  RefineLogoStep-->>User: show generated_logo_preview_or_error_state
Loading

Updated class diagram for workspace media state and brand kit logo types

classDiagram
  class WorkspaceMediaProvider {
    +generatedImage
    +selectedVibes
    +selectedFonts
    +selectedPalette
    +brandKitCurrentStep
    +isSavingBrandKit
    +selectedLogoStyle LogoStyle
    +selectedSymbolStyle SymbolStyle
    +selectedWordmarkStyle WordmarkStyle
    +wordmarkName string
    +logoReferenceAssets LogoReferenceAsset[]
    +wordmarkPreset WordmarkStyle
    +wordmarkCasing WordmarkCasing
    +wordmarkTracking number
    +wordmarkFontWeight WordmarkFontWeight
    +setSelectedLogoStyle(style)
    +setSelectedSymbolStyle(style)
    +setSelectedWordmarkStyle(style)
    +setWordmarkName(name)
    +setLogoReferenceAssets(assets)
    +addLogoReferenceAsset(asset,index)
    +removeLogoReferenceAsset(index)
    +setWordmarkPreset(preset)
    +setWordmarkCasing(casing)
    +setWordmarkTracking(tracking)
    +setWordmarkFontWeight(weight)
  }

  class LogoStyle {
    <<enumeration>>
    +wordmark
    +symbol
  }

  class LogoReferenceAsset {
    +type
    +previewUrl string
    +label string
    +metadata Record
  }

  class SymbolStyle {
    <<enumeration>>
    +monogram
    +geometric-badge
    +soft-blob
    +sharp-crest
    +abstract-symbol
    +literal-pictogram
    +negative-space
    +line-grid
    +emblem-seal
    +motion-arrow
    +mascot-head
    +pattern-tile
  }

  class WordmarkStyle {
    <<enumeration>>
    +all-caps-tight
    +all-caps-wide
    +title-case-neutral
    +lowercase-soft
    +heavy-weight
    +light-weight
    +condensed
    +extended-wide
    +small-caps
    +custom-letter-tweak
    +accent-touch
    +color-system-set
  }

  class WordmarkCasing {
    <<enumeration>>
    +all-caps
    +title-case
    +lowercase
    +small-caps
  }

  class WordmarkFontWeight {
    <<enumeration>>
    +extra_light
    +light
    +regular
    +medium
    +semibold
    +bold
    +black
  }

  class WordmarkPreset {
    +id WordmarkStyle
    +title string
    +subtitle string
    +casingMode
    +fixedCasing WordmarkCasing
    +trackingMin number
    +trackingMax number
    +defaultTracking number
    +allowedWeights WordmarkFontWeight[]
    +defaultWeight WordmarkFontWeight
    +notes string
  }

  class CasingOption {
    +id WordmarkCasing
    +label string
    +icon LucideIcon
  }

  WorkspaceMediaProvider "1" --> "*" LogoReferenceAsset : manages
  WorkspaceMediaProvider --> LogoStyle
  WorkspaceMediaProvider --> SymbolStyle
  WorkspaceMediaProvider --> WordmarkStyle
  WorkspaceMediaProvider --> WordmarkCasing
  WorkspaceMediaProvider --> WordmarkFontWeight
  WordmarkPreset --> WordmarkStyle
  WordmarkPreset --> WordmarkCasing
  WordmarkPreset --> WordmarkFontWeight
  CasingOption --> WordmarkCasing
Loading

Flow diagram for logo reference assets and AI generation

flowchart LR
  subgraph Brand_Kit_state
    A_selectedFonts["Selected fonts in WorkspaceMedia"]
    B_selectedPalette["Selected palette in WorkspaceMedia"]
    C_logoStyle["selectedLogoStyle"]
    D_wordmarkStyle["selectedWordmarkStyle"]
    E_symbolStyle["selectedSymbolStyle"]
  end

  subgraph Refine_Logo_step
    F_preload["preloadLogoReferenceAssets"]
    G_assets["logoReferenceAssets[4]"]
    H_slot["LogoReferenceSlot components"]
    I_checksums["getLogoReferenceChecksums"]
  end

  subgraph Image_generation
    J_generate["useImageGeneration.generateImage"]
    K_model["Gemini image model"]
  end

  A_selectedFonts --> F_preload
  B_selectedPalette --> F_preload
  C_logoStyle --> F_preload
  D_wordmarkStyle --> F_preload
  E_symbolStyle --> F_preload

  F_preload --> G_assets
  G_assets --> H_slot
  H_slot --> I_checksums

  A_selectedFonts --> I_checksums
  B_selectedPalette --> I_checksums
  C_logoStyle --> I_checksums
  D_wordmarkStyle --> I_checksums
  E_symbolStyle --> I_checksums

  I_checksums --> J_generate
  J_generate --> K_model

  subgraph Checksum_lookup
    L_fonts["BRAND_KIT_CHECKSUMS.fonts"]
    M_palettes["BRAND_KIT_CHECKSUMS.palettes"]
    N_wordmark["BRAND_KIT_CHECKSUMS.wordmark"]
    O_symbol["BRAND_KIT_CHECKSUMS.symbol"]
  end

  I_checksums --> L_fonts
  I_checksums --> M_palettes
  I_checksums --> N_wordmark
  I_checksums --> O_symbol
Loading

File-Level Changes

Change Details Files
Extend wizard stepper to support logo generation variant with decorative checkpoint and goal icons plus improved responsive layout.
  • Add a variant prop to distinguish brand kit vs logo generation stepper behavior and visuals
  • Insert a decorative non-clickable "Checkpoint" step for logo generation flows while preserving index mapping
  • Use Goal icon for the final logo-generation step and FlagTriangleRight for the checkpoint/brand kit final step
  • Rework layout with fixed-width step containers, connector lines, and better spacing and accessibility
apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/wizard-stepper.tsx
Add logo generation state and actions to workspace media context, including wordmark preset configuration and reference asset management.
  • Introduce LogoStyle and LogoReferenceAsset types and wire them into MediaWorkspaceState
  • Track selected logo style, symbol/wordmark styles, wordmark name, reference assets, and wordmark typography parameters (preset, casing, tracking, weight)
  • Expose setters plus helpers to add/remove reference assets and update wordmark configuration
  • Initialize sensible defaults for logo-related state and re-export font weight helpers for use in UI components
apps/pro-web/lib/hooks/use-workspace-media.tsx
Refactor BrandKit dialog into separate Brand Kit and Logo Generation wizards with navigation between entry, checkpoint, and logo flows.
  • Expand BrandKitDialog view state to include distinct brandKit and logoGeneration views and adjust subtitle logic
  • Route "New Brand" into the BrandKitWizard and "Generate Logo" into the LogoGenerationWizard
  • Change checkpoint step to call an onGenerateLogo callback instead of advancing the generic wizard, saving brand kit first when possible
  • Simplify BrandKitWizard to always advance internally while delegating logo-generation transition to its parent
apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/brand-kit-dialog.tsx
apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/checkpoint-step.tsx
apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/brand-kit-wizard.tsx
apps/pro-web/components/ui/wizard/index.tsx
Introduce a Logo Generation wizard with four steps and shared stepper integration.
  • Create LogoGenerationWizard wrapper that uses useWizard, plugs into the shared WizardStepper with logoGeneration variant, and handles finish/cancel behavior
  • Wire steps for style selection, direction, refinement, and confirmation into a single flow
apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/logo-generation/logo-generation-wizard.tsx
Implement a full-featured Logo Style step with wordmark presets, live preview, typography controls, and font-weight availability logic.
  • Build LogoStyleStep UI with main preview card, name input, font-role radio selection, casing controls, tracking slider, and weight slider
  • Integrate WORDMARK_PRESETS and CASING_OPTIONS to drive presets, enforce fixed/free casing rules, and apply preset-specific tracking/weight defaults
  • Use getAvailableWordmarkWeights and getFallbackWeight to constrain weights to those supported by the chosen Google font family
  • Persist selections into workspace media state (logo style, name, preset, casing, tracking, weight) and advance the wizard once a valid name is set
apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/logo-generation/steps/logo-style-step.tsx
apps/pro-web/lib/constants/brand-kit/wordmark.ts
apps/pro-web/types/brand-kit.types.ts
Add reusable wordmark preview, snap-slider, and radio-group UI primitives to support interactive logo-typography editing.
  • Create WordmarkPreview component that applies casing (including small-caps), tracking, weight, and auto-scales font size using canvas measurement and ResizeObserver
  • Add SnapSlider component for discrete option selection with keyboard support, drag interaction, and labeled extremes/icons
  • Introduce a Radix-based RadioGroup and RadioGroupItem wrapper for choosing between primary and secondary fonts
apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/logo-generation/wordmark-preview.tsx
apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/logo-generation/snap-slider.tsx
apps/pro-web/components/ui/radio-group.tsx
Add Google Fonts constants and helpers for wordmark typography, including per-family weight availability and semantic-to-numeric weight mapping.
  • Move and significantly expand Google Fonts definitions into a brand-kit–scoped module with 80+ families and full weight ranges
  • Expose FONT_WEIGHTS_BY_FAMILY, a GOOGLE_FONTS map, and type aliases for font family/weights
  • Map semantic WordmarkFontWeight values to numeric CSS weights and back
  • Provide getAvailableWordmarkWeights and getFallbackWeight helpers for filtering and choosing valid weights per family
  • Remove the old google-fonts constants file in favor of the new brand-kit namespace
apps/pro-web/lib/constants/brand-kit/google-fonts.ts
apps/pro-web/lib/constants/google-fonts.ts
Add reference-asset infrastructure (checksums, preload helpers, and UI slots) to feed logo generation with precomputed image references.
  • Introduce BRAND_KIT_CHECKSUMS and getBrandKitChecksum to map static font/palette/wordmark/symbol images to precomputed checksums
  • Add helpers to preload LogoReferenceAsset instances from current brand-kit selections and to derive reference checksums without uploads
  • Create LogoReferenceSlot UI for dropzone/file-input based logo reference tiles with thumbnail preview/removal and drag-and-drop handling
  • Implement an ImagePreviewModal that overlays a large preview on top of Radix dialogs while carefully intercepting events
apps/pro-web/lib/constants/brand-kit/brand-kit-checksums.ts
apps/pro-web/lib/helpers/logo-reference-assets.ts
apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/logo-generation/logo-reference-slot.tsx
apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/logo-generation/image-preview-modal.tsx
Implement a Refine Logo step that builds an AI prompt from brand-kit selections, wires in reference assets/checksums, and triggers image generation.
  • Preload logo reference assets into workspace media when the step mounts based on selected fonts, palette, logo style, and style variants
  • Auto-generate a prompt via buildLogoPrompt while allowing users to edit it (tracked via a dirty flag)
  • Use getLogoReferenceChecksums and useImageGeneration to generate a new logo image with reference checksums and a fixed 1:1 aspect ratio, handling loading and error states
  • Render four LogoReferenceSlot tiles, a preview pane for the generated image, and a prompt textarea with submit button
apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/logo-generation/steps/refine-logo-step.tsx
apps/pro-web/lib/helpers/logo-prompt.ts
Implement a Logo Direction step and a stub Confirm step for selecting high-level logo layout and finalizing generation.
  • Add LogoDirectionStep that conditionally displays symbol or wordmark style options backed by static preview images and persists the selected style in workspace media
  • Include LogoConfirmStep placeholder that provides basic confirmation UI and a stubbed generate handler
  • Define SymbolStyle and WordmarkStyle enumerations consistent with available preview assets
apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/logo-generation/steps/logo-direction-step.tsx
apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/logo-generation/steps/logo-confirm-step.tsx
apps/pro-web/types/brand-kit.types.ts
Wire Radix radio-group dependency for new typography controls.
  • Add @radix-ui/react-radio-group to the pro-web app dependencies
apps/pro-web/package.json

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 16, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a full brand‑kit and multi‑scope logo generation system: new UI wizards and steps, per‑scope image generation, GCS upload/deduplication for logo assets, checksum-backed reference image mapping, many brand‑kit constants/types, image/base64/ZIP helpers, and refactors to media/workspace hooks and components.

Changes

Cohort / File(s) Summary
Organization API
apps/pro-web/app/api/organizations/[id]/route.ts
PATCH accepts logoAssets?: LogoAssetsInput and brandKit?: Partial<BrandKitData, validates/merges brandKit, uploads assets via uploadLogoAssetToGCS, persists merged brandKit, and returns transformed org data.
Media templates route
apps/pro-web/app/api/media/templates/route.ts
Updated import path for parseTemplateFilename to .../media-tab/template.
Brand‑kit UI & wizards
apps/pro-web/components/routes/workspace/media-tab/brand-kit/...
Adds BrandKitFlow, BrandKitGenerator/LogoGenerator wizards, many wizard steps (vibe, fonts, colors, checkpoint, refine, confirm, etc.), and shared UI primitives (DualActionCard, SnapSlider, SelectableCarousel, WizardStepper, WordmarkPreview).
Chat & prompt UI
apps/pro-web/components/routes/chat/prompt-form/index.tsx, .../pro/chat-panel-pro-controls.tsx, .../pro/chat-panel-pro.tsx
PromptForm extended for multi‑scope generation (media/wordmark/symbol) with per‑scope handlers; adds ChatPanelProControls and refactors ChatPanelPro to provider pattern.
Workspace media hook & context
apps/pro-web/lib/hooks/use-workspace-media.tsx, apps/pro-web/lib/contexts/chat-panel-pro-context.tsx
Major API expansion: per‑scope reference images, multi‑instance generation (media/wordmark/symbol) with getGenerationInstance, scope-aware prompt handlers, saveBrandKit/saveLogoAssets mutations, palette shuffling, and reference image management APIs.
Logo asset helpers (server & client)
apps/pro-web/lib/helpers/workspace/media-tab/logo-assets.server.ts, .../logo-assets.client.ts
Server: uploadLogoAssetToGCS (base64 decode, SHA‑256 dedupe, GCS upload, signed URL); Client: fetchSignedUrlAsBase64.
Logo prompts & reference mapping
apps/pro-web/lib/helpers/workspace/media-tab/logo-prompt.ts, .../logo-reference-image-mapper.ts
Adds buildLogoBasePrompt, buildRefineLogoPrompt, and utilities to resolve logo reference specs to ReferenceImagePayloads via precomputed checksums.
Brand‑kit constants & types
apps/pro-web/types/brand-kit.types.ts, apps/pro-web/types/media.types.ts, apps/pro-web/lib/constants/brand-kit/*
Adds typed models, constants (PALETTES, VIBES, SYMBOL_OPTIONS, font options, WORDMARK_PRESETS, prompts), font-weight helpers, and generated BRAND_KIT_CHECKSUMS.
Image helpers & generation
apps/pro-web/lib/helpers/ai-image.ts, apps/pro-web/lib/helpers/color-utils.ts, apps/pro-web/lib/hooks/use-image-generation.tsx
Adds base64/URL/ZIP helpers, WCAG color utilities, permutations helper, and extends image-generation hook with history (undo/redo), retry, addInitialImage.
Reference images & sidebar
apps/pro-web/components/routes/workspace/media-tab/reference-images/*, .../media-sidebar.tsx, media-canvas.tsx, media-workspace.tsx, wizards/media-wizards.tsx
ReferenceImagesPanel and ReferenceUploadSlot converted to prop-driven components; media workspace wired to scope-aware handlers; minor sizing/class token refactors.
UI primitives & layout
apps/pro-web/components/ui/radio-group.tsx, apps/pro-web/components/ui/sheet.tsx, apps/pro-web/components/ui/workspace-sheet/index.tsx, apps/pro-web/components/ui/workspace-dialog/*
Adds Radix-based RadioGroup, SheetContent gains optional container prop, WorkspaceSheet replaces removed WorkspaceDialog and provides panel slot and image-zoom escape handling.
ImageDisplay & types
apps/pro-web/components/shared/image-display.tsx, apps/pro-web/types/index.ts
ImageDisplay props: showModelLabel, showDownloadButton, labelText; types updated to expose image-generation history/retry APIs (undo/redo/retry/addInitialImage).
Styles & build
apps/pro-web/app/globals.css, apps/pro-web/package.json, apps/pro-web/scripts/upload-brand-kit-assets.ts
Radix ScrollArea overflow fix; package additions (@radix-ui/react-radio-group, jszip) and upload-brand-kit-assets.ts script that uploads assets and emits brand-kit-checksums.ts.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant API as Org API
    participant Uploader as uploadLogoAssetToGCS
    participant GCS as GoogleCloudStorage
    participant GraphQL as GraphQL
    participant DB as OrgDataStore

    Client->>API: PATCH /api/organizations/:id (brandKit + logoAssets)
    API->>API: validate & merge incoming brandKit
    API->>Uploader: uploadLogoAssetToGCS(orgId, type, base64)
    Uploader->>GCS: compute checksum, check object existence
    alt object missing
        Uploader->>GCS: upload object
    end
    GCS-->>Uploader: bucketKey + signedUrl
    Uploader-->>API: LogoAsset metadata (url, checksum, bucketKey)
    API->>GraphQL: mutate organization { brandKit: updatedBrandKit }
    GraphQL->>DB: persist updatedBrandKit
    DB-->>GraphQL: mutation result
    GraphQL-->>API: updated organization
    API-->>Client: 200 OK with transformed org data
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

Suggested labels

difficulty:hard

Suggested reviewers

  • AndlerRL

"I’m a rabbit who paints with font and hue,
I hop through wizards, prompts, checksums too.
I stash logos in clouds with a tiny cheer,
Zip colors and prompts — brand magic’s here! 🐇✨"

🚥 Pre-merge checks | ✅ 3 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly and concisely summarizes the main feature: implementing a logo generation wizard with a multi-step creation flow, which aligns with the extensive changes throughout the codebase.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into develop

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/logo-generator

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@vercel vercel bot temporarily deployed to Preview – masterbots January 16, 2026 16:56 Inactive
@vercel vercel bot temporarily deployed to Preview – masterbots January 20, 2026 19:15 Inactive
@vercel vercel bot temporarily deployed to Preview – masterbots January 21, 2026 22:34 Inactive
@vercel vercel bot temporarily deployed to Preview – masterbots January 22, 2026 21:34 Inactive
@vercel vercel bot temporarily deployed to Preview – masterbots January 24, 2026 02:29 Inactive
@vercel vercel bot temporarily deployed to Preview – masterbots January 26, 2026 22:45 Inactive
@vercel vercel bot temporarily deployed to Preview – masterbots January 28, 2026 22:08 Inactive
@vercel vercel bot temporarily deployed to Preview – masterbots January 29, 2026 16:52 Inactive
@vercel vercel bot temporarily deployed to Preview – masterbots January 29, 2026 20:15 Inactive
@vercel vercel bot temporarily deployed to Preview – masterbots January 29, 2026 22:12 Inactive
@vercel vercel bot temporarily deployed to Preview – masterbots February 3, 2026 22:31 Inactive
@vercel vercel bot temporarily deployed to Preview – masterbots February 5, 2026 01:11 Inactive
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@apps/pro-web/lib/hooks/use-workspace-media.tsx`:
- Around line 759-817: shuffleSelectedPaletteOrder currently builds permutations
from the live selectedPalette colors so once you swap them the next call
permutes the wrong input; fix by storing a canonical color array for each
palette (e.g., a map keyed by selectedPalette.name to the original
[primary,accent,bg,text] created when a palette is first selected) and have
shuffleSelectedPaletteOrder generate permutations from that canonical array
instead of selectedPalette; keep using shuffleState[paletteName] to track the
cursor, update setShuffleState and setSelectedPalette as before (using
nextPerm), and ensure setColorRoleItems still maps positions consistently to
roles (primary->0, accent->1, bg->2, text->3) so selection state remains per
position.
- Around line 297-310: The getGenerationInstance useCallback is being recreated
every render because useImageGeneration() returns fresh objects; fix by
memoizing each generation instance (the results of useImageGeneration()) with
useMemo so wordmarkGeneration, symbolGeneration, and mediaGeneration have stable
references, then keep getGenerationInstance as a useCallback that depends on
those memoized values (or remove useCallback if consumer identity isn't
required); update consumers like logo-generator-wizard.tsx to rely on the
now-stable getGenerationInstance reference.
🧹 Nitpick comments (9)
apps/pro-web/components/routes/pro/chat-panel-pro.tsx (1)

13-29: Several declared props are unused.

onConvertToDocument is destructured (Line 36) but never referenced. The dialog-state props on Lines 20–28 (convertDialogOpen, setConvertDialogOpen, selectedMessageId, convertedText, setConvertedText, targetProject, setTargetProject, targetDocument, setTargetDocument) are declared in the interface but neither destructured nor passed anywhere. If these are planned for ChatPanelProControls, they should be wired through the context or passed as props; otherwise, trim the interface to avoid a misleading public API.

apps/pro-web/components/routes/pro/chat-panel-pro-controls.tsx (4)

138-145: Avoid any for chatOptions parameter.

The biome-ignore suppresses the lint warning, but ChatRequestOptions (already imported on Line 22) is the expected type here.

Proposed fix
 const prepareMessageOptions = useCallback(
-  // biome-ignore lint/suspicious/noExplicitAny: <explanation>
-  (chatOptions: any) => ({
+  (chatOptions: ChatRequestOptions) => ({
     ...chatOptions,
     webSearch: webSearch,
   }),
   [webSearch],
 )

147-178: handleContinueGeneration is recreated every render — consider useCallback.

This async handler is passed to a Button's onClick. Without useCallback, a new function reference is created each render, which can cause unnecessary re-renders of child components. The dependencies are stable (getContinuationPrompt, append, chatId, continueGeneration, chatbot, setIsCutOff, setIsContinuing).


309-344: Remove commented-out code blocks.

These large commented-out sections (active action indicator, Deep Expertise toggle, web search toggle) add visual noise and reduce readability. If they represent planned features, track them in issues instead.


446-453: Simplify !Boolean(chatbot) to !chatbot.

The double negation with Boolean() is unnecessary for a truthiness check on an optional object, hence the biome lint warning. !chatbot is equivalent and idiomatic.

Proposed fix
 disabled={
-  // biome-ignore lint/complexity/noExtraBooleanCast: <explanation>
-  !Boolean(chatbot) ||
+  !chatbot ||
   isLoading ||
   isPreProcessing ||
   processingState !== 'idle'
 }
apps/pro-web/lib/hooks/use-workspace-media.tsx (4)

158-175: Extract the logo assets payload type to avoid triple duplication.

This exact inline type is repeated in saveLogoAssetsMutation (lines 634–648) and saveLogoAssets (lines 713–727). Extract it once and reuse.

♻️ Suggested refactor

Define the type once near the other interfaces (or in brand-kit.types.ts):

interface LogoAssetsPayload {
  symbol?: {
    base64: string
    style?: string
  }
  wordmark?: {
    base64: string
    style?: string
    name: string
    fontFamily?: string
    casing?: string
    tracking?: number
    fontWeight?: string
  }
}

Then reference LogoAssetsPayload in the interface, mutation, and callback.


629-702: Near-identical mutation logic duplicated from saveBrandKitMutation.

saveLogoAssetsMutation (lines 629–702) mirrors saveBrandKitMutation (lines 567–626) almost line-for-line — same endpoint, same IDB sync, same invalidation. Only the request body key differs (brandKit vs logoAssets).

Extract the shared PATCH → IDB-sync → invalidate pattern into a helper to reduce ~70 lines of duplication.

♻️ Sketch
async function patchOrganizationAndSyncIDB(
  organizationId: string,
  body: Record<string, unknown>,
  getItem: typeof getItem,
  updateItem: typeof updateItem,
) {
  const response = await fetch(`/api/organizations/${organizationId}`, {
    method: 'PATCH',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(body),
  })
  if (!response.ok) throw new Error(`Failed to save to server`)
  const result = await response.json()

  const workspaceData = await getItem(WORKSPACE_STRUCTURE_KEY)
  if (workspaceData && typeof workspaceData === 'object') {
    const workspace = workspaceData as unknown as WorkspaceStructure
    const updatedBrandKit = result.data?.brandKit as BrandKitData | undefined
    if (updatedBrandKit) {
      const updatedWorkspace: WorkspaceStructure = {
        ...workspace,
        organizationData: workspace.organizationData.map(
          (org: OrganizationData) =>
            org.id === organizationId
              ? { ...org, brandKit: updatedBrandKit }
              : org,
        ),
      }
      await updateItem(WORKSPACE_STRUCTURE_KEY, structureToIDBPayload(updatedWorkspace))
    }
  }
  return result
}

Then both mutations become one-liners in mutationFn.


379-423: handleWordmarkPromptSubmit and handleSymbolPromptSubmit are near-duplicates — consider a shared helper.

The two functions differ only in the scope ('wordmark' / 'symbol') and the generation instance. A single parameterized function would eliminate ~40 lines of duplication:

♻️ Sketch
const handleScopedPromptSubmit = async (
  scope: 'wordmark' | 'symbol',
) => async (prompt: string) => {
  const refs = referenceImagesByScope[scope]
  const checksums = refs.length > 0 ? refs.map(r => r.checksum) : undefined
  const gen = scope === 'wordmark' ? wordmarkGeneration : symbolGeneration
  await gen.generateImage(prompt, 'gemini-3-pro-image-preview', {
    previousImageData: gen.generatedImage || undefined,
    referenceImageChecksums: checksums,
    aspectRatio: '1:1',
  })
}

826-927: Consider splitting this provider — 50+ context values will re-render all consumers on any change.

This single context carries image generation state (3 scopes), brand kit state, logo generation state, UI toggles, palette state, and all associated actions. Any state change triggers a re-render of every consumer.

During active image generation (where isLoading/generatedImage update frequently), unrelated consumers (e.g., brand kit step UI) will re-render needlessly. Consider splitting into focused contexts (e.g., LogoGenerationContext, BrandKitContext) or memoizing sub-objects with useMemo.

Comment on lines +297 to +310
// Three separate image generation instances
const mediaGeneration = useImageGeneration()
const wordmarkGeneration = useImageGeneration()
const symbolGeneration = useImageGeneration()

// Helper to get the correct instance based on scope
const getGenerationInstance = useCallback(
(scope: GenerationScope) => {
if (scope === 'wordmark') return wordmarkGeneration
if (scope === 'symbol') return symbolGeneration
return mediaGeneration
},
[wordmarkGeneration, symbolGeneration, mediaGeneration],
)
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find the useImageGeneration hook implementation
fd "use-image-generation" --type f -e ts -e tsx

Repository: bitcashorg/masterbots

Length of output: 156


🏁 Script executed:

#!/bin/bash
# Read the useImageGeneration hook implementation
cat -n apps/pro-web/lib/hooks/use-image-generation.tsx

Repository: bitcashorg/masterbots

Length of output: 9453


🏁 Script executed:

#!/bin/bash
# Check how getGenerationInstance is used in the file
cat -n apps/pro-web/lib/hooks/use-workspace-media.tsx | grep -A 5 -B 5 "getGenerationInstance"

Repository: bitcashorg/masterbots

Length of output: 1268


🏁 Script executed:

#!/bin/bash
# Check the full context of how getGenerationInstance is used and what context it's part of
sed -n '290,330p' apps/pro-web/lib/hooks/use-workspace-media.tsx

Repository: bitcashorg/masterbots

Length of output: 1649


🏁 Script executed:

#!/bin/bash
# Check where getGenerationInstance is consumed - look for useEffect/useCallback dependencies
rg "getGenerationInstance" apps/pro-web/lib/hooks/use-workspace-media.tsx -A 3 -B 1

Repository: bitcashorg/masterbots

Length of output: 614


🏁 Script executed:

#!/bin/bash
# Check the full return value of this hook to see the context object structure
sed -n '860,950p' apps/pro-web/lib/hooks/use-workspace-media.tsx

Repository: bitcashorg/masterbots

Length of output: 1914


🏁 Script executed:

#!/bin/bash
# Look for where getGenerationInstance is actually consumed/used
# Check if it's used in useEffect dependencies or passed to children
rg "getGenerationInstance" apps/pro-web --type tsx --type ts -B 2 -A 2

Repository: bitcashorg/masterbots

Length of output: 92


🏁 Script executed:

#!/bin/bash
# Look for where getGenerationInstance is actually consumed/used
rg "getGenerationInstance" apps/pro-web -A 2 -B 2

Repository: bitcashorg/masterbots

Length of output: 8949


useCallback for getGenerationInstance is ineffective due to unstable dependencies and causes downstream effects to re-run unnecessarily.

useImageGeneration() returns a new object reference on every render and is not memoized. This means the dependency array [wordmarkGeneration, symbolGeneration, mediaGeneration] always contains new references, making the useCallback recreate the function on every render. Additionally, consumers like logo-generator-wizard.tsx include getGenerationInstance in useEffect dependencies, causing those effects to re-run every render instead of only when actual logic changes.

Memoize the return value of useImageGeneration() using useMemo to provide stable references, or alternatively remove the useCallback if function identity is not critical for consumers.

🤖 Prompt for AI Agents
In `@apps/pro-web/lib/hooks/use-workspace-media.tsx` around lines 297 - 310, The
getGenerationInstance useCallback is being recreated every render because
useImageGeneration() returns fresh objects; fix by memoizing each generation
instance (the results of useImageGeneration()) with useMemo so
wordmarkGeneration, symbolGeneration, and mediaGeneration have stable
references, then keep getGenerationInstance as a useCallback that depends on
those memoized values (or remove useCallback if consumer identity isn't
required); update consumers like logo-generator-wizard.tsx to rely on the
now-stable getGenerationInstance reference.

Comment on lines +759 to +817
// Shuffle selected palette order deterministically
const shuffleSelectedPaletteOrder = useCallback(() => {
if (!selectedPalette) return

const paletteName = selectedPalette.name
const baseColors = [
selectedPalette.primary,
selectedPalette.accent,
selectedPalette.bg,
selectedPalette.text,
]

// Generate all valid permutations
const allPermutations = generatePermutations(baseColors)

// Filter to valid combinations only
const validPermutations = allPermutations.filter((perm) => {
const [, , bg, text] = perm
// bg and text must have good contrast (WCAG AA)
return isValidTextCombination(bg, text)
})

if (validPermutations.length === 0) {
console.warn('[shuffleSelectedPaletteOrder] No valid permutations found')
return
}

// Get current cursor for this palette (default to 0)
const currentCursor = shuffleState[paletteName] ?? 0

// Advance to next valid permutation (cycle)
const nextCursor = (currentCursor + 1) % validPermutations.length
const nextPerm = validPermutations[nextCursor]

// Update shuffle state
setShuffleState((prev) => ({ ...prev, [paletteName]: nextCursor }))

// Update selectedPalette with new color assignments
setSelectedPalette({
name: selectedPalette.name,
category: selectedPalette.category,
primary: nextPerm[0],
accent: nextPerm[1],
bg: nextPerm[2],
text: nextPerm[3],
})

// Also update colorRoleItems to match the new palette order
// Keep selection state per position
setColorRoleItems((prev) => {
const selections = prev.map((item) => item.selected)
return [
{ role: 'primary', color: nextPerm[0], selected: selections[0] },
{ role: 'secondary', color: nextPerm[2], selected: selections[1] }, // bg maps to secondary
{ role: 'accent', color: nextPerm[1], selected: selections[2] },
{ role: 'muted', color: nextPerm[3], selected: selections[3] }, // text maps to muted
]
})
}, [selectedPalette, shuffleState])
Copy link
Contributor

@coderabbitai coderabbitai bot Feb 13, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Bug: Cursor-based shuffle breaks after the first invocation because input colors change.

shuffleSelectedPaletteOrder generates permutations from selectedPalette's current colors (line 764–769), then updates selectedPalette with the chosen permutation (line 797–804). On the next call, baseColors reflects the already-shuffled order, producing a completely different permutation set — the stored cursor (shuffleState[paletteName]) now indexes into the wrong list.

Effect: After the first shuffle, subsequent shuffles will not cycle through all valid permutations deterministically; they may repeat, skip, or land on unexpected orderings.

Fix: Derive permutations from the palette's original/canonical color ordering (e.g., store the initial [primary, accent, bg, text] once per palette) rather than the live selectedPalette state.

🤖 Prompt for AI Agents
In `@apps/pro-web/lib/hooks/use-workspace-media.tsx` around lines 759 - 817,
shuffleSelectedPaletteOrder currently builds permutations from the live
selectedPalette colors so once you swap them the next call permutes the wrong
input; fix by storing a canonical color array for each palette (e.g., a map
keyed by selectedPalette.name to the original [primary,accent,bg,text] created
when a palette is first selected) and have shuffleSelectedPaletteOrder generate
permutations from that canonical array instead of selectedPalette; keep using
shuffleState[paletteName] to track the cursor, update setShuffleState and
setSelectedPalette as before (using nextPerm), and ensure setColorRoleItems
still maps positions consistently to roles (primary->0, accent->1, bg->2,
text->3) so selection state remains per position.

Copy link
Member

Choose a reason for hiding this comment

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

Good to dive deep and test edge cases in case they shuffle a lot or one or more colours are disabled

Copy link
Contributor

Choose a reason for hiding this comment

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

@AndlerRL, great point about the edge cases! Let me break down the current behavior and potential issues:

Current Issues & Edge Cases

1. Main Bug (Shuffling Multiple Times)

As CodeRabbit identified, after the first shuffle, the baseColors array is built from already-shuffled colors. This means:

  • 1st shuffle: permutes [primary, accent, bg, text]
  • 2nd shuffle: permutes whatever the 1st shuffle produced ❌
  • 3rd+ shuffles: increasingly unpredictable ❌

The cursor becomes meaningless because it's indexing into a different permutation set each time.

2. Disabled Colors Edge Case

Currently, shuffleSelectedPaletteOrder generates permutations from all 4 colors regardless of which ones are selected/disabled in colorRoleItems. The selection state is preserved per position (lines 811-817), but disabled colors still participate in the shuffle.

Question: When one or more colors are disabled, should the shuffle:

  • Option A: Only permute the selected/enabled colors, leaving disabled ones in their positions?
  • Option B: Permute all 4 colors but only show selected ones in the UI (current behavior)?

3. Empty Permutations

If all permutations fail the WCAG contrast check, the function warns and exits (line 781-782), but leaves the palette in whatever state it was in.

Suggested Fix

To address the main bug and these edge cases:

  1. Store canonical colors when a palette is first selected
  2. Generate permutations from canonical colors (not current shuffled state)
  3. Respect disabled colors (if desired) by filtering permutations or only shuffling selected colors
  4. Handle edge cases: empty valid permutations, rapid multiple shuffles

Would you like me to provide implementation guidance for these fixes? Also, please clarify the desired behavior for disabled colors during shuffle (Option A or B above).


🧠 Learnings used
Learnt from: AndlerRL
Repo: bitcashorg/masterbots PR: 0
File: :0-0
Timestamp: 2026-01-22T23:10:37.056Z
Learning: AndlerRL emphasizes Modular Development: avoid duplication by centralizing shared utilities/types, prefer small composable units, and keep flows simple and explicit (no cumbersome validations or over-engineering).

Learnt from: AndlerRL
Repo: bitcashorg/masterbots PR: 542
File: apps/web/components/layout/profile/layout.tsx:67-68
Timestamp: 2025-08-29T20:34:24.433Z
Learning: AndlerRL prefers skeleton loading states over conditional rendering for better UX when components are waiting for data to load. Components should show loading indicators rather than blank spaces.

Learnt from: AndlerRL
Repo: bitcashorg/masterbots PR: 600
File: apps/pro-web/components/routes/chat/chat-list/message-pair-accordion.tsx:221-221
Timestamp: 2026-01-22T23:03:44.735Z
Learning: In TSX files (e.g., apps/pro-web/components/routes/chat/chat-list/message-pair-accordion.tsx), adopt a convention where variables that reference DOM elements are prefixed with '$' (e.g., $questionElement, $el) to quickly identify DOM refs. Apply this consistently across the codebase to improve readability and searchability; ensure the prefix is only used for actual DOM element references and not for non-DOM variables.

Learnt from: AndlerRL
Repo: bitcashorg/masterbots PR: 600
File: apps/pro-web/components/routes/chat/chat-list/message-pair-accordion.tsx:40-40
Timestamp: 2026-01-22T23:03:49.088Z
Learning: In TypeScript React files, prefer using optional chaining with a nullish coalescing fallback (e.g., object?.param ?? fallback) over explicit type-guard helpers like isObject or asType when accessing nested properties. This keeps code simpler and more idiomatic. Ensure you still handle cases where intermediate properties may be undefined before applying the ?? fallback.

Learnt from: AndlerRL
Repo: bitcashorg/masterbots PR: 600
File: apps/pro-web/components/routes/chat/chat-list/message-pair-accordion.tsx:221-221
Timestamp: 2026-01-22T23:03:43.504Z
Learning: Enforce a DOM element naming convention in the pro-web app: in TSX files (e.g., apps/pro-web/**/*.tsx), prefix variables that hold DOM element references with a leading '$'. For example: const $questionElement = document.getElementById('question'); or const $list = document.querySelector('.list'); This helps quickly identify DOM refs and distinguish them from other variables. Apply this to variables obtained via document.getElementById, querySelector, etc., and ensure non-DOM variables are not prefixed.

@vercel vercel bot temporarily deployed to Preview – masterbots February 13, 2026 16:14 Inactive
"format:write": "prettier --write \"{app,lib,components}/**/*.{ts,tsx,mdx}\" --cache",
"format:check": "prettier --check \"{app,lib,components}**/*.{ts,tsx,mdx}\" --cache"
"format:check": "prettier --check \"{app,lib,components}**/*.{ts,tsx,mdx}\" --cache",
"upload-brand-kit-assets": "bun run scripts/upload-brand-kit-assets.ts"
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Note: With the current approach, if the GCS environment variables change or the bucket is cleared/removed, this script needs to be run again to re-upload the base Brand Kit assets (logo styles, fonts, and palettes).

TODO: Investigate whether there is a better “seed” or automatic preload strategy for assets in GCS, or move this step to CI/CD in the future to avoid manual dependency.

CC: @AndlerRL

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/pro-web/components/routes/chat/prompt-form/index.tsx (1)

509-616: ⚠️ Potential issue | 🟠 Major

Async submission handlers lack error handling — unhandled rejections possible.

handleWordmarkPromptSubmit, handleSymbolPromptSubmit, handleMediaPromptSubmit, and onSubmit are all awaited but none are wrapped in try/catch. If any of these reject, the promise rejection goes unhandled. Combined with the premature setInput(''), the user loses their input with no feedback.

Wrap the async calls (or the entire function body after validation) in a try/catch that shows an error via customSonner and restores the input on failure.

As per coding guidelines: "Handle GraphQL operation failures at network, parsing, and business logic levels with meaningful fallbacks for degraded functionality" and "Test assumptions about API calls and DOM operations - don't assume success, always verify results and provide meaningful fallbacks."

🤖 Fix all issues with AI agents
In `@apps/pro-web/components/routes/chat/prompt-form/index.tsx`:
- Around line 334-340: getGenerationInstance is being recreated every render
because mediaGeneration, wordmarkGeneration, and symbolGeneration are new
objects each time (they come from direct useImageGeneration() calls), so memoize
those three instances with useMemo and then keep getGenerationInstance wrapped
with useCallback depending on the memoized values; specifically, replace direct
useImageGeneration() invocations with memoized variables (e.g., const
mediaGeneration = useMemo(() => useImageGeneration(...), [...deps]) or memoize
the returned objects), update getGenerationInstance's dependency array to
reference those memoized symbols (mediaGeneration, wordmarkGeneration,
symbolGeneration), and this will stabilize getGenerationInstance so the effect
that depends on it (the useEffect at the block ending with setInput) no longer
re-runs every render.
- Around line 178-179: The current logic allows a fragile state where
isBrandKitOpen can be true while activeGenerationScope remains 'media', making
the media submission path unreachable; update state coordination by either (A)
when toggling brand kit open (where isBrandKitOpen is set), also set
activeGenerationScope to a valid input scope like 'wordmark' or 'symbol' (update
the brand kit open/close handler that mutates isBrandKitOpen and
activeGenerationScope), or (B) add an explicit guard/reset where
isBrandKitInputDisabled is computed or in the submit handlers (e.g., the media
submission handler) to detect the invalid combination (isBrandKitOpen &&
activeGenerationScope === 'media') and reset activeGenerationScope to a safe
default before proceeding; reference isBrandKitOpen, activeGenerationScope,
isBrandKitInputDisabled, and the media submission handler to locate where to
apply the change.
🧹 Nitpick comments (1)
apps/pro-web/components/routes/chat/prompt-form/index.tsx (1)

322-333: DOM element variable should use $ prefix per codebase convention.

Line 325: el should be $el to follow the pro-web convention of prefixing DOM element references with $.

Proposed fix
 		setTimeout(() => {
 			if (!inputRef.current) return
 
-			const el = inputRef.current
-			el.focus()
+			const $el = inputRef.current
+			$el.focus()
 
 			// Move caret to end
-			el.setSelectionRange(draft.length, draft.length)
+			$el.setSelectionRange(draft.length, draft.length)
 
 			// Scroll to bottom
-			el.scrollTop = el.scrollHeight
+			$el.scrollTop = $el.scrollHeight
 		}, 0)

Based on learnings: "In TSX files, adopt a convention where variables that reference DOM elements are prefixed with '$' (e.g., $questionElement, $el) to quickly identify DOM refs."

Comment on lines +178 to +179
const isBrandKitInputDisabled =
isBrandKitOpen && !isWordmarkScope && !isSymbolScope
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, let's look at the file in question to understand the context better
fd 'prompt-form' --type f -e tsx | head -5

Repository: bitcashorg/masterbots

Length of output: 47


🏁 Script executed:

# Search for where activeGenerationScope is defined/assigned
rg -n 'activeGenerationScope\s*=' --type=ts --type=tsx -B 2 -A 2

Repository: bitcashorg/masterbots

Length of output: 92


🏁 Script executed:

# Search for isBrandKitOpen and its assignments
rg -n 'isBrandKitOpen' --type=ts --type=tsx -B 2 -A 2 | head -100

Repository: bitcashorg/masterbots

Length of output: 92


🏁 Script executed:

# Look for media scope handling
rg -n "media" --type=ts --type=tsx -i -B 2 -A 2 | grep -A 5 -B 5 "scope\|media"  | head -80

Repository: bitcashorg/masterbots

Length of output: 92


🏁 Script executed:

# Find the file directly
find . -path "*prompt-form/index.tsx" -type f

Repository: bitcashorg/masterbots

Length of output: 180


🏁 Script executed:

# Search for activeGenerationScope without file type specification
rg -n 'activeGenerationScope' -B 2 -A 2

Repository: bitcashorg/masterbots

Length of output: 3486


🏁 Script executed:

# Search for isBrandKitOpen assignments
rg -n 'isBrandKitOpen.*=' -B 1 -A 1 | head -100

Repository: bitcashorg/masterbots

Length of output: 1339


🏁 Script executed:

# Look at the prompt-form component structure
find . -path "*prompt-form*" -type f | grep -E "\.(ts|tsx|js|jsx)$"

Repository: bitcashorg/masterbots

Length of output: 536


🏁 Script executed:

# Search for handleMediaPromptSubmit to understand media scope handling
rg -n 'handleMediaPromptSubmit' -B 3 -A 5

Repository: bitcashorg/masterbots

Length of output: 3892


🏁 Script executed:

# Search for setActiveGenerationScope calls to understand when scope changes
rg -n 'setActiveGenerationScope' -B 3 -A 3

Repository: bitcashorg/masterbots

Length of output: 4088


🏁 Script executed:

# Look at the BrandKitFlow and how it manages state
rg -n 'BrandKitFlow' -B 2 -A 10

Repository: bitcashorg/masterbots

Length of output: 4973


🏁 Script executed:

# Check lines around 178-179 in the actual prompt-form file to see full context
sed -n '165,200p' ./apps/pro-web/components/routes/chat/prompt-form/index.tsx

Repository: bitcashorg/masterbots

Length of output: 1538


🏁 Script executed:

# Check lines 595-650 to see the submitPrompt logic and media scope handling
sed -n '590,650p' ./apps/pro-web/components/routes/chat/prompt-form/index.tsx

Repository: bitcashorg/masterbots

Length of output: 1948


🏁 Script executed:

# Check if activeGenerationScope changes based on isBrandKitOpen
rg -n 'isBrandKitOpen' -B 5 -A 5

Repository: bitcashorg/masterbots

Length of output: 10744


🏁 Script executed:

# Search for where brand kit state might affect activeGenerationScope
rg -n 'setIsBrandKitOpen' -B 5 -A 5

Repository: bitcashorg/masterbots

Length of output: 5817


🏁 Script executed:

# Check if there's any effect that manages scope when brand kit changes
sed -n '1,100p' ./apps/pro-web/lib/hooks/use-workspace-media.tsx | head -50

Repository: bitcashorg/masterbots

Length of output: 1479


🏁 Script executed:

# Look for any effects that coordinate isBrandKitOpen with activeGenerationScope
rg -n 'useEffect' ./apps/pro-web/lib/hooks/use-workspace-media.tsx -A 10 | head -80

Repository: bitcashorg/masterbots

Length of output: 47


🏁 Script executed:

# Check the textarea disabled prop to confirm the condition
sed -n '795,820p' ./apps/pro-web/components/routes/chat/prompt-form/index.tsx

Repository: bitcashorg/masterbots

Length of output: 951


🏁 Script executed:

# Check if there's any logic that prevents media scope when brand kit opens, or verifies the UI flow
sed -n '35,120p' ./apps/pro-web/components/routes/pro/chat-panel-pro.tsx

Repository: bitcashorg/masterbots

Length of output: 2282


🏁 Script executed:

# Look at BrandKitFlow to understand how it manages scope
sed -n '20,80p' ./apps/pro-web/components/routes/workspace/media-tab/brand-kit/brand-kit-flow.tsx

Repository: bitcashorg/masterbots

Length of output: 1687


🏁 Script executed:

# Check if there's documentation or comments explaining the intended relationship
rg -n 'Brand.*Kit.*scope|media.*Brand.*Kit' -B 2 -A 2

Repository: bitcashorg/masterbots

Length of output: 47


Clarify state coordination between isBrandKitOpen and activeGenerationScope to prevent unreachable media submission path.

The input disables when isBrandKitOpen && !isWordmarkScope && !isSymbolScope (i.e., when scope is 'media'). While the prompt form is currently hidden entirely when brand kit opens (chat-panel-pro.tsx line 97), there's no explicit guarantee that activeGenerationScope changes from its default 'media' value. This creates a fragile dependency where the media submission handler (lines 595–597) could become unreachable if the UI architecture changes. Consider either: (1) explicitly setting activeGenerationScope to 'wordmark' or 'symbol' when brand kit opens, or (2) adding an explicit guard/reset mechanism to prevent this state combination.

🤖 Prompt for AI Agents
In `@apps/pro-web/components/routes/chat/prompt-form/index.tsx` around lines 178 -
179, The current logic allows a fragile state where isBrandKitOpen can be true
while activeGenerationScope remains 'media', making the media submission path
unreachable; update state coordination by either (A) when toggling brand kit
open (where isBrandKitOpen is set), also set activeGenerationScope to a valid
input scope like 'wordmark' or 'symbol' (update the brand kit open/close handler
that mutates isBrandKitOpen and activeGenerationScope), or (B) add an explicit
guard/reset where isBrandKitInputDisabled is computed or in the submit handlers
(e.g., the media submission handler) to detect the invalid combination
(isBrandKitOpen && activeGenerationScope === 'media') and reset
activeGenerationScope to a safe default before proceeding; reference
isBrandKitOpen, activeGenerationScope, isBrandKitInputDisabled, and the media
submission handler to locate where to apply the change.

Comment on lines +334 to +340
}, [
isWorkspaceActive,
activeGenerationScope,
selectedSymbolStyle,
getGenerationInstance,
setInput,
])
Copy link
Contributor

@coderabbitai coderabbitai bot Feb 13, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# Find the useWorkspaceMedia hook definition
fd 'use-workspace-media' --type f -e ts -e tsx

Repository: bitcashorg/masterbots

Length of output: 111


🏁 Script executed:

# Also search for useWorkspaceMedia in the codebase
rg 'useWorkspaceMedia' --type ts --type tsx -l

Repository: bitcashorg/masterbots

Length of output: 92


🏁 Script executed:

# Search for getGenerationInstance declarations and usage
rg 'getGenerationInstance' --type ts --type tsx -B2 -A2 | head -150

Repository: bitcashorg/masterbots

Length of output: 92


🏁 Script executed:

# Check the specific file and lines mentioned in the review
cat -n apps/pro-web/components/routes/chat/prompt-form/index.tsx | sed -n '170,180p;330,345p'

Repository: bitcashorg/masterbots

Length of output: 1029


🏁 Script executed:

# Read the useWorkspaceMedia hook to check if getGenerationInstance is wrapped in useCallback
cat -n apps/pro-web/lib/hooks/use-workspace-media.tsx

Repository: bitcashorg/masterbots

Length of output: 34237


🏁 Script executed:

# Also search for where getGenerationInstance is derived from or defined
rg 'getGenerationInstance\s*=' --type ts --type js -B5 -A5

Repository: bitcashorg/masterbots

Length of output: 991


🏁 Script executed:

# Check the exact hook definition and its return
rg 'export.*useWorkspaceMedia' --type ts --type js -A30

Repository: bitcashorg/masterbots

Length of output: 719


🏁 Script executed:

# Find useImageGeneration hook
fd 'use-image-generation' --type f -e ts -e tsx

Repository: bitcashorg/masterbots

Length of output: 156


🏁 Script executed:

# Search for useImageGeneration definition
rg 'export.*useImageGeneration|function useImageGeneration' --type ts --type js -A 20

Repository: bitcashorg/masterbots

Length of output: 3504


🏁 Script executed:

# Read the full useImageGeneration hook from pro-web
cat -n apps/pro-web/lib/hooks/use-image-generation.tsx | head -200

Repository: bitcashorg/masterbots

Length of output: 6865


🏁 Script executed:

# Look for the return statement of useImageGeneration
rg 'return \{' apps/pro-web/lib/hooks/use-image-generation.tsx -A 30

Repository: bitcashorg/masterbots

Length of output: 286


🏁 Script executed:

# Check if the generation instances are wrapped in useMemo in useWorkspaceMedia
rg -n 'mediaGeneration|wordmarkGeneration|symbolGeneration' apps/pro-web/lib/hooks/use-workspace-media.tsx | head -20

Repository: bitcashorg/masterbots

Length of output: 1083


Memoize the generation instances to prevent getGenerationInstance from being recreated on every render.

getGenerationInstance is wrapped in useCallback, but its dependency array references mediaGeneration, wordmarkGeneration, and symbolGeneration—three new object instances created on every render via direct useImageGeneration() calls. This causes getGenerationInstance itself to be recreated every render despite useCallback, making the effect at line 334 re-run wastefully. Wrap the three generation instances in useMemo to stabilize the dependency chain.

🤖 Prompt for AI Agents
In `@apps/pro-web/components/routes/chat/prompt-form/index.tsx` around lines 334 -
340, getGenerationInstance is being recreated every render because
mediaGeneration, wordmarkGeneration, and symbolGeneration are new objects each
time (they come from direct useImageGeneration() calls), so memoize those three
instances with useMemo and then keep getGenerationInstance wrapped with
useCallback depending on the memoized values; specifically, replace direct
useImageGeneration() invocations with memoized variables (e.g., const
mediaGeneration = useMemo(() => useImageGeneration(...), [...deps]) or memoize
the returned objects), update getGenerationInstance's dependency array to
reference those memoized symbols (mediaGeneration, wordmarkGeneration,
symbolGeneration), and this will stabilize getGenerationInstance so the effect
that depends on it (the useEffect at the block ending with setInput) no longer
re-runs every render.

Copy link
Member

Choose a reason for hiding this comment

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

Only remove the functions in the dependency array. That way we can avoid re-renders.

Copy link
Contributor

Choose a reason for hiding this comment

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

@AndlerRL, understood! You're right—removing getGenerationInstance and setInput from the dependency array is a simpler and cleaner approach. This aligns with your established pattern for handling useEffect dependencies, where function references that don't represent changing values are excluded to prevent unnecessary re-renders.

The effect only needs to track the actual state values that determine when the draft should be inserted:

  • isWorkspaceActive
  • activeGenerationScope
  • selectedSymbolStyle

Both getGenerationInstance and setInput are stable function references that don't need to be in the dependency array, and removing them will eliminate the unnecessary re-renders without adding memoization complexity.


✏️ Learnings added
Learnt from: AndlerRL
Repo: bitcashorg/masterbots PR: 601
File: apps/pro-web/components/routes/chat/prompt-form/index.tsx:333-339
Timestamp: 2026-02-24T16:33:41.127Z
Learning: In the pro-web app prompt-form component (apps/pro-web/components/routes/chat/prompt-form/index.tsx), exclude function references like getGenerationInstance and setInput from useEffect dependency arrays when they cause unnecessary re-renders. Only include the actual state values that determine when the effect should run.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: CR
Repo: bitcashorg/masterbots PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-06T18:53:44.700Z
Learning: Applies to **/*.{tsx,jsx} : Minimize useEffect dependencies to prevent infinite loops and unnecessary re-renders

Learnt from: AndlerRL
Repo: bitcashorg/masterbots PR: 300
File: apps/masterbots.ai/components/routes/thread/user-thread-panel.tsx:120-123
Timestamp: 2024-11-20T04:00:45.413Z
Learning: In React code, when reviewing `useEffect` dependencies, it's not always necessary to add functions like `handleThreadsChange` to the dependency array, as this may lead to unnecessary re-renders and performance issues. Only include dependencies that are essential and may change.

Learnt from: CR
Repo: bitcashorg/masterbots PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-06T18:53:00.520Z
Learning: Applies to **/*.{jsx,tsx} : In React/State Management, minimize useEffect dependencies to prevent loops, use refs for non-re-render values, consider component lifecycle timing for DOM operations

Learnt from: Bran18
Repo: bitcashorg/masterbots PR: 376
File: apps/masterbots.ai/components/routes/chat/chat.tsx:92-102
Timestamp: 2025-02-17T16:51:19.102Z
Learning: For the Chat component in apps/masterbots.ai/components/routes/chat/chat.tsx, the developer prefers to verify potential optimizations through local testing before accepting suggestions, particularly for performance-related changes like useCallback usage in debounce implementations.

Learnt from: AndlerRL
Repo: bitcashorg/masterbots PR: 340
File: apps/masterbots.ai/components/routes/thread/user-thread-panel.tsx:191-195
Timestamp: 2025-01-16T20:48:20.108Z
Learning: In React's useEffect, it's acceptable to exclude function dependencies from the dependency array when:
1. The function only uses stable references (like setState functions)
2. Including the function could lead to unnecessary re-renders and performance issues

Learnt from: AndlerRL
Repo: bitcashorg/masterbots PR: 337
File: apps/masterbots.ai/components/routes/thread/user-thread-panel.tsx:195-205
Timestamp: 2025-01-14T06:20:03.578Z
Learning: React's useState setters (setState functions) maintain referential stability between renders and don't need to be included in useEffect dependency arrays. Include only the state values that the effect actually needs to track.

Learnt from: CR
Repo: bitcashorg/masterbots PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-06T18:53:44.700Z
Learning: Applies to **/*.{tsx,jsx} : Use regular function calls on components instead of arrow functions to prevent unnecessary re-renders

Learnt from: CR
Repo: bitcashorg/masterbots PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-06T18:53:00.520Z
Learning: Applies to **/*.{jsx,tsx} : Use regular function calls on components instead of arrow functions for event handlers and callbacks to prevent unnecessary re-renders

Learnt from: CR
Repo: bitcashorg/masterbots PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-02-06T18:52:26.701Z
Learning: Applies to **/*.{tsx,jsx} : When attaching event handlers to components, use regular function calls instead of arrow functions to prevent unnecessary re-renders and potential build errors from hoisting

Learnt from: AndlerRL
Repo: bitcashorg/masterbots PR: 0
File: :0-0
Timestamp: 2026-01-22T23:10:37.056Z
Learning: AndlerRL emphasizes Modular Development: avoid duplication by centralizing shared utilities/types, prefer small composable units, and keep flows simple and explicit (no cumbersome validations or over-engineering).

Learnt from: AndlerRL
Repo: bitcashorg/masterbots PR: 542
File: apps/web/components/layout/profile/layout.tsx:67-68
Timestamp: 2025-08-29T20:34:24.433Z
Learning: AndlerRL prefers skeleton loading states over conditional rendering for better UX when components are waiting for data to load. Components should show loading indicators rather than blank spaces.

Learnt from: AndlerRL
Repo: bitcashorg/masterbots PR: 600
File: apps/pro-web/components/routes/chat/chat-list/message-pair-accordion.tsx:221-221
Timestamp: 2026-01-22T23:03:44.735Z
Learning: In TSX files (e.g., apps/pro-web/components/routes/chat/chat-list/message-pair-accordion.tsx), adopt a convention where variables that reference DOM elements are prefixed with '$' (e.g., $questionElement, $el) to quickly identify DOM refs. Apply this consistently across the codebase to improve readability and searchability; ensure the prefix is only used for actual DOM element references and not for non-DOM variables.

Learnt from: AndlerRL
Repo: bitcashorg/masterbots PR: 600
File: apps/pro-web/components/routes/chat/chat-list/message-pair-accordion.tsx:40-40
Timestamp: 2026-01-22T23:03:49.088Z
Learning: In TypeScript React files, prefer using optional chaining with a nullish coalescing fallback (e.g., object?.param ?? fallback) over explicit type-guard helpers like isObject or asType when accessing nested properties. This keeps code simpler and more idiomatic. Ensure you still handle cases where intermediate properties may be undefined before applying the ?? fallback.

Learnt from: AndlerRL
Repo: bitcashorg/masterbots PR: 600
File: apps/pro-web/components/routes/chat/chat-list/message-pair-accordion.tsx:221-221
Timestamp: 2026-01-22T23:03:43.504Z
Learning: Enforce a DOM element naming convention in the pro-web app: in TSX files (e.g., apps/pro-web/**/*.tsx), prefix variables that hold DOM element references with a leading '$'. For example: const $questionElement = document.getElementById('question'); or const $list = document.querySelector('.list'); This helps quickly identify DOM refs and distinguish them from other variables. Apply this to variables obtained via document.getElementById, querySelector, etc., and ensure non-DOM variables are not prefixed.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@apps/pro-web/components/routes/chat/prompt-form/index.tsx`:
- Around line 593-597: The media submission guard currently checks activeTab ===
'media' and can call handleMediaPromptSubmit even when the workspace is
inactive; change the condition to use the existing boolean isWorkspaceMediaTab
(which is defined as isWorkspaceActive && activeTab === 'media') so media
submissions only run when both the workspace is active and the media tab is
selected, i.e., replace the activeTab === 'media' check with isWorkspaceMediaTab
where handleMediaPromptSubmit(trimmedInput) is invoked.
🧹 Nitpick comments (1)
apps/pro-web/components/routes/chat/prompt-form/index.tsx (1)

320-332: Nit: Use $el prefix for DOM element references.

Per the project's DOM naming convention, variables holding DOM element references should be prefixed with $.

Proposed fix
 		setTimeout(() => {
 			if (!inputRef.current) return
 
-			const el = inputRef.current
-			el.focus()
+			const $el = inputRef.current
+			$el.focus()
 
 			// Move caret to end
-			el.setSelectionRange(draft.length, draft.length)
+			$el.setSelectionRange(draft.length, draft.length)
 
 			// Scroll to bottom
-			el.scrollTop = el.scrollHeight
+			$el.scrollTop = $el.scrollHeight
 		}, 0)

Based on learnings: "In TSX files, adopt a convention where variables that reference DOM elements are prefixed with '$' (e.g., $questionElement, $el) to quickly identify DOM refs."

Comment on lines +593 to 597
// Media scope: generate or refine image
if (activeTab === 'media') {
await handleMediaPromptSubmit(trimmedInput)
return
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Use isWorkspaceMediaTab instead of activeTab === 'media' to guard media submission.

Line 169 correctly defines isWorkspaceMediaTab = isWorkspaceActive && activeTab === 'media', but the submission guard on line 594 only checks activeTab === 'media'. If activeTab retains a stale 'media' value while the workspace is inactive, the regular chat submission path is bypassed in favor of handleMediaPromptSubmit.

Proposed fix
 	// Media scope: generate or refine image
-	if (activeTab === 'media') {
+	if (isWorkspaceMediaTab) {
 		await handleMediaPromptSubmit(trimmedInput)
 		return
 	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Media scope: generate or refine image
if (activeTab === 'media') {
await handleMediaPromptSubmit(trimmedInput)
return
}
// Media scope: generate or refine image
if (isWorkspaceMediaTab) {
await handleMediaPromptSubmit(trimmedInput)
return
}
🤖 Prompt for AI Agents
In `@apps/pro-web/components/routes/chat/prompt-form/index.tsx` around lines 593 -
597, The media submission guard currently checks activeTab === 'media' and can
call handleMediaPromptSubmit even when the workspace is inactive; change the
condition to use the existing boolean isWorkspaceMediaTab (which is defined as
isWorkspaceActive && activeTab === 'media') so media submissions only run when
both the workspace is active and the media tab is selected, i.e., replace the
activeTab === 'media' check with isWorkspaceMediaTab where
handleMediaPromptSubmit(trimmedInput) is invoked.

Copy link
Member

@AndlerRL AndlerRL left a comment

Choose a reason for hiding this comment

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

LGTM! Revising deeply, this nails with the core intention for this new whole BrandKit feature. The steps and experience are smooth, I was able to go from vibe & tone to logo generation (including closing and opening again) with no issues—however, doing a thoughtful test, I discovered a set of flaws that are part of the happy paths which aren't a blocker to merge this PR, but key to work on those later on:

  1. When creating a logo symbol, there was no continuation of it: I asked to do a logo based on another draft logo that I had. It did a decent job at first iteration, but failed to continue it from the 2nd and I couldn't go back with iterations, but I did manually. The image references are colliding with continuation. The prompt has to improve and/or references should be removed once the first iteration is done, so the AI grab what it is mostly important, the new generated logo in addition with summarized context from the vibe & tone and symbol direction. With wordmark happens the same, on 2nd iteration the text is completely replaced by the initial text reference and results were average.
  2. When creating both logo based on a reference, fails to follow user intention: It is clear that a direction is given as user prompt, however, the way how the prompt is presented is not the most optimal. We should design a set of bullet that are sticky with the input bar to see the (suggested) options available for the logo symbol direction or prompt the user for the styles (like when creating a prompt template with prompt enhancer) with examples that align with their selection, so they are conscious of what direction we are giving to them and help them to do it. I could modify inline the directions, but I had to think and see what direction should I make without any suggestion that is beyond the logo style.
  3. When finishing the steps, is confusing where it takes: At the end of the steps, I see a successful state, however, I was incapable to see the BrandKit overview showing me my current status and CTAs to do (go back to vibe & tone, logo generation or see brand assets). When I close and open again, it will suggest me to create a new/import brand.
  4. Logo generator steps are aligned to the top: align them to the centre, so it can be centred on large screens. Layout looks sharp✨

🗒️ Major Feedback:
We need to review the prompts, most of the experience weren't good enough due the prompt that is injected into the nano banana pro and gpt-image-1 models when creating the logos. Once that standardized and we remove the old (non-related) references on continuous iterations, the experience would definitely improve.

Let's handle those in the next PRs. I will be creating issues with coderabbitai to track these and provide feedback too. The rest of feedbacks in this PR, please take them too in the new PRs. Thanks for this great feature! 🔥 Merging now.

Comment on lines +111 to +142
// Validate logoAssets if provided
if (body.logoAssets !== undefined) {
if (typeof body.logoAssets !== 'object' || body.logoAssets === null) {
return NextResponse.json(
{ error: 'Logo assets must be an object' },
{ status: 422 },
)
}

if (!body.logoAssets.symbol && !body.logoAssets.wordmark) {
return NextResponse.json(
{
error:
'At least one logo asset (symbol or wordmark) must be provided',
},
{ status: 422 },
)
}

if (body.logoAssets.wordmark) {
if (
!body.logoAssets.wordmark.name ||
typeof body.logoAssets.wordmark.name !== 'string' ||
!body.logoAssets.wordmark.name.trim()
) {
return NextResponse.json(
{ error: 'Wordmark name is required and must be non-empty' },
{ status: 422 },
)
}
}
}
Copy link
Member

Choose a reason for hiding this comment

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

It is better to add a verification similar as decodeBase64Content util to do this validation elsewhere needed.

Comment on lines +334 to +340
}, [
isWorkspaceActive,
activeGenerationScope,
selectedSymbolStyle,
getGenerationInstance,
setInput,
])
Copy link
Member

Choose a reason for hiding this comment

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

Only remove the functions in the dependency array. That way we can avoid re-renders.

const fullMarkdown = currentDocumentData?.content ?? ''
const sections =
currentDocumentData?.sections ??
(fullMarkdown ? parseMarkdownSections(fullMarkdown) : undefined)
Copy link
Member

Choose a reason for hiding this comment

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

Here I made a quick fix on impr/prompt-enhancer branch

(fullMarkdown ? parseMarkdownSections(fullMarkdown) : [])


// Handle switch fonts
const handleSwitchFonts = () => {
if (selectedFonts.length === 2) {
Copy link
Member

Choose a reason for hiding this comment

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

💡 PRO TIP: It is better to handle negative situations rather than positives. When doing conditions where the conditions met are the happy path. That shouldn't be always the case, since a function should be built focused on the happy path and handling the edge cases and/or negative conditions. For this function for example, it is easier to read/maintain:

const handleSwitchFonts = () => {
    if (selectedFonts.length !== 2) return

	setSelectedFonts([
		{ family: selectedFonts[1].family, role: 'primary' },
		{ family: selectedFonts[0].family, role: 'secondary' },
	])
}

See how we focus the core logic in the main function context window. Not priority to take though, for further additions consider this pattern.

if (!wordmarkFontFamily && selectedFonts.length > 0) {
setWordmarkFontFamily(selectedFonts[0].family)
}
}, [wordmarkFontFamily, selectedFonts, setWordmarkFontFamily])
Copy link
Member

Choose a reason for hiding this comment

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

Keep array dependency as short as possible, mostly on useEffects. On next updates, keep in mind to remove the dispatch state functions into the array unless necessary; otherwise it may cause more re-renders.

<div className="sticky top-0 z-10 bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/80 flex flex-col items-center w-full gap-3 py-4">
<div className="flex items-start justify-center w-full px-4 sm:px-6">
<div className="flex items-start w-full max-w-6xl">
{allSteps.map((step, displayIndex) => {
Copy link
Member

Choose a reason for hiding this comment

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

💡 PRO TIP: Moving this away from the render cycle benefits the component performance, making it optimal on re-renders. While in some cases is OK to have it like it, it would depend on how many variables, you are adding into the render cycle. This case is better to have them separately, so it can optimize receiving the re-render new data.

Comment on lines +97 to +113
/**
* Upload logo asset to GCS - symbol overload
*/
export async function uploadLogoAssetToGCS(
organizationId: string,
type: 'symbol',
input: SymbolLogoAssetInput,
): Promise<SymbolLogoAsset>

/**
* Upload logo asset to GCS - wordmark overload
*/
export async function uploadLogoAssetToGCS(
organizationId: string,
type: 'wordmark',
input: WordmarkLogoAssetInput,
): Promise<WordmarkLogoAsset>
Copy link
Member

Choose a reason for hiding this comment

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

Duplicates! Please remove these.

Comment on lines +759 to +817
// Shuffle selected palette order deterministically
const shuffleSelectedPaletteOrder = useCallback(() => {
if (!selectedPalette) return

const paletteName = selectedPalette.name
const baseColors = [
selectedPalette.primary,
selectedPalette.accent,
selectedPalette.bg,
selectedPalette.text,
]

// Generate all valid permutations
const allPermutations = generatePermutations(baseColors)

// Filter to valid combinations only
const validPermutations = allPermutations.filter((perm) => {
const [, , bg, text] = perm
// bg and text must have good contrast (WCAG AA)
return isValidTextCombination(bg, text)
})

if (validPermutations.length === 0) {
console.warn('[shuffleSelectedPaletteOrder] No valid permutations found')
return
}

// Get current cursor for this palette (default to 0)
const currentCursor = shuffleState[paletteName] ?? 0

// Advance to next valid permutation (cycle)
const nextCursor = (currentCursor + 1) % validPermutations.length
const nextPerm = validPermutations[nextCursor]

// Update shuffle state
setShuffleState((prev) => ({ ...prev, [paletteName]: nextCursor }))

// Update selectedPalette with new color assignments
setSelectedPalette({
name: selectedPalette.name,
category: selectedPalette.category,
primary: nextPerm[0],
accent: nextPerm[1],
bg: nextPerm[2],
text: nextPerm[3],
})

// Also update colorRoleItems to match the new palette order
// Keep selection state per position
setColorRoleItems((prev) => {
const selections = prev.map((item) => item.selected)
return [
{ role: 'primary', color: nextPerm[0], selected: selections[0] },
{ role: 'secondary', color: nextPerm[2], selected: selections[1] }, // bg maps to secondary
{ role: 'accent', color: nextPerm[1], selected: selections[2] },
{ role: 'muted', color: nextPerm[3], selected: selections[3] }, // text maps to muted
]
})
}, [selectedPalette, shuffleState])
Copy link
Member

Choose a reason for hiding this comment

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

Good to dive deep and test edge cases in case they shuffle a lot or one or more colours are disabled

@AndlerRL
Copy link
Member

@coderabbitai I recently reviewed this PR and we should be take care of the observations and final revision feedback made on this PR. I would like to track these feedback in a set of issues detailed with suggested actions to take. Consider the major feedback as the core of the improvements, since they are mostly the reason of these issues for UI/UX and functionality improvements:

  • Improve prompt injection technique.
  • Improve "chain-of-though" or the "attachments" given to the LLMs.
  • Improve BrandKit overview and after completion scenarios.
  • Improve AI tools for logo generation (currently on scope in another branch)

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 24, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

@AndlerRL I'll gather all the review observations and feedback from this PR and create consolidated tracking issues organized by your major themes. Let me fetch the review comments first.


🏁 Script executed:

#!/bin/bash
# Fetch all review comments and conversations from PR `#601`
echo "=== PR Review Comments ==="
gh pr view 601 --repo bitcashorg/masterbots --json reviews --jq '.reviews[] | {author: .author.login, state: .state, body: .body, submittedAt: .submittedAt}'

echo -e "\n=== PR Review Threads ==="
gh pr view 601 --repo bitcashorg/masterbots --json reviewThreads --jq '.reviewThreads[] | {
  path: .path,
  line: .line,
  isResolved: .isResolved,
  comments: [.comments[] | {author: .author.login, body: .body, createdAt: .createdAt}]
}'

echo -e "\n=== PR Comments ==="
gh pr view 601 --repo bitcashorg/masterbots --json comments --jq '.comments[] | {author: .author.login, body: .body, createdAt: .createdAt}'

Length of output: 189628


@AndlerRL Acknowledged. I’ll create a master tracking issue plus four themed issues (one per major area), each with concrete action items, affected files/lines, and acceptance criteria, all linked back to PR #601 and your comment. Assigning to you. Running now.


🏁 Script executed:

#!/bin/bash
set -euo pipefail

REPO="bitcashorg/masterbots"
PR_NUMBER="601"
PR_URL="https://github.com/${REPO}/pull/${PR_NUMBER}"

# Find the specific AndlerRL comment that lists the four major themes to use as a backlink
COMMENT_URL="$(gh api -H 'Accept: application/vnd.github+json' "repos/${REPO}/issues/${PR_NUMBER}/comments" --paginate \
  | jq -r '[.[] | select(.user.login=="AndlerRL") | select((.body|tostring) | contains("Improve prompt injection technique"))] 
            | sort_by(.created_at) 
            | last 
            | .html_url')"

if [[ -z "${COMMENT_URL}" || "${COMMENT_URL}" == "null" ]]; then
  COMMENT_URL="${PR_URL}"
fi

# Helper to write an issue body to a file
write_body() {
  local file="$1"; shift
  cat > "$file" <<EOF
Requester: `@AndlerRL`
Source PR: ${PR_URL}
Reference comment: ${COMMENT_URL}

Summary
- This issue tracks post-merge improvements for the BrandKit + Logo Generator work in PR #${PR_NUMBER}, scoped to the theme below.
- Please prioritize P0 items first; P1 can follow; P2 are polish/opportunistic.

Goals
- Deliver measurable UX and quality improvements aligned with the theme.
- Ship safe, incremental changes with testable acceptance criteria.

Theme
$1

Proposed actions and tasks
$2

Acceptance criteria
$3

Notes
- Create follow-up small PRs per subtask where practical.
- Reference concrete file paths and “Line X–Y” ranges in PR descriptions when applicable.
- Ensure error paths are user-visible via toasts or inline messaging where noted.

Backlinks
- PR #${PR_NUMBER}
- Comment: ${COMMENT_URL}
EOF
}

# ---------- Issue 1: Prompt injection technique ----------
cat > /tmp/issue1_tasks.md <<'EOF'
- P0: Standardize base vs refine prompt builders
  - apps/pro-web/lib/helpers/workspace/media-tab/logo-prompt.ts
    - Add friendly label for 'small-caps' casing (around Line 91–98).
    - Remove identity mapping for logoStyleMap (around Line 56–60) and use selectedLogoStyle directly.
    - Hoist static lists (e.g., styles that allow initial) to module scope to avoid re-allocation.
  - Ensure refine prompts only clear user input after successful submit; restore input on error (apps/pro-web/components/routes/chat/prompt-form/index.tsx around Line 518–537).
  - Wrap async submitters with try/catch and show a toast on failure; do not lose user-entered text (index.tsx around Line 509–616).

- P0: Make refine iterations build on the last generated image, not stale references
  - In use-workspace-media submitters, pass previousImageData as the primary seed for refine paths and remove or down-weight initial references after the first successful generation.
  - Add a switch to exclude early reference images automatically once a first pass image exists (wordmark and symbol scopes).

- P0: Concurrency guard and atomic history updates in image generation
  - apps/pro-web/lib/hooks/use-image-generation.tsx
    - Add early return if isLoading is true to prevent overlapping generateImage calls (around Line 67–72).
    - Make history+cursor updates atomic (avoid stale cursor closures, around Line 164–172); either use a ref or a single reducer.

- P1: Stabilize generation instance routing
  - apps/pro-web/lib/hooks/use-workspace-media.tsx
    - Memoize generation instances or use refs so getGenerationInstance is stable (around Line 303–310).
    - Ensure dependent effects in logo-generator-wizard.tsx do not re-run spuriously.

- P1: Prevent runaway auto-generate loops
  - apps/pro-web/components/routes/workspace/media-tab/brand-kit/logo-generator/steps/refine-logo-step.tsx
    - Add a maxRetries with a retried count ref; stop retries after threshold (around Line 91–146).

- P1: Correct chatId fallback and media gating
  - apps/pro-web/components/routes/pro/chat-panel-pro-controls.tsx
    - Fix chatId fallback order (preserve params.threadId; around Line 122–131).
  - apps/pro-web/components/routes/chat/prompt-form/index.tsx
    - Gate media submission by isWorkspaceMediaTab, not only activeTab (around Line 593–597).

- P2: Motion polish
  - apps/pro-web/components/routes/workspace/media-tab/brand-kit/logo-generator/logo-generator-wizard.tsx
    - Wrap motion.div with <AnimatePresence> to enable exit animations (around Line 324–342).
EOF

cat > /tmp/issue1_ac.md <<'EOF'
- Submitting in wordmark/symbol scopes never clears user text unless the action succeeds; on failures, users see a toast and their input remains.
- Double-clicking “Generate” does not spawn concurrent requests; only one image generation runs at a time.
- A second refine iteration clearly builds from the last generated image (visual continuity); early reference images are not re-asserted by default.
- No infinite retry loops on auto-generate; after max retries the UI shows a safe, idle state with an error toast.
- Chat route threadId precedence works; media submissions are blocked unless the workspace media tab is active.
- Step transitions animate in/out as designed.
EOF

ISSUE1_THEME="[Prompt injection] Standardize base/refine prompts, prevent stale references from overriding refinements, guard concurrency, and preserve user input"
ISSUE1_TASKS="$(cat /tmp/issue1_tasks.md)"
ISSUE1_AC="$(cat /tmp/issue1_ac.md)"
write_body /tmp/issue1_body.md "$ISSUE1_THEME" "$ISSUE1_TASKS" "$ISSUE1_AC"

ISSUE1_JSON="$(gh issue create --repo "$REPO" --assignee AndlerRL \
  --title "[BrandKit/LogoGen] Improve prompt injection technique (PR #${PR_NUMBER} follow-ups)" \
  --body-file /tmp/issue1_body.md --json url,number)"
ISSUE1_URL="$(jq -r '.url' <<<"$ISSUE1_JSON")"
ISSUE1_NUM="$(jq -r '.number' <<<"$ISSUE1_JSON")"

# ---------- Issue 2: Chain-of-thought / attachments ----------
cat > /tmp/issue2_tasks.md <<'EOF'
- P0: Attachment strategy for iterated refinement
  - In wordmark/symbol flows, after first success:
    - Make the last generated image the sole “previousImageData” seed.
    - Automatically clear or de-prioritize initial reference attachments unless user re-enables them.
  - Expose a simple toggle in UI: “Use prior output as the main reference” (default on after first success).

- P0: Canonical palette permutations and stable role mapping
  - apps/pro-web/lib/hooks/use-workspace-media.tsx (around Line 759–817)
    - Store a canonical [primary, accent, bg, text] per palette (by name); generate permutations from this base each shuffle.
    - Keep role-to-position mapping stable across shuffles.

- P0: Robust fetches for attachments and restores
  - apps/pro-web/lib/helpers/ai-image.ts
    - Add fetch timeout to imageUrlToBase64 (e.g., AbortSignal.timeout(15s)).
  - apps/pro-web/lib/helpers/workspace/media-tab/logo-assets.client.ts
    - Add AbortController timeout (~30s) to fetchSignedUrlAsBase64.
  - apps/pro-web/lib/helpers/ai-image.ts
    - DRY: base64ToBlob delegates to base64ToUint8Array.
    - DRY: shareOrDownload delegates to downloadBlob for the download path.

- P1: Accessibility and resiliency
  - apps/pro-web/components/routes/workspace/media-tab/brand-kit/shared/selectable-carousel.tsx
    - Add aria-pressed to buttons reflecting selection state.
  - apps/pro-web/components/routes/workspace/media-tab/brand-kit/logo-generator/snap-slider.tsx
    - Guard empty options: return null; add touch events for drag on mobile.

- P1: Logging and DX
  - Remove stray console.log and dev-only logs (multiple files; e.g., use-workspace-media.tsx around 674–675, symbol-style-step.tsx around 19–23).
  - Gate verbose logs behind a debug flag (brand-kit-generator-wizard.tsx Lines 61, 66, 92–94, 125).

- P2: Minor prompt and filter polish
  - logo-prompt.ts: tighten filters on allowedColors (guard against 'undefined' role too); hoist static arrays; document intent.
EOF

cat > /tmp/issue2_ac.md <<'EOF'
- After a successful first generation, subsequent refinements primarily leverage the last output; early references no longer override intent unless explicitly re-added.
- Palette shuffling cycles deterministically with stable role assignments.
- Fetches for signed URLs/remote images abort on slow endpoints and surface user-visible errors.
- Selection state is conveyed to assistive tech; SnapSlider does not render invalid ARIA states and supports touch dragging.
- No debug logs linger in production builds of the affected components.
EOF

ISSUE2_THEME="[Chain-of-thought & attachments] Make reference assets intentional and resilient across iterations; a11y and fetch hardening"
ISSUE2_TASKS="$(cat /tmp/issue2_tasks.md)"
ISSUE2_AC="$(cat /tmp/issue2_ac.md)"
write_body /tmp/issue2_body.md "$ISSUE2_THEME" "$ISSUE2_TASKS" "$ISSUE2_AC"

ISSUE2_JSON="$(gh issue create --repo "$REPO" --assignee AndlerRL \
  --title "[BrandKit/LogoGen] Improve chain-of-thought and attachments to LLMs (PR #${PR_NUMBER} follow-ups)" \
  --body-file /tmp/issue2_body.md --json url,number)"
ISSUE2_URL="$(jq -r '.url' <<<"$ISSUE2_JSON")"
ISSUE2_NUM="$(jq -r '.number' <<<"$ISSUE2_JSON")"

# ---------- Issue 3: BrandKit overview and post-completion ----------
cat > /tmp/issue3_tasks.md <<'EOF'
- P0: Add a BrandKit Overview screen/state after completion
  - On save/finish in LogoConfirmStep, navigate to an Overview instead of the entry “New/Import” state.
  - Overview contents:
    - Status summary: Vibes, Fonts, Palette, Wordmark, Symbol (with thumbnails if present).
    - CTAs: “Edit vibes & fonts,” “Open Logo Generator,” “View assets,” “Download kit.”
  - Persist last-opened view so re-opening returns to Overview.

- P0: User-facing errors on save
  - apps/pro-web/components/routes/workspace/media-tab/brand-kit/logo-generator/steps/logo-confirm-step.tsx (around Line 32–84)
    - Toast when no org found; toast when no images; toast with error on save failure; keep finally block.

- P1: Center the logo generator stepper and layout on large screens
  - Ensure the stepper aligns centrally; keep responsive behavior.

- P1: Global ScrollArea overflow fix
  - apps/pro-web/app/globals.css
    - Prefer attribute-based selector [data-radix-scroll-area-viewport] > div to avoid per-file class wiring.

- P2: Minor UX/content
  - Fix grammar (“fits the most”) and copy where noted.
  - Clean dead guards and inaccurate comments (e.g., wordmark-style-step: resolvedName guard; wordmark-preview margin comment).
EOF

cat > /tmp/issue3_ac.md <<'EOF'
- Completing the wizard lands on a clear BrandKit Overview with current state and obvious next steps.
- Save flows show actionable toasts on all failure paths.
- Stepper is centered on large screens without regressions on smaller breakpoints.
- All ScrollArea instances inherit the overflow fix without per-component classes.
- Minor copy and guard fixes merged.
EOF

ISSUE3_THEME="[BrandKit UX] Overview and post-completion flow (land on an informative summary with clear CTAs)"
ISSUE3_TASKS="$(cat /tmp/issue3_tasks.md)"
ISSUE3_AC="$(cat /tmp/issue3_ac.md)"
write_body /tmp/issue3_body.md "$ISSUE3_THEME" "$ISSUE3_TASKS" "$ISSUE3_AC"

ISSUE3_JSON="$(gh issue create --repo "$REPO" --assignee AndlerRL \
  --title "[BrandKit] Overview and after-completion scenarios (PR #${PR_NUMBER} follow-ups)" \
  --body-file /tmp/issue3_body.md --json url,number)"
ISSUE3_URL="$(jq -r '.url' <<<"$ISSUE3_JSON")"
ISSUE3_NUM="$(jq -r '.number' <<<"$ISSUE3_JSON")"

# ---------- Issue 4: AI tools for logo generation ----------
cat > /tmp/issue4_tasks.md <<'EOF'
- P0: Persist original model metadata with saved assets
  - Store modelId used for generation on symbol/wordmark assets so restored previews don’t fabricate model IDs.

- P0: Iteration ergonomics
  - Provide a “Continue from last” action that seeds from the latest image and prunes stale references by default.
  - Expose Retry (already present) and ensure canRetry ties to last successful model/options.

- P1: Download UX and error handling
  - apps/pro-web/components/routes/workspace/media-tab/brand-kit/logo-generator/steps/logo-confirm-step.tsx (around Line 87–108)
    - Wrap downloadZip in try/catch + toast.
  - apps/pro-web/lib/helpers/ai-image.ts
    - Throw on empty files array for downloadZip or return a boolean result; update callers accordingly.

- P1: Infra polish
  - apps/pro-web/lib/helpers/workspace/media-tab/logo-assets.server.ts
    - Lazy-initialize GCS Storage bucket to avoid repeated client creation.

- P2: Typography helpers
  - apps/pro-web/lib/constants/brand-kit/google-fonts.ts
    - Align Fraunces supported weights in FONT_WEIGHTS_BY_FAMILY and font instantiation; warn if getFallbackWeight returns an unavailable weight.

- P2: Interaction polish
  - SnapSlider: touch drag support for mobile.
  - Motion: ensure AnimatePresence wraps elements using exit transitions.
EOF

cat > /tmp/issue4_ac.md <<'EOF'
- Restored assets display the correct model source; downstream routing based on modelId is accurate.
- “Continue from last” produces coherent iterative results with minimal prompt friction.
- Download-all errors surface a toast; empty selection is treated as an error or cleanly no-ops with feedback.
- GCS uploads initialize once per process.
- Fonts: no listed weights that aren’t actually loadable; fallback warnings appear in dev.
- Mobile users can drag SnapSlider; exit animations work as authored.
EOF

ISSUE4_THEME="[AI tools] Iteration flow, model metadata, download UX, infra polish, and mobile interactions"
ISSUE4_TASKS="$(cat /tmp/issue4_tasks.md)"
ISSUE4_AC="$(cat /tmp/issue4_ac.md)"
write_body /tmp/issue4_body.md "$ISSUE4_THEME" "$ISSUE4_TASKS" "$ISSUE4_AC"

ISSUE4_JSON="$(gh issue create --repo "$REPO" --assignee AndlerRL \
  --title "[LogoGen] AI tools improvements (PR #${PR_NUMBER} follow-ups; branch-scoped)" \
  --body-file /tmp/issue4_body.md --json url,number)"
ISSUE4_URL="$(jq -r '.url' <<<"$ISSUE4_JSON")"
ISSUE4_NUM="$(jq -r '.number' <<<"$ISSUE4_JSON")"

# ---------- Meta tracker ----------
cat > /tmp/meta_body.md <<EOF
Requester: `@AndlerRL`
Source PR: ${PR_URL}
Reference comment: ${COMMENT_URL}

This umbrella issue tracks all post-merge follow-ups for the BrandKit + Logo Generator feature. Child issues:
- [#${ISSUE1_NUM}](${ISSUE1_URL}) — Improve prompt injection technique
- [#${ISSUE2_NUM}](${ISSUE2_URL}) — Improve chain-of-thought and attachments
- [#${ISSUE3_NUM}](${ISSUE3_URL}) — BrandKit overview and after-completion scenarios
- [#${ISSUE4_NUM}](${ISSUE4_URL}) — AI tools improvements (branch-scoped)

Status and priorities
- P0: core UX/resilience (prompt flow, attachments, overview/save toasts)
- P1: a11y/perf/infra polish (fetch timeouts, SnapSlider guards/touch, lazy GCS init)
- P2: copy/motion niceties and small refactors

How we’ll work
- Land focused PRs per subtask; link PRs back here and to the relevant child issue.
- Check off tasks in child issues as they merge; update this tracker weekly with status.

Backlinks
- PR #${PR_NUMBER}
- Comment: ${COMMENT_URL}
EOF

META_JSON="$(gh issue create --repo "$REPO" --assignee AndlerRL \
  --title "[BrandKit/LogoGen] Follow-ups tracker • PR #${PR_NUMBER}" \
  --body-file /tmp/meta_body.md --json url,number)"
META_URL="$(jq -r '.url' <<<"$META_JSON")"
META_NUM="$(jq -r '.number' <<<"$META_JSON")"

echo "Created issues:"
echo "META: #${META_NUM} -> ${META_URL}"
echo "1: #${ISSUE1_NUM} -> ${ISSUE1_URL}"
echo "2: #${ISSUE2_NUM} -> ${ISSUE2_URL}"
echo "3: #${ISSUE3_NUM} -> ${ISSUE3_URL}"
echo "4: #${ISSUE4_NUM} -> ${ISSUE4_URL}"

Length of output: 897


@AndlerRL Thanks for the direction. I’m creating a master tracker plus four theme-focused issues now, each with concrete tasks, file paths/line refs, and acceptance criteria. Assigning them to you and linking back to PR #601 and your comment.


🏁 Script executed:

#!/bin/bash
set -euo pipefail

REPO="bitcashorg/masterbots"
PR_NUMBER="601"
PR_URL="https://github.com/${REPO}/pull/${PR_NUMBER}"

# Resolve backlink to your comment listing the four majors
COMMENT_URL="$(gh api -H 'Accept: application/vnd.github+json' "repos/${REPO}/issues/${PR_NUMBER}/comments" --paginate \
  | jq -r '[.[] | select(.user.login=="AndlerRL") | select((.body|tostring) | contains("Improve prompt injection technique"))] 
            | sort_by(.created_at) 
            | last 
            | .html_url')"
if [[ -z "${COMMENT_URL}" || "${COMMENT_URL}" == "null" ]]; then
  COMMENT_URL="${PR_URL}"
fi

write_body() {
  local file="$1"; shift
  cat > "$file" <<EOF
Requester: `@AndlerRL`
Source PR: ${PR_URL}
Reference comment: ${COMMENT_URL}

Summary
- This issue tracks post-merge improvements for the BrandKit + Logo Generator work in PR #${PR_NUMBER}, scoped to the theme below.
- Please prioritize P0 items first; P1 can follow; P2 are polish/opportunistic.

Goals
- Deliver measurable UX and quality improvements aligned with the theme.
- Ship safe, incremental changes with testable acceptance criteria.

Theme
$1

Proposed actions and tasks
$2

Acceptance criteria
$3

Notes
- Create follow-up small PRs per subtask where practical.
- Reference concrete file paths and “Line X–Y” ranges in PR descriptions when applicable.
- Ensure error paths are user-visible via toasts or inline messaging where noted.

Backlinks
- PR #${PR_NUMBER}
- Comment: ${COMMENT_URL}
EOF
}

# ---------- Issue 1 ----------
cat > /tmp/issue1_tasks.md <<'EOF'
- P0: Standardize base vs refine prompt builders
  - apps/pro-web/lib/helpers/workspace/media-tab/logo-prompt.ts
    - Add friendly label for 'small-caps' casing (around Line 91–98).
    - Remove identity mapping for logoStyleMap (around Line 56–60) and use selectedLogoStyle directly.
    - Hoist static lists (e.g., styles that allow initial) to module scope to avoid re-allocation.
  - Ensure refine prompts only clear user input after successful submit; restore input on error (apps/pro-web/components/routes/chat/prompt-form/index.tsx around Line 518–537).
  - Wrap async submitters with try/catch and show a toast on failure; do not lose user-entered text (index.tsx around Line 509–616).

- P0: Make refine iterations build on the last generated image, not stale references
  - In use-workspace-media submitters, pass previousImageData as the primary seed for refine paths and remove or down-weight initial references after the first successful generation.
  - Add a switch to exclude early reference images automatically once a first pass image exists (wordmark and symbol scopes).

- P0: Concurrency guard and atomic history updates in image generation
  - apps/pro-web/lib/hooks/use-image-generation.tsx
    - Add early return if isLoading is true to prevent overlapping generateImage calls (around Line 67–72).
    - Make history+cursor updates atomic (avoid stale cursor closures, around Line 164–172); either use a ref or a single reducer.

- P1: Stabilize generation instance routing
  - apps/pro-web/lib/hooks/use-workspace-media.tsx
    - Memoize generation instances or use refs so getGenerationInstance is stable (around Line 303–310).
    - Ensure dependent effects in logo-generator-wizard.tsx do not re-run spuriously.

- P1: Prevent runaway auto-generate loops
  - apps/pro-web/components/routes/workspace/media-tab/brand-kit/logo-generator/steps/refine-logo-step.tsx
    - Add a maxRetries with a retried count ref; stop retries after threshold (around Line 91–146).

- P1: Correct chatId fallback and media gating
  - apps/pro-web/components/routes/pro/chat-panel-pro-controls.tsx
    - Fix chatId fallback order (preserve params.threadId; around Line 122–131).
  - apps/pro-web/components/routes/chat/prompt-form/index.tsx
    - Gate media submission by isWorkspaceMediaTab, not only activeTab (around Line 593–597).

- P2: Motion polish
  - apps/pro-web/components/routes/workspace/media-tab/brand-kit/logo-generator/logo-generator-wizard.tsx
    - Wrap motion.div with <AnimatePresence> to enable exit animations (around Line 324–342).
EOF

cat > /tmp/issue1_ac.md <<'EOF'
- Submitting in wordmark/symbol scopes never clears user text unless the action succeeds; on failures, users see a toast and their input remains.
- Double-clicking “Generate” does not spawn concurrent requests; only one image generation runs at a time.
- A second refine iteration clearly builds from the last generated image (visual continuity); early reference images are not re-asserted by default.
- No infinite retry loops on auto-generate; after max retries the UI shows a safe, idle state with an error toast.
- Chat route threadId precedence works; media submissions are blocked unless the workspace media tab is active.
- Step transitions animate in/out as designed.
EOF

ISSUE1_THEME="[Prompt injection] Standardize base/refine prompts, prevent stale references from overriding refinements, guard concurrency, and preserve user input"
write_body /tmp/issue1_body.md "$ISSUE1_THEME" "$(cat /tmp/issue1_tasks.md)" "$(cat /tmp/issue1_ac.md)"
ISSUE1_OUT="$(gh issue create -R "$REPO" -a "AndlerRL" -t "[BrandKit/LogoGen] Improve prompt injection technique (PR #${PR_NUMBER} follow-ups)" -F /tmp/issue1_body.md)"
ISSUE1_URL="$(echo "$ISSUE1_OUT" | tail -n1)"
ISSUE1_NUM="$(sed -E 's#.*/issues/([0-9]+).*#\1#' <<<"$ISSUE1_URL")"

# ---------- Issue 2 ----------
cat > /tmp/issue2_tasks.md <<'EOF'
- P0: Attachment strategy for iterated refinement
  - In wordmark/symbol flows, after first success:
    - Make the last generated image the sole “previousImageData” seed.
    - Automatically clear or de-prioritize initial reference attachments unless user re-enables them.
  - Expose a simple toggle in UI: “Use prior output as the main reference” (default on after first success).

- P0: Canonical palette permutations and stable role mapping
  - apps/pro-web/lib/hooks/use-workspace-media.tsx (around Line 759–817)
    - Store a canonical [primary, accent, bg, text] per palette (by name); generate permutations from this base each shuffle.
    - Keep role-to-position mapping stable across shuffles.

- P0: Robust fetches for attachments and restores
  - apps/pro-web/lib/helpers/ai-image.ts
    - Add fetch timeout to imageUrlToBase64 (e.g., AbortSignal.timeout(15s)).
  - apps/pro-web/lib/helpers/workspace/media-tab/logo-assets.client.ts
    - Add AbortController timeout (~30s) to fetchSignedUrlAsBase64.
  - apps/pro-web/lib/helpers/ai-image.ts
    - DRY: base64ToBlob delegates to base64ToUint8Array.
    - DRY: shareOrDownload delegates to downloadBlob for the download path.

- P1: Accessibility and resiliency
  - apps/pro-web/components/routes/workspace/media-tab/brand-kit/shared/selectable-carousel.tsx
    - Add aria-pressed to buttons reflecting selection state.
  - apps/pro-web/components/routes/workspace/media-tab/brand-kit/logo-generator/snap-slider.tsx
    - Guard empty options: return null; add touch events for drag on mobile.

- P1: Logging and DX
  - Remove stray console.log and dev-only logs (multiple files; e.g., use-workspace-media.tsx around 674–675, symbol-style-step.tsx around 19–23).
  - Gate verbose logs behind a debug flag (brand-kit-generator-wizard.tsx Lines 61, 66, 92–94, 125).

- P2: Minor prompt and filter polish
  - logo-prompt.ts: tighten filters on allowedColors (guard against 'undefined' role too); hoist static arrays; document intent.
EOF

cat > /tmp/issue2_ac.md <<'EOF'
- After a successful first generation, subsequent refinements primarily leverage the last output; early references no longer override intent unless explicitly re-added.
- Palette shuffling cycles deterministically with stable role assignments.
- Fetches for signed URLs/remote images abort on slow endpoints and surface user-visible errors.
- Selection state is conveyed to assistive tech; SnapSlider does not render invalid ARIA states and supports touch dragging.
- No debug logs linger in production builds of the affected components.
EOF

ISSUE2_THEME="[Chain-of-thought & attachments] Make reference assets intentional and resilient across iterations; a11y and fetch hardening"
write_body /tmp/issue2_body.md "$ISSUE2_THEME" "$(cat /tmp/issue2_tasks.md)" "$(cat /tmp/issue2_ac.md)"
ISSUE2_OUT="$(gh issue create -R "$REPO" -a "AndlerRL" -t "[BrandKit/LogoGen] Improve chain-of-thought and attachments to LLMs (PR #${PR_NUMBER} follow-ups)" -F /tmp/issue2_body.md)"
ISSUE2_URL="$(echo "$ISSUE2_OUT" | tail -n1)"
ISSUE2_NUM="$(sed -E 's#.*/issues/([0-9]+).*#\1#' <<<"$ISSUE2_URL")"

# ---------- Issue 3 ----------
cat > /tmp/issue3_tasks.md <<'EOF'
- P0: Add a BrandKit Overview screen/state after completion
  - On save/finish in LogoConfirmStep, navigate to an Overview instead of the entry “New/Import” state.
  - Overview contents:
    - Status summary: Vibes, Fonts, Palette, Wordmark, Symbol (with thumbnails if present).
    - CTAs: “Edit vibes & fonts,” “Open Logo Generator,” “View assets,” “Download kit.”
  - Persist last-opened view so re-opening returns to Overview.

- P0: User-facing errors on save
  - apps/pro-web/components/routes/workspace/media-tab/brand-kit/logo-generator/steps/logo-confirm-step.tsx (around Line 32–84)
    - Toast when no org found; toast when no images; toast with error on save failure; keep finally block.

- P1: Center the logo generator stepper and layout on large screens
  - Ensure the stepper aligns centrally; keep responsive behavior.

- P1: Global ScrollArea overflow fix
  - apps/pro-web/app/globals.css
    - Prefer attribute-based selector [data-radix-scroll-area-viewport] > div to avoid per-file class wiring.

- P2: Minor UX/content
  - Fix grammar (“fits the most”) and copy where noted.
  - Clean dead guards and inaccurate comments (e.g., wordmark-style-step: resolvedName guard; wordmark-preview margin comment).
EOF

cat > /tmp/issue3_ac.md <<'EOF'
- Completing the wizard lands on a clear BrandKit Overview with current state and obvious next steps.
- Save flows show actionable toasts on all failure paths.
- Stepper is centered on large screens without regressions on smaller breakpoints.
- All ScrollArea instances inherit the overflow fix without per-component classes.
- Minor copy and guard fixes merged.
EOF

ISSUE3_THEME="[BrandKit UX] Overview and post-completion flow (land on an informative summary with clear CTAs)"
write_body /tmp/issue3_body.md "$ISSUE3_THEME" "$(cat /tmp/issue3_tasks.md)" "$(cat /tmp/issue3_ac.md)"
ISSUE3_OUT="$(gh issue create -R "$REPO" -a "AndlerRL" -t "[BrandKit] Overview and after-completion scenarios (PR #${PR_NUMBER} follow-ups)" -F /tmp/issue3_body.md)"
ISSUE3_URL="$(echo "$ISSUE3_OUT" | tail -n1)"
ISSUE3_NUM="$(sed -E 's#.*/issues/([0-9]+).*#\1#' <<<"$ISSUE3_URL")"

# ---------- Issue 4 ----------
cat > /tmp/issue4_tasks.md <<'EOF'
- P0: Persist original model metadata with saved assets
  - Store modelId used for generation on symbol/wordmark assets so restored previews don’t fabricate model IDs.

- P0: Iteration ergonomics
  - Provide a “Continue from last” action that seeds from the latest image and prunes stale references by default.
  - Expose Retry (already present) and ensure canRetry ties to last successful model/options.

- P1: Download UX and error handling
  - apps/pro-web/components/routes/workspace/media-tab/brand-kit/logo-generator/steps/logo-confirm-step.tsx (around Line 87–108)
    - Wrap downloadZip in try/catch + toast.
  - apps/pro-web/lib/helpers/ai-image.ts
    - Throw on empty files array for downloadZip or return a boolean result; update callers accordingly.

- P1: Infra polish
  - apps/pro-web/lib/helpers/workspace/media-tab/logo-assets.server.ts
    - Lazy-initialize GCS Storage bucket to avoid repeated client creation.

- P2: Typography helpers
  - apps/pro-web/lib/constants/brand-kit/google-fonts.ts
    - Align Fraunces supported weights in FONT_WEIGHTS_BY_FAMILY and font instantiation; warn if getFallbackWeight returns an unavailable weight.

- P2: Interaction polish
  - SnapSlider: touch drag support for mobile.
  - Motion: ensure AnimatePresence wraps elements using exit transitions.
EOF

cat > /tmp/issue4_ac.md <<'EOF'
- Restored assets display the correct model source; downstream routing based on modelId is accurate.
- “Continue from last” produces coherent iterative results with minimal prompt friction.
- Download-all errors surface a toast; empty selection is treated as an error or cleanly no-ops with feedback.
- GCS uploads initialize once per process.
- Fonts: no listed weights that aren’t actually loadable; fallback warnings appear in dev.
- Mobile users can drag SnapSlider; exit animations work as authored.
EOF

ISSUE4_THEME="[AI tools] Iteration flow, model metadata, download UX, infra polish, and mobile interactions"
write_body /tmp/issue4_body.md "$ISSUE4_THEME" "$(cat /tmp/issue4_tasks.md)" "$(cat /tmp/issue4_ac.md)"
ISSUE4_OUT="$(gh issue create -R "$REPO" -a "AndlerRL" -t "[LogoGen] AI tools improvements (PR #${PR_NUMBER} follow-ups; branch-scoped)" -F /tmp/issue4_body.md)"
ISSUE4_URL="$(echo "$ISSUE4_OUT" | tail -n1)"
ISSUE4_NUM="$(sed -E 's#.*/issues/([0-9]+).*#\1#' <<<"$ISSUE4_URL")"

# ---------- Meta tracker ----------
cat > /tmp/meta_body.md <<EOF
Requester: `@AndlerRL`
Source PR: ${PR_URL}
Reference comment: ${COMMENT_URL}

This umbrella issue tracks all post-merge follow-ups for the BrandKit + Logo Generator feature. Child issues:
- [#${ISSUE1_NUM}](${ISSUE1_URL}) — Improve prompt injection technique
- [#${ISSUE2_NUM}](${ISSUE2_URL}) — Improve chain-of-thought and attachments
- [#${ISSUE3_NUM}](${ISSUE3_URL}) — BrandKit overview and after-completion scenarios
- [#${ISSUE4_NUM}](${ISSUE4_URL}) — AI tools improvements (branch-scoped)

Status and priorities
- P0: core UX/resilience (prompt flow, attachments, overview/save toasts)
- P1: a11y/perf/infra polish (fetch timeouts, SnapSlider guards/touch, lazy GCS init)
- P2: copy/motion niceties and small refactors

How we’ll work
- Land focused PRs per subtask; link PRs back here and to the relevant child issue.
- Check off tasks in child issues as they merge; update this tracker weekly with status.

Backlinks
- PR #${PR_NUMBER}
- Comment: ${COMMENT_URL}
EOF

META_OUT="$(gh issue create -R "$REPO" -a "AndlerRL" -t "[BrandKit/LogoGen] Follow-ups tracker • PR #${PR_NUMBER}" -F /tmp/meta_body.md)"
META_URL="$(echo "$META_OUT" | tail -n1)"
META_NUM="$(sed -E 's#.*/issues/([0-9]+).*#\1#' <<<"$META_URL")"

printf "\nCreated issues:\n"
printf "META: #%s -> %s\n" "$META_NUM" "$META_URL"
printf "1:    #%s -> %s\n" "$ISSUE1_NUM" "$ISSUE1_URL"
printf "2:    #%s -> %s\n" "$ISSUE2_NUM" "$ISSUE2_URL"
printf "3:    #%s -> %s\n" "$ISSUE3_NUM" "$ISSUE3_URL"
printf "4:    #%s -> %s\n" "$ISSUE4_NUM" "$ISSUE4_URL"

Length of output: 346


@AndlerRL Done. I created a consolidated tracker and four themed issues with concrete tasks, file paths/line refs, and acceptance criteria, all linked back to PR #601 and your comment:

  • Tracker: #622 — BrandKit/LogoGen follow-ups • PR #601
  • Prompt injection: #618 — Improve prompt injection technique
  • Chain-of-thought & attachments: #619 — Improve chain-of-thought and attachments to LLMs
  • BrandKit UX: #620 — Overview and after-completion scenarios
  • AI tools (logo gen): #621 — Iteration flow, model metadata, download UX, infra polish

These follow your preference for a consolidated tracker with clear P0/P1/P2 priorities. Would you like me to add labels (e.g., enhancement, UX, backend/frontend) or assign co-owners for each theme? I can also spin out separate micro-issues for any of the minor/nitpick inline comments you want tracked individually.


✏️ Learnings added
Learnt from: AndlerRL
URL: https://github.com/bitcashorg/masterbots/pull/601

Timestamp: 2026-02-24T16:48:09.595Z
Learning: bitcashorg/masterbots: For BrandKit/LogoGen follow-ups, AndlerRL prefers a master tracker issue plus themed child issues (Prompt injection; Chain-of-thought/attachments; BrandKit overview; AI tools), each with P0/P1/P2 priorities, file paths/line references, acceptance criteria, and backlinks to PR `#601` and the originating comment.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: AndlerRL
Repo: bitcashorg/masterbots PR: 0
File: :0-0
Timestamp: 2026-02-03T19:09:24.567Z
Learning: bitcashorg/masterbots: For RAG-related refactors, AndlerRL prefers a single consolidated tracking issue that links all relevant PR comments and issues and organizes observations by priority/status (P0/P1/P2), to accompany the active PR.

Learnt from: AndlerRL
Repo: bitcashorg/masterbots PR: 0
File: :0-0
Timestamp: 2025-11-07T18:41:56.562Z
Learning: Successfully created comprehensive GitHub issue `#581` consolidating 16 unresolved observations from PR `#579` review, including critical TDZ crash, migration bugs, transaction safety issues, and validation improvements, with specific file paths, line numbers, and action items organized by priority (4 critical, 8 major, 2 minor, 2 acknowledged for future PRs).

Learnt from: AndlerRL
Repo: bitcashorg/masterbots PR: 0
File: :0-0
Timestamp: 2025-09-04T21:31:18.830Z
Learning: Successfully created comprehensive GitHub issue `#555` consolidating performance feedback and edge-case management for pro-web workspace features, including prioritized action items, testing requirements, and specific file references for implementation.

Learnt from: AndlerRL
Repo: bitcashorg/masterbots PR: 0
File: :0-0
Timestamp: 2026-01-22T23:10:37.056Z
Learning: AndlerRL emphasizes Modular Development: avoid duplication by centralizing shared utilities/types, prefer small composable units, and keep flows simple and explicit (no cumbersome validations or over-engineering).

Learnt from: AndlerRL
Repo: bitcashorg/masterbots PR: 542
File: apps/web/components/layout/profile/layout.tsx:67-68
Timestamp: 2025-08-29T20:34:24.433Z
Learning: AndlerRL prefers skeleton loading states over conditional rendering for better UX when components are waiting for data to load. Components should show loading indicators rather than blank spaces.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants