Skip to content

Cavai Theme Branding Enhancement Implementation Plan

For agentic workers: REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Enhance the Cavai theme with mascot (Bubbles), star decorations, gradient accents, and micro-animations to give it brand personality.

Architecture: CSS-only for decorative elements (stars, gradients, twinkle animations) via _cavai-decorations.scss, scoped under [data-theme="cavai"]. One CavaiMascot.vue component with mode prop for interactive mascot behaviors. All tokens in theme.css. Uses existing useTheme composable for reactive theme detection.

Tech Stack: Vue 2 Options API, SCSS, CSS custom properties, CSS keyframe animations, Vite (build), vitest (tests).

Spec: docs/superpowers/specs/2026-03-28-cavai-theme-branding-design.md


Chunk 1: Commit Existing Changes

Before ANY Cavai theme work begins, all ~71 uncommitted file changes must be committed in small, logical groups. This ensures a clean git state for easy rollback of theme branding work.

The changelog at docs/superpowers/specs/2026-03-28-theme-polish-changelog.md documents all changes.


Task 1: Commit theme tokens and code editor themes

Files:

  • Stage: src/styles/theme.css

  • Stage: src/utils/codemirror/theme-cavai-html.css

  • Stage: src/utils/aceEditor/theme-cavai-theme.js

  • [ ] Step 1: Stage and commit

bash
git add src/styles/theme.css src/utils/codemirror/theme-cavai-html.css src/utils/aceEditor/theme-cavai-theme.js
git commit -m "Add revert tokens and refine theme colors across all themes"

Task 2: Commit accessibility fixes

Files:

  • Stage: src/components/common/RadioButton.vue

  • Stage: src/pages/Chatbots/components/BuilderVisuals/Blocks/AddBlockTool.vue

  • Stage: src/pages/Chatbots/components/BuilderVisuals/Blocks/BlockButton.vue

  • Stage: src/pages/Chatbots/components/BuilderVisuals/Blocks/BlockActionsMenu.vue

  • Stage: src/pages/Chatbots/components/BuilderVisuals/Blocks/BlockPanel.vue

  • [ ] Step 1: Stage and commit

bash
git add src/components/common/RadioButton.vue \
  src/pages/Chatbots/components/BuilderVisuals/Blocks/AddBlockTool.vue \
  src/pages/Chatbots/components/BuilderVisuals/Blocks/BlockButton.vue \
  src/pages/Chatbots/components/BuilderVisuals/Blocks/BlockActionsMenu.vue \
  src/pages/Chatbots/components/BuilderVisuals/Blocks/BlockPanel.vue
git commit -m "Fix invisible text and icons in light themes"

Task 3: Commit shortcuts dialog theming

Files:

  • Stage: src/components/common/shortcuts/OsTypeToggle.vue

  • Stage: src/components/common/shortcuts/ShortcutCategory.vue

  • Stage: src/components/dialogs/ShortcutsDialog.vue

  • [ ] Step 1: Stage and commit

bash
git add src/components/common/shortcuts/OsTypeToggle.vue \
  src/components/common/shortcuts/ShortcutCategory.vue \
  src/components/dialogs/ShortcutsDialog.vue
git commit -m "Theme shortcuts dialog for all themes"

Task 4: Commit common component tokenization

Files:

  • Stage: src/components/common/Card/Card.vue

  • Stage: src/components/common/Card/CardSection.vue

  • Stage: src/components/common/ColorPicker.vue

  • Stage: src/components/common/Dropdown.vue

  • Stage: src/components/common/InputField.vue

  • Stage: src/components/common/InputSelect.vue

  • Stage: src/components/common/TextArea.vue

  • Stage: src/components/common/ToggleSwitch.vue

  • Stage: src/components/common/TooltipWrapper.vue

  • [ ] Step 1: Stage and commit

bash
git add src/components/common/Card/Card.vue \
  src/components/common/Card/CardSection.vue \
  src/components/common/ColorPicker.vue \
  src/components/common/Dropdown.vue \
  src/components/common/InputField.vue \
  src/components/common/InputSelect.vue \
  src/components/common/TextArea.vue \
  src/components/common/ToggleSwitch.vue \
  src/components/common/TooltipWrapper.vue
git commit -m "Tokenize common components for multi-theme support"

Task 5: Commit revert UI unification

Files:

  • Stage: src/pages/Chatbots/components/BuilderVisuals/Configuration/components/TemplateButton.vue

  • Stage: src/pages/Chatbots/components/BuilderVisuals/Configuration/components/AnimationEffectList.vue

  • Stage: src/pages/Chatbots/components/BuilderVisuals/Configuration/components/AnimationEffectRow.vue

  • Stage: src/pages/Chatbots/components/BuilderVisuals/Configuration/components/AnimationConfigCard.vue

  • [ ] Step 1: Stage and commit

