app-tutor-ai-consumer 1.43.0 → 1.44.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/package.json +1 -1
  3. package/src/index.tsx +3 -1
  4. package/src/lib/components/ai-icon/ai-icon.builder.ts +59 -0
  5. package/src/lib/components/ai-icon/ai-icon.spec.tsx +12 -0
  6. package/src/lib/components/ai-icon/ai-icon.tsx +43 -0
  7. package/src/lib/components/ai-icon/index.ts +2 -0
  8. package/src/lib/components/ai-icon/styles.module.css +47 -0
  9. package/src/lib/components/ai-icon/types.ts +7 -0
  10. package/src/lib/components/ai-icon-circle/ai-icon-circle.builder.ts +50 -0
  11. package/src/lib/components/ai-icon-circle/ai-icon-circle.spec.tsx +15 -0
  12. package/src/lib/components/ai-icon-circle/ai-icon-circle.tsx +101 -0
  13. package/src/lib/components/ai-icon-circle/index.ts +2 -0
  14. package/src/lib/components/ai-icon-circle/types.ts +6 -0
  15. package/src/lib/components/index.ts +2 -0
  16. package/src/lib/utils/get-membership-color.ts +12 -0
  17. package/src/lib/utils/index.ts +1 -0
  18. package/src/modules/widget/components/ai-disclaimer/ai-disclaimer.tsx +2 -2
  19. package/src/modules/widget/components/avatar-animation/avatar-animation.tsx +5 -2
  20. package/src/modules/widget/components/greetings-card/greetings-card.tsx +4 -2
  21. package/src/modules/widget/components/header/widget-header.spec.tsx +1 -1
  22. package/src/modules/widget/components/header/widget-header.tsx +4 -3
  23. package/src/modules/widget/components/information-page/information-page.tsx +4 -2
  24. package/src/modules/widget/components/loading-page/loading-page.tsx +5 -1
  25. package/src/modules/widget/components/starter-page/starter-page-content/starter-page-content.spec.tsx +1 -1
  26. package/src/modules/widget/hooks/index.ts +1 -0
  27. package/src/modules/widget/hooks/use-membership-color/index.ts +1 -0
  28. package/src/modules/widget/hooks/use-membership-color/use-membership-color.tsx +12 -0
  29. package/src/types.ts +5 -0
