Skip to content

VoiceChrome, VoiceChromeWave DS 컴포넌트 구현#138

Merged
HamBeomJoon merged 6 commits into
developfrom
feat/#54-prezel-voice-chrome
May 26, 2026
Merged

VoiceChrome, VoiceChromeWave DS 컴포넌트 구현#138
HamBeomJoon merged 6 commits into
developfrom
feat/#54-prezel-voice-chrome

Conversation

@HamBeomJoon
Copy link
Copy Markdown
Contributor

@HamBeomJoon HamBeomJoon commented May 25, 2026

📌 작업 내용

  • 음성 인식 상태를 표시하는 PrezelVoiceChrome 디자인 시스템 컴포넌트를 추가했습니다.
  • 음성 볼륨 데이터를 막대형 파형으로 보여주는 PrezelVoiceChromeWave 컴포넌트를 추가했습니다.
  • IDLE, LISTENING, WAITING 상태별 UI 표현을 분리했습니다.
  • VoiceChrome의 gradient, 하단 라인, idle 타이틀 브러시 스타일을 구현했습니다.
  • VoiceChromeWave의 볼륨 샘플링, baseline 표시 옵션, 상태별 색상 처리를 구현했습니다.

🧩 관련 이슈


📸 스크린샷

  • VoiceChrome
스크린샷 2026-05-26 12 15 13
  • VoiceChromeWave
스크린샷 2026-05-26 12 15 58

📢 논의하고 싶은 내용

  • 동적 Preview는 피그마 demo에 맞춰 작성했습니다.

Summary by CodeRabbit

Summary by CodeRabbit

릴리스 노트

  • New Features
    • 음성 UI 크롬 컴포넌트 추가: 상태별 애니메이션과 그라디언트/배경 효과 포함
    • 음성 웨이브 시각화 컴포넌트 추가: 상태 전환과 실시간 볼륨 기반 바 애니메이션 지원
    • 음성 안내 문자열 리소스 2건 추가 (리스닝·대기 텍스트)

Review Change Stack

- `VoiceChromeStatus` (IDLE, LISTENING, WAITING)에 따른 UI 상태 정의
- 음성 인식 강도 표현을 위한 `VoiceChromeGradient` 애니메이션 로직 추가
- 상태별 하단 라인 표시 및 텍스트 브러시 효과 적용
- "듣고 있어요", "일시정지됨" 등 관련 시스템 문자열 추가
- 음성 입력 상태(IDLE, LISTENING, WAITING)에 따른 웨이브 애니메이션 UI 추가
- 입력 볼륨 크기에 따라 막대 높이가 동적으로 변하는 시각화 로직 구현
- 기준선 표시 여부 및 커스텀 스타일(브러시, 색상, 간격) 설정 기능 제공
- 컴포넌트 동작 확인을 위한 프리뷰 및 애니메이션 샘플 추가
- `PrezelVoiceChromeComponentPreview`를 상태별(Status, Gradient, Title) 세션으로 분리하여 가독성 향상
- `PreviewSurface`, `PreviewColumn` 등 공통 미리보기 컴포넌트 적용
- 미리보기용 보조 컴포넌트(`VoiceChromePreviewSection`, `VoiceChromePreviewItem`) 추가
- 애니메이션 미리보기(`PrezelVoiceChromeAnimatedPreview`)에 레이블 및 배경 스타일 적용
- `VoiceChromeStatus.LISTENING` 상태일 때 그라데이션이 자동으로 움직이는 무한 애니메이션 추가
- 상태 변경 시 라인 컬러 및 타이틀 텍스트 컬러가 부드럽게 전환되도록 `animateColorAsState` 적용
- `VoiceChromeStatus.IDLE`에서 다른 상태로 전환될 때 라인이 중앙에서 양옆으로 확장되는 애니메이션 구현
- `VoiceChromeGradient`에 따른 애니메이션 시작/종료 지점 계산 로직 추가
- 컴포넌트 동작 확인을 위한 클릭 토글 프리뷰 추가 및 기존 프리뷰 구조 정리
- `activationProgress`와 `volumeProgress`를 도입하여 상태 전환 시 부드러운 애니메이션 효과 추가
- `drawVoiceChromeWaveContent`를 통해 IDLE에서 LISTENING 상태로 전환 시 가로 스크롤 연출 구현
- `volumeToBarHeight` 계산 로직에 `progress` 파라미터를 추가하여 가변적인 높이 조절 지원
- `PreviewVolumePattern` 상수를 정의하고 애니메이션 프리뷰 로직을 상태 기반으로 리팩터링
- 불필요한 `rememberInfiniteTransition` 및 `sin` 함수 기반의 볼륨 샘플링 제거
@HamBeomJoon HamBeomJoon self-assigned this May 25, 2026
@HamBeomJoon HamBeomJoon requested a review from moondev03 as a code owner May 25, 2026 13:47
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 25, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 851b88de-9135-48c7-8cef-9ee457e215fd