bash
git add src/pages/Chatbots/components/BuilderVisuals/Configuration/components/TemplateButton.vue \
  src/pages/Chatbots/components/BuilderVisuals/Configuration/components/AnimationEffectList.vue \
  src/pages/Chatbots/components/BuilderVisuals/Configuration/components/AnimationEffectRow.vue \
  src/pages/Chatbots/components/BuilderVisuals/Configuration/components/AnimationConfigCard.vue
git commit -m "Unify revert indicator styling between templates and presets"

Task 6: Commit animation preset persistence

Files:

  • Stage: src/pages/Chatbots/components/BuilderVisuals/Blocks/data/types.ts

  • Stage: src/pages/Chatbots/components/BuilderVisuals/Blocks/data/defaults.ts

  • Stage: src/pages/Chatbots/components/BuilderVisuals/Configuration/mixins/configurationLogic.ts

  • [ ] Step 1: Stage and commit

bash
git add src/pages/Chatbots/components/BuilderVisuals/Blocks/data/types.ts \
  src/pages/Chatbots/components/BuilderVisuals/Blocks/data/defaults.ts \
  src/pages/Chatbots/components/BuilderVisuals/Configuration/mixins/configurationLogic.ts
git commit -m "Persist animation lastPreset for revert across navigation"

Task 7: Commit configuration section components

Files:

  • Stage: src/pages/Chatbots/components/BuilderVisuals/Configuration/components/BoxShadowSection.vue

  • Stage: src/pages/Chatbots/components/BuilderVisuals/Configuration/components/CodeEditor/CodeMirror/CodeMirrorEditor.vue

  • Stage: src/pages/Chatbots/components/BuilderVisuals/Configuration/components/CustomCSSEditor.vue

  • Stage: src/pages/Chatbots/components/BuilderVisuals/Configuration/components/DelaySection.vue

  • Stage: src/pages/Chatbots/components/BuilderVisuals/Configuration/components/DurationSection.vue

  • Stage: src/pages/Chatbots/components/BuilderVisuals/Configuration/components/FilterSection.vue

  • Stage: src/pages/Chatbots/components/BuilderVisuals/Configuration/components/FontSettings/FontSizeSection.vue

  • Stage: src/pages/Chatbots/components/BuilderVisuals/Configuration/components/HtmlSection.vue

  • Stage: src/pages/Chatbots/components/BuilderVisuals/Configuration/components/IterationsSection.vue

  • Stage: src/pages/Chatbots/components/BuilderVisuals/Configuration/components/MarginSection.vue

  • Stage: src/pages/Chatbots/components/BuilderVisuals/Configuration/components/MultiPostfixInput.vue

  • Stage: src/pages/Chatbots/components/BuilderVisuals/Configuration/components/OverrideIndicator.vue

  • Stage: src/pages/Chatbots/components/BuilderVisuals/Configuration/components/PlaybackSection.vue

  • Stage: src/pages/Chatbots/components/BuilderVisuals/Configuration/components/PositionSection.vue

  • Stage: src/pages/Chatbots/components/BuilderVisuals/Configuration/components/RotationSection.vue

  • Stage: src/pages/Chatbots/components/BuilderVisuals/Configuration/components/SlideSection.vue

  • Stage: src/pages/Chatbots/components/BuilderVisuals/Configuration/components/SlideSectionsPagination.vue

  • Stage: src/pages/Chatbots/components/BuilderVisuals/Configuration/components/TabNavigation.vue

  • Stage: src/pages/Chatbots/components/BuilderVisuals/Configuration/components/BorderRadius/CustomBorderRadius.vue

  • [ ] Step 1: Stage and commit

bash
git add \
  src/pages/Chatbots/components/BuilderVisuals/Configuration/components/BoxShadowSection.vue \
  src/pages/Chatbots/components/BuilderVisuals/Configuration/components/CodeEditor/CodeMirror/CodeMirrorEditor.vue \
  src/pages/Chatbots/components/BuilderVisuals/Configuration/components/CustomCSSEditor.vue \
  src/pages/Chatbots/components/BuilderVisuals/Configuration/components/DelaySection.vue \
  src/pages/Chatbots/components/BuilderVisuals/Configuration/components/DurationSection.vue \
  src/pages/Chatbots/components/BuilderVisuals/Configuration/components/FilterSection.vue \
  src/pages/Chatbots/components/BuilderVisuals/Configuration/components/FontSettings/FontSizeSection.vue \
  src/pages/Chatbots/components/BuilderVisuals/Configuration/components/HtmlSection.vue \
  src/pages/Chatbots/components/BuilderVisuals/Configuration/components/IterationsSection.vue \
  src/pages/Chatbots/components/BuilderVisuals/Configuration/components/MarginSection.vue \
  src/pages/Chatbots/components/BuilderVisuals/Configuration/components/MultiPostfixInput.vue \
  src/pages/Chatbots/components/BuilderVisuals/Configuration/components/OverrideIndicator.vue \
  src/pages/Chatbots/components/BuilderVisuals/Configuration/components/PlaybackSection.vue \
  src/pages/Chatbots/components/BuilderVisuals/Configuration/components/PositionSection.vue \
  src/pages/Chatbots/components/BuilderVisuals/Configuration/components/RotationSection.vue \
  src/pages/Chatbots/components/BuilderVisuals/Configuration/components/SlideSection.vue \
  src/pages/Chatbots/components/BuilderVisuals/Configuration/components/SlideSectionsPagination.vue \
  src/pages/Chatbots/components/BuilderVisuals/Configuration/components/TabNavigation.vue \
  src/pages/Chatbots/components/BuilderVisuals/Configuration/components/BorderRadius/CustomBorderRadius.vue
