Skip to content

Conversation

@derianrddev
Copy link
Contributor

@derianrddev derianrddev commented Dec 11, 2025

📖 Description

Implements a comprehensive Brand Kit creation wizard for the Media Tab, enabling users to configure their brand identity through a guided multi-step process. This PR introduces a reusable dialog component architecture, establishes the foundation for brand customization features, fully integrates the Brand Kit functionality into the Media Tab workflow with functional vibe, fonts, and colors selection steps, implements a checkpoint review screen, and adds complete persistence layer with IndexedDB caching and server synchronization.

✅ Changes Made

Component Architecture & Foundation:

  • Create WorkspaceDialog reusable component for standardized dialog structure across media tab wizards
  • Refactor version-history-dialog to use new WorkspaceDialog component (reducing 192 lines of duplicate code)
  • Extract BrandKitEntry component with "New Brand" and "Import Brand" options for better code organization
  • Implement BrandKitWizard with multi-step navigation flow using custom wizard hook
  • Add WizardStepper component for visual step progress indication with animations
  • Integrate BrandKitDialog with MediaWizards and MediaSidebar for seamless user access
  • Wire up Brand Kit button in MediaSidebar to open dialog
  • Remove placeholder Social Media button from sidebar (24 lines removed)

Vibe Step (Brand Personality):

  • Implement fully functional VibeStep with 12 brand vibes, 2-selection limit, and color preview badges
  • Add dynamic subtitle showing organization name + "Logo & Style" in wizard mode
  • Implement sticky wizard stepper and navigation buttons layout with proper scroll container
  • Style BadgeCheck icon and color dots with proper borders for dark mode compatibility
  • Update wizard container to use viewport-based height for proper layout
  • Optimize scroll behavior across wizard steps for better UX

Fonts Step (Typography):

  • Implement complete FontsStep with vibe-based font recommendations system
  • Create Google Fonts integration with 70+ font families (primary and secondary fonts for each vibe)
  • Add primary/secondary font role badges and selection management
  • Implement sticky selection bar showing selected fonts with removal capability
  • Add vertical scroll snap carousel for optimized font browsing
  • Include comprehensive font previews with multiple samples (pangrams, bold, italic, numbers)
  • Update font state management with role-based structure (SelectedFont type)
  • Add font validation when vibes change to remove incompatible selections
  • Automatically reassign font roles (primary/secondary) after filtering

Colors Step (Palette Selection):

  • Add ColorsStep with 8 curated color palettes across multiple categories (Minimal Neutrals, Cool Tech, Bold Saturated, Warm & Friendly, Fresh Greens, Vintage/Retro, Elegant Monochrome)
  • Implement live preview card with selected fonts and organization name
  • Add AA contrast ratio validation with gentle auto-adjustment for readability
  • Create shuffle functionality for random palette selection
  • Build carousel navigation for palette browsing with pagination dots
  • Display color strips with primary, accent, bg, and text tokens
  • Add palette state management to useWorkspaceMedia hook
  • Integrate selected fonts from previous step into color preview
  • Enhance layout with improved scrolling and responsive design
  • Refactor for theme-based color adjustments and improve contrast handling
  • Optimize preview card layout with better spacing and alignment

Checkpoint Step (Review & Save):

  • Create CheckpointStep component (265 lines) to review selected brand elements
  • Display color scheme with palette circles showing primary, accent, bg, and text colors
  • Show palette name and category information below color preview
  • Implement font family preview with primary and secondary fonts
  • Add font switching functionality to swap primary/secondary roles
  • Conditional rendering: single font layout (centered, no badges) vs. dual font layout (three-column with switch button)
  • Display comprehensive character previews (alphabet, numbers, symbols) for each font
  • Integrate organization name and vibe label in checkpoint UI (badge display)
  • Add vertical divider between fonts on desktop with cutout for switch button
  • Implement "Continue Later" button to save progress and close wizard
  • Implement "Generate Logo" button to save and proceed to logo generation
  • Add loading states with "Saving..." text during save operations
  • Add flag icon (FlagTriangleRight) to final step in wizard stepper for visual clarity
  • Replace placeholder LogoStep with functional CheckpointStep

Database Schema & Hasura Integration:

  • Add brand_kit JSONB column to organization table in PostgreSQL
  • Generate Hasura migration files (up.sql/down.sql) for brand_kit column
  • Update Hasura metadata (public_organization.yaml) with brand_kit field
  • Update Drizzle schema with brandKit field definition
  • Regenerate GraphQL schema and types from Hasura metadata (6766 lines affected in mb-genql)
  • Update mb-genql types for brand_kit field access across the application

Persistence Layer & API Integration:

  • Implement hybrid persistence strategy: Server (primary) + IndexedDB (offline cache)
  • Add saveBrandKit mutation in useWorkspaceMedia using TanStack Query
  • Create saveBrandKitStep function to track wizard progress in LocalStorage per organization
  • Create clearBrandKitStep function to reset progress after wizard completion
  • Implement save handlers in CheckpointStep:
    • "Continue Later": Saves brandKit to server + IDB, keeps wizard step for resumption
    • "Generate Logo": Saves brandKit to server + IDB, clears step, proceeds to next feature
  • Add brandKit data restoration in BrandKitWizard on mount:
    • Restore vibes, fonts, and palette from workspace brandKit data
    • Restore currentStep from LocalStorage for wizard resumption
    • Use useRef to prevent unnecessary re-renders from brandKit changes
  • Add brandKit support in /api/organizations/[id] route (PATCH):
    • Validate brandKit object in request body
    • Update organization's brandKit field via Hasura mutation
    • Return updated organization with brandKit data
  • Add brandKit support in /api/organizations route (GET):
    • Include brandKit field in organization queries
    • Transform and return brandKit in API responses
  • Integrate brandKit fetch in getUserOrganizations server action
  • Sync brandKit to IndexedDB after successful server save
  • Merge brandKit data in workspace structure sync (use-workspace-structure.ts)
  • Handle offline scenarios with IDB-first fallback

Type Definitions:

  • Define BrandKitData interface in media.types.ts:
    • vibes: string[] - Brand personality descriptors
    • fonts: { primary: string, secondary?: string } - Typography selection
    • palette: { name, category, primary, accent, bg, text } - Color configuration
    • logo?: { generatedImageUrl, variations } - Generated logo assets (future)
    • createdAt and updatedAt timestamps
  • Add brandKit?: BrandKitData | null to OrganizationData interface
  • Export BrandKitData from main types index

State Management:

  • Add Brand Kit state to useWorkspaceMedia hook:
    • selectedVibes, selectedFonts, selectedPalette - Wizard selections
    • brandKitCurrentStep - Current wizard step number
    • isSavingBrandKit - Loading state for save operations
    • showBrandKit, openBrandKit - UI visibility controls
  • Add Brand Kit actions to useWorkspaceMedia:
    • setSelectedVibes, setSelectedFonts, setSelectedPalette - State setters
    • saveBrandKit(brandKitData, organizationId) - Persist to server + IDB
    • saveBrandKitStep(step, organizationId) - Track wizard progress
    • clearBrandKitStep(organizationId) - Reset wizard progress
  • Establish consistent state flow across wizard steps and persistence layer

Data Flow Architecture:

User Selection → Wizard Steps → useWorkspaceMedia State
                                        ↓
                          CheckpointStep Save Button
                                        ↓
                    saveBrandKit Mutation (TanStack Query)
                                        ↓
                    PATCH /api/organizations/:id
                                        ↓
                          Hasura (PostgreSQL)
                                        ↓
              Success → Update IDB → Invalidate Queries
                                        ↓
                    Refetch & Merge → useWorkspaceStructure
                                        ↓
                    BrandKitWizard Restoration (on re-open)

Code Quality & Polish:

  • Adjust padding and spacing in dialog and wizard components for visual consistency
  • Improve code reusability and maintainability across media tab dialog components
  • Establish consistent dialog patterns for future media tab features
  • Add comprehensive loading states and error handling throughout save flow
  • Optimize re-render performance with useRef for brandKit tracking
  • Follow TypeScript best practices with proper interface definitions

🖼️ Media (screenshots/videos)

https://www.loom.com/share/486914fdb4ba4f30a6151589a2632abe

Summary by Sourcery

Introduce a multi-step Brand Kit creation wizard to the media tab and standardize media dialogs with a reusable workspace dialog layout.

New Features:

  • Add a Brand Kit dialog with an entry screen and multi-step wizard flow for configuring brand vibe, fonts, colors, and logo.
  • Provide a visual wizard stepper component to indicate progress and enable navigation between completed steps in multi-step flows.

Enhancements:

  • Create a reusable WorkspaceDialog component to unify the layout, header, and footer patterns for media workspace dialogs.
  • Refactor the version history dialog to use the new WorkspaceDialog and align its styling with other media dialogs.
  • Wire the Brand Kit dialog into the media tab sidebar and wizards flow, including new state in the workspace media hook and removing the unused social media button.

Summary by Sourcery

Introduce a multi-step Brand Kit wizard for the media workspace and persist brand configuration on organizations, while standardizing media dialogs with a reusable workspace dialog layout.

New Features:

  • Add a Brand Kit dialog with an entry screen and guided wizard to configure brand vibes, fonts, colors, and checkpoint review.
  • Provide dedicated wizard steps for vibe selection, vibe-driven font recommendations, curated color palettes with accessibility adjustments, and a final review checkpoint.
  • Introduce a visual WizardStepper component and Google Fonts integration to power the branding wizard experience.

Enhancements:

  • Create a reusable WorkspaceDialog shell and refactor the version history dialog to use it for consistent media workspace modals.
  • Extend the media workspace state and actions to manage Brand Kit selections, dialog visibility, and wizard step navigation.
  • Enhance workspace structure merging logic so organization Brand Kit data is preserved and preferred from IndexedDB when available.

Build:

  • Update the Drizzle schema, relations, and Hasura metadata to add a JSONB brand_kit column on organizations and expose it via GraphQL types.

Chores:

  • Add API support for reading and updating organization Brand Kit data and include it in server actions and organization listings.

Summary by CodeRabbit

  • New Features

    • Brand Kit: dialog, entry screen, multi-step wizard (Vibe → Fonts → Colors → Checkpoint), stepper, previews, import/new flows, and workspace integration.
    • Fonts & palettes: Google Fonts support, font-selection UX, palette carousel, live previews, per-org persistence and restore.
    • API: organization payloads include brandKit; endpoints accept/validate brandKit and persist changes.
  • Chores

    • Database: added brand_kit column and updated permissions for relevant roles.
  • Refactor

    • Dialogs: new WorkspaceDialog wrapper used across modals.
  • UI

    • Media sidebar: replaced Instagram block with Brand Kit action.

✏️ Tip: You can customize this high-level summary in your review settings.

@derianrddev derianrddev self-assigned this Dec 11, 2025
@vercel
Copy link

vercel bot commented Dec 11, 2025

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

Project Deployment Review Updated (UTC)
masterbots-pro Ready Ready Preview, Comment Dec 30, 2025 1:31am
1 Skipped Deployment
Project Deployment Review Updated (UTC)
masterbots Skipped Skipped Dec 30, 2025 1:31am

@sourcery-ai
Copy link

sourcery-ai bot commented Dec 11, 2025

Reviewer's Guide

Implements a multi-step Brand Kit creation wizard in the Media Tab, backed by a new reusable WorkspaceDialog shell and a full persistence path (React state, localStorage, IndexedDB, Hasura/PG brand_kit JSONB) so users can configure vibes, fonts, and colors, review them on a checkpoint step, and resume progress per organization.

Sequence diagram for saving Brand Kit from CheckpointStep

sequenceDiagram
    actor User
    participant MediaSidebar
    participant BrandKitDialog
    participant BrandKitWizard
    participant CheckpointStep
    participant useWorkspaceMedia
    participant saveBrandKitMutation
    participant ApiOrganizationsId as Api_organizations_id
    participant Hasura
    participant Postgres
    participant IndexedDB
    participant ReactQuery as TanStack_Query

    User->>MediaSidebar: click BrandKit button
    MediaSidebar->>useWorkspaceMedia: openBrandKit()
    useWorkspaceMedia-->>MediaSidebar: set showBrandKit true
    MediaSidebar->>BrandKitDialog: open=true
    BrandKitDialog->>BrandKitWizard: render wizard

    loop Wizard navigation
        User->>BrandKitWizard: navigate steps (Vibe, Fonts, Colors)
        BrandKitWizard->>useWorkspaceMedia: setSelectedVibes / setSelectedFonts / setSelectedPalette
        BrandKitWizard->>useWorkspaceMedia: saveBrandKitStep(currentStep, organizationId)
        useWorkspaceMedia->>LocalStorage: set brandkit_step_organizationId
    end

    User->>CheckpointStep: click Continue Later or Generate Logo
    CheckpointStep->>useWorkspaceMedia: saveBrandKit(brandKitData, organizationId)
    useWorkspaceMedia->>saveBrandKitMutation: mutateAsync(brandKitData, organizationId)

    saveBrandKitMutation->>ApiOrganizationsId: PATCH /api/organizations/:id { brandKit }
    ApiOrganizationsId->>Hasura: updateOrganizationByPk(brand_kit)
    Hasura->>Postgres: update organization.brand_kit JSONB
    Postgres-->>Hasura: updated row
    Hasura-->>ApiOrganizationsId: organization with brand_kit
    ApiOrganizationsId-->>saveBrandKitMutation: response json

    saveBrandKitMutation->>IndexedDB: getItem(WORKSPACE_STRUCTURE_KEY)
    IndexedDB-->>saveBrandKitMutation: workspaceStructure
    saveBrandKitMutation->>IndexedDB: updateItem(WORKSPACE_STRUCTURE_KEY, updatedWorkspace)

    saveBrandKitMutation->>ReactQuery: invalidateQueries(workspace userId structure)
    ReactQuery->>ApiOrganizationsId: refetch organizations with brandKit
    ApiOrganizationsId-->>ReactQuery: latest organization data
    ReactQuery-->>useWorkspaceMedia: updated cache merged via useWorkspaceStructure

    alt Continue Later
        CheckpointStep->>BrandKitDialog: close()
        BrandKitDialog-->>MediaSidebar: onOpenChange(false)
        useWorkspaceMedia->>useWorkspaceMedia: brandKitCurrentStep remains at Checkpoint
    else Generate Logo
        CheckpointStep->>useWorkspaceMedia: clearBrandKitStep(organizationId)
        useWorkspaceMedia->>LocalStorage: remove brandkit_step_organizationId
        CheckpointStep->>BrandKitWizard: next() to logo flow
    end
Loading

ER diagram for organization brand_kit integration