📥 Commits

Reviewing files that changed from the base of the PR and between 221f12a and e8e5de8.

📒 Files selected for processing (2)
  • Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/voice/PrezelVoiceChrome.kt
  • Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/voice/PrezelVoiceChromeWave.kt

📝 Walkthrough

Walkthrough

음성 상태 열거형과 두 컴포저블(PrezelVoiceChrome, PrezelVoiceChromeWave)을 추가해 그라디언트 기반 크롬과 파형 기반 웨이브로 IDLE/LISTENING/WAITING 상태를 시각화합니다.

Changes

음성 UI 컴포넌트 추가

Layer / File(s) 요약
음성 상태 정의 및 문자열 리소스
Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/voice/PrezelVoiceChrome.kt, Prezel/core/designsystem/src/main/res/values/strings.xml
VoiceChromeStatus(IDLE/LISTENING/WAITING) 및 VoiceChromeGradient(NONE/MIN/MAX) 열거형을 추가하고 리스닝/대기 문자열 리소스를 추가했습니다.
VoiceChrome 컴포넌트 구현
Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/voice/PrezelVoiceChrome.kt
PrezelVoiceChrome에서 LISTENING + 유효 그라디언트일 때 무한 애니메이션으로 gradient stop을 갱신하고, 콘텐츠에 애니메이션된 색·진행값을 전달하여 타이틀, 라인, 조건부 방사형 배경을 렌더링합니다.
VoiceChrome 배경 및 확장 함수
Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/voice/PrezelVoiceChrome.kt
voiceChromeBackground modifier는 LISTENING 및 stop>0 조건일 때만 방사형 그라디언트를 생성해 클리핑 및 Y축 스케일 변환으로 원형 효과를 렌더링하며, VoiceChromeGradient에 stop/initial/target 확장 프로퍼티를 제공합니다.
VoiceChrome 프리뷰
Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/voice/PrezelVoiceChrome.kt
대형 디바이스 및 기본 프리뷰들에서 상태·그라디언트 조합을 렌더링하고, 클릭 토글(Idle↔Listening, Waiting↔Listening)과 고정 조합 프리뷰를 제공합니다.
VoiceChromeWave 컴포넌트 구현
Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/voice/PrezelVoiceChromeWave.kt
PrezelVoiceChromeWave에서 상태에 따른 volumes 클리핑, activationProgress/volumeProgress 애니메이션 계산, drawWithCache 기반 바 렌더링(전환 구간 분할 렌더링 포함), 베이스라인 그리기, 볼륨 샘플링 및 높이 매핑 로직을 구현합니다.
VoiceChromeWave 프리뷰
Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/voice/PrezelVoiceChromeWave.kt
베이스라인 on/off, 상태별 표시, 클릭 상호작용 프리뷰와 다양한 볼륨 패턴을 생성하는 유틸(previewVolumes, PreviewVolumePattern)을 추가했습니다.

요약

음성 입력/출력 상태를 시각화하는 두 가지 UI 컴포넌트를 설계 시스템에 추가했습니다. VoiceChrome은 그라디언트 애니메이션으로, VoiceChromeWave는 파형 바로 음성 상태(IDLE/LISTENING/WAITING)를 표현하며, 각각 상태별 애니메이션과 프리뷰를 포함합니다.

변경 사항

음성 UI 컴포넌트 추가