git commit -m "Tokenize configuration section components"

Task 8: Commit configuration panel and configs

Files:

  • Stage: src/pages/Chatbots/components/BuilderVisuals/Configuration/ConfigurationPanel.vue

  • Stage: src/pages/Chatbots/components/BuilderVisuals/Configuration/configs/HtmlConfiguration.vue

  • Stage: src/pages/Chatbots/components/BuilderVisuals/Configuration/configs/SliderConfiguration.vue

  • [ ] Step 1: Stage and commit

bash
git add src/pages/Chatbots/components/BuilderVisuals/Configuration/ConfigurationPanel.vue \
  src/pages/Chatbots/components/BuilderVisuals/Configuration/configs/HtmlConfiguration.vue \
  src/pages/Chatbots/components/BuilderVisuals/Configuration/configs/SliderConfiguration.vue
git commit -m "Refine configuration panel, code editor, and config layouts"

Task 9: Commit flow editor changes

Files:

  • Stage: src/pages/Chatbots/components/CavaiFlow/flowactors/OperatorBase.vue

  • Stage: src/pages/Chatbots/components/CavaiFlow/NodeLabel.vue

  • Stage: src/pages/Chatbots/components/CavaiFlow/brandingToolbarConstants.ts

  • [ ] Step 1: Stage and commit

bash
git add src/pages/Chatbots/components/CavaiFlow/flowactors/OperatorBase.vue \
  src/pages/Chatbots/components/CavaiFlow/NodeLabel.vue \
  src/pages/Chatbots/components/CavaiFlow/brandingToolbarConstants.ts
git commit -m "Use dashed borders for branded operators, tokenize flow labels"

Task 10: Commit builder delivery and option rows

Files:

  • Stage: src/pages/Chatbots/components/BuilderDelivery/ImpressionPixelOptions.vue

  • Stage: src/pages/Chatbots/components/BuilderDelivery/TagCode.vue

  • Stage: src/pages/Chatbots/components/BuilderDelivery/VastOptions.vue

  • Stage: src/pages/Chatbots/components/BuilderDelivery/VpaidOptions.vue

  • Stage: src/pages/Chatbots/components/OptionRow/OptionRow.vue

  • Stage: src/pages/Chatbots/components/OptionRow/OptionInputField.vue

  • Stage: src/pages/Chatbots/components/OptionRow/OverrideControls.vue

  • [ ] Step 1: Stage and commit

bash
git add src/pages/Chatbots/components/BuilderDelivery/ImpressionPixelOptions.vue \
  src/pages/Chatbots/components/BuilderDelivery/TagCode.vue \
  src/pages/Chatbots/components/BuilderDelivery/VastOptions.vue \
  src/pages/Chatbots/components/BuilderDelivery/VpaidOptions.vue \
  src/pages/Chatbots/components/OptionRow/OptionRow.vue \
  src/pages/Chatbots/components/OptionRow/OptionInputField.vue \
  src/pages/Chatbots/components/OptionRow/OverrideControls.vue
git commit -m "Tokenize delivery options and option row components"

Task 11: Commit builder visuals misc and creative wizard

Files:

  • Stage: src/pages/Chatbots/components/BuilderVisuals/FormatSelector.vue

  • Stage: src/pages/Chatbots/components/BuilderVisuals/Preview/PreviewFormatSelector.vue

  • Stage: src/pages/Chatbots/components/BuilderVisuals/Preview/PreviewPanel.vue

  • Stage: src/components/CreativeWizard/CreativeTypeIcon.vue

  • Stage: src/components/CreativeWizard/SizePreset.vue

  • Stage: src/components/CreativeWizard/TemplateSelector/TemplateRow.vue

  • [ ] Step 1: Stage and commit

bash
git add src/pages/Chatbots/components/BuilderVisuals/FormatSelector.vue \
  src/pages/Chatbots/components/BuilderVisuals/Preview/PreviewFormatSelector.vue \
  src/pages/Chatbots/components/BuilderVisuals/Preview/PreviewPanel.vue \
  src/components/CreativeWizard/CreativeTypeIcon.vue \
  src/components/CreativeWizard/SizePreset.vue \
  src/components/CreativeWizard/TemplateSelector/TemplateRow.vue
git commit -m "Tokenize preview selectors and creative wizard components"

Task 12: Commit global styles and remaining files

Files:

  • Stage: src/styles/_components-builder.scss

  • Stage: src/styles/_vuetify-reset.scss

  • Stage: src/pages/Chatbots/ChatbotBuilder.vue

  • Stage: src/assets/i18n/en.js

  • Stage: docs/theming-reference.md

  • Stage: docs/superpowers/specs/2026-03-28-theme-polish-changelog.md

  • Stage: src/assets/images/bubbles.png (mascot asset, needed by CavaiMascot in Chunk 2)

  • [ ] Step 1: Stage and commit