package/CHANGELOG.md CHANGED
@@ -1,3 +1,26 @@
1
+ # [1.44.0](https://github.com/Hotmart-Org/app-tutor-ai-consumer/compare/v1.43.1...v1.44.0) (2025-12-22)
2
+
3
+ ### Bug Fixes
4
+
5
+ - animation icon ([b6c4f7c](https://github.com/Hotmart-Org/app-tutor-ai-consumer/commit/b6c4f7c7815be5158cc30ed79cc1b4dd343c1a20))
6
+
7
+ ### Features
8
+
9
+ - change ia icon disclaimer ([a45724c](https://github.com/Hotmart-Org/app-tutor-ai-consumer/commit/a45724c262a6204bb5c556423a381fbb966d461f))
10
+ - create circle ([6f7a660](https://github.com/Hotmart-Org/app-tutor-ai-consumer/commit/6f7a660717426f1f6b918f155a44710c7c5cf4a1))
11
+ - create ia icon cosmos ds ([1cc1f27](https://github.com/Hotmart-Org/app-tutor-ai-consumer/commit/1cc1f2728dffc9c9591135d1862fe5341d95b2fc))
12
+ - update ([54caf52](https://github.com/Hotmart-Org/app-tutor-ai-consumer/commit/54caf527db88b299ffb003123ab454bf2f510330))
13
+ - update ia-consumer ([1b97f6b](https://github.com/Hotmart-Org/app-tutor-ai-consumer/commit/1b97f6b13ad38075919ea0a05d2afd1434918ee3))
14
+ - update tutor ([809a465](https://github.com/Hotmart-Org/app-tutor-ai-consumer/commit/809a465464fe5aba13a994bb7dcb9a7e73673fb7))
15
+ - update unit test ([2ddb0e4](https://github.com/Hotmart-Org/app-tutor-ai-consumer/commit/2ddb0e41e89f792355e34be4159889947e89a37d))
16
+ - use new ai icon ([87dc20e](https://github.com/Hotmart-Org/app-tutor-ai-consumer/commit/87dc20ec62374f729215b8f3bdfdcef85ea1ab2e))
17
+
18
+ ### Reverts
19
+
20
+ - animation icon change ([b3f98a5](https://github.com/Hotmart-Org/app-tutor-ai-consumer/commit/b3f98a55cc2557f061ee1e9b05d92ae0f958c562))
21
+
22
+ ## [1.43.1](https://github.com/Hotmart-Org/app-tutor-ai-consumer/compare/v1.43.0...v1.43.1) (2025-12-16)
23
+
1
24
  # [1.43.0](https://github.com/Hotmart-Org/app-tutor-ai-consumer/compare/v1.42.0...v1.43.0) (2025-12-02)
2
25
 
3
26
  ### Features
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "app-tutor-ai-consumer",
3
- "version": "1.43.0",
3
+ "version": "1.44.0",
4
4
  "main": "index.js",
5
5
  "scripts": {
6
6
  "dev": "rspack serve --env=development --config config/rspack/rspack.config.js",
package/src/index.tsx CHANGED
@@ -10,7 +10,7 @@ import { bootstrap } from './bootstrap'
10
10
  import { initTheme } from './config/theme'
11
11
  import { devMode, productionMode } from './lib/utils'
12
12
  import { SparkieService } from './modules/sparkie'
13
- import { createStore } from './modules/widget/store'
13
+ import { createStore, widgetSettingsAtom } from './modules/widget/store'
14
14
  import type { Theme, WidgetSettingProps } from './types'
15
15
  import Wrapper from './wrapper'
16
16
 
@@ -54,6 +54,8 @@ window.startChatWidget = async (
54
54
  const widgetSettings = { ...settings, config: { ...settings.config, theme } }
55
55
  const store = createStore()
56
56
 
57
+ store.set(widgetSettingsAtom, widgetSettings)
58
+
57
59
  root.render(
58
60
  <Wrapper settings={widgetSettings} store={store} queryClient={queryClient} state={'LOADING'} />
59
61
  )
@@ -0,0 +1,59 @@
1
+ import { chance } from '@/src/config/tests'
2
+
3
+ import type { AiIconProps } from './types'
4
+
5
+ export class AiIconPropsBuilder implements AiIconProps {
6
+ animate?: boolean
7
+ color?: string
8
+ className?: string
9
+ size?: number
10
+ renderCustomAvatar?: boolean
11
+ customAvatarSize?: 'sm' | 'lg'
12
+
13
+ constructor() {
14
+ this.animate = false
15
+ this.size = 30
16
+ this.color = '#ffffff'
17
+ this.renderCustomAvatar = false
18
+ this.customAvatarSize = 'sm'
19
+ }
20
+
21
+ withAnimate(animate: boolean) {
22
+ this.animate = animate
23
+ return this
24
+ }
25
+
26
+ withSize(size: number) {
27
+ this.size = size
28
+ return this
29
+ }
30
+
31
+ withColor(color: string) {
32
+ this.color = color
33
+ return this
34
+ }
35
+
36
+ withClassName(className: string) {
37
+ this.className = className
38
+ return this
39
+ }
40
+
41
+ withRenderCustomAvatar(renderCustomAvatar: boolean) {
42
+ this.renderCustomAvatar = renderCustomAvatar
43
+ return this
44
+ }
45
+
46
+ withCustomAvatarSize(customAvatarSize: 'sm' | 'lg') {
47
+ this.customAvatarSize = customAvatarSize
48
+ return this
49
+ }
50
+
51
+ withRandomColor() {
52
+ this.color = chance.color({ format: 'hex' })
53
+ return this
54
+ }
55
+
56
+ build() {
57
+ return this
58
+ }
59
+ }
@@ -0,0 +1,12 @@
1
+ import { render, screen } from '@testing-library/react'
2
+ import { describe, expect, it } from 'vitest'
3
+
4
+ import { AiIcon } from './ai-icon'
5
+
6
+ describe('AiIcon', () => {
7
+ it('should render the icon', () => {
8
+ render(<AiIcon />)
9
+
10
+ expect(screen.getByTestId('ai-icon')).toBeInTheDocument()
11
+ })
12
+ })
@@ -0,0 +1,43 @@
1
+ import clsx from 'clsx'
2
+
3
+ import type { AiIconProps } from './types'
4
+
5
+ import styles from './styles.module.css'
6
+
7
+ export function AiIcon({ animate = false, size, color, className, style, ...props }: AiIconProps) {
8
+ return (
9
+ <svg
10
+ data-test='ai-icon'
11
+ width={size}
12
+ height={size}
13
+ viewBox='0 0 96 96'
14
+ fill='none'
15
+ xmlns='http://www.w3.org/2000/svg'
16
+ className={className}
17
+ style={{ color, ...style }}
18
+ {...props}>
19
+ <defs>
20
+ <filter id='ai-icon-glow-light' x='-50%' y='-50%' width='200%' height='200%'>
21
+ <feGaussianBlur stdDeviation='8' result='coloredBlur' />
22
+ <feMerge>
23
+ <feMergeNode in='coloredBlur' />
24
+ <feMergeNode in='SourceGraphic' />
25
+ </feMerge>
26
+ </filter>
27
+ </defs>
28
+
29
+ <path
30
+ d='M.919 47.865c-.119-.014-.119-.197 0-.21C45.02 42.415 46.093 16.083 47.904.563c.014-.118.178-.118.192 0 1.81 15.52 2.883 41.852 46.986 47.09.118.014.118.197 0 .21-44.103 5.239-45.175 31.571-46.986 47.092-.014.117-.178.117-.192 0-1.81-15.52-2.883-41.853-46.985-47.091Z'
31
+ fill='currentColor'
32
+ fillOpacity='0.5'
33
+ className={clsx(styles.backLayer, animate && styles.backRotate)}
34
+ style={{ filter: animate ? `url(#ai-icon-glow-light)` : undefined }}
35
+ />
36
+
37
+ <path
38
+ d='M.919 47.865c-.119-.014-.119-.197 0-.21C45.02 42.415 46.093 16.083 47.904.563c.014-.118.178-.118.192 0 1.81 15.52 2.883 41.852 46.986 47.09.118.014.118.197 0 .21-44.103 5.239-45.175 31.571-46.986 47.092-.014.117-.178.117-.192 0-1.81-15.52-2.883-41.853-46.985-47.091Z'
39
+ fill='currentColor'
40
+ />
41
+ </svg>
42
+ )
43
+ }
@@ -0,0 +1,2 @@
1
+ export { AiIcon } from './ai-icon'
2
+ export type { AiIconProps } from './types'
@@ -0,0 +1,47 @@
1
+ @keyframes ai-icon-back-rotate {
2
+ 0% {
3
+ opacity: 0.4;
4
+ transform: rotate(45deg) scale(0.9);
5
+ }
6
+ 10% {
7
+ opacity: 0.7;
8
+ transform: rotate(135deg) scale(1.1);
9
+ }
10
+ 25% {
11
+ opacity: 0.7;
12
+ transform: rotate(135deg) scale(1.1);
13
+ }
14
+ 35% {
15
+ opacity: 0.7;
16
+ transform: rotate(225deg) scale(1.1);
17
+ }
18
+ 50% {
19
+ opacity: 0.7;
20
+ transform: rotate(225deg) scale(1.1);
21
+ }
22
+ 60% {
23
+ opacity: 0.7;
24
+ transform: rotate(315deg) scale(1.1);
25
+ }
26
+ 75% {
27
+ opacity: 0.7;
28
+ transform: rotate(315deg) scale(1.1);
29
+ }
30
+ 85% {
31
+ opacity: 0.4;
32
+ transform: rotate(405deg) scale(0.9);
33
+ }
34
+ 100% {
35
+ opacity: 0.4;
36
+ transform: rotate(405deg) scale(0.9);
37
+ }
38
+ }
39
+
40
+ .backLayer {
41
+ transform-origin: center;
42
+ transform: rotate(45deg);
43
+ }
44
+
45
+ .backRotate {
46
+ animation: ai-icon-back-rotate 3s cubic-bezier(0.5, -0.5, 0, 1.5) infinite;
47
+ }
@@ -0,0 +1,7 @@
1
+ import type { SVGProps } from 'react'
2
+
3
+ export interface AiIconProps extends SVGProps<SVGSVGElement> {
4
+ animate?: boolean
5
+ size?: number
6
+ color?: string
7
+ }
@@ -0,0 +1,50 @@
1
+ import { chance } from '@/src/config/tests'
2
+
3
+ import type { AiIconCircleProps } from './types'
4
+
5
+ export class AiIconCirclePropsBuilder implements AiIconCircleProps {
6
+ size: number
7
+ animate?: boolean
8
+ className?: string
9
+ color?: string
10
+
11
+ constructor() {
12
+ this.size = 36
13
+ this.animate = false
14
+ this.color = '#ffffff'
15
+ }
16
+
17
+ withSize(size: number) {
18
+ this.size = size
19
+ return this
20
+ }
21
+
22
+ withAnimate(animate: boolean) {
23
+ this.animate = animate
24
+ return this
25
+ }
26
+
27
+ withClassName(className: string) {
28
+ this.className = className
29
+ return this
30
+ }
31
+
32
+ withColor(color: string) {
33
+ this.color = color
34
+ return this
35
+ }
36
+
37
+ withRandomSize() {
38
+ this.size = chance.integer({ min: 40, max: 200 })
39
+ return this
40
+ }
41
+
42
+ withRandomColor() {
43
+ this.color = chance.color({ format: 'hex' })
44
+ return this
45
+ }
46
+
47
+ build() {
48
+ return this
49
+ }
50
+ }
@@ -0,0 +1,15 @@
1
+ import { render, screen } from '@testing-library/react'
2
+ import { describe, expect, it } from 'vitest'
3
+
4
+ import { AiIconCircle } from './ai-icon-circle'
5
+ import { AiIconCirclePropsBuilder } from './ai-icon-circle.builder'
6
+
7
+ describe('AiIconCircle', () => {
8
+ it('should render the component', () => {
9
+ const props = new AiIconCirclePropsBuilder().build()
10
+
11
+ render(<AiIconCircle {...props} />)
12
+
13
+ expect(screen.getByTestId('ai-icon-circle')).toBeInTheDocument()
14
+ })
15
+ })
@@ -0,0 +1,101 @@
1
+ import clsx from 'clsx'
2
+
3
+ import { useWidgetSettingsAtomValue } from '@/src/modules/widget/store'
4
+ import { AiIcon } from '../ai-icon'
5
+
6
+ import type { AiIconCircleProps } from './types'
7
+
8
+ export function AiIconCircle({ size = 56, animate = false, className, color }: AiIconCircleProps) {
9
+ const settings = useWidgetSettingsAtomValue()
10
+ const theme = settings?.config?.theme || 'dark'
11
+ const customAvatarImg = settings?.avatar
12
+ const iconSize = size * 0.5
13
+ const isLightMode = theme === 'light'
14
+
15
+ if (customAvatarImg) {
16
+ return (
17
+ <div
18
+ data-test='ai-icon-circle'
19
+ className={clsx('overflow-hidden rounded-full', className)}
20
+ style={{
21
+ width: `${size}px`,
22
+ height: `${size}px`,
23
+ border: `1.5px solid ${color}`
24
+ }}>
25
+ <picture>
26
+ <source srcSet={customAvatarImg.webp} type='image/webp' />
27
+ <img src={customAvatarImg.original} alt='Avatar' className='h-full w-full object-cover' />
28
+ </picture>
29
+ </div>
30
+ )
31
+ }
32
+
33
+ return (
34
+ <div
35
+ data-test='ai-icon-circle'
36
+ className={clsx('relative', className)}
37
+ style={{ width: size, height: size }}>
38
+ <svg
39
+ width={size}
40
+ height={size}
41
+ viewBox='0 0 59 59'
42
+ fill='none'
43
+ xmlns='http://www.w3.org/2000/svg'
44
+ style={{ position: 'absolute', top: 0, left: 0 }}>
45
+ <rect x='0.75' y='0.75' width='57.5' height='57.5' rx='28.75' fill={color} />
46
+ <rect x='0.75' y='0.75' width='57.5' height='57.5' rx='28.75' fill='url(#paint0_linear)' />
47
+ <rect
48
+ x='0.75'
49
+ y='0.75'
50
+ width='57.5'
51
+ height='57.5'
52
+ rx='28.75'
53
+ stroke={color}
54
+ strokeWidth='1.5'
55
+ />
56
+ <rect
57
+ x='0.75'
58
+ y='0.75'
59
+ width='57.5'
60
+ height='57.5'
61
+ rx='28.75'
62
+ stroke='url(#paint1_linear)'
63
+ strokeOpacity='0.5'
64
+ strokeWidth='1.5'
65
+ />
66
+ <circle cx='29.5' cy='29.5' r='24' fill={isLightMode ? 'white' : 'black'} />
67
+ <defs>
68
+ <linearGradient
69
+ id='paint0_linear'
70
+ x1='29.5'
71
+ y1={isLightMode ? '57.5' : '1.5'}
72
+ x2='29.5'
73
+ y2={isLightMode ? '1.5' : '57.5'}
74
+ gradientUnits='userSpaceOnUse'>
75
+ <stop stopColor={isLightMode ? 'white' : '#090909'} stopOpacity='0.9' />
76
+ <stop offset='1' stopColor={isLightMode ? 'white' : '#090909'} stopOpacity='0.95' />
77
+ </linearGradient>
78
+ <linearGradient
79
+ id='paint1_linear'
80
+ x1='29.5'
81
+ y1={isLightMode ? '57.5' : '1.5'}
82
+ x2='29.5'
83
+ y2={isLightMode ? '1.5' : '57.5'}
84
+ gradientUnits='userSpaceOnUse'>
85
+ <stop stopColor={isLightMode ? 'white' : undefined} stopOpacity='0' />
86
+ <stop offset='1' stopColor={isLightMode ? 'white' : undefined} />
87
+ </linearGradient>
88
+ </defs>
89
+ </svg>
90
+
91
+ <div
92
+ className='absolute left-1/2 top-1/2 flex -translate-x-1/2 -translate-y-1/2 items-center justify-center'
93
+ style={{
94
+ width: iconSize,
95
+ height: iconSize
96
+ }}>
97
+ <AiIcon animate={animate} size={iconSize} color={color} />
98
+ </div>
99
+ </div>
100
+ )
101
+ }
@@ -0,0 +1,2 @@
1
+ export { AiIconCircle } from './ai-icon-circle'
2
+ export type { AiIconCircleProps } from './types'
@@ -0,0 +1,6 @@
1
+ export interface AiIconCircleProps {
2
+ size?: number
3
+ animate?: boolean
4
+ className?: string
5
+ color?: string
6
+ }
@@ -1,3 +1,5 @@
1
+ export * from './ai-icon'
2
+ export * from './ai-icon-circle'
1
3
  export * from './button'
2
4
  export * from './dropdown-actions'
3
5
  export * from './errors'
@@ -0,0 +1,12 @@
1
+ import type { Theme } from '@/src/types'
2
+
3
+ const DEFAULT_COLOR = '#a5a09f'
4
+
5
+ export const getMembershipColor = (
6
+ darkTransformed?: string,
7
+ lightTransformed?: string,
8
+ theme?: Theme
9
+ ): string => {
10
+ const color = theme === 'dark' ? darkTransformed : lightTransformed
11
+ return color || DEFAULT_COLOR
12
+ }
@@ -1,6 +1,7 @@
1
1
  export * from './constants'
2
2
  export * from './copy-text-to-clipboard'
3
3
  export * from './extract-text-from-react-nodes'
4
+ export * from './get-membership-color'
4
5
  export { default as HttpCodes } from './http-codes'
5
6
  export * from './is-theme-dark'
6
7
  export * from './languages'
@@ -1,6 +1,6 @@
1
1
  import { useTranslation } from 'react-i18next'
2
2
 
3
- import { Icon } from '@/src/lib/components/icons'
3
+ import { AiIcon } from '@/src/lib/components'
4
4
 
5
5
  const AIDisclaimer = () => {
6
6
  const { t } = useTranslation()
@@ -9,7 +9,7 @@ const AIDisclaimer = () => {
9
9
  <div className='mt-4 flex w-full items-center gap-1 text-xs text-neutral-500'>
10
10
  <p className='mb-0'>{t('ai_disclaimer.technology')}</p>
11
11
 
12
- <Icon name='tutor-logo' className='inline-flex h-3 w-3 align-middle' />
12
+ <AiIcon className='inline-flex h-3 w-3 align-middle' />
13
13
 
14
14
  <p className='mb-0'>{t('ai_disclaimer.hotmart_ai')}</p>
15
15
  </div>
@@ -1,9 +1,12 @@
1
- const AVATAR_ANIMATION_URL = `${process.env.STATIC_URL}/tutor/web/tutor_sparkle.gif`
1
+ import { AiIcon } from '@/src/lib/components'
2
+ import { useMembershipColor } from '../../hooks'
2
3
 
3
4
  const AvatarAnimation = () => {
5
+ const membershipColor = useMembershipColor()
6
+
4
7
  return (
5
8
  <div className='flex h-11 w-11 items-center justify-center rounded-lg bg-neutral-300'>
6
- <img src={AVATAR_ANIMATION_URL} alt='' aria-hidden className='max-w-[70%]' loading='lazy' />
9
+ <AiIcon size={30} animate aria-hidden color={membershipColor} />
7
10
  </div>
8
11
  )
9
12
  }
@@ -1,8 +1,9 @@
1
1
  import clsx from 'clsx'
2
2
  import { useTranslation } from 'react-i18next'
3
3
 
4
+ import { AiIconCircle } from '@/src/lib/components'
5
+ import { useMembershipColor } from '../../hooks'
4
6
  import { useIsAgentParentAtomValue } from '../../store'
5
- import { AIAvatar } from '../ai-avatar'
6
7
 
7
8
  export type GreetingsCardProps = {
8
9
  tutorName: string
@@ -13,11 +14,12 @@ export type GreetingsCardProps = {
13
14
  function GreetingsCard({ author, tutorName, isDarkTheme = false }: GreetingsCardProps) {
14
15
  const { t } = useTranslation()
15
16
  const isAgentMode = useIsAgentParentAtomValue()
17
+ const membershipColor = useMembershipColor()
16
18
 
17
19
  return (
18
20
  <div className='flex flex-col items-center justify-center'>
19
21
  <div className='max-md:hidden md:mb-4 md:block'>
20
- <AIAvatar size='lg' />
22
+ <AiIconCircle size={56} color={membershipColor} />
21
23
  </div>
22
24
  <div className='flex flex-col items-center justify-center gap-4 text-center'>
23
25
  <div className='flex flex-col gap-2'>
@@ -24,7 +24,7 @@ describe('<WidgetHeader />', () => {
24
24
  it('should render WidgetHeaderContent when prop showContent is true', () => {
25
25
  renderComponent()
26
26
 
27
- expect(screen.getByText(/sparkle-tutor-light/i)).toBeInTheDocument()
27
+ expect(screen.getByTestId(/ai-icon-circle/i)).toBeInTheDocument()
28
28
 
29
29
  expect(screen.queryByRole('button', { name: /Arrow Left Icon/i })).not.toBeInTheDocument()
30
30
  })
@@ -9,11 +9,11 @@ import {
9
9
  ClickTutorInfoSchema,
10
10
  ClickTutorMinimizeSchema
11
11
  } from '@/src/config/datahub/schemas/tutor'
12
- import { Button, Icon, Tooltip } from '@/src/lib/components'
12
+ import { AiIconCircle, Button, Icon, Tooltip } from '@/src/lib/components'
13
13
  import { useMediaQuery } from '@/src/lib/hooks'
14
14
  import { TutorWidgetEvents } from '../../events'
15
+ import { useMembershipColor } from '../../hooks'
15
16
  import { useWidgetGoBackTabAtom, useWidgetTabsAtom } from '../../store'
16
- import { AIAvatar } from '../ai-avatar'
17
17
 
18
18
  import type { WidgetHeaderProps } from './types'
19
19
 
@@ -91,6 +91,7 @@ function WidgetHeader({
91
91
  }: WidgetHeaderProps) {
92
92
  const { t } = useTranslation()
93
93
  const [, goBack] = useWidgetGoBackTabAtom()
94
+ const membershipColor = useMembershipColor()
94
95
  const name = tutorName ?? t('general.name')
95
96
 
96
97
  const handleHideWidget = () => {
@@ -144,7 +145,7 @@ function WidgetHeader({
144
145
  })}>
145
146
  {showContent && (
146
147
  <>
147
- {!showContentWithoutMeta && <AIAvatar />}
148
+ {!showContentWithoutMeta && <AiIconCircle size={40} color={membershipColor} />}
148
149
  {name && <h4 className='text-sm/loose font-bold'>{name}</h4>}
149
150
  </>
150
151
  )}
@@ -1,8 +1,9 @@
1
1
  import clsx from 'clsx'
2
2
  import { useTranslation } from 'react-i18next'
3
3
 
4
+ import { AiIconCircle } from '@/src/lib/components'
5
+ import { useMembershipColor } from '../../hooks'
4
6
  import { useWidgetSettingsAtom } from '../../store'
5
- import { AIAvatar } from '../ai-avatar'
6
7
  import { WidgetHeader } from '../header'
7
8
  import { PageLayout } from '../page-layout'
8
9
 
@@ -12,6 +13,7 @@ import { InformationCard } from './information-card'
12
13
  function WidgetInformationPage() {
13
14
  const { t } = useTranslation()
14
15
  const [settings] = useWidgetSettingsAtom()
16
+ const membershipColor = useMembershipColor()
15
17
  const isDarkMode = settings?.config?.theme === 'dark'
16
18
  const tutorName = settings?.tutorName ?? t('general.name')
17
19
 
@@ -26,7 +28,7 @@ function WidgetInformationPage() {
26
28
 
27
29
  <div className='my-8 flex justify-center'>
28
30
  <div className='flex flex-col items-center gap-2'>
29
- <AIAvatar size='lg' />
31
+ <AiIconCircle size={56} color={membershipColor} />
30
32
 
31
33
  <h3
32
34
  className={clsx('font-bold', {
@@ -5,7 +5,11 @@ import { ChatInput, MessageSkeleton } from '@/src/modules/messages/components'
5
5
  import { WidgetHeader } from '../header'
6
6
  import { PageLayout } from '../page-layout'
7
7
 
8
- function WidgetLoadingPage({ showHeader = true }: { showHeader?: boolean }) {
8
+ type WidgetLoadingPageProps = {
9
+ showHeader?: boolean
10
+ }
11
+
12
+ function WidgetLoadingPage({ showHeader = true }: WidgetLoadingPageProps) {
9
13
  const chatInputRef = useRef<HTMLTextAreaElement>(null)
10
14
 
11
15
  const handler = useCallback((e: Event) => {
@@ -8,7 +8,7 @@ describe('<WidgetStarterPageContent />', () => {
8
8
  it('should render greetings card when not in agent mode', () => {
9
9
  renderComponent()
10
10
 
11
- expect(screen.getByText(/sparkle-tutor-light/i)).toBeInTheDocument()
11
+ expect(screen.getByTestId('ai-icon-circle')).toBeInTheDocument()
12
12
  expect(screen.getByText(/general.greetings.hello/i)).toBeInTheDocument()
13
13
  expect(screen.getByText(/general.greetings.firstMessage/i)).toBeInTheDocument()
14
14
  expect(screen.getByText(/general.greetings.description/i)).toBeInTheDocument()
@@ -1,4 +1,5 @@
1
1
  export * from './use-listen-to-theme-change-event'
2
2
  export * from './use-listen-to-visibility-events'
3
+ export * from './use-membership-color'
3
4
  export * from './use-send-view-chat-event'
4
5
  export * from './use-send-view-tutor-event'
@@ -0,0 +1 @@
1
+ export * from './use-membership-color'
@@ -0,0 +1,12 @@
1
+ import { getMembershipColor } from '@/src/lib/utils'
2
+ import { useWidgetSettingsAtomValue } from '@/src/modules/widget/store'
3
+
4
+ export const useMembershipColor = () => {
5
+ const settings = useWidgetSettingsAtomValue()
6
+
7
+ return getMembershipColor(
8
+ settings?.membershipPrimaryColor?.darkTransformed,
9
+ settings?.membershipPrimaryColor?.lightTransformed,
10
+ settings?.config?.theme
11
+ )
12
+ }
package/src/types.ts CHANGED
@@ -43,6 +43,11 @@ export type WidgetSettingProps = {
43
43
  productName: string
44
44
  sessionId: string
45
45
  membershipId?: string
46
+ membershipPrimaryColor?: {
47
+ darkTransformed: string
48
+ lightTransformed: string
49
+ original: string
50
+ }
46
51
  membershipSlug?: string
47
52
  userId?: string
48
53
  tutorName?: string