Layer / File(s) 요약
음성 상태 정의 및 문자열 리소스
PrezelVoiceChrome.kt (1-59줄), strings.xml
VoiceChromeStatus(IDLE/LISTENING/WAITING), VoiceChromeGradient(NONE/MIN/MAX) 열거형을 정의하고 상태별 텍스트 리소스를 추가합니다.
VoiceChrome 컴포넌트 구현
PrezelVoiceChrome.kt (60-231줄)
메인 컴포저블에서 무한 반복 그라디언트 애니메이션을 관리하고, 타이틀(상태별 텍스트 및 색상), 하단 라인 진행도(animateFloatAsState로 폭 애니메이션), 배경 방사형 그라디언트(조건부 렌더링 및 스케일 변환)를 렌더링합니다.
VoiceChrome 배경 및 확장 함수
PrezelVoiceChrome.kt (233-299줄)
voiceChromeBackground에서 LISTENING 상태 + 유효한 그라디언트일 때만 방사형 그라디언트를 그리며, 그라디언트별 stop 값과 애니메이션 타겟을 확장 프로퍼티로 제공합니다.
VoiceChrome 프리뷰
PrezelVoiceChrome.kt (301-462줄)
상태 및 그라디언트 조합 표시, 토글 가능한 상태 전환(IDLE↔LISTENING, WAITING↔LISTENING), 고정 렌더링 프리뷰를 포함한 여러 프리뷰 컴포저블을 제공합니다.
VoiceChromeWave 컴포넌트 구현
PrezelVoiceChromeWave.kt (38-273줄)
메인 컴포저블에서 상태별 볼륨 조정 및 활성화/볼륨 진행도 애니메이션을 관리하고, drawVoiceChromeWaveContent에서 LISTENING 전환 구간의 애니메이션, 현재 상태 바 렌더링, 베이스라인 표시를 구현합니다. 헬퍼 함수들(drawVoiceChromeWaveBars, volumeToBarHeight, sampleVolume 등)은 볼륨 샘플링, 바 높이 계산, 상태별 색상 적용을 수행합니다.
VoiceChromeWave 프리뷰
PrezelVoiceChromeWave.kt (274-479줄)
베이스라인 표시/숨김, 상태별 표시, 클릭으로 상태 전환 가능한 인터랙티브 프리뷰와 다양한 볼륨 패턴을 생성하는 유틸(previewVolumes, PreviewVolumePattern)을 제공합니다.

Suggested labels