bash
git add src/styles/_components-builder.scss \
  src/styles/_vuetify-reset.scss \
  src/pages/Chatbots/ChatbotBuilder.vue \
  src/assets/i18n/en.js \
  src/assets/images/bubbles.png \
  docs/theming-reference.md \
  docs/superpowers/specs/2026-03-28-theme-polish-changelog.md
git commit -m "Add global Vuetify resets, mascot asset, docs, and remaining polish"

Task 13: Commit package.json changes

Files:

  • Stage: package.json

  • Stage: package-lock.json

  • [ ] Step 1: Review changes to ensure no secrets or local file refs

bash
git diff HEAD -- package.json

Check that no file:../ local references are present. If they are, skip this commit and flag to user.

  • [ ] Step 2: Stage and commit
bash
git add package.json package-lock.json
git commit -m "Update dependencies"

Task 14: Verify clean git state

  • [ ] Step 1: Verify no remaining changes
bash
git status
git diff --stat HEAD

Expected: Clean working tree (no modified, untracked, or staged files except the brainstorm directory which can be gitignored).

  • [ ] Step 2: Verify build passes
bash
cd /Users/nicolay/CavaiProduct/Application-Frontend && npx vite build 2>&1 | tail -20

Expected: Build succeeds without errors.


Chunk 2: Cavai Theme Foundation

After all existing changes are committed, this chunk creates the foundation: tokens, decorations file, and the CavaiMascot component.


Task 15: Add Cavai gradient and star tokens to theme.css

Files:

  • Modify: src/styles/theme.css (inside [data-theme="cavai"] block)

  • [ ] Step 1: Add tokens

Add these tokens to the end of the [data-theme="cavai"] block in theme.css (before the closing }):

css
  /* Cavai decorative tokens — theme-exclusive, used only by _cavai-decorations.scss and CavaiMascot.vue */
  --cavai-gradient:        linear-gradient(135deg, #FF6B9E, #FFA07A, #FFDB58);
  --cavai-gradient-subtle: linear-gradient(135deg, rgba(255,107,158,.06), rgba(255,160,122,.04), rgba(255,219,88,.02));
  --cavai-gradient-accent: linear-gradient(135deg, rgba(255,107,158,.15), rgba(255,160,122,.10), rgba(255,219,88,.06));
  --cavai-star-color:      #FFDB58;
  --cavai-star-glow:       rgba(255,219,88,.3);
  • [ ] Step 2: Verify build
bash
cd /Users/nicolay/CavaiProduct/Application-Frontend && npx vite build 2>&1 | tail -5
  • [ ] Step 3: Commit
bash
git add src/styles/theme.css
git commit -m "Add Cavai gradient and star decoration tokens"

Task 16: Create _cavai-decorations.scss

Files:

  • Create: src/styles/_cavai-decorations.scss

  • Modify: The SCSS entry point that imports style partials (find the file that imports other _*.scss partials, likely src/styles/main.scss or similar)

  • [ ] Step 1: Find the style import location

Style partials are imported in src/main.ts (around lines 12-20), not via SCSS @import. Find the last style import line:

  • [ ] Step 2: Create the decorations file

Create src/styles/_cavai-decorations.scss with keyframes and star pseudo-elements:

scss
// Cavai theme decorative elements — stars, gradients, animations
// All rules scoped under [data-theme="cavai"] so they have zero effect on other themes.

// ─── Keyframes ───────────────────────────────────────────────
@keyframes cavai-twinkle {
  0%, 100% { opacity: 0.3; }
  50%      { opacity: 1; }
}

@keyframes cavai-bob {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-4px); }
}

@keyframes cavai-glow-pulse {
  0%, 100% { filter: drop-shadow(0 0 2px var(--cavai-star-glow)); }
  50%      { filter: drop-shadow(0 0 6px var(--cavai-star-glow)); }
}

@keyframes cavai-slide-in {
  from { transform: translateY(100%); opacity: 0; }
  to   { transform: translateY(0); opacity: 1; }
}

@keyframes cavai-star-burst {
  0%   { transform: scale(0); opacity: 1; }
  70%  { transform: scale(1.2); opacity: 0.8; }
  100% { transform: scale(1); opacity: 0; }
}

// ─── Reduced motion ─────────────────────────────────────────
// Broad catch-all: disables ALL cavai animations including pseudo-element stars.
@media (prefers-reduced-motion: reduce) {
  [data-theme="cavai"] {
    *, *::before, *::after {
      animation-duration: 0s !important;
      animation-delay: 0s !important;
      transition-duration: 0s !important;
    }
  }
}