erDiagram
    user {
        uuid userId PK
        text email
        text name
    }

    organization {
        uuid organizationId PK
        text name
        uuid userId FK
        jsonb brand_kit
    }

    organization_chatbot {
        uuid id PK
        uuid organizationId FK
        integer chatbotId
        boolean isActive
    }

    user ||--o{ organization : owns
    organization ||--o{ organization_chatbot : has
Loading

Class diagram for Brand Kit wizard components and state

classDiagram
    class WorkspaceDialog {
        +boolean open
        +function onOpenChange(open boolean)
        +string title
        +string subtitle
        +ReactNode children
        +ReactNode footer
        +string maxWidth
    }

    class BrandKitDialog {
        +boolean open
        +function onOpenChange(open boolean)
        -BrandKitView currentView
        -function handleNewBrand()
        -function handleImportBrand()
        -function handleWizardFinish()
        -function handleWizardCancel()
    }

    class BrandKitEntry {
        +function onNewBrand()
        +function onImportBrand()
    }

    class BrandKitWizard {
        +function onFinish()
        +function onCancel()
        -number currentStep
        -boolean isInitialized
        -function handleNext()
        -function handleClose()
    }

    class WizardStep {
        +string name
        +ReactComponent component
    }

    class WizardStepper {
        +string[] steps
        +number currentStep
        +function onStepClick(index number)
    }

    class VibeStep {
        +function next()
        +function prev()
        +function close()
    }

    class FontsStep {
        +function next()
        +function prev()
    }

    class ColorsStep {
        +function next()
        +function prev()
    }

    class CheckpointStep {
        +function next()
        +function close()
        -function buildBrandKitData()
        -function handleContinueLater()
        -function handleGenerateLogo()
        -function handleSwitchFonts()
    }

    class BrandKitData {
        +string[] vibes
        +BrandKitFonts fonts
        +BrandKitPalette palette
        +BrandKitLogo logo
        +string createdAt
        +string updatedAt
    }

    class BrandKitFonts {
        +string primary
        +string secondary
    }

    class BrandKitPalette {
        +string name
        +string category
        +string primary
        +string accent
        +string bg
        +string text
    }

    class BrandKitLogo {
        +string generatedImageUrl
        +string[] variations
    }

    class SelectedFont {
        +string family
        +string role
    }

    class Palette {
        +string name
        +string category
        +string primary
        +string accent
        +string bg
        +string text
    }

    class OrganizationData {
        +string id
        +string name
        +OrganizationChatbot[] chatbots
        +BrandKitData brandKit
    }

    class useWorkspaceMedia {
        +string[] selectedVibes
        +SelectedFont[] selectedFonts
        +Palette selectedPalette
        +number brandKitCurrentStep
        +boolean isSavingBrandKit
        +boolean showBrandKit
        +function openBrandKit()
        +function setSelectedVibes(vibes string[])
        +function setSelectedFonts(fonts SelectedFont[])
        +function setSelectedPalette(palette Palette)
        +function saveBrandKit(brandKitData BrandKitData, organizationId string)
        +function saveBrandKitStep(step number, organizationId string)
        +function loadBrandKitStep(organizationId string) number
        +function clearBrandKitStep(organizationId string)
    }

    class useWorkspace {
        +string activeOrganization
        +WorkspaceInitialState initialState
    }

    class WorkspaceInitialState {
        +OrganizationData[] organizationData
    }

    WorkspaceDialog <|-- BrandKitDialog
    BrandKitDialog o-- BrandKitEntry
    BrandKitDialog o-- BrandKitWizard
    BrandKitWizard o-- WizardStepper
    BrandKitWizard o-- VibeStep
    BrandKitWizard o-- FontsStep
    BrandKitWizard o-- ColorsStep
    BrandKitWizard o-- CheckpointStep

    BrandKitData o-- BrandKitFonts
    BrandKitData o-- BrandKitPalette
    BrandKitData o-- BrandKitLogo

    useWorkspaceMedia o-- SelectedFont
    useWorkspaceMedia o-- Palette
    useWorkspaceMedia o-- BrandKitData

    OrganizationData o-- BrandKitData
    useWorkspace o-- WorkspaceInitialState
    WorkspaceInitialState o-- OrganizationData
Loading

File-Level Changes

Change Details Files
Introduce a reusable WorkspaceDialog shell and refactor the existing version history dialog onto it.
  • Create WorkspaceDialog component which standardizes dialog layout (header, subtitle, scrollable body, optional footer) for media workspace flows.
  • Refactor VersionHistoryDialog to render inside WorkspaceDialog instead of inlining Dialog markup, preserving existing preview/carousel behavior while delegating title and footer structure.
  • Update styling in the refactored dialog to use neutral background tokens (zinc-based) instead of hard-coded card backgrounds for better theme consistency.
apps/pro-web/components/ui/workspace-dialog/index.tsx
apps/pro-web/components/routes/workspace/media-tab/wizards/version-history-dialog.tsx
Add a Brand Kit dialog with an entry screen and multi-step wizard (vibe, fonts, colors, checkpoint) wired into the media tab sidebar.
  • Add BrandKitDialog wrapper that uses WorkspaceDialog and manages an internal view state (entry vs wizard vs future summary) with a dynamic subtitle showing the active organization when in wizard mode.
  • Implement BrandKitEntry screen with primary actions for creating a new brand or importing an existing brand, styled as a centered card.
  • Create BrandKitWizard that uses the shared wizard hook and WizardStepper, defines the four steps, and orchestrates navigation, finish/cancel behavior, and per-organization step restoration.
  • Implement VibeStep with a capped (2) multi-select grid of predefined vibes (name, description, color dots), including Skip behavior that defaults to Minimal.
  • Implement FontsStep with a vibe-based recommendation list, a vertical scroll-snap carousel, single-source-of-truth SelectedFont[] state, and selection/role reassignment logic.
  • Implement ColorsStep with curated palettes, live preview card that incorporates selected fonts and organization name, AA contrast-aware preview adjustments, shuffle, and carousel browsing with pagination dots.
  • Implement CheckpointStep that summarizes colors and fonts, supports swapping primary/secondary fonts, and wires "Continue Later" and "Generate Logo" buttons to the persistence layer.
  • Add WizardStepper component with animated, clickable step indicators and a flag icon for the final step, and integrate a sticky header/stepper layout for the wizard content.
apps/pro-web/components/routes/workspace/media-tab/ui/sidebar/media-sidebar.tsx
apps/pro-web/components/routes/workspace/media-tab/wizards/media-wizards.tsx
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/brand-kit-entry.tsx
apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/brand-kit-wizard.tsx
apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/wizard-stepper.tsx
apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/vibe-step.tsx
apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/fonts-step.tsx
apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/colors-step.tsx
apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/checkpoint-step.tsx
Extend workspace media state with Brand Kit selections and saving logic, including server mutation and IndexedDB sync.
  • Extend useWorkspaceMedia state with showBrandKit and a set of Brand Kit fields (selectedVibes, selectedFonts, selectedPalette, brandKitCurrentStep, isSavingBrandKit) and corresponding setters and helpers.
  • Expose openBrandKit, saveBrandKit, saveBrandKitStep, loadBrandKitStep, and clearBrandKitStep actions, backed by localStorage for per-organization step tracking.
  • Introduce SelectedFont and Palette types to model font roles and palette tokens, and use them across the wizard.
  • Add a TanStack Query mutation in useWorkspaceMedia that PATCHes /api/organizations/:id with brandKit, then updates the workspace structure in IndexedDB and invalidates the workspace structure query so the server and cache stay in sync.
  • Wire MediaSidebar Brand Kit button to openBrandKit and remove the placeholder Instagram/social media button from the sidebar.
apps/pro-web/lib/hooks/use-workspace-media.tsx
apps/pro-web/components/routes/workspace/media-tab/ui/sidebar/media-sidebar.tsx
Introduce BrandKitData domain types and surface brandKit on organizations throughout the app and backend.
  • Define BrandKitData in media.types.ts (vibes, fonts, palette, optional logo, createdAt/updatedAt) and export it for reuse.
  • Add optional brandKit?: BrandKitData
null to OrganizationData in thread.types.ts.
  • Expose brand_kit/brandKit field in Hasura metadata permissions for organization, ensuring insert, select, and update include the new column for moderator and user roles.
  • Regenerate GraphQL schema/types (mb-genql) so brand_kit is available on organization queries and mutations and update usage sites to map brand_kit → brandKit.
  • Update getUserOrganizations action and /api/organizations GET handler to request brandKit from Hasura and include it in the OrganizationData mapping/JSON responses.
  • Enhance useWorkspaceStructure merging logic so that for existing organizations, brandKit and chatbots are merged from the more recent source (IDB vs server) rather than blindly overwriting.
  • Persist Brand Kit to the database and API via a new brand_kit column on organization and associated Drizzle schema/relations updates.
    • Add brand_kit JSONB column with non-null default '{}' to the public.organization table via Hasura migration (up/down SQL).
    • Update Drizzle organization table definition to include a brandKit jsonb column with default {}, and adjust schema ordering for models/modelsEnum/categoryEnum/etc. while preserving foreign keys.
    • Update Drizzle relations to account for the reordered tables and maintain relations for models/modelsEnum, organization, referral, categoryEnum, thread, and prompt/promptTypeEnum.
    • Extend the /api/organizations/[id] PATCH handler to accept and validate an optional brandKit object, issue an updateOrganizationByPk mutation to set brand_kit, and return the updated organization including brandKit in the response mapping.
    apps/hasura/migrations/masterbots/1766505347896_alter_table_public_organization_add_column_brand_kit/up.sql
    apps/hasura/migrations/masterbots/1766505347896_alter_table_public_organization_add_column_brand_kit/down.sql
    packages/mb-drizzle/src/drizzle/schema.ts
    packages/mb-drizzle/src/drizzle/relations.ts
    apps/pro-web/app/api/organizations/[id]/route.ts
    Add Google Fonts integration/utilities to support font previews in the Brand Kit wizard.
    • Create a google-fonts.ts constant module that imports ~70 Google fonts via next/font/google, wires them into a GOOGLE_FONTS map keyed by human-readable family name, and exposes a FontFamily union type and getGoogleFontClass helper.
    • Use getGoogleFontClass in FontsStep, ColorsStep, and CheckpointStep to apply the appropriate font className for each preview sample (headlines, body text, character sets).
    apps/pro-web/lib/constants/google-fonts.ts
    apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/fonts-step.tsx
    apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/colors-step.tsx
    apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/checkpoint-step.tsx

    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 Dec 11, 2025

    Note

    Other AI code review bot(s) detected

    CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

    📝 Walkthrough

    Walkthrough

    Add Brand Kit: DB column and Hasura permissions, server API support, types, client fetching/merge, workspace media hook state and persistence (IDB/localStorage), Google Fonts support, a multi-step Brand Kit wizard/dialog UI, and Drizzle schema/relations updates. (34 words)

    Changes

    Cohort / File(s) Summary
    DB Schema & Hasura
    apps/hasura/metadata/databases/masterbots/tables/public_organization.yaml, apps/hasura/migrations/masterbots/1766505347896_alter_table_public_organization_add_column_brand_kit/*
    Add brand_kit JSONB column (default '{}') to public.organization; migration up/down added; include brand_kit in Hasura insert/select/update permissions for moderator and user roles.
    Server API
    apps/pro-web/app/api/organizations/route.ts, apps/pro-web/app/api/organizations/[id]/route.ts
    Select brandKit in GETs; accept/validate brandKit in PATCH body (UpdateOrganizationBody) and persist via Hasura mutation; include brandKit in responses.
    Client fetching & merge
    apps/pro-web/app/actions/thread.actions.ts, apps/pro-web/lib/queries/use-workspace-structure.ts
    GraphQL selection mapped to OrganizationData.brandKit; merge logic updated to replace existing org.brandKit/chatbots with incoming values when present.
    Types
    apps/pro-web/types/media.types.ts, apps/pro-web/types/thread.types.ts
    Add BrandKitData interface and extend OrganizationData with optional `brandKit?: BrandKitData
    Workspace media hook & persistence
    apps/pro-web/lib/hooks/use-workspace-media.tsx
    Add Brand Kit state (showBrandKit, selectedVibes/fonts/palette, current step, isSaving) and actions (openBrandKit, setShowBrandKit, saveBrandKit, save/load/clear step); implement server mutation + IndexedDB + localStorage sync and expose types SelectedFont/Palette.
    Brand Kit UI — Dialog & Entry
    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/brand-kit-entry.tsx
    New BrandKitDialog (views: entry
    Brand Kit Wizard & Steps
    apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/*
    New BrandKitWizard, WizardStepper, and steps: VibeStep, FontsStep, ColorsStep, CheckpointStep — multi-step UX with local persistence, previews, navigation, and finish/cancel flows.
    UI wrapper & integrations
    apps/pro-web/components/ui/workspace-dialog/index.tsx, apps/pro-web/components/routes/workspace/media-tab/ui/sidebar/media-sidebar.tsx, apps/pro-web/components/routes/workspace/media-tab/wizards/media-wizards.tsx, apps/pro-web/components/routes/workspace/media-tab/wizards/version-history-dialog.tsx
    Add WorkspaceDialog wrapper; wire openBrandKit into MediaSidebar; integrate BrandKitDialog into MediaWizards; migrate Version History content into WorkspaceDialog structure.
    Google Fonts constants
    apps/pro-web/lib/constants/google-fonts.ts
    New module exporting many Next.js Google font constants, GOOGLE_FONTS map, FontFamily type, and getGoogleFontClass helper.
    Drizzle schema & relations
    packages/mb-drizzle/src/drizzle/schema.ts, packages/mb-drizzle/src/drizzle/relations.ts
    Add brandKit JSONB column to organization pgTable; add/relocate department table; move / deduplicate promptRelations and promptTypeEnumRelations; reorder some schema blocks.

    Sequence Diagram(s)

    sequenceDiagram
        participant User
        participant UI as BrandKit UI
        participant Hook as WorkspaceMedia Hook
        participant IDB as IndexedDB
        participant API as Server API
        participant DB as Database
    
        Note over User,UI: Open Brand Kit
        User->>UI: open dialog
        UI->>Hook: init/load step for org
        Hook->>IDB: read cached brandKit & step
        IDB-->>Hook: cached data
        Hook-->>UI: populate wizard state
    
        loop Wizard steps
            User->>UI: select vibes / fonts / colors
            UI->>Hook: update in-memory state
            Hook->>IDB: persist step & selections
            IDB-->>Hook: ack
        end
    
        User->>UI: Finish / Save
        UI->>Hook: saveBrandKit(brandKitData)
        Hook->>API: PATCH organization.brand_kit
        API->>DB: persist brand_kit column
        DB-->>API: confirm
        API-->>Hook: return updated org
        Hook->>IDB: update local workspace cache
        Hook-->>UI: success
        UI->>User: show summary / close
    
    Loading

    Estimated code review effort

    🎯 4 (Complex) | ⏱️ ~55 minutes

    Possibly related PRs

    Suggested reviewers

    • AndlerRL

    Poem

    🐇 I nibble palettes, fonts, and vibes with cheer,

    I hop through dialogs and save what’s dear.
    From entry to wizard, step by tiny hop,
    Brand Kit tucked safe — then I stop.
    A rabbit winks: “Design, now go pop!”

    Pre-merge checks and finishing touches

    ❌ Failed checks (1 warning)
    Check name Status Explanation Resolution
    Docstring Coverage ⚠️ Warning Docstring coverage is 26.92% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
    ✅ Passed checks (2 passed)
    Check name Status Explanation
    Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
    Title check ✅ Passed The title directly describes the main feature: implementing a brand kit wizard with a multi-step creation flow, which is the primary and most comprehensive change in this changeset.
    ✨ Finishing touches
    • 📝 Generate docstrings

    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 December 11, 2025 21:37 Inactive
    @vercel vercel bot temporarily deployed to Preview – masterbots December 16, 2025 19:24 Inactive
    @vercel vercel bot temporarily deployed to Preview – masterbots December 16, 2025 22:10 Inactive
    @vercel vercel bot temporarily deployed to Preview – masterbots December 17, 2025 21:58 Inactive
    @vercel vercel bot temporarily deployed to Preview – masterbots December 18, 2025 15:41 Inactive
    @vercel vercel bot temporarily deployed to Preview – masterbots December 22, 2025 14:37 Inactive
    @vercel vercel bot temporarily deployed to Preview – masterbots December 22, 2025 21:19 Inactive
    @vercel vercel bot temporarily deployed to Preview – masterbots December 23, 2025 15:34 Inactive
    @vercel vercel bot temporarily deployed to Preview – masterbots December 26, 2025 22:09 Inactive
    @vercel vercel bot temporarily deployed to Preview – masterbots December 29, 2025 16:34 Inactive
    - Add dynamic subtitle showing org name + "Logo & Style" in wizard mode
    - Implement sticky wizard stepper and navigation buttons layout
    - Create VibeStep with 12 brand vibes, 2-selection limit, and color preview
    - Add proper scroll container for wizard content with fixed header/footer
    - Style BadgeCheck icon and color dots with proper borders for dark mode
    - Update wizard container to use viewport-based height for proper layout
    - Add FontsStep with primary/secondary font selection
    - Create Google Fonts integration with 70+ font families
    - Implement vibe-based font recommendations system
    - Add role badges (primary/secondary) for selected fonts
    - Include font preview with multiple samples
    - Update Brand Kit state management in useWorkspaceMedia hook
    - Adjust wizard and dialog padding for better spacing
    - Fix VibeStep to use shared state from context
    … scroll optimization
    
    - Add sticky bar showing selected fonts with removal capability
    - Implement vertical scroll snap carousel for font grid
    - Update font state management with role-based structure
    - Optimize scroll behavior in VibeStep and wizard stepper
    - Remove border from stepper for cleaner visual separation
    …e preview
    
    - Add ColorsStep with 8 curated color palettes across multiple categories
    - Implement live preview card with selected fonts and organization name
    - Add AA contrast ratio validation with gentle auto-adjustment
    - Create shuffle functionality for random palette selection
    - Build carousel navigation for palette browsing
    - Display color strips with primary, accent, bg, and text tokens
    - Add palette state management to useWorkspaceMedia hook
    - Integrate selected fonts from previous step into color preview
    - Create CheckpointStep component to review selected brand elements
    - Display color scheme with palette circles and category information
    - Show font family preview with primary and secondary fonts
    - Add font switching functionality between primary and secondary roles
    - Integrate organization name and vibe label in checkpoint UI
    - Replace placeholder LogoStep with functional CheckpointStep
    - Add flag icon to final step in wizard stepper for visual clarity
    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

    ♻️ Duplicate comments (2)
    apps/pro-web/lib/constants/google-fonts.ts (1)

    76-410: Consider supporting multiple font weights for typography flexibility.

    All fonts are configured with a single weight ('400'), which limits typographic hierarchy for brand kits. Brand identity typically requires multiple weights (e.g., 400, 600, 700) for headers, body text, and emphasis. Consider enabling multiple weights or variable fonts where supported.

    Example: Supporting multiple weights
    export const inter = Inter({
      subsets: ['latin'],
      display: 'swap',
      weight: ['400', '600', '700'], // Regular, semi-bold, bold
    })
    
    // Or for variable fonts:
    export const inter = Inter({
      subsets: ['latin'],
      display: 'swap',
      variable: true, // Enable CSS variable
    })

    As per coding guidelines.

    apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/checkpoint-step.tsx (1)

    33-48: Add defensive check for empty selectedFonts array.

    The buildBrandKitData function accesses selectedFonts[0].family without verifying the array has at least one element. While the wizard flow should enforce font selection, adding a defensive check improves robustness and prevents potential runtime errors.

    🔎 Suggested fix
     const buildBrandKitData = (): BrandKitData => {
     	if (!selectedPalette) {
     		throw new Error('Palette is required')
     	}
    +	if (selectedFonts.length === 0) {
    +		throw new Error('At least one font is required')
    +	}
    
     	return {
     		vibes: selectedVibes,
     		fonts: {
     			primary: selectedFonts[0].family,
     			secondary: selectedFonts[1]?.family,
     		},
     		palette: selectedPalette,
     		createdAt: new Date().toISOString(),
     		updatedAt: new Date().toISOString(),
     	}
     }
    🧹 Nitpick comments (7)
    apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/wizard-stepper.tsx (1)

    31-40: Extract inline arrow function to stable handler reference.

    The inline arrow function in the onClick prop violates the coding guideline to use regular function references for event handlers. Extract to a stable handler or memoize to avoid unnecessary re-renders.

    🔎 Proposed refactor
     <button
     	type="button"
    -	onClick={() => isClickable && onStepClick(index)}
    +	onClick={isClickable ? () => onStepClick(index) : undefined}
     	disabled={!isClickable}
     	className={cn(
     		'flex flex-col items-center transition-all',
     		isClickable && 'cursor-pointer',
     		!isClickable && 'cursor-not-allowed',
     	)}
     >

    Or better yet, extract to a handler function:

    +const handleStepClick = (stepIndex: number) => {
    +	if (onStepClick && stepIndex <= currentStep) {
    +		onStepClick(stepIndex)
    +	}
    +}
    +
     return (
     	<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 pb-6 pt-2">
     		{/* Steps row */}
     		<div className="flex items-center justify-center w-full max-w-3xl">
     			{steps.map((step, index) => {
     				const isActive = index === currentStep
     				const isCompleted = index < currentStep
     				const isFuture = index > currentStep
     				const isClickable = onStepClick && index <= currentStep
    
     				return (
     					<React.Fragment key={step}>
     						{/* Step circle + label */}
     						<button
     							type="button"
    -							onClick={() => isClickable && onStepClick(index)}
    +							onClick={() => handleStepClick(index)}
     							disabled={!isClickable}

    As per coding guidelines.

    apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/checkpoint-step.tsx (1)

    51-98: Improve error handling with user feedback.

    Both handleContinueLater and handleGenerateLogo silently swallow errors after logging to console. Users won't know if the save operation failed. Consider adding toast notifications or error UI to inform users of save failures.

    Example enhancement
    import { toast } from '@/components/ui/use-toast' // or your toast system
    
    const handleContinueLater = async () => {
    	if (!currentOrg?.id) {
    		close()
    		return
    	}
    
    	setIsSaving(true)
    
    	try {
    		const brandKitData = buildBrandKitData()
    		await saveBrandKit(brandKitData, currentOrg.id)
    		
    		toast({
    			title: 'Success',
    			description: 'Brand Kit saved successfully',
    		})
    		close()
    	} catch (error) {
    		console.error('❌ Failed to save brand kit:', error)
    		toast({
    			title: 'Error',
    			description: 'Failed to save Brand Kit. Please try again.',
    			variant: 'destructive',
    		})
    		// Don't close on error - let user retry
    	} finally {
    		setIsSaving(false)
    	}
    }
    apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/vibe-step.tsx (1)

    134-144: Consider extracting the inline arrow function to avoid re-renders.

    The inline arrow function onClick={() => handleVibeClick(vibe.name)} creates a new function on every render. While not critical, consider using a data attribute pattern or useCallback for better performance.

    🔎 Alternative approach using data attributes
    +	const handleVibeButtonClick = (e: React.MouseEvent<HTMLButtonElement>) => {
    +		const vibeName = e.currentTarget.dataset.vibeName
    +		if (vibeName) handleVibeClick(vibeName)
    +	}
    +
     	return (
     		<button
     			key={vibe.name}
     			type="button"
    -			onClick={() => handleVibeClick(vibe.name)}
    +			onClick={handleVibeButtonClick}
    +			data-vibe-name={vibe.name}
     			className={cn(
    apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/brand-kit-wizard.tsx (1)

    57-57: Remove debug console.log statements or replace with proper logging.

    Multiple console.log statements are present for debugging purposes. These should be removed before production or replaced with a proper logging service for better observability and log management.

    Also applies to: 62-62, 88-88, 119-119

    apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/brand-kit-dialog.tsx (1)

    36-36: Remove debug console.log statements.

    Console.log statements in handlers should be removed before production or replaced with proper logging for production observability.

    Also applies to: 41-41

    apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/colors-step.tsx (1)

    296-305: Consider stable function references for palette selection.

    The inline arrow function in the onClick handler creates a new function instance on each render. For better performance, consider using data attributes or event delegation.

    🔎 Alternative approach
    +	const handlePaletteButtonClick = (e: React.MouseEvent<HTMLButtonElement>) => {
    +		const paletteName = e.currentTarget.dataset.paletteName
    +		const palette = PALETTES.find(p => p.name === paletteName)
    +		if (palette) setSelectedPalette(palette)
    +	}
    +
     	<button
     		type="button"
    -		onClick={() => handlePaletteClick(palette)}
    +		onClick={handlePaletteButtonClick}
    +		data-palette-name={palette.name}
     		className={cn(
    apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/fonts-step.tsx (1)

    104-115: Consider stable function reference for font selection.

    The inline arrow function onClick={() => onFontClick(font.family)} is recreated on every render. Consider using data attributes for better performance across all font cards.

    🔎 Alternative approach
    +	const handleFontButtonClick = (e: React.MouseEvent<HTMLButtonElement>) => {
    +		const fontFamily = e.currentTarget.dataset.fontFamily
    +		if (fontFamily) onFontClick(fontFamily)
    +	}
    +
     	<button
     		key={font.family}
     		type="button"
    -		onClick={() => onFontClick(font.family)}
    +		onClick={handleFontButtonClick}
    +		data-font-family={font.family}
     		className={cn(
    📜 Review details

    Configuration used: defaults

    Review profile: CHILL

    Plan: Pro

    📥 Commits

    Reviewing files that changed from the base of the PR and between d98b66d and 0e113d2.

    ⛔ Files ignored due to path filters (3)
    • packages/mb-genql/generated/schema.graphql is excluded by !**/generated/**
    • packages/mb-genql/generated/schema.ts is excluded by !**/generated/**
    • packages/mb-genql/generated/types.ts is excluded by !**/generated/**
    📒 Files selected for processing (25)
    • apps/hasura/metadata/databases/masterbots/tables/public_organization.yaml
    • apps/hasura/migrations/masterbots/1766505347896_alter_table_public_organization_add_column_brand_kit/down.sql
    • apps/hasura/migrations/masterbots/1766505347896_alter_table_public_organization_add_column_brand_kit/up.sql
    • apps/pro-web/app/actions/thread.actions.ts
    • apps/pro-web/app/api/organizations/[id]/route.ts
    • apps/pro-web/app/api/organizations/route.ts
    • 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/brand-kit-entry.tsx
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/brand-kit-wizard.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/steps/colors-step.tsx
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/fonts-step.tsx
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/vibe-step.tsx
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/wizard-stepper.tsx
    • apps/pro-web/components/routes/workspace/media-tab/ui/sidebar/media-sidebar.tsx
    • apps/pro-web/components/routes/workspace/media-tab/wizards/media-wizards.tsx
    • apps/pro-web/components/routes/workspace/media-tab/wizards/version-history-dialog.tsx
    • apps/pro-web/components/ui/workspace-dialog/index.tsx
    • apps/pro-web/lib/constants/google-fonts.ts
    • apps/pro-web/lib/hooks/use-workspace-media.tsx
    • apps/pro-web/lib/queries/use-workspace-structure.ts
    • apps/pro-web/types/media.types.ts
    • apps/pro-web/types/thread.types.ts
    • packages/mb-drizzle/src/drizzle/relations.ts
    • packages/mb-drizzle/src/drizzle/schema.ts
    🚧 Files skipped from review as they are similar to previous changes (9)
    • apps/pro-web/app/actions/thread.actions.ts
    • apps/hasura/migrations/masterbots/1766505347896_alter_table_public_organization_add_column_brand_kit/up.sql
    • apps/pro-web/components/ui/workspace-dialog/index.tsx
    • apps/pro-web/components/routes/workspace/media-tab/wizards/version-history-dialog.tsx
    • apps/pro-web/lib/hooks/use-workspace-media.tsx
    • apps/pro-web/app/api/organizations/route.ts
    • apps/pro-web/lib/queries/use-workspace-structure.ts
    • apps/pro-web/app/api/organizations/[id]/route.ts
    • packages/mb-drizzle/src/drizzle/schema.ts
    🧰 Additional context used
    📓 Path-based instructions (10)
    apps/{web,pro-web}/components/routes/**

    📄 CodeRabbit inference engine (.github/copilot-instructions.md)

    Place page-specific components under components/routes/[feature]/

    Organize page-specific components under components/routes/

    Files:

    • apps/pro-web/components/routes/workspace/media-tab/wizards/media-wizards.tsx
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/brand-kit-wizard.tsx
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/fonts-step.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/sidebar/media-sidebar.tsx
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/brand-kit-entry.tsx
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/vibe-step.tsx
    • 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/colors-step.tsx
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/wizard-stepper.tsx
    apps/{web,pro-web}/**/*.tsx

    📄 CodeRabbit inference engine (.github/copilot-instructions.md)

    apps/{web,pro-web}/**/*.tsx: Declare React components with the function keyword
    Name React components using PascalCase
    Order component files: imports → constants → component → styled components → types/interfaces
    Use functional components and hooks; type props with a TypeScript interface
    Use regular function references (not inline arrows) for React event handlers to avoid extra re-renders

    apps/{web,pro-web}/**/*.tsx: Use regular function references for event handlers instead of inline arrow functions in JSX
    Declare React components with the function keyword (function ComponentName() {})
    Use PascalCase for React component names
    Order React component files: imports, constants, component, styled components, types/interfaces
    Style components with Tailwind CSS utility classes

    Files:

    • apps/pro-web/components/routes/workspace/media-tab/wizards/media-wizards.tsx
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/brand-kit-wizard.tsx
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/fonts-step.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/sidebar/media-sidebar.tsx
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/brand-kit-entry.tsx
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/vibe-step.tsx
    • 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/colors-step.tsx
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/wizard-stepper.tsx
    **/*.{ts,tsx}

    📄 CodeRabbit inference engine (.github/copilot-instructions.md)

    **/*.{ts,tsx}: Avoid default exports; prefer named exports
    Use meaningful variable names; prefix booleans with is/has/does/should
    Use interface for objects/classes; type for unions/tuples/aliases; enum for fixed sets; const for literals
    Avoid any; prefer specific types
    Leverage type inference where clear; annotate function params and return types
    Prefer type assertions with 'as' over angle-bracket syntax
    Use type guards to narrow types in conditionals

    **/*.{ts,tsx}: Avoid default exports; prefer named exports
    Use modern JavaScript features with TypeScript across the codebase
    Use meaningful names; prefix booleans with is/has/does/should (e.g., isDisabled)
    Receive an object and return an object (RORO) for functions interfacing with external services
    Type definitions: use interface for objects/classes; type for unions/tuples/aliases; const for literals; enum for fixed sets
    Avoid any; prefer specific types
    Leverage type inference; omit explicit annotations when obvious
    Add explicit type annotations for function parameters and return values
    Prefer type assertions with as over angle-bracket syntax
    Use type guards to narrow types within conditionals
    Favor composition over inheritance in code structure

    **/*.{ts,tsx}: Avoid default exports; prefer named exports
    Prefer RORO (Receive an object, return an object) for function signatures, especially for external service interactions
    Use interface for object shapes/classes; type for unions/tuples/aliases; const for literals; enum for fixed enumerations
    Avoid using any; prefer precise types
    Leverage type inference when clear; omit redundant annotations
    Explicitly annotate function parameters and return types
    Prefer type assertions with as over angle-bracket syntax
    Use type guards to narrow types safely
    Use meaningful variable names; prefix booleans with is/has/does/should
    Favor composition over inheritance
    Use lowercase dash-separated names for files and folders; use extensions like .config.ts, .test.ts, .context.tsx, .typ...

    Files:

    • apps/pro-web/components/routes/workspace/media-tab/wizards/media-wizards.tsx
    • packages/mb-drizzle/src/drizzle/relations.ts
    • apps/pro-web/types/media.types.ts
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/brand-kit-wizard.tsx
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/fonts-step.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/sidebar/media-sidebar.tsx
    • apps/pro-web/types/thread.types.ts
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/brand-kit-entry.tsx
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/vibe-step.tsx
    • 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/colors-step.tsx
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/wizard-stepper.tsx
    • apps/pro-web/lib/constants/google-fonts.ts
    apps/{web,pro-web}/**/*.{ts,tsx}

    📄 CodeRabbit inference engine (.github/copilot-instructions.md)

    apps/{web,pro-web}/**/*.{ts,tsx}: Use lowercase kebab-case for directories and filenames (components can omit extension)
    Verify DOM operations and consider timing (RAF/delays) when manipulating the DOM in React
    Use Tailwind CSS utility classes for styling React components

    apps/{web,pro-web}/**/*.{ts,tsx}: Use TypeScript to define React props types (interfaces for props)
    Use custom hooks (useMBChat, useThread, useSidebar, useModel) for domain-specific state and keep providers focused and composed hierarchically
    Separate presentation from business logic and compose small components
    Use hasura.service.ts for all GraphQL operations from UI layers

    apps/{web,pro-web}/**/*.{ts,tsx}: Prefer Server Components; use the use client directive only when client-side features are required
    Implement Nested Layouts using the App Router
    Use Streaming and Suspense in Next.js 14/15 where appropriate
    Follow Next.js file conventions as per documentation
    Use IndexedDB for local caching to enable immediate UI updates

    Files:

    • apps/pro-web/components/routes/workspace/media-tab/wizards/media-wizards.tsx
    • apps/pro-web/types/media.types.ts
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/brand-kit-wizard.tsx
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/fonts-step.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/sidebar/media-sidebar.tsx
    • apps/pro-web/types/thread.types.ts
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/brand-kit-entry.tsx
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/vibe-step.tsx
    • 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/colors-step.tsx
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/wizard-stepper.tsx
    • apps/pro-web/lib/constants/google-fonts.ts
    **/*

    📄 CodeRabbit inference engine (CLAUDE.md)

    **/*: Use lowercase with dash-separated directory and file names; use specific extensions: .config.ts, .test.ts, .context.tsx, .type.ts, .hook.ts; components may omit extension
    Use Bun for package management and scripts instead of npm/yarn
    Use Biome for formatting and linting via bun format-and-lint:fix; avoid Prettier/ESLint configs

    Files:

    • apps/pro-web/components/routes/workspace/media-tab/wizards/media-wizards.tsx
    • packages/mb-drizzle/src/drizzle/relations.ts
    • apps/hasura/migrations/masterbots/1766505347896_alter_table_public_organization_add_column_brand_kit/down.sql
    • apps/pro-web/types/media.types.ts
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/brand-kit-wizard.tsx
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/fonts-step.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/sidebar/media-sidebar.tsx
    • apps/hasura/metadata/databases/masterbots/tables/public_organization.yaml
    • apps/pro-web/types/thread.types.ts
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/brand-kit-entry.tsx
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/vibe-step.tsx
    • 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/colors-step.tsx
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/wizard-stepper.tsx
    • apps/pro-web/lib/constants/google-fonts.ts
    **/*.tsx

    📄 CodeRabbit inference engine (AGENTS.md)

    **/*.tsx: Avoid inline arrow functions in JSX props; use stable function references for event handlers
    Declare React components with the function keyword (functional components)
    Use PascalCase for React component names
    Use functional components and hooks instead of class components
    Type component props with TypeScript interfaces

    Files:

    • apps/pro-web/components/routes/workspace/media-tab/wizards/media-wizards.tsx
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/brand-kit-wizard.tsx
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/fonts-step.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/sidebar/media-sidebar.tsx
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/brand-kit-entry.tsx
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/vibe-step.tsx
    • 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/colors-step.tsx
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/wizard-stepper.tsx
    apps/{web,pro-web}/components/**/*.tsx

    📄 CodeRabbit inference engine (AGENTS.md)

    Order component files: imports → constants → component → styled components → types/interfaces

    Files:

    • apps/pro-web/components/routes/workspace/media-tab/wizards/media-wizards.tsx
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/brand-kit-wizard.tsx
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/fonts-step.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/sidebar/media-sidebar.tsx
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/brand-kit-entry.tsx
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/vibe-step.tsx
    • 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/colors-step.tsx
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/wizard-stepper.tsx
    apps/hasura/migrations/masterbots/**

    📄 CodeRabbit inference engine (.github/copilot-instructions.md)

    Store and manage Hasura migrations under apps/hasura/migrations/masterbots/

    Store Hasura-managed migrations in timestamped folders under apps/hasura/migrations/masterbots/

    Keep Hasura-managed database migrations in timestamped folders under apps/hasura/migrations/masterbots/

    Files:

    • apps/hasura/migrations/masterbots/1766505347896_alter_table_public_organization_add_column_brand_kit/down.sql
    apps/{web,pro-web}/types/**

    📄 CodeRabbit inference engine (.github/copilot-instructions.md)

    Place shared TypeScript definitions under types/

    Keep TypeScript type definitions under types/

    Files:

    • apps/pro-web/types/media.types.ts
    • apps/pro-web/types/thread.types.ts
    apps/{web,pro-web}/types/**/*.ts

    📄 CodeRabbit inference engine (AGENTS.md)

    Place shared TypeScript types under types/

    Files:

    • apps/pro-web/types/media.types.ts
    • apps/pro-web/types/thread.types.ts
    🧠 Learnings (23)
    📓 Common learnings
    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.
    
    📚 Learning: 2025-09-30T22:11:18.270Z
    Learnt from: CR
    Repo: bitcashorg/masterbots PR: 0
    File: AGENTS.md:0-0
    Timestamp: 2025-09-30T22:11:18.270Z
    Learning: Applies to apps/hasura/migrations/masterbots/** : Keep Hasura-managed database migrations in timestamped folders under apps/hasura/migrations/masterbots/
    

    Applied to files:

    • apps/hasura/migrations/masterbots/1766505347896_alter_table_public_organization_add_column_brand_kit/down.sql
    📚 Learning: 2025-09-30T22:08:30.965Z
    Learnt from: CR
    Repo: bitcashorg/masterbots PR: 0
    File: .github/copilot-instructions.md:0-0
    Timestamp: 2025-09-30T22:08:30.965Z
    Learning: Applies to apps/hasura/migrations/masterbots/** : Store and manage Hasura migrations under apps/hasura/migrations/masterbots/
    

    Applied to files:

    • apps/hasura/migrations/masterbots/1766505347896_alter_table_public_organization_add_column_brand_kit/down.sql
    📚 Learning: 2025-09-30T22:09:37.969Z
    Learnt from: CR
    Repo: bitcashorg/masterbots PR: 0
    File: CLAUDE.md:0-0
    Timestamp: 2025-09-30T22:09:37.969Z
    Learning: Applies to apps/{web,pro-web}/**/*.{ts,tsx} : Use TypeScript to define React props types (interfaces for props)
    

    Applied to files:

    • apps/pro-web/types/media.types.ts
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/fonts-step.tsx
    • apps/pro-web/types/thread.types.ts
    📚 Learning: 2025-09-30T22:08:30.965Z
    Learnt from: CR
    Repo: bitcashorg/masterbots PR: 0
    File: .github/copilot-instructions.md:0-0
    Timestamp: 2025-09-30T22:08:30.965Z
    Learning: Applies to apps/{web,pro-web}/types/** : Place shared TypeScript definitions under types/
    

    Applied to files:

    • apps/pro-web/types/media.types.ts
    • apps/pro-web/types/thread.types.ts
    • apps/pro-web/lib/constants/google-fonts.ts
    📚 Learning: 2025-09-30T22:11:18.270Z
    Learnt from: CR
    Repo: bitcashorg/masterbots PR: 0
    File: AGENTS.md:0-0
    Timestamp: 2025-09-30T22:11:18.270Z
    Learning: Applies to apps/{web,pro-web}/components/shared/**/*.{ts,tsx} : Store reusable UI elements in components/shared/
    

    Applied to files:

    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/brand-kit-wizard.tsx
    • apps/pro-web/components/routes/workspace/media-tab/ui/sidebar/media-sidebar.tsx
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/brand-kit-entry.tsx
    • 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/wizard-stepper.tsx
    📚 Learning: 2025-09-30T22:09:37.969Z
    Learnt from: CR
    Repo: bitcashorg/masterbots PR: 0
    File: CLAUDE.md:0-0
    Timestamp: 2025-09-30T22:09:37.969Z
    Learning: Applies to apps/{web,pro-web}/**/*.tsx : Order React component files: imports, constants, component, styled components, types/interfaces
    

    Applied to files:

    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/brand-kit-wizard.tsx
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/fonts-step.tsx
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/wizard-stepper.tsx
    📚 Learning: 2025-09-30T22:11:18.270Z
    Learnt from: CR
    Repo: bitcashorg/masterbots PR: 0
    File: AGENTS.md:0-0
    Timestamp: 2025-09-30T22:11:18.270Z
    Learning: Applies to apps/{web,pro-web}/components/ui/**/*.{ts,tsx} : Follow Radix UI patterns under components/ui/
    

    Applied to files:

    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/brand-kit-wizard.tsx
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/fonts-step.tsx
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/wizard-stepper.tsx
    📚 Learning: 2025-09-30T22:08:30.965Z
    Learnt from: CR
    Repo: bitcashorg/masterbots PR: 0
    File: .github/copilot-instructions.md:0-0
    Timestamp: 2025-09-30T22:08:30.965Z
    Learning: Applies to apps/{web,pro-web}/**/*.tsx : Order component files: imports → constants → component → styled components → types/interfaces
    

    Applied to files:

    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/brand-kit-wizard.tsx
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/fonts-step.tsx
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/wizard-stepper.tsx
    • apps/pro-web/lib/constants/google-fonts.ts
    📚 Learning: 2025-09-30T22:11:18.270Z
    Learnt from: CR
    Repo: bitcashorg/masterbots PR: 0
    File: AGENTS.md:0-0
    Timestamp: 2025-09-30T22:11:18.270Z
    Learning: Applies to apps/{web,pro-web}/components/**/*.tsx : Order component files: imports → constants → component → styled components → types/interfaces
    

    Applied to files:

    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/brand-kit-wizard.tsx
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/fonts-step.tsx
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/wizard-stepper.tsx
    • apps/pro-web/lib/constants/google-fonts.ts
    📚 Learning: 2025-09-12T20:29:24.733Z
    Learnt from: AndlerRL
    Repo: bitcashorg/masterbots PR: 561
    File: apps/pro-web/lib/hooks/use-accessibility.tsx:7-7
    Timestamp: 2025-09-12T20:29:24.733Z
    Learning: When extending FontSize type in use-accessibility.tsx hooks, corresponding CSS rules must exist in globals.css for [data-font-size="value"] selectors, otherwise the unsupported font size values should be removed from the type definition to maintain consistency between TypeScript types and actual styling support.
    

    Applied to files:

    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/fonts-step.tsx
    • apps/pro-web/lib/constants/google-fonts.ts
    📚 Learning: 2025-09-30T22:11:18.270Z
    Learnt from: CR
    Repo: bitcashorg/masterbots PR: 0
    File: AGENTS.md:0-0
    Timestamp: 2025-09-30T22:11:18.270Z
    Learning: Applies to apps/{web,pro-web}/**/*.{ts,tsx} : Follow Next.js file conventions as per documentation
    

    Applied to files:

    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/fonts-step.tsx
    • apps/pro-web/lib/constants/google-fonts.ts
    📚 Learning: 2025-09-30T22:08:30.965Z
    Learnt from: CR
    Repo: bitcashorg/masterbots PR: 0
    File: .github/copilot-instructions.md:0-0
    Timestamp: 2025-09-30T22:08:30.965Z
    Learning: Applies to apps/{web,pro-web}/**/*.tsx : Use functional components and hooks; type props with a TypeScript interface
    

    Applied to files:

    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/fonts-step.tsx
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/wizard-stepper.tsx
    📚 Learning: 2025-09-30T22:09:37.969Z
    Learnt from: CR
    Repo: bitcashorg/masterbots PR: 0
    File: CLAUDE.md:0-0
    Timestamp: 2025-09-30T22:09:37.969Z
    Learning: Applies to apps/{web,pro-web}/**/*.{ts,tsx} : Use custom hooks (useMBChat, useThread, useSidebar, useModel) for domain-specific state and keep providers focused and composed hierarchically
    

    Applied to files:

    • apps/pro-web/components/routes/workspace/media-tab/ui/sidebar/media-sidebar.tsx
    • apps/pro-web/types/thread.types.ts
    📚 Learning: 2025-04-26T10:58:44.341Z
    Learnt from: AndlerRL
    Repo: bitcashorg/masterbots PR: 465
    File: apps/masterbots.ai/components/routes/browse/browse-list.tsx:1-1
    Timestamp: 2025-04-26T10:58:44.341Z
    Learning: The browse-list.tsx component and related components in the browse directory are redundant since thread-list.tsx has been developed to support all pages including public pages, and should be removed to simplify the codebase.
    

    Applied to files:

    • apps/pro-web/components/routes/workspace/media-tab/ui/sidebar/media-sidebar.tsx
    📚 Learning: 2025-04-26T10:58:44.341Z
    Learnt from: AndlerRL
    Repo: bitcashorg/masterbots PR: 465
    File: apps/masterbots.ai/components/routes/browse/browse-list.tsx:1-1
    Timestamp: 2025-04-26T10:58:44.341Z
    Learning: The browse-list.tsx component is redundant as thread-list.tsx has been developed to support all the same functionality including public pages. The codebase should be cleaned up to remove browse-list.tsx and related components in the browse directory, along with the BrowseProvider context.
    

    Applied to files:

    • apps/pro-web/components/routes/workspace/media-tab/ui/sidebar/media-sidebar.tsx
    📚 Learning: 2025-09-30T22:11:18.270Z
    Learnt from: CR
    Repo: bitcashorg/masterbots PR: 0
    File: AGENTS.md:0-0
    Timestamp: 2025-09-30T22:11:18.270Z
    Learning: Applies to apps/{web,pro-web}/types/**/*.ts : Place shared TypeScript types under types/
    

    Applied to files:

    • apps/pro-web/types/thread.types.ts
    📚 Learning: 2025-09-30T22:09:37.969Z
    Learnt from: CR
    Repo: bitcashorg/masterbots PR: 0
    File: CLAUDE.md:0-0
    Timestamp: 2025-09-30T22:09:37.969Z
    Learning: Applies to apps/{web,pro-web}/types/** : Keep TypeScript type definitions under types/
    

    Applied to files:

    • apps/pro-web/types/thread.types.ts
    📚 Learning: 2025-10-24T20:40:56.510Z
    Learnt from: AndlerRL
    Repo: bitcashorg/masterbots PR: 573
    File: apps/pro-web/components/routes/pro/pro.tsx:49-49
    Timestamp: 2025-10-24T20:40:56.510Z
    Learning: In apps/pro-web/lib/hooks/use-mb-chat.tsx, the useMBChat() hook returns a tuple [MBChatHookState, MBChatHookActions], not a direct object. Use array destructuring to access state (index 0) and actions (index 1), e.g., const [state, actions] = useMBChat() or const [{ isLoading }] = useMBChat().
    

    Applied to files:

    • apps/pro-web/types/thread.types.ts
    📚 Learning: 2025-09-30T22:09:37.969Z
    Learnt from: CR
    Repo: bitcashorg/masterbots PR: 0
    File: CLAUDE.md:0-0
    Timestamp: 2025-09-30T22:09:37.969Z
    Learning: Applies to **/*.{ts,tsx} : Use meaningful names; prefix booleans with is/has/does/should (e.g., isDisabled)
    

    Applied to files:

    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/brand-kit-dialog.tsx
    📚 Learning: 2024-11-07T14:40:39.595Z
    Learnt from: Bran18
    Repo: bitcashorg/masterbots PR: 301
    File: apps/masterbots.ai/components/routes/chat/chat-combobox.tsx:33-33
    Timestamp: 2024-11-07T14:40:39.595Z
    Learning: In `apps/masterbots.ai/components/routes/chat/chat-combobox.tsx`, the 'MB' logo is temporarily represented by the string `'MB'` until the custom MB icon is created.
    

    Applied to files:

    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/brand-kit-dialog.tsx
    📚 Learning: 2025-09-30T22:08:30.965Z
    Learnt from: CR
    Repo: bitcashorg/masterbots PR: 0
    File: .github/copilot-instructions.md:0-0
    Timestamp: 2025-09-30T22:08:30.965Z
    Learning: Applies to **/*.{ts,tsx} : Use meaningful variable names; prefix booleans with is/has/does/should
    

    Applied to files:

    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/brand-kit-dialog.tsx
    📚 Learning: 2025-09-30T22:09:37.969Z
    Learnt from: CR
    Repo: bitcashorg/masterbots PR: 0
    File: CLAUDE.md:0-0
    Timestamp: 2025-09-30T22:09:37.969Z
    Learning: Applies to apps/{web,pro-web}/**/*.{ts,tsx} : Separate presentation from business logic and compose small components
    

    Applied to files:

    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/wizard-stepper.tsx
    🧬 Code graph analysis (9)
    packages/mb-drizzle/src/drizzle/relations.ts (1)
    packages/mb-drizzle/src/drizzle/schema.ts (4)
    • prompt (43-61)
    • promptTypeEnum (63-65)
    • promptChatbot (463-489)
    • promptUser (491-517)
    apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/brand-kit-wizard.tsx (5)
    apps/pro-web/components/ui/wizard/index.tsx (1)
    • WizardStep (23-26)
    apps/pro-web/lib/hooks/use-workspace-media.tsx (1)
    • useWorkspaceMedia (496-504)
    apps/pro-web/lib/hooks/use-workspace.tsx (1)
    • useWorkspace (668-674)
    apps/pro-web/components/ui/wizard/hook/useWizard.tsx (1)
    • useWizard (18-83)
    apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/wizard-stepper.tsx (1)
    • WizardStepper (13-82)
    apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/checkpoint-step.tsx (6)
    apps/pro-web/components/ui/wizard/index.tsx (1)
    • WizardStepProps (14-21)
    apps/pro-web/lib/hooks/use-workspace-media.tsx (1)
    • useWorkspaceMedia (496-504)
    apps/pro-web/lib/hooks/use-workspace.tsx (1)
    • useWorkspace (668-674)
    apps/pro-web/types/media.types.ts (1)
    • BrandKitData (55-95)
    apps/pro-web/lib/constants/google-fonts.ts (2)
    • getGoogleFontClass (488-490)
    • FontFamily (486-486)
    apps/pro-web/lib/utils.ts (1)
    • cn (9-11)
    apps/pro-web/components/routes/workspace/media-tab/ui/sidebar/media-sidebar.tsx (1)
    apps/pro-web/lib/hooks/use-workspace-media.tsx (1)
    • useWorkspaceMedia (496-504)
    apps/pro-web/types/thread.types.ts (1)
    apps/pro-web/types/media.types.ts (1)
    • BrandKitData (55-95)
    apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/vibe-step.tsx (3)
    apps/pro-web/components/ui/wizard/index.tsx (1)
    • WizardStepProps (14-21)
    apps/pro-web/lib/hooks/use-workspace-media.tsx (1)
    • useWorkspaceMedia (496-504)
    apps/pro-web/lib/utils.ts (1)
    • cn (9-11)
    apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/brand-kit-dialog.tsx (4)
    apps/pro-web/lib/hooks/use-workspace.tsx (1)
    • useWorkspace (668-674)
    apps/pro-web/components/ui/workspace-dialog/index.tsx (1)
    • WorkspaceDialog (23-67)
    apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/brand-kit-entry.tsx (1)
    • BrandKitEntry (11-53)
    apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/brand-kit-wizard.tsx (1)
    • BrandKitWizard (27-193)
    apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/colors-step.tsx (7)
    apps/pro-web/components/ui/wizard/index.tsx (1)
    • WizardStepProps (14-21)
    apps/pro-web/lib/hooks/use-workspace-media.tsx (2)
    • useWorkspaceMedia (496-504)
    • Palette (65-72)
    apps/pro-web/lib/hooks/use-workspace.tsx (1)
    • useWorkspace (668-674)
    apps/pro-web/components/ui/carousel.tsx (2)
    • CarouselApi (257-257)
    • Carousel (258-258)
    apps/pro-web/lib/constants/google-fonts.ts (2)
    • getGoogleFontClass (488-490)
    • FontFamily (486-486)
    apps/pro-web/lib/memoized-markdown-components.tsx (1)
    • p (62-68)
    apps/pro-web/lib/utils.ts (1)
    • cn (9-11)
    apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/wizard-stepper.tsx (1)
    apps/pro-web/lib/utils.ts (1)
    • cn (9-11)
    🔇 Additional comments (7)
    apps/pro-web/components/routes/workspace/media-tab/ui/sidebar/media-sidebar.tsx (1)

    11-11: LGTM! Clean integration of Brand Kit feature.

    The changes correctly wire the Brand Kit button to the openBrandKit handler from useWorkspaceMedia, following the established pattern used by the Templates button. The import updates (removing Instagram, keeping Palette) align with the feature changes, and the implementation follows all coding guidelines:

    • Uses function reference for onClick (not inline arrow)
    • Maintains consistent styling and tooltip structure
    • Properly extracts the handler from the custom hook

    Also applies to: 14-14, 43-43

    apps/hasura/migrations/masterbots/1766505347896_alter_table_public_organization_add_column_brand_kit/down.sql (1)

    1-2: LGTM! Down migration correctly reverts the brand_kit column addition.

    The migration properly drops the brand_kit column to revert the schema change, following Hasura migration conventions.

    apps/pro-web/types/thread.types.ts (1)

    2-2: LGTM! Type extension properly integrates BrandKitData.

    The addition of the optional brandKit field to OrganizationData follows TypeScript best practices with type-only import and proper optional/nullable typing.

    Also applies to: 21-21

    apps/pro-web/components/routes/workspace/media-tab/wizards/media-wizards.tsx (1)

    9-9: LGTM! Clean integration of BrandKitDialog.

    The BrandKitDialog integration follows the existing pattern used for other dialogs (VersionHistoryDialog) and properly wires up state management from the useWorkspaceMedia hook.

    Also applies to: 25-26, 114-115

    apps/hasura/metadata/databases/masterbots/tables/public_organization.yaml (1)

    30-30: LGTM! Hasura permissions correctly expose brand_kit column.

    The metadata updates properly grant insert, select, and update permissions for the brand_kit column to both moderator and user roles, following the existing permission patterns for the organization table.

    Also applies to: 40-40, 48-48, 57-57, 69-69, 77-77

    apps/pro-web/types/media.types.ts (1)

    51-95: LGTM! Well-structured and documented BrandKitData interface.

    The interface is comprehensive with clear JSDoc comments for each field. The type structure properly represents brand identity data with appropriate optionality and nesting.

    Optional: Consider hex color validation

    For stricter type safety, you could define a branded type for hex colors:

    type HexColor = `#${string}`
    
    export interface BrandKitData {
    	// ...
    	palette: {
    		name: string
    		category: string
    		primary: HexColor
    		accent: HexColor
    		bg: HexColor
    		text: HexColor
    	}
    	// ...
    }

    However, this is optional and the current string typing is acceptable.

    packages/mb-drizzle/src/drizzle/relations.ts (1)

    36-50: No changes to remove. These relation definitions at lines 36-50 were not modified in this PR; the file remains unchanged according to git. Remove this review comment.

    Likely an incorrect or invalid review comment.

    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: 3

    ♻️ Duplicate comments (5)
    apps/pro-web/lib/hooks/use-workspace-media.tsx (1)

    380-386: Previous review comment addressed.

    The hardcoded query key ['workspace', userId, 'structure'] from previous review has been correctly replaced with workspaceKeys.structure(userId) at line 384.

    apps/pro-web/app/api/organizations/[id]/route.ts (1)

    70-79: Previous null validation issue addressed.

    The validation now correctly handles null with the explicit check body.brandKit === null. This prevents null from passing validation since typeof null === 'object' in JavaScript.

    apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/checkpoint-step.tsx (1)

    33-52: Previous review comment addressed with defensive check.

    The buildBrandKitData function now includes the defensive check for empty selectedFonts array at lines 38-40, throwing a clear error message.

    apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/fonts-step.tsx (1)

    286-288: Previous grammar issue addressed.

    The heading text has been corrected from "Which font family you look for?" to "Which font family are you looking for?"

    apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/brand-kit-dialog.tsx (1)

    20-27: Previous possessive apostrophe issue addressed.

    The subtitle now correctly handles possessives with the logic at lines 23-24, using just an apostrophe for names ending in 's' and "'s" otherwise.

    🧹 Nitpick comments (6)
    apps/pro-web/lib/hooks/use-workspace-media.tsx (1)

    30-33: Consider consolidating Palette type with BrandKitData.palette.

    The Palette interface (lines 65-72) duplicates the palette shape defined in BrandKitData (from apps/pro-web/types/media.types.ts). Consider importing and reusing BrandKitData['palette'] to avoid drift between the two definitions.

    🔎 Suggested approach
    +import type { BrandKitData, ReferenceImagePayload } from '@/types/media.types'
    +
    +export type Palette = BrandKitData['palette']
    
     export interface SelectedFont {
     	family: string
     	role: 'primary' | 'secondary'
     }
    -
    -export interface Palette {
    -	name: string
    -	category: string
    -	primary: string
    -	accent: string
    -	bg: string
    -	text: string
    -}

    Also applies to: 65-72

    apps/pro-web/app/api/organizations/[id]/route.ts (1)

    179-191: Consider adding schema validation for brandKit structure.

    The API accepts any object as brandKit without validating required fields (vibes, fonts, palette, createdAt, updatedAt). While TypeScript provides compile-time safety on the client, malformed data from other sources could be persisted.

    For production robustness, consider using Zod or similar to validate the BrandKitData shape:

    import { z } from 'zod'
    
    const brandKitSchema = z.object({
      vibes: z.array(z.string()),
      fonts: z.object({
        primary: z.string(),
        secondary: z.string().optional(),
      }),
      palette: z.object({
        name: z.string(),
        category: z.string(),
        primary: z.string(),
        accent: z.string(),
        bg: z.string(),
        text: z.string(),
      }),
      createdAt: z.string(),
      updatedAt: z.string(),
    })
    apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/checkpoint-step.tsx (1)

    182-202: Add aria-label for screen reader accessibility on color swatches.

    The color swatch divs have title attributes for hover tooltips, but screen readers don't announce title. Consider adding aria-label and role="img" for better accessibility.

    🔎 Suggested improvement
     <div
       className="w-12 h-12 rounded-full border-2 border-border shadow-sm"
       style={{ backgroundColor: selectedPalette.primary }}
       title="Primary"
    +  role="img"
    +  aria-label={`Primary color: ${selectedPalette.primary}`}
     />
    apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/fonts-step.tsx (2)

    211-242: Potential unnecessary re-runs due to setSelectedFonts in dependencies.

    The setSelectedFonts function comes from context state and should be stable, but including it in the dependency array could cause unnecessary effect re-runs if the context provider re-renders. Consider wrapping the effect logic more defensively or using a ref for the setter.

    The current implementation is functionally correct since line 235 prevents infinite loops. This is a minor optimization consideration.


    327-430: Consider extracting font data to a separate constants file.

    The Vibe type, FontOption interface, and font dictionaries (PRIMARY_FONTS, SECONDARY_FONTS) are substantial data definitions (~190 lines). Extracting them to a dedicated file (e.g., lib/constants/brand-kit-fonts.ts) would improve maintainability and keep the component focused on rendering logic.

    This also allows reuse of font mappings if needed elsewhere in the Brand Kit feature.

    apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/brand-kit-dialog.tsx (1)

    33-43: Placeholder handlers and missing summary view noted.

    The handleImportBrand and handleWizardFinish handlers are placeholders. Setting currentView to 'summary' will render nothing since the summary view isn't implemented yet (line 71). Ensure these are tracked for completion.

    Would you like me to open an issue to track the implementation of the Brand Kit summary view and import flow?

    Also applies to: 70-71

    📜 Review details

    Configuration used: defaults

    Review profile: CHILL

    Plan: Pro

    📥 Commits

    Reviewing files that changed from the base of the PR and between 0e113d2 and 1e883e4.

    📒 Files selected for processing (7)
    • apps/pro-web/app/api/organizations/[id]/route.ts
    • 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/steps/fonts-step.tsx
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/vibe-step.tsx
    • apps/pro-web/lib/hooks/use-workspace-media.tsx
    • apps/pro-web/lib/queries/use-workspace-structure.ts
    🚧 Files skipped from review as they are similar to previous changes (1)
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/vibe-step.tsx
    🧰 Additional context used
    📓 Path-based instructions (10)
    apps/{web,pro-web}/components/routes/**

    📄 CodeRabbit inference engine (.github/copilot-instructions.md)

    Place page-specific components under components/routes/[feature]/

    Organize page-specific components under components/routes/

    Files:

    • 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/steps/fonts-step.tsx
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/brand-kit-dialog.tsx
    apps/{web,pro-web}/**/*.tsx

    📄 CodeRabbit inference engine (.github/copilot-instructions.md)

    apps/{web,pro-web}/**/*.tsx: Declare React components with the function keyword
    Name React components using PascalCase
    Order component files: imports → constants → component → styled components → types/interfaces
    Use functional components and hooks; type props with a TypeScript interface
    Use regular function references (not inline arrows) for React event handlers to avoid extra re-renders

    apps/{web,pro-web}/**/*.tsx: Use regular function references for event handlers instead of inline arrow functions in JSX
    Declare React components with the function keyword (function ComponentName() {})
    Use PascalCase for React component names
    Order React component files: imports, constants, component, styled components, types/interfaces
    Style components with Tailwind CSS utility classes

    Files:

    • 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/steps/fonts-step.tsx
    • apps/pro-web/lib/hooks/use-workspace-media.tsx
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/brand-kit-dialog.tsx
    **/*.{ts,tsx}

    📄 CodeRabbit inference engine (.github/copilot-instructions.md)

    **/*.{ts,tsx}: Avoid default exports; prefer named exports
    Use meaningful variable names; prefix booleans with is/has/does/should
    Use interface for objects/classes; type for unions/tuples/aliases; enum for fixed sets; const for literals
    Avoid any; prefer specific types
    Leverage type inference where clear; annotate function params and return types
    Prefer type assertions with 'as' over angle-bracket syntax
    Use type guards to narrow types in conditionals

    **/*.{ts,tsx}: Avoid default exports; prefer named exports
    Use modern JavaScript features with TypeScript across the codebase
    Use meaningful names; prefix booleans with is/has/does/should (e.g., isDisabled)
    Receive an object and return an object (RORO) for functions interfacing with external services
    Type definitions: use interface for objects/classes; type for unions/tuples/aliases; const for literals; enum for fixed sets
    Avoid any; prefer specific types
    Leverage type inference; omit explicit annotations when obvious
    Add explicit type annotations for function parameters and return values
    Prefer type assertions with as over angle-bracket syntax
    Use type guards to narrow types within conditionals
    Favor composition over inheritance in code structure

    **/*.{ts,tsx}: Avoid default exports; prefer named exports
    Prefer RORO (Receive an object, return an object) for function signatures, especially for external service interactions
    Use interface for object shapes/classes; type for unions/tuples/aliases; const for literals; enum for fixed enumerations
    Avoid using any; prefer precise types
    Leverage type inference when clear; omit redundant annotations
    Explicitly annotate function parameters and return types
    Prefer type assertions with as over angle-bracket syntax
    Use type guards to narrow types safely
    Use meaningful variable names; prefix booleans with is/has/does/should
    Favor composition over inheritance
    Use lowercase dash-separated names for files and folders; use extensions like .config.ts, .test.ts, .context.tsx, .typ...

    Files:

    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/checkpoint-step.tsx
    • apps/pro-web/lib/queries/use-workspace-structure.ts
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/fonts-step.tsx
    • apps/pro-web/app/api/organizations/[id]/route.ts
    • apps/pro-web/lib/hooks/use-workspace-media.tsx
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/brand-kit-dialog.tsx
    apps/{web,pro-web}/**/*.{ts,tsx}

    📄 CodeRabbit inference engine (.github/copilot-instructions.md)

    apps/{web,pro-web}/**/*.{ts,tsx}: Use lowercase kebab-case for directories and filenames (components can omit extension)
    Verify DOM operations and consider timing (RAF/delays) when manipulating the DOM in React
    Use Tailwind CSS utility classes for styling React components

    apps/{web,pro-web}/**/*.{ts,tsx}: Use TypeScript to define React props types (interfaces for props)
    Use custom hooks (useMBChat, useThread, useSidebar, useModel) for domain-specific state and keep providers focused and composed hierarchically
    Separate presentation from business logic and compose small components
    Use hasura.service.ts for all GraphQL operations from UI layers

    apps/{web,pro-web}/**/*.{ts,tsx}: Prefer Server Components; use the use client directive only when client-side features are required
    Implement Nested Layouts using the App Router
    Use Streaming and Suspense in Next.js 14/15 where appropriate
    Follow Next.js file conventions as per documentation
    Use IndexedDB for local caching to enable immediate UI updates

    Files:

    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/checkpoint-step.tsx
    • apps/pro-web/lib/queries/use-workspace-structure.ts
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/fonts-step.tsx
    • apps/pro-web/app/api/organizations/[id]/route.ts
    • apps/pro-web/lib/hooks/use-workspace-media.tsx
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/brand-kit-dialog.tsx
    **/*

    📄 CodeRabbit inference engine (CLAUDE.md)

    **/*: Use lowercase with dash-separated directory and file names; use specific extensions: .config.ts, .test.ts, .context.tsx, .type.ts, .hook.ts; components may omit extension
    Use Bun for package management and scripts instead of npm/yarn
    Use Biome for formatting and linting via bun format-and-lint:fix; avoid Prettier/ESLint configs

    Files:

    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/checkpoint-step.tsx
    • apps/pro-web/lib/queries/use-workspace-structure.ts
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/fonts-step.tsx
    • apps/pro-web/app/api/organizations/[id]/route.ts
    • apps/pro-web/lib/hooks/use-workspace-media.tsx
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/brand-kit-dialog.tsx
    **/*.tsx

    📄 CodeRabbit inference engine (AGENTS.md)

    **/*.tsx: Avoid inline arrow functions in JSX props; use stable function references for event handlers
    Declare React components with the function keyword (functional components)
    Use PascalCase for React component names
    Use functional components and hooks instead of class components
    Type component props with TypeScript interfaces

    Files:

    • 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/steps/fonts-step.tsx
    • apps/pro-web/lib/hooks/use-workspace-media.tsx
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/brand-kit-dialog.tsx
    apps/{web,pro-web}/components/**/*.tsx

    📄 CodeRabbit inference engine (AGENTS.md)

    Order component files: imports → constants → component → styled components → types/interfaces

    Files:

    • 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/steps/fonts-step.tsx
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/brand-kit-dialog.tsx
    apps/{web,pro-web}/app/**

    📄 CodeRabbit inference engine (.github/copilot-instructions.md)

    apps/{web,pro-web}/app/**: Prefer Next.js Server Components; use 'use client' only when necessary
    Leverage Streaming and Suspense for responsiveness in Next.js 15
    Follow Next.js file conventions in the App Router

    apps/{web,pro-web}/app/**: Leverage Server Components by default; use the use client directive only when client-side features are needed
    Implement Nested Layouts using the App Router
    Use Streaming and Suspense features in Next.js 14/15 where appropriate
    Follow Next.js file conventions for routing and layout in the App Router
    Optimize data fetching: fetch on the server, fetch where needed, and use preload patterns to avoid waterfalls

    Files:

    • apps/pro-web/app/api/organizations/[id]/route.ts
    apps/{web,pro-web}/lib/hooks/**

    📄 CodeRabbit inference engine (.github/copilot-instructions.md)

    apps/{web,pro-web}/lib/hooks/**: Place custom React hooks under lib/hooks/
    Encapsulate complex state in custom hooks and separate presentation from business logic

    Store custom React hooks under lib/hooks/

    Files:

    • apps/pro-web/lib/hooks/use-workspace-media.tsx
    apps/{web,pro-web}/lib/hooks/**/*.{ts,tsx}

    📄 CodeRabbit inference engine (AGENTS.md)

    Place custom React hooks in lib/hooks/

    Files:

    • apps/pro-web/lib/hooks/use-workspace-media.tsx
    🧠 Learnings (21)
    📓 Common learnings
    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.
    
    📚 Learning: 2025-09-30T22:11:18.270Z
    Learnt from: CR
    Repo: bitcashorg/masterbots PR: 0
    File: AGENTS.md:0-0
    Timestamp: 2025-09-30T22:11:18.270Z
    Learning: Applies to apps/{web,pro-web}/components/shared/**/*.{ts,tsx} : Store reusable UI elements in components/shared/
    

    Applied to files:

    • 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/steps/fonts-step.tsx
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/brand-kit-dialog.tsx
    📚 Learning: 2025-11-07T18:38:42.273Z
    Learnt from: AndlerRL
    Repo: bitcashorg/masterbots PR: 579
    File: apps/pro-web/app/api/auth/signup/route.ts:151-157
    Timestamp: 2025-11-07T18:38:42.273Z
    Learning: In apps/pro-web/app/actions/admin.actions.ts, the insertUserOrganizations function should use Drizzle DB transactions to ensure atomicity when inserting organizations and their associated chatbots, preventing partial persistence issues.
    

    Applied to files:

    • apps/pro-web/lib/queries/use-workspace-structure.ts
    📚 Learning: 2025-06-13T08:55:45.217Z
    Learnt from: AndlerRL
    Repo: bitcashorg/masterbots PR: 508
    File: apps/masterbots.ai/lib/helpers/ai-helpers.ts:280-283
    Timestamp: 2025-06-13T08:55:45.217Z
    Learning: In `apps/masterbots.ai/lib/helpers/ai-helpers.ts` the `verifyDuplicateMessage` function should:
    • Use `message.content` as the sole de-duplication key so provisional (no-slug) and persisted (slugged) messages merge properly.
    • Return the full `message` object (not just a string key) for the continuation prompts (`CONTINUE_GENERATION_PROMPT`, `CONTINUE_GENERATION_PROMPT_2`) because their content repeats and they must remain distinct.
    • No explicit `!message.content` guard is needed; `return message.content` already yields `undefined` when content is missing.
    

    Applied to files:

    • apps/pro-web/lib/queries/use-workspace-structure.ts
    • apps/pro-web/lib/hooks/use-workspace-media.tsx
    📚 Learning: 2025-09-30T22:11:18.270Z
    Learnt from: CR
    Repo: bitcashorg/masterbots PR: 0
    File: AGENTS.md:0-0
    Timestamp: 2025-09-30T22:11:18.270Z
    Learning: Applies to apps/{web,pro-web}/components/ui/**/*.{ts,tsx} : Follow Radix UI patterns under components/ui/
    

    Applied to files:

    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/fonts-step.tsx
    📚 Learning: 2025-09-12T20:29:24.733Z
    Learnt from: AndlerRL
    Repo: bitcashorg/masterbots PR: 561
    File: apps/pro-web/lib/hooks/use-accessibility.tsx:7-7
    Timestamp: 2025-09-12T20:29:24.733Z
    Learning: When extending FontSize type in use-accessibility.tsx hooks, corresponding CSS rules must exist in globals.css for [data-font-size="value"] selectors, otherwise the unsupported font size values should be removed from the type definition to maintain consistency between TypeScript types and actual styling support.
    

    Applied to files:

    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/fonts-step.tsx
    📚 Learning: 2025-09-30T22:08:30.965Z
    Learnt from: CR
    Repo: bitcashorg/masterbots PR: 0
    File: .github/copilot-instructions.md:0-0
    Timestamp: 2025-09-30T22:08:30.965Z
    Learning: Applies to apps/{web,pro-web}/**/*.tsx : Order component files: imports → constants → component → styled components → types/interfaces
    

    Applied to files:

    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/fonts-step.tsx
    📚 Learning: 2025-09-30T22:11:18.270Z
    Learnt from: CR
    Repo: bitcashorg/masterbots PR: 0
    File: AGENTS.md:0-0
    Timestamp: 2025-09-30T22:11:18.270Z
    Learning: Applies to apps/{web,pro-web}/components/**/*.tsx : Order component files: imports → constants → component → styled components → types/interfaces
    

    Applied to files:

    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/fonts-step.tsx
    📚 Learning: 2025-09-30T22:11:18.270Z
    Learnt from: CR
    Repo: bitcashorg/masterbots PR: 0
    File: AGENTS.md:0-0
    Timestamp: 2025-09-30T22:11:18.270Z
    Learning: Applies to apps/{web,pro-web}/**/*.{ts,tsx} : Follow Next.js file conventions as per documentation
    

    Applied to files:

    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/fonts-step.tsx
    📚 Learning: 2025-09-30T22:09:37.969Z
    Learnt from: CR
    Repo: bitcashorg/masterbots PR: 0
    File: CLAUDE.md:0-0
    Timestamp: 2025-09-30T22:09:37.969Z
    Learning: Applies to apps/{web,pro-web}/**/*.tsx : Order React component files: imports, constants, component, styled components, types/interfaces
    

    Applied to files:

    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/fonts-step.tsx
    📚 Learning: 2025-09-30T22:09:37.969Z
    Learnt from: CR
    Repo: bitcashorg/masterbots PR: 0
    File: CLAUDE.md:0-0
    Timestamp: 2025-09-30T22:09:37.969Z
    Learning: Applies to apps/{web,pro-web}/**/*.{ts,tsx} : Use custom hooks (useMBChat, useThread, useSidebar, useModel) for domain-specific state and keep providers focused and composed hierarchically
    

    Applied to files:

    • apps/pro-web/app/api/organizations/[id]/route.ts
    • apps/pro-web/lib/hooks/use-workspace-media.tsx
    📚 Learning: 2025-09-30T22:09:37.969Z
    Learnt from: CR
    Repo: bitcashorg/masterbots PR: 0
    File: CLAUDE.md:0-0
    Timestamp: 2025-09-30T22:09:37.969Z
    Learning: Pro workspace editors: controlled/uncontrolled patterns, Markdown utilities, section state sync, and timed DOM operations
    

    Applied to files:

    • apps/pro-web/lib/hooks/use-workspace-media.tsx
    📚 Learning: 2025-09-30T22:08:30.965Z
    Learnt from: CR
    Repo: bitcashorg/masterbots PR: 0
    File: .github/copilot-instructions.md:0-0
    Timestamp: 2025-09-30T22:08:30.965Z
    Learning: Applies to apps/{web,pro-web}/**/*.tsx : Use functional components and hooks; type props with a TypeScript interface
    

    Applied to files:

    • apps/pro-web/lib/hooks/use-workspace-media.tsx
    📚 Learning: 2025-09-30T22:11:18.270Z
    Learnt from: CR
    Repo: bitcashorg/masterbots PR: 0
    File: AGENTS.md:0-0
    Timestamp: 2025-09-30T22:11:18.270Z
    Learning: Applies to apps/{web,pro-web}/**/*.{ts,tsx} : Use Streaming and Suspense in Next.js 14/15 where appropriate
    

    Applied to files:

    • apps/pro-web/lib/hooks/use-workspace-media.tsx
    📚 Learning: 2025-11-14T19:14:07.243Z
    Learnt from: AndlerRL
    Repo: bitcashorg/masterbots PR: 584
    File: apps/pro-web/app/api/generate-images/route.ts:86-107
    Timestamp: 2025-11-14T19:14:07.243Z
    Learning: In apps/pro-web/app/api/generate-images/route.ts, the hardcoded 'image/png' MIME type for Gemini previousImage is intentional—LLM-generated images are PNG by default. When the library/template features are added (allowing user-uploaded reference images), the architecture should use Next.js server actions with checksum-based caching (computeChecksum fn) to handle files larger than the 2.5-3MB API route limit, rather than detecting MIME types in the API route directly.
    

    Applied to files:

    • apps/pro-web/lib/hooks/use-workspace-media.tsx
    📚 Learning: 2024-10-23T07:33:36.041Z
    Learnt from: AndlerRL
    Repo: bitcashorg/masterbots PR: 287
    File: apps/masterbots.ai/lib/hooks/use-thread-visibility.tsx:3-3
    Timestamp: 2024-10-23T07:33:36.041Z
    Learning: Always use explicit imports for React hooks to maintain consistency across the monorepo, and avoid using both named and default imports simultaneously.
    

    Applied to files:

    • apps/pro-web/lib/hooks/use-workspace-media.tsx
    📚 Learning: 2025-09-30T22:11:18.270Z
    Learnt from: CR
    Repo: bitcashorg/masterbots PR: 0
    File: AGENTS.md:0-0
    Timestamp: 2025-09-30T22:11:18.270Z
    Learning: Applies to apps/{web,pro-web}/**/*.{ts,tsx} : Use IndexedDB for local caching to enable immediate UI updates
    

    Applied to files:

    • apps/pro-web/lib/hooks/use-workspace-media.tsx
    📚 Learning: 2025-09-30T22:09:37.969Z
    Learnt from: CR
    Repo: bitcashorg/masterbots PR: 0
    File: CLAUDE.md:0-0
    Timestamp: 2025-09-30T22:09:37.969Z
    Learning: Applies to apps/{web,pro-web}/**/*.{ts,tsx} : Separate presentation from business logic and compose small components
    

    Applied to files:

    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/brand-kit-dialog.tsx
    📚 Learning: 2025-09-30T22:09:37.969Z
    Learnt from: CR
    Repo: bitcashorg/masterbots PR: 0
    File: CLAUDE.md:0-0
    Timestamp: 2025-09-30T22:09:37.969Z
    Learning: Applies to **/*.{ts,tsx} : Use meaningful names; prefix booleans with is/has/does/should (e.g., isDisabled)
    

    Applied to files:

    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/brand-kit-dialog.tsx
    📚 Learning: 2024-11-07T14:40:39.595Z
    Learnt from: Bran18
    Repo: bitcashorg/masterbots PR: 301
    File: apps/masterbots.ai/components/routes/chat/chat-combobox.tsx:33-33
    Timestamp: 2024-11-07T14:40:39.595Z
    Learning: In `apps/masterbots.ai/components/routes/chat/chat-combobox.tsx`, the 'MB' logo is temporarily represented by the string `'MB'` until the custom MB icon is created.
    

    Applied to files:

    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/brand-kit-dialog.tsx
    📚 Learning: 2025-09-30T22:08:30.965Z
    Learnt from: CR
    Repo: bitcashorg/masterbots PR: 0
    File: .github/copilot-instructions.md:0-0
    Timestamp: 2025-09-30T22:08:30.965Z
    Learning: Applies to **/*.{ts,tsx} : Use meaningful variable names; prefix booleans with is/has/does/should
    

    Applied to files:

    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/brand-kit-dialog.tsx
    🧬 Code graph analysis (4)
    apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/checkpoint-step.tsx (5)
    apps/pro-web/components/ui/wizard/index.tsx (1)
    • WizardStepProps (14-21)
    apps/pro-web/lib/hooks/use-workspace-media.tsx (1)
    • useWorkspaceMedia (496-504)
    apps/pro-web/lib/hooks/use-workspace.tsx (1)
    • useWorkspace (668-674)
    apps/pro-web/types/media.types.ts (1)
    • BrandKitData (55-95)
    apps/pro-web/lib/constants/google-fonts.ts (2)
    • getGoogleFontClass (488-490)
    • FontFamily (486-486)
    apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/fonts-step.tsx (4)
    apps/pro-web/lib/hooks/use-workspace-media.tsx (2)
    • SelectedFont (30-33)
    • useWorkspaceMedia (496-504)
    apps/pro-web/lib/constants/google-fonts.ts (2)
    • getGoogleFontClass (488-490)
    • FontFamily (486-486)
    apps/pro-web/lib/utils.ts (1)
    • cn (9-11)
    apps/pro-web/components/ui/wizard/index.tsx (1)
    • WizardStepProps (14-21)
    apps/pro-web/app/api/organizations/[id]/route.ts (1)
    apps/pro-web/types/media.types.ts (1)
    • BrandKitData (55-95)
    apps/pro-web/lib/hooks/use-workspace-media.tsx (8)
    apps/pro-web/types/media.types.ts (1)
    • BrandKitData (55-95)
    apps/pro-web/lib/query-client.ts (1)
    • queryClient (12-28)
    apps/pro-web/lib/hooks/use-indexed-db.ts (1)
    • getUserIndexedDBKeys (35-40)
    apps/pro-web/lib/constants/workspace.ts (1)
    • WORKSPACE_STRUCTURE_KEY (27-27)
    apps/pro-web/lib/queries/use-workspace-structure.ts (1)
    • WorkspaceStructure (22-38)
    apps/pro-web/types/thread.types.ts (1)
    • OrganizationData (17-22)
    apps/pro-web/lib/queries/use-workspace-mutations.ts (1)
    • structureToIDBPayload (912-918)
    apps/pro-web/lib/queries/workspace-keys.ts (1)
    • workspaceKeys (16-38)
    🔇 Additional comments (6)
    apps/pro-web/lib/hooks/use-workspace-media.tsx (2)

    340-390: Well-structured mutation with proper persistence flow.

    The saveBrandKitMutation correctly implements the server-first, then IDB sync pattern. The query invalidation on success ensures the cache stays fresh.


    409-415: No action needed. The loadBrandKitStep function is in a 'use client' component, which means it only executes in the browser and is not subject to SSR hydration cycles. localStorage access in client-only components has no hydration risk. Additionally, the actual usage shows the function is called within a useEffect (based on the conditional organization change pattern), which is the correct timing for localStorage operations.

    Likely an incorrect or invalid review comment.

    apps/pro-web/app/api/organizations/[id]/route.ts (1)

    193-225: Clean integration of brandKit in query and response.

    The brandKit field is properly fetched from Hasura and included in the transformed response, maintaining consistency with the existing response structure.

    apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/checkpoint-step.tsx (1)

    109-127: Font handling and switch logic looks correct.

    The fallback of secondaryFont to primaryFont when only one font is selected provides visual consistency. The switch button is appropriately disabled when fewer than 2 fonts are selected.

    apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/fonts-step.tsx (1)

    172-173: Clean wizard step implementation.

    The component properly destructures only the needed props (next, prev) from WizardStepProps and disables navigation appropriately based on selection state.

    Also applies to: 314-322

    apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/brand-kit-dialog.tsx (1)

    16-18: View state persists across dialog open/close cycles.

    When the dialog closes and reopens, currentView retains its previous value (e.g., staying on 'wizard' instead of resetting to 'entry'). Verify this is the intended UX behavior. If users expect a fresh start on reopen, add a reset effect.

    🔎 Potential fix if reset is desired
    +import { useEffect, useMemo, useState } from 'react'
    
     export function BrandKitDialog({ open, onOpenChange }: BrandKitDialogProps) {
     	const [currentView, setCurrentView] = useState<BrandKitView>('entry')
    +
    +	// Reset view when dialog closes
    +	useEffect(() => {
    +		if (!open) {
    +			setCurrentView('entry')
    +		}
    +	}, [open])

    Also applies to: 49-73

    Comment on lines +70 to +76
    } catch (error) {
    console.error('❌ Failed to save brand kit:', error)
    close()
    } finally {
    setIsSaving(false)
    }
    }
    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

    Silent failure could cause data loss without user feedback.

    Both handleContinueLater and handleGenerateLogo catch errors but proceed with navigation (close() / next()) without notifying the user. If the save fails, the user may believe their brand kit was saved when it wasn't.

    🔎 Suggested improvement

    Consider adding user feedback on failure:

     } catch (error) {
       console.error('❌ Failed to save brand kit:', error)
    -  close()
    +  // Show error toast before closing
    +  toast.error('Failed to save brand kit. Please try again.')
    +  // Optionally: don't close on failure so user can retry
     } finally {

    Also applies to: 96-102

    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

    ♻️ Duplicate comments (2)
    apps/pro-web/lib/queries/use-workspace-structure.ts (1)

    243-261: Name-based deduplication looks correct now.

    The switch to name-based organization lookup resolves the previous ID mismatch issue during anonymous→user migration. The merge logic now correctly finds existing orgs by name and updates their brandKit and chatbots fields.

    One minor note from the previous review still applies: the || fallback on lines 258-259 means explicitly setting brandKit to null (to clear it) won't take effect. If clearing brand kit is a supported use case, consider using 'brandKit' in orgData ? orgData.brandKit : existingOrg.brandKit instead.

    apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/checkpoint-step.tsx (1)

    70-76: Silent failure could cause data loss without user feedback.

    Both error handlers log the error but proceed with navigation (close() / next()). If the save fails, the user may believe their brand kit was saved when it wasn't.

    Consider adding a toast notification on failure, or keeping the dialog open so the user can retry:

     } catch (error) {
       console.error('❌ Failed to save brand kit:', error)
    -  close()
    +  // Show error toast and optionally keep dialog open for retry
    +  toast.error('Failed to save brand kit. Please try again.')
     } finally {

    Also applies to: 96-102

    🧹 Nitpick comments (2)
    apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/checkpoint-step.tsx (1)

    159-164: Possessive apostrophe logic could be extracted.

    The pattern for handling possessive apostrophes (activeOrganization?.endsWith('s') ? "'" : "'s") appears here and in brand-kit-dialog.tsx. Consider extracting a small helper function for consistency.

    🔎 Optional helper extraction
    // In a shared utils file
    export function possessive(name: string | null | undefined): string {
      if (!name) return ''
      return name.endsWith('s') ? `${name}'` : `${name}'s`
    }
    apps/pro-web/lib/hooks/use-workspace-media.tsx (1)

    340-390: Brand Kit persistence follows the correct pattern.

    The mutation properly:

    1. Saves to server first (source of truth)
    2. Updates IDB for offline access
    3. Invalidates queries to trigger refetch

    Minor concern: if the IDB update (lines 361-376) fails after successful server save, there's no error handling. The next page load will sync from server anyway, but a console warning would help debugging.

    🔎 Optional: Add IDB error handling
     // 2. Update workspace IDB after successful server save
    +try {
       const workspaceData = await getItem(WORKSPACE_STRUCTURE_KEY)
       if (workspaceData && typeof workspaceData === 'object') {
         // ... existing logic
         await updateItem(WORKSPACE_STRUCTURE_KEY, payload)
       }
    +} catch (idbError) {
    +  console.warn('IDB update failed, will sync on next load:', idbError)
    +}
    📜 Review details

    Configuration used: defaults

    Review profile: CHILL

    Plan: Pro

    📥 Commits

    Reviewing files that changed from the base of the PR and between 1e883e4 and 49a7027.

    📒 Files selected for processing (7)
    • apps/pro-web/app/api/organizations/[id]/route.ts
    • 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/steps/fonts-step.tsx
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/vibe-step.tsx
    • apps/pro-web/lib/hooks/use-workspace-media.tsx
    • apps/pro-web/lib/queries/use-workspace-structure.ts
    🚧 Files skipped from review as they are similar to previous changes (2)
    • apps/pro-web/app/api/organizations/[id]/route.ts
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/vibe-step.tsx
    🧰 Additional context used
    📓 Path-based instructions (9)
    **/*.{ts,tsx}

    📄 CodeRabbit inference engine (.github/copilot-instructions.md)

    **/*.{ts,tsx}: Avoid default exports; prefer named exports
    Use meaningful variable names; prefix booleans with is/has/does/should
    Use interface for objects/classes; type for unions/tuples/aliases; enum for fixed sets; const for literals
    Avoid any; prefer specific types
    Leverage type inference where clear; annotate function params and return types
    Prefer type assertions with 'as' over angle-bracket syntax
    Use type guards to narrow types in conditionals

    **/*.{ts,tsx}: Avoid default exports; prefer named exports
    Use modern JavaScript features with TypeScript across the codebase
    Use meaningful names; prefix booleans with is/has/does/should (e.g., isDisabled)
    Receive an object and return an object (RORO) for functions interfacing with external services
    Type definitions: use interface for objects/classes; type for unions/tuples/aliases; const for literals; enum for fixed sets
    Avoid any; prefer specific types
    Leverage type inference; omit explicit annotations when obvious
    Add explicit type annotations for function parameters and return values
    Prefer type assertions with as over angle-bracket syntax
    Use type guards to narrow types within conditionals
    Favor composition over inheritance in code structure

    **/*.{ts,tsx}: Avoid default exports; prefer named exports
    Prefer RORO (Receive an object, return an object) for function signatures, especially for external service interactions
    Use interface for object shapes/classes; type for unions/tuples/aliases; const for literals; enum for fixed enumerations
    Avoid using any; prefer precise types
    Leverage type inference when clear; omit redundant annotations
    Explicitly annotate function parameters and return types
    Prefer type assertions with as over angle-bracket syntax
    Use type guards to narrow types safely
    Use meaningful variable names; prefix booleans with is/has/does/should
    Favor composition over inheritance
    Use lowercase dash-separated names for files and folders; use extensions like .config.ts, .test.ts, .context.tsx, .typ...

    Files:

    • apps/pro-web/lib/queries/use-workspace-structure.ts
    • apps/pro-web/lib/hooks/use-workspace-media.tsx
    • 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/fonts-step.tsx
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/checkpoint-step.tsx
    apps/{web,pro-web}/**/*.{ts,tsx}

    📄 CodeRabbit inference engine (.github/copilot-instructions.md)

    apps/{web,pro-web}/**/*.{ts,tsx}: Use lowercase kebab-case for directories and filenames (components can omit extension)
    Verify DOM operations and consider timing (RAF/delays) when manipulating the DOM in React
    Use Tailwind CSS utility classes for styling React components

    apps/{web,pro-web}/**/*.{ts,tsx}: Use TypeScript to define React props types (interfaces for props)
    Use custom hooks (useMBChat, useThread, useSidebar, useModel) for domain-specific state and keep providers focused and composed hierarchically
    Separate presentation from business logic and compose small components
    Use hasura.service.ts for all GraphQL operations from UI layers

    apps/{web,pro-web}/**/*.{ts,tsx}: Prefer Server Components; use the use client directive only when client-side features are required
    Implement Nested Layouts using the App Router
    Use Streaming and Suspense in Next.js 14/15 where appropriate
    Follow Next.js file conventions as per documentation
    Use IndexedDB for local caching to enable immediate UI updates

    Files:

    • apps/pro-web/lib/queries/use-workspace-structure.ts
    • apps/pro-web/lib/hooks/use-workspace-media.tsx
    • 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/fonts-step.tsx
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/checkpoint-step.tsx
    **/*

    📄 CodeRabbit inference engine (CLAUDE.md)

    **/*: Use lowercase with dash-separated directory and file names; use specific extensions: .config.ts, .test.ts, .context.tsx, .type.ts, .hook.ts; components may omit extension
    Use Bun for package management and scripts instead of npm/yarn
    Use Biome for formatting and linting via bun format-and-lint:fix; avoid Prettier/ESLint configs

    Files:

    • apps/pro-web/lib/queries/use-workspace-structure.ts
    • apps/pro-web/lib/hooks/use-workspace-media.tsx
    • 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/fonts-step.tsx
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/checkpoint-step.tsx
    apps/{web,pro-web}/lib/hooks/**

    📄 CodeRabbit inference engine (.github/copilot-instructions.md)

    apps/{web,pro-web}/lib/hooks/**: Place custom React hooks under lib/hooks/
    Encapsulate complex state in custom hooks and separate presentation from business logic

    Store custom React hooks under lib/hooks/

    Files:

    • apps/pro-web/lib/hooks/use-workspace-media.tsx
    apps/{web,pro-web}/**/*.tsx

    📄 CodeRabbit inference engine (.github/copilot-instructions.md)

    apps/{web,pro-web}/**/*.tsx: Declare React components with the function keyword
    Name React components using PascalCase
    Order component files: imports → constants → component → styled components → types/interfaces
    Use functional components and hooks; type props with a TypeScript interface
    Use regular function references (not inline arrows) for React event handlers to avoid extra re-renders

    apps/{web,pro-web}/**/*.tsx: Use regular function references for event handlers instead of inline arrow functions in JSX
    Declare React components with the function keyword (function ComponentName() {})
    Use PascalCase for React component names
    Order React component files: imports, constants, component, styled components, types/interfaces
    Style components with Tailwind CSS utility classes

    Files:

    • apps/pro-web/lib/hooks/use-workspace-media.tsx
    • 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/fonts-step.tsx
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/checkpoint-step.tsx
    **/*.tsx

    📄 CodeRabbit inference engine (AGENTS.md)

    **/*.tsx: Avoid inline arrow functions in JSX props; use stable function references for event handlers
    Declare React components with the function keyword (functional components)
    Use PascalCase for React component names
    Use functional components and hooks instead of class components
    Type component props with TypeScript interfaces

    Files:

    • apps/pro-web/lib/hooks/use-workspace-media.tsx
    • 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/fonts-step.tsx
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/checkpoint-step.tsx
    apps/{web,pro-web}/lib/hooks/**/*.{ts,tsx}

    📄 CodeRabbit inference engine (AGENTS.md)

    Place custom React hooks in lib/hooks/

    Files:

    • apps/pro-web/lib/hooks/use-workspace-media.tsx
    apps/{web,pro-web}/components/routes/**

    📄 CodeRabbit inference engine (.github/copilot-instructions.md)

    Place page-specific components under components/routes/[feature]/

    Organize page-specific components under components/routes/

    Files:

    • 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/fonts-step.tsx
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/checkpoint-step.tsx
    apps/{web,pro-web}/components/**/*.tsx

    📄 CodeRabbit inference engine (AGENTS.md)

    Order component files: imports → constants → component → styled components → types/interfaces

    Files:

    • 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/fonts-step.tsx
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/checkpoint-step.tsx
    🧠 Learnings (15)
    📓 Common learnings
    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.
    
    📚 Learning: 2025-11-07T18:38:42.273Z
    Learnt from: AndlerRL
    Repo: bitcashorg/masterbots PR: 579
    File: apps/pro-web/app/api/auth/signup/route.ts:151-157
    Timestamp: 2025-11-07T18:38:42.273Z
    Learning: In apps/pro-web/app/actions/admin.actions.ts, the insertUserOrganizations function should use Drizzle DB transactions to ensure atomicity when inserting organizations and their associated chatbots, preventing partial persistence issues.
    

    Applied to files:

    • apps/pro-web/lib/queries/use-workspace-structure.ts
    📚 Learning: 2025-06-13T08:55:45.217Z
    Learnt from: AndlerRL
    Repo: bitcashorg/masterbots PR: 508
    File: apps/masterbots.ai/lib/helpers/ai-helpers.ts:280-283
    Timestamp: 2025-06-13T08:55:45.217Z
    Learning: In `apps/masterbots.ai/lib/helpers/ai-helpers.ts` the `verifyDuplicateMessage` function should:
    • Use `message.content` as the sole de-duplication key so provisional (no-slug) and persisted (slugged) messages merge properly.
    • Return the full `message` object (not just a string key) for the continuation prompts (`CONTINUE_GENERATION_PROMPT`, `CONTINUE_GENERATION_PROMPT_2`) because their content repeats and they must remain distinct.
    • No explicit `!message.content` guard is needed; `return message.content` already yields `undefined` when content is missing.
    

    Applied to files:

    • apps/pro-web/lib/queries/use-workspace-structure.ts
    • apps/pro-web/lib/hooks/use-workspace-media.tsx
    📚 Learning: 2025-11-07T18:41:56.562Z
    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).
    

    Applied to files:

    • apps/pro-web/lib/queries/use-workspace-structure.ts
    📚 Learning: 2025-09-30T22:09:37.969Z
    Learnt from: CR
    Repo: bitcashorg/masterbots PR: 0
    File: CLAUDE.md:0-0
    Timestamp: 2025-09-30T22:09:37.969Z
    Learning: Applies to apps/{web,pro-web}/**/*.{ts,tsx} : Use custom hooks (useMBChat, useThread, useSidebar, useModel) for domain-specific state and keep providers focused and composed hierarchically
    

    Applied to files:

    • apps/pro-web/lib/hooks/use-workspace-media.tsx
    📚 Learning: 2025-09-30T22:09:37.969Z
    Learnt from: CR
    Repo: bitcashorg/masterbots PR: 0
    File: CLAUDE.md:0-0
    Timestamp: 2025-09-30T22:09:37.969Z
    Learning: Pro workspace editors: controlled/uncontrolled patterns, Markdown utilities, section state sync, and timed DOM operations
    

    Applied to files:

    • apps/pro-web/lib/hooks/use-workspace-media.tsx
    📚 Learning: 2025-09-30T22:08:30.965Z
    Learnt from: CR
    Repo: bitcashorg/masterbots PR: 0
    File: .github/copilot-instructions.md:0-0
    Timestamp: 2025-09-30T22:08:30.965Z
    Learning: Applies to apps/{web,pro-web}/**/*.tsx : Use functional components and hooks; type props with a TypeScript interface
    

    Applied to files:

    • apps/pro-web/lib/hooks/use-workspace-media.tsx
    📚 Learning: 2025-11-14T19:14:07.243Z
    Learnt from: AndlerRL
    Repo: bitcashorg/masterbots PR: 584
    File: apps/pro-web/app/api/generate-images/route.ts:86-107
    Timestamp: 2025-11-14T19:14:07.243Z
    Learning: In apps/pro-web/app/api/generate-images/route.ts, the hardcoded 'image/png' MIME type for Gemini previousImage is intentional—LLM-generated images are PNG by default. When the library/template features are added (allowing user-uploaded reference images), the architecture should use Next.js server actions with checksum-based caching (computeChecksum fn) to handle files larger than the 2.5-3MB API route limit, rather than detecting MIME types in the API route directly.
    

    Applied to files:

    • apps/pro-web/lib/hooks/use-workspace-media.tsx
    📚 Learning: 2024-10-23T07:33:36.041Z
    Learnt from: AndlerRL
    Repo: bitcashorg/masterbots PR: 287
    File: apps/masterbots.ai/lib/hooks/use-thread-visibility.tsx:3-3
    Timestamp: 2024-10-23T07:33:36.041Z
    Learning: Always use explicit imports for React hooks to maintain consistency across the monorepo, and avoid using both named and default imports simultaneously.
    

    Applied to files:

    • apps/pro-web/lib/hooks/use-workspace-media.tsx
    📚 Learning: 2025-09-30T22:11:18.270Z
    Learnt from: CR
    Repo: bitcashorg/masterbots PR: 0
    File: AGENTS.md:0-0
    Timestamp: 2025-09-30T22:11:18.270Z
    Learning: Applies to apps/{web,pro-web}/**/*.{ts,tsx} : Use IndexedDB for local caching to enable immediate UI updates
    

    Applied to files:

    • apps/pro-web/lib/hooks/use-workspace-media.tsx
    📚 Learning: 2025-09-30T22:11:18.270Z
    Learnt from: CR
    Repo: bitcashorg/masterbots PR: 0
    File: AGENTS.md:0-0
    Timestamp: 2025-09-30T22:11:18.270Z
    Learning: Applies to apps/{web,pro-web}/components/shared/**/*.{ts,tsx} : Store reusable UI elements in components/shared/
    

    Applied to files:

    • 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/fonts-step.tsx
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/checkpoint-step.tsx
    📚 Learning: 2024-11-07T14:40:39.595Z
    Learnt from: Bran18
    Repo: bitcashorg/masterbots PR: 301
    File: apps/masterbots.ai/components/routes/chat/chat-combobox.tsx:33-33
    Timestamp: 2024-11-07T14:40:39.595Z
    Learning: In `apps/masterbots.ai/components/routes/chat/chat-combobox.tsx`, the 'MB' logo is temporarily represented by the string `'MB'` until the custom MB icon is created.
    

    Applied to files:

    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/brand-kit-dialog.tsx
    📚 Learning: 2025-09-30T22:09:37.969Z
    Learnt from: CR
    Repo: bitcashorg/masterbots PR: 0
    File: CLAUDE.md:0-0
    Timestamp: 2025-09-30T22:09:37.969Z
    Learning: Applies to **/*.{ts,tsx} : Use meaningful names; prefix booleans with is/has/does/should (e.g., isDisabled)
    

    Applied to files:

    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/brand-kit-dialog.tsx
    📚 Learning: 2025-09-30T22:11:18.270Z
    Learnt from: CR
    Repo: bitcashorg/masterbots PR: 0
    File: AGENTS.md:0-0
    Timestamp: 2025-09-30T22:11:18.270Z
    Learning: Applies to apps/{web,pro-web}/components/ui/**/*.{ts,tsx} : Follow Radix UI patterns under components/ui/
    

    Applied to files:

    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/fonts-step.tsx
    📚 Learning: 2025-09-12T20:29:24.733Z
    Learnt from: AndlerRL
    Repo: bitcashorg/masterbots PR: 561
    File: apps/pro-web/lib/hooks/use-accessibility.tsx:7-7
    Timestamp: 2025-09-12T20:29:24.733Z
    Learning: When extending FontSize type in use-accessibility.tsx hooks, corresponding CSS rules must exist in globals.css for [data-font-size="value"] selectors, otherwise the unsupported font size values should be removed from the type definition to maintain consistency between TypeScript types and actual styling support.
    

    Applied to files:

    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/fonts-step.tsx
    🧬 Code graph analysis (2)
    apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/fonts-step.tsx (4)
    apps/pro-web/lib/hooks/use-workspace-media.tsx (2)
    • SelectedFont (30-33)
    • useWorkspaceMedia (496-504)
    apps/pro-web/lib/constants/google-fonts.ts (2)
    • getGoogleFontClass (488-490)
    • FontFamily (486-486)
    apps/pro-web/lib/utils.ts (1)
    • cn (9-11)
    apps/pro-web/components/ui/wizard/index.tsx (1)
    • WizardStepProps (14-21)
    apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/checkpoint-step.tsx (7)
    apps/pro-web/components/ui/wizard/index.tsx (1)
    • WizardStepProps (14-21)
    apps/pro-web/lib/hooks/use-workspace-media.tsx (1)
    • useWorkspaceMedia (496-504)
    apps/pro-web/lib/hooks/use-workspace.tsx (1)
    • useWorkspace (668-674)
    apps/pro-web/types/media.types.ts (1)
    • BrandKitData (55-95)
    apps/pro-web/lib/constants/google-fonts.ts (2)
    • getGoogleFontClass (488-490)
    • FontFamily (486-486)
    apps/pro-web/lib/memoized-markdown-components.tsx (3)
    • h2 (88-103)
    • p (62-68)
    • h3 (105-120)
    apps/pro-web/lib/utils.ts (1)
    • cn (9-11)
    🔇 Additional comments (11)
    apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/checkpoint-step.tsx (2)

    33-52: Defensive validation looks good.

    The buildBrandKitData function now properly validates both selectedPalette and selectedFonts before building the payload. This prevents runtime errors if the wizard state is somehow incomplete.


    119-127: Font switching logic is correct.

    The swap only triggers when exactly two fonts are selected, and roles are explicitly reassigned. The button is also correctly disabled when selectedFonts.length < 2.

    apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/fonts-step.tsx (3)

    19-56: SelectedFontsBar implementation is clean.

    The component correctly handles the empty state, renders chips with role badges, and provides accessible removal buttons with proper aria-label. Good use of Tailwind utilities for styling.


    244-276: Font selection handlers are well-implemented.

    Both handleFontClick and handleRemoveFont correctly:

    • Use functional setState to avoid stale closures
    • Reassign roles deterministically after any change
    • Enforce the 2-font maximum

    327-430: Font dictionaries are well-curated.

    The Vibe type covers a good range of brand personalities, and the font pairings are professionally chosen. The overlap of fonts across vibes (e.g., Inter appearing in Modern, Bold, Techy) is appropriate since some fonts are versatile.

    apps/pro-web/lib/hooks/use-workspace-media.tsx (3)

    30-72: New Brand Kit types are well-defined.

    SelectedFont and Palette interfaces provide clear type safety for the wizard state. The structure aligns with BrandKitData in media.types.ts.


    447-484: Context provider exposes Brand Kit state and actions correctly.

    The new state fields and actions are properly wired into the context value, following the existing patterns. The isSavingBrandKit: saveBrandKitMutation.isPending derivation is clean.


    399-421: localStorage usage for wizard step tracking is appropriate and safe as implemented.

    This pattern for non-critical UI state is solid. Since the functions are only invoked from useEffect hooks (which run exclusively on the client) and the component is marked 'use client', SSR hydration issues won't occur. The implementation is safe without explicit guards, as evidenced by similar patterns in use-sidebar.tsx.

    apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/brand-kit-dialog.tsx (3)

    20-27: Possessive apostrophe handling is correct.

    The fix properly handles organization names ending in 's' (e.g., "Dynamics'" vs "Acme's"). Good use of useMemo to avoid recalculating on every render.


    29-47: View state transitions are straightforward.

    The handlers correctly manage the entry → wizard → summary flow. The TODO placeholders for import functionality are acceptable for this PR scope.


    49-73: Dialog composition is clean.

    The conditional rendering based on currentView is clear and maintainable. The WorkspaceDialog wrapper provides consistent styling. The TODO comment for summary view appropriately marks future work.

    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: 3

    ♻️ Duplicate comments (1)
    apps/pro-web/lib/queries/use-workspace-structure.ts (1)

    258-259: Clarify or adjust merge semantics for explicit clearing.

    This was flagged in a previous review but remains unresolved. The || operator prevents intentional clearing of brandKit or chatbots. If clearing is a supported use case, consider using an explicit check:

     if (existingOrg) {
    -  existingOrg.brandKit = orgData.brandKit || existingOrg.brandKit
    -  existingOrg.chatbots = orgData.chatbots || existingOrg.chatbots
    +  existingOrg.brandKit = orgData.brandKit !== undefined ? orgData.brandKit : existingOrg.brandKit
    +  existingOrg.chatbots = orgData.chatbots !== undefined ? orgData.chatbots : existingOrg.chatbots
     }

    If this behavior is intentional (to prevent accidental data loss), update the comment to reflect it.

    🧹 Nitpick comments (3)
    apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/brand-kit-dialog.tsx (2)

    33-37: Remove debug console.log statements before merging.

    The console.log('Importing brand...') is a debug artifact that should be removed or replaced with proper logging/toast notification when the import flow is implemented.


    39-43: Remove debug console.log statement.

    Similar to the import handler, this debug log should be removed.

    apps/pro-web/lib/hooks/use-workspace-media.tsx (1)

    30-33: Consider moving shared types to types/media.types.ts.

    SelectedFont and Palette are exported from this hook file but could be co-located with BrandKitData in types/media.types.ts for better discoverability and reuse across the codebase. This aligns with the coding guideline for organizing type definitions.

    Also applies to: 65-72

    📜 Review details

    Configuration used: defaults

    Review profile: CHILL

    Plan: Pro

    📥 Commits

    Reviewing files that changed from the base of the PR and between 49a7027 and 6037eb6.

    📒 Files selected for processing (7)
    • apps/pro-web/app/api/organizations/[id]/route.ts
    • 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/steps/fonts-step.tsx
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/vibe-step.tsx
    • apps/pro-web/lib/hooks/use-workspace-media.tsx
    • apps/pro-web/lib/queries/use-workspace-structure.ts
    🚧 Files skipped from review as they are similar to previous changes (4)
    • apps/pro-web/app/api/organizations/[id]/route.ts
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/fonts-step.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/steps/vibe-step.tsx
    🧰 Additional context used
    📓 Path-based instructions (9)
    **/*.{ts,tsx}

    📄 CodeRabbit inference engine (.github/copilot-instructions.md)

    **/*.{ts,tsx}: Avoid default exports; prefer named exports
    Use meaningful variable names; prefix booleans with is/has/does/should
    Use interface for objects/classes; type for unions/tuples/aliases; enum for fixed sets; const for literals
    Avoid any; prefer specific types
    Leverage type inference where clear; annotate function params and return types
    Prefer type assertions with 'as' over angle-bracket syntax
    Use type guards to narrow types in conditionals

    **/*.{ts,tsx}: Avoid default exports; prefer named exports
    Use modern JavaScript features with TypeScript across the codebase
    Use meaningful names; prefix booleans with is/has/does/should (e.g., isDisabled)
    Receive an object and return an object (RORO) for functions interfacing with external services
    Type definitions: use interface for objects/classes; type for unions/tuples/aliases; const for literals; enum for fixed sets
    Avoid any; prefer specific types
    Leverage type inference; omit explicit annotations when obvious
    Add explicit type annotations for function parameters and return values
    Prefer type assertions with as over angle-bracket syntax
    Use type guards to narrow types within conditionals
    Favor composition over inheritance in code structure

    **/*.{ts,tsx}: Avoid default exports; prefer named exports
    Prefer RORO (Receive an object, return an object) for function signatures, especially for external service interactions
    Use interface for object shapes/classes; type for unions/tuples/aliases; const for literals; enum for fixed enumerations
    Avoid using any; prefer precise types
    Leverage type inference when clear; omit redundant annotations
    Explicitly annotate function parameters and return types
    Prefer type assertions with as over angle-bracket syntax
    Use type guards to narrow types safely
    Use meaningful variable names; prefix booleans with is/has/does/should
    Favor composition over inheritance
    Use lowercase dash-separated names for files and folders; use extensions like .config.ts, .test.ts, .context.tsx, .typ...

    Files:

    • apps/pro-web/lib/queries/use-workspace-structure.ts
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/brand-kit-dialog.tsx
    • apps/pro-web/lib/hooks/use-workspace-media.tsx
    apps/{web,pro-web}/**/*.{ts,tsx}

    📄 CodeRabbit inference engine (.github/copilot-instructions.md)

    apps/{web,pro-web}/**/*.{ts,tsx}: Use lowercase kebab-case for directories and filenames (components can omit extension)
    Verify DOM operations and consider timing (RAF/delays) when manipulating the DOM in React
    Use Tailwind CSS utility classes for styling React components

    apps/{web,pro-web}/**/*.{ts,tsx}: Use TypeScript to define React props types (interfaces for props)
    Use custom hooks (useMBChat, useThread, useSidebar, useModel) for domain-specific state and keep providers focused and composed hierarchically
    Separate presentation from business logic and compose small components
    Use hasura.service.ts for all GraphQL operations from UI layers

    apps/{web,pro-web}/**/*.{ts,tsx}: Prefer Server Components; use the use client directive only when client-side features are required
    Implement Nested Layouts using the App Router
    Use Streaming and Suspense in Next.js 14/15 where appropriate
    Follow Next.js file conventions as per documentation
    Use IndexedDB for local caching to enable immediate UI updates

    Files:

    • apps/pro-web/lib/queries/use-workspace-structure.ts
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/brand-kit-dialog.tsx
    • apps/pro-web/lib/hooks/use-workspace-media.tsx
    **/*

    📄 CodeRabbit inference engine (CLAUDE.md)

    **/*: Use lowercase with dash-separated directory and file names; use specific extensions: .config.ts, .test.ts, .context.tsx, .type.ts, .hook.ts; components may omit extension
    Use Bun for package management and scripts instead of npm/yarn
    Use Biome for formatting and linting via bun format-and-lint:fix; avoid Prettier/ESLint configs

    Files:

    • apps/pro-web/lib/queries/use-workspace-structure.ts
    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/brand-kit-dialog.tsx
    • apps/pro-web/lib/hooks/use-workspace-media.tsx
    apps/{web,pro-web}/components/routes/**

    📄 CodeRabbit inference engine (.github/copilot-instructions.md)

    Place page-specific components under components/routes/[feature]/

    Organize page-specific components under components/routes/

    Files:

    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/brand-kit-dialog.tsx
    apps/{web,pro-web}/**/*.tsx

    📄 CodeRabbit inference engine (.github/copilot-instructions.md)

    apps/{web,pro-web}/**/*.tsx: Declare React components with the function keyword
    Name React components using PascalCase
    Order component files: imports → constants → component → styled components → types/interfaces
    Use functional components and hooks; type props with a TypeScript interface
    Use regular function references (not inline arrows) for React event handlers to avoid extra re-renders

    apps/{web,pro-web}/**/*.tsx: Use regular function references for event handlers instead of inline arrow functions in JSX
    Declare React components with the function keyword (function ComponentName() {})
    Use PascalCase for React component names
    Order React component files: imports, constants, component, styled components, types/interfaces
    Style components with Tailwind CSS utility classes

    Files:

    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/brand-kit-dialog.tsx
    • apps/pro-web/lib/hooks/use-workspace-media.tsx
    **/*.tsx

    📄 CodeRabbit inference engine (AGENTS.md)

    **/*.tsx: Avoid inline arrow functions in JSX props; use stable function references for event handlers
    Declare React components with the function keyword (functional components)
    Use PascalCase for React component names
    Use functional components and hooks instead of class components
    Type component props with TypeScript interfaces

    Files:

    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/brand-kit-dialog.tsx
    • apps/pro-web/lib/hooks/use-workspace-media.tsx
    apps/{web,pro-web}/components/**/*.tsx

    📄 CodeRabbit inference engine (AGENTS.md)

    Order component files: imports → constants → component → styled components → types/interfaces

    Files:

    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/brand-kit-dialog.tsx
    apps/{web,pro-web}/lib/hooks/**

    📄 CodeRabbit inference engine (.github/copilot-instructions.md)

    apps/{web,pro-web}/lib/hooks/**: Place custom React hooks under lib/hooks/
    Encapsulate complex state in custom hooks and separate presentation from business logic

    Store custom React hooks under lib/hooks/

    Files:

    • apps/pro-web/lib/hooks/use-workspace-media.tsx
    apps/{web,pro-web}/lib/hooks/**/*.{ts,tsx}

    📄 CodeRabbit inference engine (AGENTS.md)

    Place custom React hooks in lib/hooks/

    Files:

    • apps/pro-web/lib/hooks/use-workspace-media.tsx
    🧠 Learnings (13)
    📓 Common learnings
    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.
    
    📚 Learning: 2025-11-07T18:38:42.273Z
    Learnt from: AndlerRL
    Repo: bitcashorg/masterbots PR: 579
    File: apps/pro-web/app/api/auth/signup/route.ts:151-157
    Timestamp: 2025-11-07T18:38:42.273Z
    Learning: In apps/pro-web/app/actions/admin.actions.ts, the insertUserOrganizations function should use Drizzle DB transactions to ensure atomicity when inserting organizations and their associated chatbots, preventing partial persistence issues.
    

    Applied to files:

    • apps/pro-web/lib/queries/use-workspace-structure.ts
    📚 Learning: 2025-06-13T08:55:45.217Z
    Learnt from: AndlerRL
    Repo: bitcashorg/masterbots PR: 508
    File: apps/masterbots.ai/lib/helpers/ai-helpers.ts:280-283
    Timestamp: 2025-06-13T08:55:45.217Z
    Learning: In `apps/masterbots.ai/lib/helpers/ai-helpers.ts` the `verifyDuplicateMessage` function should:
    • Use `message.content` as the sole de-duplication key so provisional (no-slug) and persisted (slugged) messages merge properly.
    • Return the full `message` object (not just a string key) for the continuation prompts (`CONTINUE_GENERATION_PROMPT`, `CONTINUE_GENERATION_PROMPT_2`) because their content repeats and they must remain distinct.
    • No explicit `!message.content` guard is needed; `return message.content` already yields `undefined` when content is missing.
    

    Applied to files:

    • apps/pro-web/lib/queries/use-workspace-structure.ts
    • apps/pro-web/lib/hooks/use-workspace-media.tsx
    📚 Learning: 2025-11-07T18:41:56.562Z
    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).
    

    Applied to files:

    • apps/pro-web/lib/queries/use-workspace-structure.ts
    📚 Learning: 2025-09-30T22:11:18.270Z
    Learnt from: CR
    Repo: bitcashorg/masterbots PR: 0
    File: AGENTS.md:0-0
    Timestamp: 2025-09-30T22:11:18.270Z
    Learning: Applies to apps/{web,pro-web}/components/shared/**/*.{ts,tsx} : Store reusable UI elements in components/shared/
    

    Applied to files:

    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/brand-kit-dialog.tsx
    📚 Learning: 2024-11-07T14:40:39.595Z
    Learnt from: Bran18
    Repo: bitcashorg/masterbots PR: 301
    File: apps/masterbots.ai/components/routes/chat/chat-combobox.tsx:33-33
    Timestamp: 2024-11-07T14:40:39.595Z
    Learning: In `apps/masterbots.ai/components/routes/chat/chat-combobox.tsx`, the 'MB' logo is temporarily represented by the string `'MB'` until the custom MB icon is created.
    

    Applied to files:

    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/brand-kit-dialog.tsx
    📚 Learning: 2025-09-30T22:09:37.969Z
    Learnt from: CR
    Repo: bitcashorg/masterbots PR: 0
    File: CLAUDE.md:0-0
    Timestamp: 2025-09-30T22:09:37.969Z
    Learning: Applies to **/*.{ts,tsx} : Use meaningful names; prefix booleans with is/has/does/should (e.g., isDisabled)
    

    Applied to files:

    • apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/brand-kit-dialog.tsx
    📚 Learning: 2025-09-30T22:09:37.969Z
    Learnt from: CR
    Repo: bitcashorg/masterbots PR: 0
    File: CLAUDE.md:0-0
    Timestamp: 2025-09-30T22:09:37.969Z
    Learning: Applies to apps/{web,pro-web}/**/*.{ts,tsx} : Use custom hooks (useMBChat, useThread, useSidebar, useModel) for domain-specific state and keep providers focused and composed hierarchically
    

    Applied to files:

    • apps/pro-web/lib/hooks/use-workspace-media.tsx
    📚 Learning: 2025-09-30T22:09:37.969Z
    Learnt from: CR
    Repo: bitcashorg/masterbots PR: 0
    File: CLAUDE.md:0-0
    Timestamp: 2025-09-30T22:09:37.969Z
    Learning: Pro workspace editors: controlled/uncontrolled patterns, Markdown utilities, section state sync, and timed DOM operations
    

    Applied to files:

    • apps/pro-web/lib/hooks/use-workspace-media.tsx
    📚 Learning: 2025-11-14T19:14:07.243Z
    Learnt from: AndlerRL
    Repo: bitcashorg/masterbots PR: 584
    File: apps/pro-web/app/api/generate-images/route.ts:86-107
    Timestamp: 2025-11-14T19:14:07.243Z
    Learning: In apps/pro-web/app/api/generate-images/route.ts, the hardcoded 'image/png' MIME type for Gemini previousImage is intentional—LLM-generated images are PNG by default. When the library/template features are added (allowing user-uploaded reference images), the architecture should use Next.js server actions with checksum-based caching (computeChecksum fn) to handle files larger than the 2.5-3MB API route limit, rather than detecting MIME types in the API route directly.
    

    Applied to files:

    • apps/pro-web/lib/hooks/use-workspace-media.tsx
    📚 Learning: 2024-10-23T07:33:36.041Z
    Learnt from: AndlerRL
    Repo: bitcashorg/masterbots PR: 287
    File: apps/masterbots.ai/lib/hooks/use-thread-visibility.tsx:3-3
    Timestamp: 2024-10-23T07:33:36.041Z
    Learning: Always use explicit imports for React hooks to maintain consistency across the monorepo, and avoid using both named and default imports simultaneously.
    

    Applied to files:

    • apps/pro-web/lib/hooks/use-workspace-media.tsx
    📚 Learning: 2025-09-30T22:08:30.965Z
    Learnt from: CR
    Repo: bitcashorg/masterbots PR: 0
    File: .github/copilot-instructions.md:0-0
    Timestamp: 2025-09-30T22:08:30.965Z
    Learning: Applies to apps/{web,pro-web}/**/*.tsx : Use functional components and hooks; type props with a TypeScript interface
    

    Applied to files:

    • apps/pro-web/lib/hooks/use-workspace-media.tsx
    📚 Learning: 2025-09-30T22:11:18.270Z
    Learnt from: CR
    Repo: bitcashorg/masterbots PR: 0
    File: AGENTS.md:0-0
    Timestamp: 2025-09-30T22:11:18.270Z
    Learning: Applies to apps/{web,pro-web}/**/*.{ts,tsx} : Use IndexedDB for local caching to enable immediate UI updates
    

    Applied to files:

    • apps/pro-web/lib/hooks/use-workspace-media.tsx
    🧬 Code graph analysis (2)
    apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/brand-kit-dialog.tsx (4)
    apps/pro-web/lib/hooks/use-workspace.tsx (1)
    • useWorkspace (668-674)
    apps/pro-web/components/ui/workspace-dialog/index.tsx (1)
    • WorkspaceDialog (23-67)
    apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/brand-kit-entry.tsx (1)
    • BrandKitEntry (11-53)
    apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/brand-kit-wizard.tsx (1)
    • BrandKitWizard (27-193)
    apps/pro-web/lib/hooks/use-workspace-media.tsx (7)
    apps/pro-web/lib/query-client.ts (1)
    • queryClient (12-28)
    apps/pro-web/lib/hooks/use-indexed-db.ts (1)
    • getUserIndexedDBKeys (35-40)
    apps/pro-web/lib/constants/workspace.ts (1)
    • WORKSPACE_STRUCTURE_KEY (27-27)
    apps/pro-web/lib/queries/use-workspace-structure.ts (1)
    • WorkspaceStructure (22-38)
    apps/pro-web/types/thread.types.ts (1)
    • OrganizationData (17-22)
    apps/pro-web/lib/queries/use-workspace-mutations.ts (1)
    • structureToIDBPayload (912-918)
    apps/pro-web/lib/queries/workspace-keys.ts (1)
    • workspaceKeys (16-38)
    🔇 Additional comments (4)
    apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/brand-kit-dialog.tsx (2)

    16-18: Consider resetting view state when dialog closes.

    When the dialog is closed and reopened, currentView retains its previous value. If a user closes the dialog mid-wizard and reopens it, they'll see the wizard instead of the entry screen. Depending on UX requirements, you may want to reset to 'entry' when open becomes false.

    🔎 Potential fix if reset is desired
     export function BrandKitDialog({ open, onOpenChange }: BrandKitDialogProps) {
     	const [currentView, setCurrentView] = useState<BrandKitView>('entry')
     	const { activeOrganization } = useWorkspace()
    +
    +	// Reset view when dialog closes
    +	const handleOpenChange = (isOpen: boolean) => {
    +		if (!isOpen) {
    +			setCurrentView('entry')
    +		}
    +		onOpenChange(isOpen)
    +	}

    Then pass handleOpenChange to WorkspaceDialog instead of onOpenChange.


    49-73: LGTM!

    The component structure follows guidelines: functional component with function keyword, named export, props typed with interface, stable handler references, and proper use of useMemo for the computed subtitle. The possessive apostrophe handling (line 23-24) correctly distinguishes names ending in 's'.

    apps/pro-web/lib/queries/use-workspace-structure.ts (1)

    243-261: LGTM! The name-based deduplication fix is correct.

    The merge logic now correctly uses organization name for both existence check and lookup, resolving the previous mismatch issue. The map is keyed by org.name (line 244), and the lookup uses orgData.name (line 255), ensuring consistent behavior during anonymous-to-user migration.

    apps/pro-web/lib/hooks/use-workspace-media.tsx (1)

    129-134: LGTM! Session integration and query invalidation are correctly implemented.

    The provider correctly retrieves the session to derive user-specific IDB keys, and the mutation's onSuccess properly invalidates the workspace structure query using workspaceKeys.structure(userId) as per the past review feedback.

    Also applies to: 380-386

    Comment on lines +340 to +390
    // Brand Kit persistence mutation
    const saveBrandKitMutation = useMutation({
    mutationFn: async ({
    brandKitData,
    organizationId,
    }: {
    brandKitData: BrandKitData
    organizationId: string
    }) => {
    // 1. Save to server first
    const response = await fetch(`/api/organizations/${organizationId}`, {
    method: 'PATCH',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ brandKit: brandKitData }),
    })

    if (!response.ok) {
    throw new Error('Failed to save brand kit to server')
    }

    // 2. Update workspace IDB after successful server save
    const workspaceData = await getItem(WORKSPACE_STRUCTURE_KEY)
    if (workspaceData && typeof workspaceData === 'object') {
    const workspace = workspaceData as unknown as WorkspaceStructure
    const updatedWorkspace: WorkspaceStructure = {
    ...workspace,
    organizationData: workspace.organizationData.map(
    (org: OrganizationData) =>
    org.id === organizationId
    ? { ...org, brandKit: brandKitData }
    : org,
    ),
    }

    const payload = structureToIDBPayload(updatedWorkspace)
    await updateItem(WORKSPACE_STRUCTURE_KEY, payload)
    }

    return await response.json()
    },
    onSuccess: () => {
    // Invalidate and refetch - merge will prioritize IDB data (most recent updatedAt)
    const userId = session?.user?.id || 'anonymous'
    queryClient.invalidateQueries({
    queryKey: workspaceKeys.structure(userId),
    })
    },
    onError: (error) => {
    console.error('Failed to save brand kit:', error)
    },
    })
    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:

    # Find where saveBrandKitMutation is called
    rg -n "saveBrandKit" apps/pro-web --type ts --type tsx -A 3 -B 2

    Repository: bitcashorg/masterbots

    Length of output: 92


    🏁 Script executed:

    # Check the component file structure and context
    fd "use-workspace-media" apps/pro-web -type f

    Repository: bitcashorg/masterbots

    Length of output: 235


    🏁 Script executed:

    # Look at the full file to understand the context better
    wc -l apps/pro-web/lib/hooks/use-workspace-media.tsx

    Repository: bitcashorg/masterbots

    Length of output: 115


    🏁 Script executed:

    # Read the full hook file to understand context
    cat -n apps/pro-web/lib/hooks/use-workspace-media.tsx

    Repository: bitcashorg/masterbots

    Length of output: 18422


    🏁 Script executed:

    # Find where saveBrandKit is called
    rg -n "saveBrandKit" apps/pro-web --type-list

    Repository: bitcashorg/masterbots

    Length of output: 454


    🏁 Script executed:

    # Search for saveBrandKit calls in components
    rg "saveBrandKit" apps/pro-web -A 3 -B 2

    Repository: bitcashorg/masterbots

    Length of output: 7107


    🏁 Script executed:

    # Find Brand Kit related components
    fd -e tsx -e ts "brand-kit" apps/pro-web

    Repository: bitcashorg/masterbots

    Length of output: 325


    🏁 Script executed:

    # Read the checkpoint-step.tsx to see the full context of saveBrandKit calls
    cat -n apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/checkpoint-step.tsx | head -100

    Repository: bitcashorg/masterbots

    Length of output: 3254


    🏁 Script executed:

    # See the full checkpoint-step.tsx
    wc -l apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/checkpoint-step.tsx

    Repository: bitcashorg/masterbots

    Length of output: 165


    🏁 Script executed:

    # Check the full checkpoint-step.tsx to see if there are more guards
    cat -n apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/checkpoint-step.tsx | tail -300

    Repository: bitcashorg/masterbots

    Length of output: 12110


    🏁 Script executed:

    # Check useWorkspace hook to understand initialState source
    grep -n "initialState" apps/pro-web/lib/hooks/use-workspace.ts -A 5 -B 5

    Repository: bitcashorg/masterbots

    Length of output: 137


    🏁 Script executed:

    # Find the use-workspace hook
    fd "use-workspace" apps/pro-web/lib/hooks -type f

    Repository: bitcashorg/masterbots

    Length of output: 235


    🏁 Script executed:

    # Also check how currentOrg is determined - it's from initialState.organizationData
    # Let's search for where initialState comes from
    rg "initialState" apps/pro-web/lib/hooks -A 2 -B 2

    Repository: bitcashorg/masterbots

    Length of output: 2412


    🏁 Script executed:

    # Check how initialState is passed to WorkspaceProvider - look for where it's created
    rg "WorkspaceProvider" apps/pro-web --context 5 -m 3

    Repository: bitcashorg/masterbots

    Length of output: 4010


    🏁 Script executed:

    # Look at the mutation onError to see if it handles this scenario
    cat -n apps/pro-web/lib/hooks/use-workspace-media.tsx | sed -n '340,390p'

    Repository: bitcashorg/masterbots

    Length of output: 1972


    🏁 Script executed:

    # Check if there's any logging or error handling for when org is not found
    rg "organizationData.*map" apps/pro-web -B 5 -A 5

    Repository: bitcashorg/masterbots

    Length of output: 5730


    🏁 Script executed:

    # Look at the complete checkpoint-step to see all guards
    cat -n apps/pro-web/components/routes/workspace/media-tab/ui/brand-kit/wizard/steps/checkpoint-step.tsx | grep -A 15 "const currentOrg"

    Repository: bitcashorg/masterbots

    Length of output: 566


    Add explicit validation when updating the organization in IDB.

    If organizationId doesn't match any organization in the cached workspace structure (e.g., due to stale IDB data), the map() silently leaves the data unchanged after a successful server save. This creates a divergence between server and local state. Either validate that the organization exists before updating, log a warning if not found, or throw an error to surface the inconsistency instead of silently skipping the IDB update.

    🤖 Prompt for AI Agents
    In apps/pro-web/lib/hooks/use-workspace-media.tsx around lines 340-390, the code
    updates organizationData with map() but doesn’t check whether any org matched
    organizationId, so a stale IDB may remain unchanged and diverge from the server;
    fix by detecting whether the map produced a change (e.g., compare lengths of
    filtered matches or use a flag during mapping), and if no organization was
    found, do not silently skip — either log a clear warning (console.warn) and
    invalidate/refetch the workspace
    (queryClient.invalidateQueries(workspaceKeys.structure(userId))) to reconcile
    local state, or throw an error to surface the inconsistency; if you choose to
    update IDB anyway, explicitly insert the organization data returned by the
    server into organizationData before calling updateItem.
    

    Comment on lines +360 to +376
    // 2. Update workspace IDB after successful server save
    const workspaceData = await getItem(WORKSPACE_STRUCTURE_KEY)
    if (workspaceData && typeof workspaceData === 'object') {
    const workspace = workspaceData as unknown as WorkspaceStructure
    const updatedWorkspace: WorkspaceStructure = {
    ...workspace,
    organizationData: workspace.organizationData.map(
    (org: OrganizationData) =>
    org.id === organizationId
    ? { ...org, brandKit: brandKitData }
    : org,
    ),
    }

    const payload = structureToIDBPayload(updatedWorkspace)
    await updateItem(WORKSPACE_STRUCTURE_KEY, payload)
    }
    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

    IDB update omits updatedAt timestamp, which may affect merge priority.

    The merge logic in use-workspace-structure.ts uses updatedAt to determine which source takes priority (line 225-227: sourcesToMerge.sort((a, b) => b.updatedAt - a.updatedAt)). The IDB update here doesn't set a new updatedAt, so the cached workspace structure may not be recognized as the most recent during subsequent merges.

    🔎 Suggested fix
     const updatedWorkspace: WorkspaceStructure = {
     	...workspace,
     	organizationData: workspace.organizationData.map(
     		(org: OrganizationData) =>
     			org.id === organizationId
     				? { ...org, brandKit: brandKitData }
     				: org,
     	),
    +	updatedAt: Date.now(),
     }
    📝 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
    // 2. Update workspace IDB after successful server save
    const workspaceData = await getItem(WORKSPACE_STRUCTURE_KEY)
    if (workspaceData && typeof workspaceData === 'object') {
    const workspace = workspaceData as unknown as WorkspaceStructure
    const updatedWorkspace: WorkspaceStructure = {
    ...workspace,
    organizationData: workspace.organizationData.map(
    (org: OrganizationData) =>
    org.id === organizationId
    ? { ...org, brandKit: brandKitData }
    : org,
    ),
    }
    const payload = structureToIDBPayload(updatedWorkspace)
    await updateItem(WORKSPACE_STRUCTURE_KEY, payload)
    }
    // 2. Update workspace IDB after successful server save
    const workspaceData = await getItem(WORKSPACE_STRUCTURE_KEY)
    if (workspaceData && typeof workspaceData === 'object') {
    const workspace = workspaceData as unknown as WorkspaceStructure
    const updatedWorkspace: WorkspaceStructure = {
    ...workspace,
    organizationData: workspace.organizationData.map(
    (org: OrganizationData) =>
    org.id === organizationId
    ? { ...org, brandKit: brandKitData }
    : org,
    ),
    updatedAt: Date.now(),
    }
    const payload = structureToIDBPayload(updatedWorkspace)
    await updateItem(WORKSPACE_STRUCTURE_KEY, payload)
    }
    🤖 Prompt for AI Agents
    In apps/pro-web/lib/hooks/use-workspace-media.tsx around lines 360 to 376, the
    code updates the workspace in IDB but doesn't set a new updatedAt timestamp so
    the merge logic may treat the cached workspace as older; update the workspace
    structure or the payload to include an updatedAt (e.g., Date.now()) before
    calling structureToIDBPayload/updateItem, ensuring the field type matches
    WorkspaceStructure and the payload includes the new timestamp so subsequent
    merges correctly prioritize this update.
    

    Comment on lines +399 to +421
    const saveBrandKitStep = useCallback(
    (step: number, organizationId: string) => {
    setBrandKitCurrentStep(step)
    // Also persist to localStorage for reliability
    const key = `brandkit_step_${organizationId}`
    localStorage.setItem(key, String(step))
    },
    [],
    )

    const loadBrandKitStep = useCallback((organizationId: string) => {
    const key = `brandkit_step_${organizationId}`
    const savedStep = localStorage.getItem(key)
    const step = savedStep ? Number.parseInt(savedStep, 10) : 0
    setBrandKitCurrentStep(step)
    return step
    }, [])

    const clearBrandKitStep = useCallback((organizationId: string) => {
    setBrandKitCurrentStep(0)
    const key = `brandkit_step_${organizationId}`
    localStorage.removeItem(key)
    }, [])
    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

    Wrap localStorage access in try-catch for SSR and storage-disabled scenarios.

    localStorage access can throw in SSR environments or when storage is disabled (e.g., private browsing in some browsers). Since this is a 'use client' component, SSR is less of a concern, but storage quota/disabled scenarios should be handled.

    🔎 Suggested fix
     const saveBrandKitStep = useCallback(
     	(step: number, organizationId: string) => {
     		setBrandKitCurrentStep(step)
    -		// Also persist to localStorage for reliability
    -		const key = `brandkit_step_${organizationId}`
    -		localStorage.setItem(key, String(step))
    +		try {
    +			const key = `brandkit_step_${organizationId}`
    +			localStorage.setItem(key, String(step))
    +		} catch {
    +			// localStorage unavailable or quota exceeded
    +		}
     	},
     	[],
     )
    
     const loadBrandKitStep = useCallback((organizationId: string) => {
     	const key = `brandkit_step_${organizationId}`
    -	const savedStep = localStorage.getItem(key)
    +	let savedStep: string | null = null
    +	try {
    +		savedStep = localStorage.getItem(key)
    +	} catch {
    +		// localStorage unavailable
    +	}
     	const step = savedStep ? Number.parseInt(savedStep, 10) : 0
     	setBrandKitCurrentStep(step)
     	return step
     }, [])
    
     const clearBrandKitStep = useCallback((organizationId: string) => {
     	setBrandKitCurrentStep(0)
    -	const key = `brandkit_step_${organizationId}`
    -	localStorage.removeItem(key)
    +	try {
    +		const key = `brandkit_step_${organizationId}`
    +		localStorage.removeItem(key)
    +	} catch {
    +		// localStorage unavailable
    +	}
     }, [])
    🤖 Prompt for AI Agents
    In apps/pro-web/lib/hooks/use-workspace-media.tsx around lines 399-421, wrap all
    localStorage access in try-catch blocks to avoid exceptions in storage-disabled
    or restricted environments: in saveBrandKitStep catch errors from
    localStorage.setItem and fall back to a no-op (but still call
    setBrandKitCurrentStep), in loadBrandKitStep catch errors from
    localStorage.getItem and return/assign a safe default (0) after calling
    setBrandKitCurrentStep, and in clearBrandKitStep catch errors from
    localStorage.removeItem and perform a no-op (still call
    setBrandKitCurrentStep(0)); optionally check typeof window !== 'undefined'
    before touching localStorage but ensure functions never throw and always update
    state.
    

    @derianrddev derianrddev requested a review from AndlerRL December 30, 2025 02:02
    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