✨ feat

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목이 두 개의 새로운 음성 관련 디자인 시스템 컴포넌트(VoiceChrome, VoiceChromeWave) 추가를 정확하게 요약하고 있습니다.
Description check ✅ Passed PR 설명이 작업 내용, 관련 이슈(#54), 스크린샷, 논의 사항을 포함하며 저장소의 템플릿 구조를 잘 따르고 있습니다.
Linked Issues check ✅ Passed 변경 사항이 이슈 #54의 모든 핵심 요구사항을 충족합니다: VoiceChrome 및 VoiceChromeWave 컴포넌트 구현, 상태별 UI 표현(IDLE/LISTENING/WAITING), 그래디언트 효과, 파형 시각화 등.
Out of Scope Changes check ✅ Passed 모든 변경 사항이 이슈 #54의 범위 내에 있습니다. 추가된 컴포넌트들과 문자열 리소스는 음성 UI 구현에 직접 관련되어 있습니다.

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


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.

@HamBeomJoon HamBeomJoon changed the title VoiceChrome 컴포넌트 추가 VoiceChrome, VoiceChromeWave DS 컴포넌트 구현 May 25, 2026
Copy link
Copy Markdown
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

🧹 Nitpick comments (3)
Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/voice/PrezelVoiceChrome.kt (3)

258-261: 💤 Low value

매직 넘버 추출 고려

scaleY = 0.45f는 타원형 그라디언트 효과를 만드는 중요한 값이지만, 코드만으로는 의도를 파악하기 어렵습니다. 파일 상단에 상수로 추출하고 의미 있는 이름과 주석을 추가하는 것을 고려해보세요.

♻️ 상수 추출 제안

파일 상단에:

private const val VOICE_CHROME_GRADIENT_SCALE_Y = 0.45f // 타원형 그라디언트를 위한 수직 스케일

사용 시:

-                                scaleY = 0.45f,
+                                scaleY = VOICE_CHROME_GRADIENT_SCALE_Y,
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/voice/PrezelVoiceChrome.kt`
around lines 258 - 261, Extract the magic literal 0.45f used in the scale(...)
call (scaleY = 0.45f) into a top-level constant (e.g.
VOICE_CHROME_GRADIENT_SCALE_Y) with a clear name and a short comment explaining
it creates the vertical compression for the elliptical gradient; then replace
the literal in PrezelVoiceChrome's scale(...) invocation with that constant so
the intent is obvious (reference: scale(...), scaleY, PrezelVoiceChrome).

276-281: 💤 Low value

그라디언트 스톱 값 문서화 권장

0.28f0.44f 값들은 시각적 효과를 결정하는 중요한 상수이지만, 어떻게 결정되었는지 알기 어렵습니다. 디자인 명세나 시각적 효과를 설명하는 주석을 추가하는 것을 권장합니다.

📝 문서화 제안
 private val VoiceChromeGradient.stop: Float
     get() = when (this) {
-        VoiceChromeGradient.NONE -> 0f
-        VoiceChromeGradient.MIN -> 0.28f
-        VoiceChromeGradient.MAX -> 0.44f
+        VoiceChromeGradient.NONE -> 0f // 그라디언트 없음
+        VoiceChromeGradient.MIN -> 0.28f // 최소 그라디언트 확산 (디자인 명세 기준)
+        VoiceChromeGradient.MAX -> 0.44f // 최대 그라디언트 확산 (디자인 명세 기준)
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/voice/PrezelVoiceChrome.kt`
around lines 276 - 281, VoiceChromeGradient.stop의 하드코딩된
값들(VoiceChromeGradient.MIN -> 0.28f, VoiceChromeGradient.MAX -> 0.44f)은 시각적
비율/픽셀 기준 또는 디자인 스펙에 따라 결정된 중요한 상수이므로, 이 getter(VoiceChromeGradient.stop) 바로 위에 각
값의 근거(디자인 명세에서 온 비율인지, 시뮬레이션/시각적 테스트 결과인지, 어떤 화면 해상도/비율을 기준으로 했는지)와 단위(비율인지
픽셀인지) 및 기대 효과(예: 페이드 시작/종료 위치, 대비 조정 등)를 간단히 주석으로 추가해 주세요; 또한 가능하면 참조한 디자인 문서나
스크린샷/토큰 이름을 주석에 링크/명시해 주세요.

67-85: ⚡ Quick win

불필요한 애니메이션 실행으로 인한 리소스 낭비 가능성

rememberInfiniteTransition이 항상 실행되지만, statusLISTENING이 아니거나 gradientNONE일 때는 애니메이션 값이 사용되지 않습니다. 상태에 따라 조건부로 애니메이션을 시작/중지하는 것을 고려해보세요.

♻️ 조건부 애니메이션 제안
 `@Composable`
 fun PrezelVoiceChrome(
     titleText: String,
     modifier: Modifier = Modifier,
     status: VoiceChromeStatus = VoiceChromeStatus.IDLE,
     gradient: VoiceChromeGradient = VoiceChromeGradient.NONE,
 ) {
+    val shouldAnimate = status == VoiceChromeStatus.LISTENING && gradient != VoiceChromeGradient.NONE
+    
     val transition = rememberInfiniteTransition(label = "VoiceChromeGradientTransition")
     val animatedGradientStop by transition.animateFloat(
         initialValue = gradient.initialAnimatedStop,
-        targetValue = gradient.targetAnimatedStop,
+        targetValue = if (shouldAnimate) gradient.targetAnimatedStop else gradient.initialAnimatedStop,
         animationSpec = infiniteRepeatable(
             animation = tween(
                 durationMillis = 2400,
                 delayMillis = 160,
                 easing = LinearEasing,
             ),
             repeatMode = RepeatMode.Reverse,
         ),
         label = "VoiceChromeGradientStop",
     )
-    val gradientStop = when {
-        status != VoiceChromeStatus.LISTENING -> VoiceChromeGradient.NONE.stop
-        gradient == VoiceChromeGradient.NONE -> VoiceChromeGradient.NONE.stop
-        else -> animatedGradientStop
-    }
+    val gradientStop = if (shouldAnimate) animatedGradientStop else VoiceChromeGradient.NONE.stop
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/voice/PrezelVoiceChrome.kt`
around lines 67 - 85, The infinite transition is always created via
rememberInfiniteTransition and animateFloat (animatedGradientStop) even when not
used; change this so the transition and animateFloat are only created when
status == VoiceChromeStatus.LISTENING and gradient != VoiceChromeGradient.NONE
(e.g., wrap the rememberInfiniteTransition/animateFloat block in that
conditional), and otherwise set gradientStop directly to
VoiceChromeGradient.NONE.stop; ensure references to rememberInfiniteTransition,
animateFloat/animatedGradientStop, gradientStop, VoiceChromeGradient and
VoiceChromeStatus.LISTENING are adjusted so no animation objects are created
when not needed.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/voice/PrezelVoiceChromeWave.kt`:
- Around line 239-251: The baseline is being drawn as a vertical line in
drawVoiceChromeWaveBaseline (visible parameter) but should be a horizontal
center line; update the drawLine call in drawVoiceChromeWaveBaseline so the
start Offset is at x = 0f, y = size.height / 2f and the end Offset is at x =
size.width, y = size.height / 2f (keep color and strokeWidth), ensuring the
baseline aligns with the waveform's central axis.

---

Nitpick comments:
In
`@Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/voice/PrezelVoiceChrome.kt`:
- Around line 258-261: Extract the magic literal 0.45f used in the scale(...)
call (scaleY = 0.45f) into a top-level constant (e.g.
VOICE_CHROME_GRADIENT_SCALE_Y) with a clear name and a short comment explaining
it creates the vertical compression for the elliptical gradient; then replace
the literal in PrezelVoiceChrome's scale(...) invocation with that constant so
the intent is obvious (reference: scale(...), scaleY, PrezelVoiceChrome).
- Around line 276-281: VoiceChromeGradient.stop의 하드코딩된
값들(VoiceChromeGradient.MIN -> 0.28f, VoiceChromeGradient.MAX -> 0.44f)은 시각적
비율/픽셀 기준 또는 디자인 스펙에 따라 결정된 중요한 상수이므로, 이 getter(VoiceChromeGradient.stop) 바로 위에 각
값의 근거(디자인 명세에서 온 비율인지, 시뮬레이션/시각적 테스트 결과인지, 어떤 화면 해상도/비율을 기준으로 했는지)와 단위(비율인지
픽셀인지) 및 기대 효과(예: 페이드 시작/종료 위치, 대비 조정 등)를 간단히 주석으로 추가해 주세요; 또한 가능하면 참조한 디자인 문서나
스크린샷/토큰 이름을 주석에 링크/명시해 주세요.
- Around line 67-85: The infinite transition is always created via
rememberInfiniteTransition and animateFloat (animatedGradientStop) even when not
used; change this so the transition and animateFloat are only created when
status == VoiceChromeStatus.LISTENING and gradient != VoiceChromeGradient.NONE
(e.g., wrap the rememberInfiniteTransition/animateFloat block in that
conditional), and otherwise set gradientStop directly to
VoiceChromeGradient.NONE.stop; ensure references to rememberInfiniteTransition,
animateFloat/animatedGradientStop, gradientStop, VoiceChromeGradient and
VoiceChromeStatus.LISTENING are adjusted so no animation objects are created
when not needed.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: ed8db0f0-b6b4-4448-a63e-bfbb4015deef

📥 Commits

Reviewing files that changed from the base of the PR and between 66275e0 and 221f12a.

📒 Files selected for processing (3)
  • Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/voice/PrezelVoiceChrome.kt
  • Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/voice/PrezelVoiceChromeWave.kt
  • Prezel/core/designsystem/src/main/res/values/strings.xml

- `PrezelVoiceChrome`의 그라데이션 애니메이션 로직을 상태(`status`)에 따라 조건부로 실행되도록 최적화
- 하단 그라데이션 영역의 너비를 고정값(360.dp)에서 `fillMaxWidth()`로 변경
- 보이스 크롬의 `radialGradient` 컬러 스톱을 세분화하여 더욱 부드러운 효과 구현
- `PrezelVoiceChromeWave`에서 라인이 그려지는 방향을 수직에서 수평으로 수정 (그려지는 좌표 계산 로직 변경)
@HamBeomJoon HamBeomJoon merged commit 13751f2 into develop May 26, 2026
2 checks passed
@HamBeomJoon HamBeomJoon deleted the feat/#54-prezel-voice-chrome branch May 26, 2026 04:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

PrezelVoiceChrome 구현

2 participants