// ─── Star utility class ─────────────────────────────────────
// Applied via _cavai-decorations.scss to elements that should show stars.
// Usage: add .cavai-star-container to a positioned parent. Stars appear via ::before/::after.
[data-theme="cavai"] {
  .cavai-star {
    color: var(--cavai-star-color);
    font-size: 10px;
    animation: cavai-twinkle 4s ease-in-out infinite;
    pointer-events: none;
    user-select: none;
  }

  .cavai-star--delayed {
    animation-delay: 1.5s;
  }

  .cavai-star--slow {
    animation-duration: 5s;
  }
}
  • [ ] Step 3: Import in main.ts

Add import '@/styles/_cavai-decorations.scss' after the last style import (around line 20) in src/main.ts.

  • [ ] Step 4: Verify build
bash
cd /Users/nicolay/CavaiProduct/Application-Frontend && npx vite build 2>&1 | tail -5
  • [ ] Step 5: Commit
bash
git add src/styles/_cavai-decorations.scss src/main.ts
git commit -m "Add Cavai decorations stylesheet with keyframes and star classes"

Task 17: Create CavaiMascot.vue component

Files:

  • Create: src/components/common/CavaiMascot/CavaiMascot.vue

  • [ ] Step 1: Write test

Create src/components/common/__tests__/CavaiMascot.test.ts:

ts
import { describe, test, expect, beforeEach, vi } from 'vitest'
import { mountComponent } from '@/utils/testUtils'
import CavaiMascot from '../CavaiMascot/CavaiMascot.vue'

// Mock useTheme to control theme reactively
const mockThemeState = { theme: 'cavai' }
vi.mock('@/composables/useTheme', () => ({
  useTheme: () => ({ state: mockThemeState }),
}))

describe('CavaiMascot', () => {
  beforeEach(() => {
    mockThemeState.theme = 'cavai'
  })

  test('renders when cavai theme is active', () => {
    const wrapper = mountComponent(CavaiMascot)
    expect(wrapper.find('.cavai-mascot').exists()).toBe(true)
  })

  test('renders nothing when theme is not cavai', () => {
    mockThemeState.theme = 'dark'
    const wrapper = mountComponent(CavaiMascot)
    expect(wrapper.find('.cavai-mascot').exists()).toBe(false)
  })

  test('applies correct size class', () => {
    const wrapper = mountComponent(CavaiMascot, { propsData: { size: 'lg' } })
    expect(wrapper.find('.cavai-mascot--lg').exists()).toBe(true)
  })

  test('applies opacity style', () => {
    const wrapper = mountComponent(CavaiMascot, { propsData: { opacity: 0.18 } })
    const img = wrapper.find('.cavai-mascot__img')
    expect(img.element.style.opacity).toBe('0.18')
  })

  test('applies mode class', () => {
    const wrapper = mountComponent(CavaiMascot, { propsData: { mode: 'celebrate' } })
    expect(wrapper.find('.cavai-mascot--celebrate').exists()).toBe(true)
  })
})
  • [ ] Step 2: Run test to verify it fails
bash
cd /Users/nicolay/CavaiProduct/Application-Frontend && npx vitest run src/components/common/__tests__/CavaiMascot.test.ts --reporter=verbose 2>&1

Expected: FAIL — module not found.

  • [ ] Step 3: Create CavaiMascot.vue

Create src/components/common/CavaiMascot/CavaiMascot.vue:

vue
<template>
  <div
    v-if="isCavaiTheme"
    :class="[
      'cavai-mascot',
      `cavai-mascot--${mode}`,
      `cavai-mascot--${size}`,
      { 'cavai-mascot--hiding': isHiding },
    ]"
  >
    <img
      src="@/assets/images/bubbles.png"
      alt=""
      class="cavai-mascot__img"
      :style="{ opacity: effectiveOpacity }"
      draggable="false"
    >
  </div>
</template>

<script lang="ts">
import { useTheme } from '@/composables/useTheme'

const AUTO_HIDE_MODES = ['celebrate']
const AUTO_HIDE_DURATION = 3000
const FADE_OUT_DURATION = 500

export default {
  name: 'CavaiMascot',
  props: {
    mode: {
      type: String,
      default: 'peek',
      validator: (v: string) => ['peek', 'celebrate', 'wave', 'think', 'upload'].includes(v),
    },
    size: {
      type: String,
      default: 'md',
      validator: (v: string) => ['sm', 'md', 'lg'].includes(v),
    },
    opacity: {
      type: Number,
      default: 1,
    },
    dragState: {
      type: String,
      default: 'idle',
      validator: (v: string) => ['idle', 'over', 'dropped'].includes(v),
    },
  },
  data() {
    return {
      themeState: useTheme().state,
      isHiding: false,
      hideTimer: null as ReturnType<typeof setTimeout> | null,
    }
  },
  computed: {
    isCavaiTheme(): boolean {
      return this.themeState.theme === 'cavai'
    },
    effectiveOpacity(): number {
      if (this.isHiding) return 0
      return this.opacity
    },
  },
  watch: {
    mode: {
      immediate: true,
      handler(newMode: string) {
        this.clearTimer()
        this.isHiding = false
        if (AUTO_HIDE_MODES.includes(newMode)) {
          this.hideTimer = setTimeout(() => {
            this.isHiding = true
            setTimeout(() => this.$emit('hidden'), FADE_OUT_DURATION)
          }, AUTO_HIDE_DURATION)
        }
      },
    },
  },
  beforeDestroy() {
    this.clearTimer()
  },
  methods: {
    clearTimer() {
      if (this.hideTimer) {
        clearTimeout(this.hideTimer)
        this.hideTimer = null
      }
    },
  },
}
</script>

<style scoped lang="scss">
.cavai-mascot {
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  user-select: none;

  &__img {
    display: block;
    transition: opacity 0.5s ease-out;
  }

  // ─── Sizes ───
  &--sm .cavai-mascot__img { width: 32px; }
  &--md .cavai-mascot__img { width: 64px; }
  &--lg .cavai-mascot__img { width: 128px; }

  // ─── Modes ───
  &--peek {
    .cavai-mascot__img {
      animation: cavai-bob 4s ease-in-out infinite;
    }
  }

  &--celebrate {
    .cavai-mascot__img {
      animation: cavai-slide-in 0.4s ease-out;
    }
  }

  &--wave {
    .cavai-mascot__img {
      animation: cavai-bob 3s ease-in-out infinite;
    }
  }

  &--think {
    .cavai-mascot__img {
      animation: cavai-glow-pulse 3s ease-in-out infinite;
    }
  }

  &--upload {
    .cavai-mascot__img {
      animation: cavai-bob 4s ease-in-out infinite;
      transition: transform 0.3s ease-out;
    }
  }

  // ─── Hiding (auto-hide fade-out) ───
  &--hiding .cavai-mascot__img {
    opacity: 0 !important;
  }
}
// Note: reduced-motion handled globally in _cavai-decorations.scss
</style>
  • [ ] Step 4: Run tests
bash
cd /Users/nicolay/CavaiProduct/Application-Frontend && npx vitest run src/components/common/__tests__/CavaiMascot.test.ts --reporter=verbose 2>&1

Expected: All 5 tests PASS.

  • [ ] Step 5: Verify build
bash
cd /Users/nicolay/CavaiProduct/Application-Frontend && npx vite build 2>&1 | tail -5
  • [ ] Step 6: Commit
bash
git add src/components/common/CavaiMascot/CavaiMascot.vue \
  src/components/common/__tests__/CavaiMascot.test.ts
git commit -m "Add CavaiMascot component with mode-driven poses and auto-hide"

Chunk 3: Phase 1 Placements (Refactoring Existing Code)

These tasks modify existing mascot integrations to use the new CavaiMascot component.


Task 18: Refactor ConfigurationPanel to use CavaiMascot

Files:

  • Modify: src/pages/Chatbots/components/BuilderVisuals/Configuration/ConfigurationPanel.vue

  • [ ] Step 1: Read the current file

Read ConfigurationPanel.vue to understand the current mascot markup and styles.

  • [ ] Step 2: Replace raw img with CavaiMascot

In the template, replace:

html
<div class="mascot-peek">
  <img src="@/assets/images/bubbles.png" alt="" class="mascot-img">
</div>

With:

html
<CavaiMascot mode="peek" size="lg" :opacity="0.18" />
  • [ ] Step 3: Add component import and registration

In the <script> section, add:

ts
import CavaiMascot from '@/components/common/CavaiMascot/CavaiMascot.vue'

Add to components: { CavaiMascot }.

  • [ ] Step 4: Remove old mascot CSS

Remove from <style>:

  • The .mascot-peek class and its nested rules

  • The .mascot-img class

  • The @keyframes mascot-bob definition

  • The [data-theme="cavai"] & wrapper for mascot opacity

  • [ ] Step 5: Verify build and visual check

bash
cd /Users/nicolay/CavaiProduct/Application-Frontend && npx vite build 2>&1 | tail -5

Start dev server and verify: switch to Cavai theme, select no block → mascot should appear at 18% opacity with bobbing animation. Switch to dark theme → mascot should disappear.

  • [ ] Step 6: Commit
bash
git add src/pages/Chatbots/components/BuilderVisuals/Configuration/ConfigurationPanel.vue
git commit -m "Refactor config panel mascot to use CavaiMascot component"

Task 19: Enhance login page with stars and gradient

Files:

  • Modify: src/styles/_pages.scss

  • [ ] Step 1: Read the current signin styles

Read _pages.scss and find the .signin_page section (around lines 700-760).

  • [ ] Step 2: Add cavai-specific decorations

After the existing .signin_page__bubbles styles, add cavai-scoped star decorations:

scss
// Cavai theme enhancements for login page
[data-theme="cavai"] {
  .signin_page {
    // Subtle gradient wash over the page background
    &::before {
      content: '';
      position: fixed;
      inset: 0;
      background: var(--cavai-gradient-subtle);
      pointer-events: none;
      z-index: 0;
    }

    // Decorative stars near the bubbles mascot
    &__bubbles::before,
    &__bubbles::after {
      position: absolute;
      font-size: 10px;
      color: var(--cavai-star-color);
      pointer-events: none;
      z-index: 1;
    }

    &__bubbles::before {
      content: '✦';
      top: -8px;
      right: 10px;
      animation: cavai-twinkle 4s ease-in-out infinite;
    }

    &__bubbles::after {
      content: '✦';
      top: 5px;
      left: -12px;
      font-size: 7px;
      animation: cavai-twinkle 5s ease-in-out infinite 1.5s;
    }
  }
}
  • [ ] Step 3: Verify build
bash
cd /Users/nicolay/CavaiProduct/Application-Frontend && npx vite build 2>&1 | tail -5
  • [ ] Step 4: Visual check

Navigate to login page with Cavai theme → verify subtle gradient wash and twinkling stars near Bubbles.

  • [ ] Step 5: Commit
bash
git add src/styles/_pages.scss
git commit -m "Add star decorations and gradient wash to Cavai login page"

Chunk 4: Phase 2 Placements (New Integrations)

These tasks add mascot to new locations that don't currently have one.


Task 20: Add mascot to publish success (BuildProgress.vue)

Files:

  • Modify: src/pages/Chatbots/components/BuildProgress.vue

  • [ ] Step 1: Read BuildProgress.vue

Understand the current completion state rendering — what happens when build finishes successfully.

  • [ ] Step 2: Add CavaiMascot celebrate on build complete

In the template, add a mascot that appears when build reaches 100%:

html
<CavaiMascot
  v-if="buildComplete"
  mode="celebrate"
  size="md"
  @hidden="onMascotHidden"
/>

Add component import, registration, and the onMascotHidden method. The exact integration depends on the current template structure — adapt to fit.

  • [ ] Step 3: Verify build and visual check

Build a creative in Cavai theme → mascot should slide in on success, disappear after 3-4s.

  • [ ] Step 4: Commit
bash
git add src/pages/Chatbots/components/BuildProgress.vue
git commit -m "Add Bubbles celebration on successful publish in Cavai theme"

Task 21: Add mascot to flow editor empty state

Files:

  • Modify: src/pages/Chatbots/components/CavaiFlow/CavaiFlow.vue

  • [ ] Step 1: Read CavaiFlow.vue

Find where operators are rendered and how an empty state could be detected (e.g., empty operators array).

  • [ ] Step 2: Add empty state with mascot

When operator list is empty, show:

html
<div v-if="isFlowEmpty" class="cavai-flow-empty">
  <CavaiMascot mode="wave" size="lg" />
  <p class="cavai-flow-empty__text">Dra inn din første operator!</p>
</div>

Style the empty state centered in the canvas. Add isFlowEmpty computed property based on operator count. Add component import and registration.

  • [ ] Step 3: Verify build and visual check

Open flow editor with no operators in Cavai theme → mascot should appear with wave animation.

  • [ ] Step 4: Commit
bash
git add src/pages/Chatbots/components/CavaiFlow/CavaiFlow.vue
git commit -m "Add Bubbles wave mascot to empty flow editor in Cavai theme"

Task 22: Add mascot to empty lists (MainTable.vue)

Files:

  • Modify: src/components/common/MainTable.vue

  • [ ] Step 1: Read MainTable.vue

Find the Vuetify v-data-table and its existing empty state handling.

  • [ ] Step 2: Add CavaiMascot in no-data slot

Use Vuetify's no-data slot:

html
<template v-slot:no-data>
  <div class="main-table-empty">
    <CavaiMascot mode="peek" size="sm" />
    <span class="main-table-empty__text">{{ $t('noData') }}</span>
  </div>
</template>

The CavaiMascot auto-hides on non-cavai themes. Add component import and registration. Style the empty state.

  • [ ] Step 3: Verify build and visual check

Navigate to an empty creative list in Cavai theme → mascot peek with text.

  • [ ] Step 4: Commit
bash
git add src/components/common/MainTable.vue
git commit -m "Add Bubbles peek to empty table states in Cavai theme"

Chunk 5: Phase 3 Placements (Interactive)


Task 23: Add mascot to upload/drag-drop zones

Files:

  • Modify: src/components/common/FileUploader.vue

  • Modify: src/components/common/ImageInput.vue

  • [ ] Step 1: Read FileUploader.vue and ImageInput.vue

Understand the drag-and-drop handling: where dragover/dragleave/drop events are handled, what state tracks drag activity. ImageInput.vue wraps FileUploader.vue — determine if the mascot should be added at the FileUploader level (inherited by both) or needs separate integration in ImageInput.

  • [ ] Step 2: Add CavaiMascot with dragState prop to FileUploader

Add mascot to the drop zone area, wired to existing drag state:

html
<CavaiMascot
  mode="upload"
  size="md"
  :drag-state="isDragging ? 'over' : (justDropped ? 'dropped' : 'idle')"
/>

Add justDropped data property, set it to true on drop, reset after 1s. Add component import and registration. Adapt to the actual drag state mechanism in the component.

  • [ ] Step 3: Add to ImageInput if needed

If ImageInput.vue has its own drop zone separate from FileUploader, add the same mascot integration there. If it simply wraps FileUploader, no additional work needed.

  • [ ] Step 4: Verify build and visual check

In Cavai theme, drag a file over an upload area → mascot bounces. Drop → short celebrate. Test both generic file upload and image upload areas.

  • [ ] Step 5: Commit
bash
git add src/components/common/FileUploader.vue src/components/common/ImageInput.vue
git commit -m "Add Bubbles mascot reactions to file upload drag-and-drop"

Task 24: Add mascot to loading spinners

Files:

  • Modify: src/pages/Chatbots/ChatbotBuilder.vue (start with main builder loading)

  • [ ] Step 1: Read ChatbotBuilder.vue

Find the v-progress-circular usage and its loading condition.

  • [ ] Step 2: Add conditional CavaiMascot

Replace or wrap the spinner with a conditional:

html
<template v-if="isCavaiTheme">
  <CavaiMascot mode="think" size="md" />
</template>
<v-progress-circular v-else ... />

Use useTheme to determine isCavaiTheme. Add component import and registration.

  • [ ] Step 3: Verify build and visual check

Trigger loading state in Cavai theme → thinking mascot instead of spinner.

  • [ ] Step 4: Commit
bash
git add src/pages/Chatbots/ChatbotBuilder.vue
git commit -m "Replace loading spinner with Bubbles think animation in Cavai theme"

Chunk 6: Gradient Accents on UI Elements


Task 25: Add gradient accents to Cavai theme UI

Files:

  • Modify: src/styles/_cavai-decorations.scss (add gradient rules)

  • Possibly modify: src/pages/Chatbots/components/BuilderVisuals/Configuration/components/TabNavigation.vue

  • [ ] Step 1: Add gradient accent rules to _cavai-decorations.scss

scss
// ─── Gradient accents ────────────────────────────────────────
[data-theme="cavai"] {
  // Active tab underline — full gradient
  .tab-navigation .tab-item.active::after {
    background: var(--cavai-gradient);
  }

  // Card section subtle gradient wash
  .card {
    background-image: var(--cavai-gradient-subtle);
    background-size: 100% 100%;
  }
}

The exact selectors depend on the current component class names — read the relevant files first and adapt. Keep it minimal — only add gradient accents where they're clearly decorative and don't interfere with readability.

  • [ ] Step 2: Verify build and visual check

Check that gradient accents appear in Cavai theme and are absent in other themes.

  • [ ] Step 3: Commit
bash
git add src/styles/_cavai-decorations.scss
git commit -m "Add gradient accents to tabs and cards in Cavai theme"

Task 26: Add star decorations to card headers

Files:

  • Modify: src/styles/_cavai-decorations.scss

  • [ ] Step 1: Add card header star decorations

scss
// ─── Card header stars ───────────────────────────────────────
[data-theme="cavai"] {
  .title-section {
    position: relative;

    &::after {
      content: '✦';
      position: absolute;
      right: 0;
      top: 0;
      color: var(--cavai-star-color);
      font-size: 8px;
      opacity: 0.5;
      animation: cavai-twinkle 5s ease-in-out infinite;
      pointer-events: none;
    }
  }
}

This targets the .title-section from Card.vue which wraps card section headers.

  • [ ] Step 2: Verify build and visual check

Check that card headers show twinkling stars in Cavai theme. Verify they don't appear in other themes.

  • [ ] Step 3: Commit
bash
git add src/styles/_cavai-decorations.scss
git commit -m "Add twinkling star decorations to card headers in Cavai theme"

Task 27: Final verification and cleanup

  • [ ] Step 1: Run full test suite
bash
cd /Users/nicolay/CavaiProduct/Application-Frontend && npx vitest run --reporter=verbose 2>&1

Expected: All tests pass.

  • [ ] Step 2: Run build
bash
cd /Users/nicolay/CavaiProduct/Application-Frontend && npx vite build 2>&1 | tail -10

Expected: Clean build.

  • [ ] Step 3: Visual QA checklist

Switch through all 5 themes and verify:

  • [ ] Cavai theme: stars twinkle, mascot bobs, gradient accents visible
  • [ ] Dark theme: no stars, no mascot, no gradients, no visual changes
  • [ ] Light theme: same as dark — no cavai elements
  • [ ] Nord theme: same — no cavai elements
  • [ ] Mocha theme: same — no cavai elements

Check the 7 mascot placements in Cavai theme:

  • [ ] Config panel empty state: mascot at 18% opacity with bob

  • [ ] Publish success: mascot slides in, auto-hides

  • [ ] Flow editor empty: mascot waves in empty canvas

  • [ ] Upload drag-drop: mascot reacts to drag events

  • [ ] Empty lists: mascot peek in empty tables

  • [ ] Loading spinner: think animation replaces spinner

  • [ ] Login page: stars twinkle near bubbles, gradient wash

  • [ ] Step 4: Check prefers-reduced-motion

In browser devtools, enable prefers-reduced-motion: reduce. Verify all cavai animations stop — mascot shows static pose, stars don't twinkle.

Internal documentation