Design/#13 : UI component 구현 - #19
Hidden character warning
Conversation
|
Warning Review limit reached
Your plan currently allows 1 review/hour. Refill in 38 minutes and 56 seconds. Your organization has run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After more review capacity refills, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than trial, open-source, and free plans. In all cases, review capacity refills continuously over time. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (18)
Walkthrough신규 DVDesign SwiftUI 컴포넌트 8종과 각 컴포넌트 전시용 PreviewView, previewSection 헬퍼, ContentView 라우팅 확장을 추가합니다. ChangesDVDesign UI 컴포넌트 및 프리뷰 시스템
Sequence DiagramsequenceDiagram
participant User
participant ContentView
participant Router as detailView(for:)
participant Preview as PreviewView
User->>ContentView: 컴포넌트 선택
ContentView->>Router: component.name 전달
Router->>Preview: 해당 PreviewView() 반환
Preview->>Preview: `@State로` 인터랙션 처리 (토글/선택/스텝 변경)
Preview-->>ContentView: UI 상태 반영(뷰 내부)
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
🧹 Nitpick comments (5)
Projects/DVDesign/Sources/Components/DVVaultContainer.swift (1)
107-115: ⚡ Quick win
fileprivate를private로 축소해주세요.현재 범위에서는 파일 단위 공개가 필요 없어
private가 더 안전하고 의도도 명확합니다.As per coding guidelines, "접근 제어가 가능한 가장 엄격한 수준인지 확인하세요. (`private` > `fileprivate` > `internal`)".제안 수정안
extension DVVaultContainer.TrailingIcon { - fileprivate var iconName: String { + private var iconName: String { switch self { case .expiringSoon: return "clock" case .expired: return "exclamationmark.circle" } } - fileprivate var iconColor: Color { + private var iconColor: Color { switch self { case .expiringSoon: return Color.dv(.warning) case .expired: return Color.dv(.danger) } } }🤖 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 `@Projects/DVDesign/Sources/Components/DVVaultContainer.swift` around lines 107 - 115, Change the access level of the computed properties from fileprivate to private to tighten encapsulation: replace the fileprivate declarations for iconName and iconColor with private in the type where they are defined (the computed properties named iconName and iconColor), and ensure any usages within the same file still compile (they should, since private allows access within the enclosing declaration); no other visibility changes are needed.Projects/DVDesign/SampleApp/Sources/DVCategoryPreviewView.swift (1)
9-33: ⚡ Quick win
body내부 중첩 레이아웃을 private 서브뷰로 분리해 주세요.섹션 블록이
body에 직접 쌓여 있어 확장 시 가독성이 빠르게 떨어집니다.interactiveSection,selectedSection,unselectedSection같은private var로 분리하고private extension으로 이동하는 편이 좋습니다.♻️ 제안 예시
var body: some View { ScrollView { - VStack(alignment: .leading, spacing: 32) { - previewSection("Interactive") { ... } - previewSection("Selected") { ... } - previewSection("Unselected") { ... } - } + content .padding(24) .frame(maxWidth: .infinity, alignment: .leading) } .navigationTitle("DVCategory") } + +private extension DVCategoryPreviewView { + var content: some View { + VStack(alignment: .leading, spacing: 32) { + interactiveSection + selectedSection + unselectedSection + } + } +}As per coding guidelines "var body 안에 중첩 레이아웃이 직접 구현되어 있으면 extension의 private var/func로 분리를 제안하세요."
🤖 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 `@Projects/DVDesign/SampleApp/Sources/DVCategoryPreviewView.swift` around lines 9 - 33, The body currently contains multiple nested layout blocks (previewSection with HStack and DVCategory rows) which hurts readability; refactor each into private computed subviews such as private var interactiveSection, private var selectedSection, and private var unselectedSection that return the corresponding View (using the existing previewSection, HStack and DVCategory usages), move those private vars into a private extension of DVCategoryPreviewView, and have body simply compose those subviews (along with the existing padding/frame/navigationTitle).Projects/DVDesign/SampleApp/Sources/DVTitleBarPreviewView.swift (1)
23-34: ⚡ Quick win프리뷰 배경의
Color.white고정은 다크 모드 확인을 왜곡할 수 있습니다.프리뷰 정확도를 위해 고정 흰색 대신 시스템 배경색을 쓰는 편이 안전합니다.
♻️ 제안 수정안
- .background(Color.white) + .background(Color(uiColor: .systemBackground)) @@ - .background(Color.white) + .background(Color(uiColor: .systemBackground))🤖 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 `@Projects/DVDesign/SampleApp/Sources/DVTitleBarPreviewView.swift` around lines 23 - 34, The previews currently hardcode Color.white which skews dark-mode rendering; update the preview background usage in the previewSection blocks that wrap DVTitleBar (the DVTitleBar preview instances) to use the system background color instead (e.g., use SwiftUI's system/background color provider such as Color(.systemBackground) or Color(UIColor.systemBackground)) so previews reflect light/dark appearances correctly.Projects/DVDesign/SampleApp/Sources/DVVaultContainerPreviewView.swift (1)
16-55: ⚡ Quick win
body의 중첩 레이아웃을private서브뷰로 분리해 주세요.현재
body에 섹션 구성과 상태 토글 UI가 한 번에 들어가 있어 수정 시 영향 범위가 큽니다. 섹션 단위로private var/func로 분리하면 유지보수성이 좋아집니다.As per coding guidelines `var body 안에 중첩 레이아웃이 직접 구현되어 있으면 extension의 private var/func로 분리를 제안하세요.`♻️ 제안 diff
struct DVVaultContainerPreviewView: View { @@ - var body: some View { - ScrollView { - VStack(alignment: .leading, spacing: 32) { - ... - } - .padding(24) - .frame(maxWidth: .infinity, alignment: .leading) - } - .navigationTitle("DVVaultContainer") - } + var body: some View { + ScrollView { content } + .navigationTitle("DVVaultContainer") + } } + +private extension DVVaultContainerPreviewView { + var content: some View { + VStack(alignment: .leading, spacing: 32) { + interactiveSection + selectedSection + unselectedSection + expiringSoonSection + expiredSection + } + .padding(24) + .frame(maxWidth: .infinity, alignment: .leading) + } +}🤖 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 `@Projects/DVDesign/SampleApp/Sources/DVVaultContainerPreviewView.swift` around lines 16 - 55, The body currently embeds multiple nested layout blocks (the previewSection stacks, ForEach over vaults, and DVVaultContainer instances) making maintenance hard; extract each logical preview into private computed vars or private funcs in a file-local extension (e.g., private var interactivePreview: some View, private var selectedPreview: some View, private func expiringPreview() -> some View) and have body simply compose those vars; ensure you reference existing symbols (body, previewSection, vaults, selectedIndex, DVVaultContainer) when moving code and preserve the selection toggle closure semantics (updating selectedIndex) and any trailingIcon parameters.Projects/DVDesign/SampleApp/Sources/ContentView.swift (1)
35-57: ⚡ Quick win라우팅 스위치가 이중화되어 이미 분기 드리프트가 생겼습니다.
detailView(for:)와destination(for:)가 같은 책임을 중복하고 있고, 현재도DVRadioButton계열 케이스가 한쪽에만 있어 매핑 불일치가 있습니다. 라우팅 함수를 하나로 합쳐 단일 소스로 유지해 주세요.♻️ 제안 diff
- NavigationLink(component.name) { - detailView(for: component) - } + NavigationLink(component.name) { + destination(for: component) + } - private func detailView(for component: Component) -> some View { + private func destination(for component: Component) -> some View { switch component.name { case "DVPageControl": DVPageControlPreviewView() @@ case "DVSecretType": DVSecretTypePreviewView() case "DVRadioButton", "DVRadioButtonGroup": RadioButtonPreviewView() default: ComponentPlaceholderView(name: component.name, owner: component.owner) } } } -// MARK: - Routing - -@ViewBuilder -private func destination(for component: Component) -> some View { - ... -}Also applies to: 64-85
🤖 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 `@Projects/DVDesign/SampleApp/Sources/ContentView.swift` around lines 35 - 57, The switch-based routing for components is duplicated between detailView(for:) and destination(for:) causing drift (e.g., the DVRadioButton case exists in only one); refactor by extracting a single mapping function (e.g., componentView(for:) or mapComponentToView) that returns the appropriate View for a Component and include every current case (DVPageControl, DVCategory, DVProjectContainer, DVVaultContainer, DVButton, DVCheckBox, DVTitleBar, DVSecretType, DVRadioButton/DVRadioButtonGroup, and default ComponentPlaceholderView) and then have both detailView(for:) and destination(for:) call that single function (or replace them with thin wrappers) so there is one authoritative switch to maintain.
🤖 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 `@Projects/DVDesign/SampleApp/Sources/DVPageControlPreviewView.swift`:
- Around line 19-43: 현재 프리뷰 코드에서 currentStep 경계값(2)이 하드코딩되어 있어 totalSteps 변경 시
동작이 깨질 수 있으니, 각 previewSection 블록과 버튼 핸들러에서 하드코딩된 2를 사용하지 말고 totalSteps에서 파생한
lastStepIndex = totalSteps - 1 값을 사용하도록 변경하세요; 즉 DVPageControl(totalSteps: X,
currentStep: currentStep)를 사용하는 영역에서 totalSteps 값을 변수로 선언하고 DVButton의 증감 가드(if
currentStep < lastStepIndex / if currentStep > 0)와 .disabled
조건(.disabled(currentStep == lastStepIndex) 등)에 lastStepIndex를 참조하도록 업데이트하세요.
In `@Projects/DVDesign/SampleApp/Sources/DVProjectContainerPreviewView.swift`:
- Around line 20-26: ForEach is using id: \.0 which can collide when project
names duplicate; change the iteration to use a stable unique identifier (e.g.,
use the element index or an explicit ID field) when iterating over projects so
each DVProjectContainer has a unique id; locate the ForEach(projects, id: \.0)
block and switch to ForEach(Array(projects.enumerated()), id: \.0 or use \.1.id)
or otherwise bind the index in the closure and use that index as the id while
keeping DVProjectContainer(name:count:isSelected:){...} and the selected logic
intact.
In `@Projects/DVDesign/Sources/Components/DVCategory.swift`:
- Around line 40-41: The clip shape uses RoundedRectangle(cornerRadius: 16) but
the border overlay uses a 12 radius causing the stroke to appear misaligned on
hover; update the border drawing to use the same corner radius (replace the 12
radius in the borderOverlay definition with 16) so the overlay stroke and
clipShape (RoundedRectangle(cornerRadius: 16)) match; apply the same change for
the second occurrence where overlay(borderOverlay) is paired with clipShape to
ensure both instances use cornerRadius: 16.
In `@Projects/DVDesign/Sources/Components/DVCheckBox.swift`:
- Around line 26-33: Update DVCheckBox's body so the tappable area is at least
the platform minimum and screen readers get state info: keep checkboxShape sized
to 16x16 (e.g., checkboxShape.frame(width:16, height:16)), but wrap the Button
content with extra hit area (e.g., add .padding(8) or set the Button view to
.frame(minWidth:44, minHeight:44) and .contentShape(Rectangle())) to ensure a
minimum tappable target; also add accessibility modifiers on the Button (or
checkboxShape) such as .accessibilityLabel("…"), .accessibilityValue(isChecked ?
"Checked" : "Unchecked") and .accessibilityAddTraits(isChecked ? .isSelected :
[]) so VoiceOver conveys the control role and state. Make these changes in the
DVCheckBox.body where Button(action:) and checkboxShape are defined.
In `@Projects/DVDesign/Sources/Components/DVProjectContainer.swift`:
- Around line 40-42: The hover border's corner radius doesn't match the clipped
background (clipShape(RoundedRectangle(cornerRadius: 6)) vs the border overlay's
cornerRadius 8), causing a visible gap when hovered; update the rounded corner
radii so both use the same value (either change clipShape's cornerRadius to 8 or
change the borderOverlay's cornerRadius to 6) wherever you set these (e.g., the
clipShape call and the borderOverlay definition used in DVProjectContainer.swift
and the same pattern at the other occurrence around lines 80-82) so the
background clipping and the overlay border share an identical cornerRadius.
In `@Projects/DVDesign/Sources/Components/DVTitleBar.swift`:
- Around line 55-62: The sortButton is an icon-only .plain Button with no
accessibility label and a small tap target; update the Button created in the
sortButton computed property (the one using onSortTapped and Image(systemName:
"arrow.up.arrow.down")) to add an accessibility label (use a localized string,
e.g. "Sort" or the appropriate Korean label) and ensure a minimum tappable area
by increasing its hit area (for example via a minimum frame size like 44x44 or
adding padding and contentShape(Rectangle())), and keep accessibility traits
(e.g., .accessibilityAddTraits(.isButton)) so VoiceOver announces it properly
while preserving the existing .buttonStyle(.plain).
---
Nitpick comments:
In `@Projects/DVDesign/SampleApp/Sources/ContentView.swift`:
- Around line 35-57: The switch-based routing for components is duplicated
between detailView(for:) and destination(for:) causing drift (e.g., the
DVRadioButton case exists in only one); refactor by extracting a single mapping
function (e.g., componentView(for:) or mapComponentToView) that returns the
appropriate View for a Component and include every current case (DVPageControl,
DVCategory, DVProjectContainer, DVVaultContainer, DVButton, DVCheckBox,
DVTitleBar, DVSecretType, DVRadioButton/DVRadioButtonGroup, and default
ComponentPlaceholderView) and then have both detailView(for:) and
destination(for:) call that single function (or replace them with thin wrappers)
so there is one authoritative switch to maintain.
In `@Projects/DVDesign/SampleApp/Sources/DVCategoryPreviewView.swift`:
- Around line 9-33: The body currently contains multiple nested layout blocks
(previewSection with HStack and DVCategory rows) which hurts readability;
refactor each into private computed subviews such as private var
interactiveSection, private var selectedSection, and private var
unselectedSection that return the corresponding View (using the existing
previewSection, HStack and DVCategory usages), move those private vars into a
private extension of DVCategoryPreviewView, and have body simply compose those
subviews (along with the existing padding/frame/navigationTitle).
In `@Projects/DVDesign/SampleApp/Sources/DVTitleBarPreviewView.swift`:
- Around line 23-34: The previews currently hardcode Color.white which skews
dark-mode rendering; update the preview background usage in the previewSection
blocks that wrap DVTitleBar (the DVTitleBar preview instances) to use the system
background color instead (e.g., use SwiftUI's system/background color provider
such as Color(.systemBackground) or Color(UIColor.systemBackground)) so previews
reflect light/dark appearances correctly.
In `@Projects/DVDesign/SampleApp/Sources/DVVaultContainerPreviewView.swift`:
- Around line 16-55: The body currently embeds multiple nested layout blocks
(the previewSection stacks, ForEach over vaults, and DVVaultContainer instances)
making maintenance hard; extract each logical preview into private computed vars
or private funcs in a file-local extension (e.g., private var
interactivePreview: some View, private var selectedPreview: some View, private
func expiringPreview() -> some View) and have body simply compose those vars;
ensure you reference existing symbols (body, previewSection, vaults,
selectedIndex, DVVaultContainer) when moving code and preserve the selection
toggle closure semantics (updating selectedIndex) and any trailingIcon
parameters.
In `@Projects/DVDesign/Sources/Components/DVVaultContainer.swift`:
- Around line 107-115: Change the access level of the computed properties from
fileprivate to private to tighten encapsulation: replace the fileprivate
declarations for iconName and iconColor with private in the type where they are
defined (the computed properties named iconName and iconColor), and ensure any
usages within the same file still compile (they should, since private allows
access within the enclosing declaration); no other visibility changes are
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 Plus
Run ID: 2d4b3f68-df31-45cd-996d-c563e97a20c1
📒 Files selected for processing (18)
Projects/DVDesign/SampleApp/Sources/ContentView.swiftProjects/DVDesign/SampleApp/Sources/DVButtonPreviewView.swiftProjects/DVDesign/SampleApp/Sources/DVCategoryPreviewView.swiftProjects/DVDesign/SampleApp/Sources/DVCheckBoxPreviewView.swiftProjects/DVDesign/SampleApp/Sources/DVPageControlPreviewView.swiftProjects/DVDesign/SampleApp/Sources/DVProjectContainerPreviewView.swiftProjects/DVDesign/SampleApp/Sources/DVSecretTypePreviewView.swiftProjects/DVDesign/SampleApp/Sources/DVTitleBarPreviewView.swiftProjects/DVDesign/SampleApp/Sources/DVVaultContainerPreviewView.swiftProjects/DVDesign/SampleApp/Sources/PreviewHelpers.swiftProjects/DVDesign/Sources/Components/DVButton.swiftProjects/DVDesign/Sources/Components/DVCategory.swiftProjects/DVDesign/Sources/Components/DVCheckBox.swiftProjects/DVDesign/Sources/Components/DVPageControl.swiftProjects/DVDesign/Sources/Components/DVProjectContainer.swiftProjects/DVDesign/Sources/Components/DVSecretType.swiftProjects/DVDesign/Sources/Components/DVTitleBar.swiftProjects/DVDesign/Sources/Components/DVVaultContainer.swift
| DVPageControl(totalSteps: 3, currentStep: currentStep) | ||
| HStack(spacing: 8) { | ||
| DVButton(titleText: "이전", style: .secondary) { | ||
| if currentStep > 0 { currentStep -= 1 } | ||
| } | ||
| .frame(width: 80) | ||
| .disabled(currentStep == 0) | ||
|
|
||
| DVButton(titleText: "다음", style: .secondary) { | ||
| if currentStep < 2 { currentStep += 1 } | ||
| } | ||
| .frame(width: 80) | ||
| .disabled(currentStep == 2) | ||
| } | ||
| } | ||
| } | ||
| previewSection("Step 1 / 3") { | ||
| DVPageControl(totalSteps: 3, currentStep: 0) | ||
| } | ||
| previewSection("Step 2 / 3") { | ||
| DVPageControl(totalSteps: 3, currentStep: 1) | ||
| } | ||
| previewSection("Step 3 / 3") { | ||
| DVPageControl(totalSteps: 3, currentStep: 2) | ||
| } |
There was a problem hiding this comment.
currentStep 경계값(2) 하드코딩을 제거해 주세요.
totalSteps와 경계 로직이 분리되어 있어 단계 수 변경 시 프리뷰 동작이 어긋날 수 있습니다. lastStepIndex = totalSteps - 1로 통일해 주세요.
🐛 제안 수정안
struct DVPageControlPreviewView: View {
@@
- `@State` private var currentStep = 0
+ `@State` private var currentStep = 0
+ private let totalSteps = 3
@@
- DVPageControl(totalSteps: 3, currentStep: currentStep)
+ DVPageControl(totalSteps: totalSteps, currentStep: currentStep)
@@
- DVButton(titleText: "다음", style: .secondary) {
- if currentStep < 2 { currentStep += 1 }
+ DVButton(titleText: "다음", style: .secondary) {
+ if currentStep < totalSteps - 1 { currentStep += 1 }
}
.frame(width: 80)
- .disabled(currentStep == 2)
+ .disabled(currentStep == totalSteps - 1)🤖 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 `@Projects/DVDesign/SampleApp/Sources/DVPageControlPreviewView.swift` around
lines 19 - 43, 현재 프리뷰 코드에서 currentStep 경계값(2)이 하드코딩되어 있어 totalSteps 변경 시 동작이 깨질
수 있으니, 각 previewSection 블록과 버튼 핸들러에서 하드코딩된 2를 사용하지 말고 totalSteps에서 파생한
lastStepIndex = totalSteps - 1 값을 사용하도록 변경하세요; 즉 DVPageControl(totalSteps: X,
currentStep: currentStep)를 사용하는 영역에서 totalSteps 값을 변수로 선언하고 DVButton의 증감 가드(if
currentStep < lastStepIndex / if currentStep > 0)와 .disabled
조건(.disabled(currentStep == lastStepIndex) 등)에 lastStepIndex를 참조하도록 업데이트하세요.
| public var body: some View { | ||
| Button(action: action) { | ||
| checkboxShape | ||
| .contentShape(Rectangle()) | ||
| } | ||
| .buttonStyle(.plain) | ||
| .onHover { isHovered = $0 } | ||
| } |
There was a problem hiding this comment.
체크박스에 접근성 상태와 최소 탭 영역을 보강해주세요.
현재 16x16 시각 크기 그대로 클릭 영역이 잡혀 조작이 어렵고, 보이스오버에서 체크 상태 의미 전달이 부족합니다. 최소 탭 영역과 접근성 라벨/값을 추가하는 게 안전합니다.
제안 수정안
public var body: some View {
Button(action: action) {
checkboxShape
+ .frame(width: 44, height: 44)
.contentShape(Rectangle())
}
.buttonStyle(.plain)
.onHover { isHovered = $0 }
+ .accessibilityLabel("체크박스")
+ .accessibilityValue(isChecked ? "선택됨" : "선택 안 됨")
}🤖 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 `@Projects/DVDesign/Sources/Components/DVCheckBox.swift` around lines 26 - 33,
Update DVCheckBox's body so the tappable area is at least the platform minimum
and screen readers get state info: keep checkboxShape sized to 16x16 (e.g.,
checkboxShape.frame(width:16, height:16)), but wrap the Button content with
extra hit area (e.g., add .padding(8) or set the Button view to
.frame(minWidth:44, minHeight:44) and .contentShape(Rectangle())) to ensure a
minimum tappable target; also add accessibility modifiers on the Button (or
checkboxShape) such as .accessibilityLabel("…"), .accessibilityValue(isChecked ?
"Checked" : "Unchecked") and .accessibilityAddTraits(isChecked ? .isSelected :
[]) so VoiceOver conveys the control role and state. Make these changes in the
DVCheckBox.body where Button(action:) and checkboxShape are defined.
| private var sortButton: some View { | ||
| Button(action: onSortTapped) { | ||
| Image(systemName: "arrow.up.arrow.down") | ||
| .dvFont(.bodyXL) | ||
| .foregroundStyle(Color.dv(.gray600)) | ||
| } | ||
| .buttonStyle(.plain) | ||
| } |
There was a problem hiding this comment.
정렬 버튼에 접근성 이름과 최소 탭 영역을 추가해주세요.
아이콘-only .plain 버튼은 보이스오버 의미 전달이 약하고, 클릭 타깃도 작아 조작성이 떨어집니다.
제안 수정안
private var sortButton: some View {
Button(action: onSortTapped) {
Image(systemName: "arrow.up.arrow.down")
.dvFont(.bodyXL)
.foregroundStyle(Color.dv(.gray600))
+ .frame(width: 44, height: 44)
+ .contentShape(Rectangle())
}
.buttonStyle(.plain)
+ .accessibilityLabel("정렬")
+ .accessibilityHint("정렬 옵션을 엽니다")
}🤖 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 `@Projects/DVDesign/Sources/Components/DVTitleBar.swift` around lines 55 - 62,
The sortButton is an icon-only .plain Button with no accessibility label and a
small tap target; update the Button created in the sortButton computed property
(the one using onSortTapped and Image(systemName: "arrow.up.arrow.down")) to add
an accessibility label (use a localized string, e.g. "Sort" or the appropriate
Korean label) and ensure a minimum tappable area by increasing its hit area (for
example via a minimum frame size like 44x44 or adding padding and
contentShape(Rectangle())), and keep accessibility traits (e.g.,
.accessibilityAddTraits(.isButton)) so VoiceOver announces it properly while
preserving the existing .buttonStyle(.plain).
There was a problem hiding this comment.
interaction 없는 ui component이므로 DVStepIndicator 로 네이밍 수정
There was a problem hiding this comment.
- .listStyle(.sidebar) 그대로 + .tint() 만 바꾸는 경우
가장 단순한 방법:
List(selection: $selected) { ... }
.listStyle(.sidebar)
.tint(Color.dv(.vaultGreen))
✅ 커스텀 가능
- 선택 배경 색: .tint()로 vaultGreen 지정 가능
- 행 내부 텍스트/아이콘 색, 폰트, 레이아웃
- 아이콘 종류 (SF Symbol, 커스텀 이미지)
❌ 커스텀 불가 / 제한적
- 선택 배경의 corner radius (시스템이 ~6pt로 결정)
- 선택 배경의 vibrancy 합성 — 순수 vaultGreen이 아니라 사이드바 머티리얼과 섞인 톤으로 렌더링됨. 디자인 시안의 색과 픽셀 단위로는 다르게 보임
- 윈도우 비활성 시 자동 dim — 끌 수 없음 (보통 끄지 않는 게 맞음)
- 호버 시 미세 배경 틴트 — 시스템이 강제
- 행 높이/패딩 — .controlSize 정도로만 조절
- .badge(count) 모양 — 시스템 회색 pill 고정. 색·폰트·모양 변경 불가
→ 한마디로: "기본 macOS 사이드바 룩 + 브랜드 컬러로 살짝 도색" 수준. Mail, Notes, Reminders가 다 이 방법
There was a problem hiding this comment.
- .listStyle(.sidebar) 포기, .listStyle(.plain) + selection 활용
List(selection: $selected) { ... }
.listStyle(.plain)
✅ 새로 얻는 자유도
- 행 배경/보더/corner radius 완전 자유
- 선택 배경 자체를 직접 그릴 수 있음 (시스템이 안 그려줌)
- 호버/프레스 시각 효과 직접 정의
- 배지 모양 직접 그리기
✅ 그래도 유지되는 네이티브 기능
- 키보드 ↑/↓ 네비게이션
- Return 선택, Space, 다중 선택 (⌘/⇧)
- 포커스 링
- selection 바인딩 자동 처리
- VoiceOver 기본 통합
❌ 잃는 것
- 사이드바 vibrancy 머티리얼 (배경이 평면 색이 됨)
- 시스템 사이드바 룩 (외부 앱과 일관성)
- 자동 윈도우 비활성 dim → 직접 @Environment(.controlActiveState) 처리 필요
→ 브랜드 컬러를 정확히 vaultGreen으로 칠하고, 디자인 시안대로 corner radius/패딩을 살리고 싶다면 이 길. 우리 케이스에 가장 맞을 가능성 높음.
There was a problem hiding this comment.
1. 비슷한 시스템 컴포넌트 / 패턴
macOS는 행(row)의 비주얼을 직접 그리되, 컨테이너로 List를 쓰는
것이 시스템 표준이에요. 즉 "List + 우리가 그린 row"가 정답.
참고할 만한 네이티브 앱의 패턴:
| 앱 | 패턴 | DVVaultContainer와 매핑 |
|---|---|---|
| Mail.app 메시지 리스트 | 아바타 + (보낸이/제목/미리보기) + 우측 날짜 + 우측 첨부/플래그 아이콘 | 가장 가까움 |
| Messages.app 대화 리스트 | 아바타 + (이름/미리보기) + 우측 시간 + unread dot | 거의 동일 구조 |
| Notes.app 노트 리스트 | (제목/미리보기/날짜) + 우측 첨부 아이콘 | 아바타 없는 변형 |
| Reminders.app 리마인더 | 체크박스 + (제목/노트/날짜) + 우측 플래그 | 트레일링 아이콘 = 만료 아이콘 |
| Finder 리스트 뷰 | 파일 아이콘 + 이름 + (수정일 / 크기 / 종류) | 비슷한 골격 |
→ 셋 다 List + 커스텀 row 입니다. 직접 ViewBuilder로 row를 만들고, 컨테이너만 List로 감싸요.
Table<Value>도 macOS에 있지만 이건 다중 컬럼 + 정렬 가능한 스프레드시트 류라서 (Finder의 컬럼 헤더 리스트 같은 거) Vault 같은 풍부한 단일 컬럼 row에는
과합니다. List가 맞는 선택.
There was a problem hiding this comment.
2. List로 옮겼을 때의 모습
호출부 (DVPresentation 같은 곳):
List(selection: $selectedVaultID) {
ForEach(vaults) { vault in
DVVaultRow(vault: vault)
.tag(vault.id)
.contextMenu { ... } // 한 줄
.draggable(vault) // 한 줄
}
}
.listStyle(.inset) // 또는 .plain
.tint(Color.dv(.vaultGreen)) // .sidebar/.inset일 때 선택색DVVaultRow는 현재 DVVaultContainer의 비주얼과 거의 동일 — 다만:
| 현재 (Button) | 변경 후 (List row) |
|---|---|
action: () -> Void 받음 |
제거. 선택은 List가 관리 |
isSelected: Bool 인자로 받음 |
부모가 selection을 알고 있으면 row가 그 값을 비교, 또는 .listRowBackground 사용 |
@State isHovered 직접 관리 |
List의 호버를 받거나, 필요하면 그대로 유지 |
Button(action:) { ... }.buttonStyle(.plain) 래핑 |
제거. 그냥 HStack { ... } |
| 키보드/포커스 직접 처리 안 됨 | List가 처리 |
→ 코드량은 오히려 줄어들고, 잃는 거 없이 ↑/↓ 키, Return, ⌘/⇧ 다중 선택, 포커스 링이 다 들어옵니다.
There was a problem hiding this comment.
.searchable(text:) 모디파이어
.searchable(text: $query, placement: .sidebar, prompt: "Search vaults")
There was a problem hiding this comment.
.searchable()은 검색 필드 위치를 시스템이 강제(주로 툴바). 시안의 '컬럼 헤더 안 인라인'을 못 살림.
There was a problem hiding this comment.
claude 의견은 이러하니 실제로 시스템 컴포넌트 살린 코드로 테스트해보고 figma랑 너무 다르면 폐기합시다
There was a problem hiding this comment.
DVCheckBox는 macOS에서 시스템 컴포넌트(Toggle(...).toggleStyle(.checkbox))가 강하게 권장되는 영역입니다.
.tint(vaultGreen)으로 브랜드 색은 유지되고, Space 키 토글·포커스 링·라벨 클릭·VoiceOver checkbox trait·Mixed 상태가 전부 무료로 따라옵니다.
잃는 건 corner radius 0.5pt와 크기 2pt 차이 정도. 디자인팀과 '시스템 룩 수용 가능?'만 합의되면 전환 권장.
시스템 전환 없이 현재 구조를 유지한다면 최소한:
- L42/L60 cornerRadius 5.5/5 불일치 수정
- API를 @binding isOn으로 (action 콜백은 macOS답지 않음)
- 라벨까지 받는 API로 확장 + 라벨 영역도 hit target
- VoiceOver 라벨/트레이트 명시
가 필요합니다.
There was a problem hiding this comment.
ButtonStyle 아키텍처는 정석이라 골격은 macOS-native. 가장 큰 문제는 Primary 스펙(pill·고정폭·40pt)이 iOS 모달 CTA에서 그대로 가져온 듯한 점. 다이얼로그/시트 컨텍스트에 들어가면 macOS Save/Cancel 컨벤션과 충돌하므로 디자인팀과 재검토 필요. 시스템 .borderedProminent로 갈지 커스텀 유지할지의 결정도 그 합의 위에서 내려야 함.
There was a problem hiding this comment.
- borderOverlay cornerRadius mismatch (L40 16 vs L82 12)
- 채움 16, stroke 12 → 4pt 어긋남. DVProjectContainer(6/8, 2pt)·DVCheckBox(5.5/5, 0.5pt)보다 가장 큰 차이.
- hover 시 보더가 채움보다 안쪽 4pt에 그어져 시각적으로 눈에 띔.
- 둘 다 16으로 통일.
- 고정 크기 108×72 (L38)
- 사이드바 폭이 사용자 조절 가능한 macOS 컨텍스트에서 부서짐.
- 사이드바를 좁히면 2×2가 1×4로 wrap되지도 않고 잘림.
- 그리드 컨테이너가 셀 크기를 결정하도록 maxWidth: .infinity 또는 GridItem(.flexible()) 검토.
- iconCircle이 placeholder (L63-67)
- 회색/흰색 빈 원. 시안에서도 동일. 디자인 미완성 or API 누락.
- 각 카테고리에 의미 있는 SF Symbol이 들어가야 자연스러움 (tray/star.fill/clock/trash).
- API에 icon: Image 또는 systemName: String? 추가.
- 호버가 stroke 방식 (L82)
- 다른 사이드바 컴포넌트와 같은 이슈. macOS 사이드바 호버는 옅은 배경 fill이 표준.
- 다만 이미 미선택 상태가 gray100 fill이라 호버 시 gray200으로 살짝 진해지는 방식이 자연스러움.
- press 피드백 없음
- .buttonStyle(.plain)이라 mousedown 색 변화 없음. ButtonStyle 만들어 configuration.isPressed 처리.
- 호버/선택 전환 무애니메이션
- .animation(.easeOut(duration: 0.12), value: isHovered) + value: isSelected.
doyeonk429
left a comment
There was a problem hiding this comment.
@yeseonglee 구현하느라 고생했고 디자인 자체가 애매한 부분이 약간 있어서 그 부분은 별도로 같이 회의하면서 정리하면 더 좋을 것 같아.
my claude의 후기
전반적으로 SwiftUI 사용은 깔끔하지만, 시안 차원에서 iOS 패턴이 macOS 컴포넌트로 그대로 옮겨진 부분들이 코드에 반영돼 있습니다. 컴포넌트 구현 자체의 즉시 수정 항목(cornerRadius 불일치 3건, 고정 width)은 이번 PR에서 정리하고, 레이아웃·디자인 패턴 차원의 부채(2×2 카테고리 그리드, FAB, 가로 라디오, Primary pill)는 디자인팀과 별도 트랙으로 정리하는 것을 권장합니다. 또한 DVProjectContainer/DVVaultContainer는 시스템 List(selection:)로 흡수 가능한지, DVCheckBox는 Toggle.toggleStyle(.checkbox)로 갈 수 있는지가 가장 큰 결정 포인트입니다.
There was a problem hiding this comment.
♻️ Duplicate comments (1)
Projects/DVDesign/Sources/Components/DVTitleBar.swift (1)
54-61:⚠️ Potential issue | 🟠 Major | ⚡ Quick win정렬 아이콘 버튼의 접근성 라벨/탭 영역을 보강해주세요.
아이콘-only
.plain버튼은 의미 전달이 약하고 클릭 영역도 작습니다. 최소 히트 타깃과 접근성 라벨을 함께 추가하는 편이 안전합니다.개선 예시
private var sortButton: some View { Button(action: onSortTapped) { Image(systemName: "arrow.up.arrow.down") .dvFont(.bodyXL) .foregroundStyle(Color.dv(.gray800)) + .frame(width: 44, height: 44) + .contentShape(Rectangle()) } .buttonStyle(.plain) + .accessibilityLabel("정렬") + .accessibilityHint("정렬 옵션을 엽니다") }🤖 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 `@Projects/DVDesign/Sources/Components/DVTitleBar.swift` around lines 54 - 61, The sortButton (in DVTitleBar.swift) is an icon-only .plain Button with a small tap area and no accessibility label; update the Button that calls onSortTapped to provide an explicit accessibility label (e.g., .accessibilityLabel("Sort")) and increase the hit target (wrap the Image with additional tappable padding or apply a .contentShape(Rectangle()) plus padding) so the control meets minimum touch size; keep the visual appearance by retaining .buttonStyle(.plain) after applying padding and ensure VoiceOver reads the label (optionally add .accessibilityHint if 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.
Duplicate comments:
In `@Projects/DVDesign/Sources/Components/DVTitleBar.swift`:
- Around line 54-61: The sortButton (in DVTitleBar.swift) is an icon-only .plain
Button with a small tap area and no accessibility label; update the Button that
calls onSortTapped to provide an explicit accessibility label (e.g.,
.accessibilityLabel("Sort")) and increase the hit target (wrap the Image with
additional tappable padding or apply a .contentShape(Rectangle()) plus padding)
so the control meets minimum touch size; keep the visual appearance by retaining
.buttonStyle(.plain) after applying padding and ensure VoiceOver reads the label
(optionally add .accessibilityHint if needed).
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: b36d05aa-f73c-42ac-8744-62aedb65070a
📒 Files selected for processing (7)
Projects/DVDesign/SampleApp/Sources/DVCategoryPreviewView.swiftProjects/DVDesign/Sources/Components/DVCategory.swiftProjects/DVDesign/Sources/Components/DVCheckBox.swiftProjects/DVDesign/Sources/Components/DVProjectContainer.swiftProjects/DVDesign/Sources/Components/DVSecretType.swiftProjects/DVDesign/Sources/Components/DVTitleBar.swiftProjects/DVDesign/Sources/Components/DVVaultContainer.swift
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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 `@Projects/DVDesign/Sources/Components/DVProjectContainer.swift`:
- Around line 97-107: The rightMouseDown implementation overwrites menuObserver
without removing any previously-registered observer and lacks cleanup in deinit,
causing lost tokens and duplicate callbacks; before calling
NotificationCenter.default.addObserver in rightMouseDown (in DVProjectContainer
and DVVaultContainer) remove the existing observer token if non-nil (using
NotificationCenter.default.removeObserver(menuObserver!) or the Swift API
matching your stored type) then assign the new token, and implement deinit to
call resetAndRemoveObserver() or explicitly remove menuObserver and nil it so
any remaining observer is unregistered when the object is deallocated; ensure
resetAndRemoveObserver clears the token after removal to avoid repeated removal
attempts.
🪄 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 Plus
Run ID: aa2c5b01-bc18-48f7-af85-93f37248bbe5
📒 Files selected for processing (7)
Projects/DVDesign/SampleApp/Sources/ContentView.swiftProjects/DVDesign/SampleApp/Sources/DVProjectContainerPreviewView.swiftProjects/DVDesign/SampleApp/Sources/DVStepIndicatorPreviewView.swiftProjects/DVDesign/SampleApp/Sources/DVVaultContainerPreviewView.swiftProjects/DVDesign/Sources/Components/DVProjectContainer.swiftProjects/DVDesign/Sources/Components/DVStepIndicator.swiftProjects/DVDesign/Sources/Components/DVVaultContainer.swift
There was a problem hiding this comment.
🧹 Nitpick comments (3)
Projects/DVDesign/SampleApp/Sources/DVProjectContainerPreviewView.swift (1)
3-4: 💤 Low value시스템 프레임워크와 프로젝트 모듈 사이에 빈 줄을 추가하세요.
내장 프레임워크(SwiftUI)와 프로젝트 모듈(DVDesign) 임포트 사이에 빈 줄을 넣으면 가독성이 향상됩니다.
♻️ 제안 수정안
import SwiftUI + import DVDesignAs per coding guidelines: 모듈 임포트가 알파벳 순으로 정렬되어 있는지 확인하세요. (내장 프레임워크 먼저, 빈 줄로 서드파티 구분)
🤖 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 `@Projects/DVDesign/SampleApp/Sources/DVProjectContainerPreviewView.swift` around lines 3 - 4, 파일 상단의 import 정렬과 포맷을 개선하세요: 내장 프레임워크(import SwiftUI)를 프로젝트 모듈(import DVDesign)보다 먼저 유지하되 두 그룹 사이에 빈 줄을 추가해 시각적으로 구분하고, 모듈들이 알파벳 순서로 정렬되어 있는지 확인하세요 (참조 심볼: import SwiftUI, import DVDesign).Projects/DVDesign/SampleApp/Sources/DVVaultContainerPreviewView.swift (2)
3-4: 💤 Low value시스템 프레임워크와 프로젝트 모듈 사이에 빈 줄을 추가하세요.
내장 프레임워크(SwiftUI)와 프로젝트 모듈(DVDesign) 임포트 사이에 빈 줄을 넣으면 가독성이 향상됩니다.
♻️ 제안 수정안
import SwiftUI + import DVDesignAs per coding guidelines: 모듈 임포트가 알파벳 순으로 정렬되어 있는지 확인하세요. (내장 프레임워크 먼저, 빈 줄로 서드파티 구분)
🤖 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 `@Projects/DVDesign/SampleApp/Sources/DVVaultContainerPreviewView.swift` around lines 3 - 4, 현재 파일의 import 목록에서 시스템 프레임워크(import SwiftUI)와 프로젝트 모듈(import DVDesign) 사이에 빈 줄이 없으니, import SwiftUI를 먼저 두고 그 다음 줄에 빈 줄을 추가한 후 import DVDesign를 배치하여 시스템 프레임워크와 프로젝트/서드파티 모듈을 분리하고, 전체 import 블록이 알파벳 순으로 정렬되어 있는지 확인하세요 (참조 심볼: import SwiftUI, import DVDesign).
10-19: 💤 Low value튜플 대신 명시적 struct 사용을 고려하세요.
3개 필드를 가진 튜플은 가독성 경계선상에 있습니다. 프리뷰 데이터지만
VaultPreviewItem같은 struct로 추출하면 코드가 더 명확해집니다.♻️ 제안 수정안
+ private struct VaultPreviewItem { + let name: String + let date: String + let trailingIcon: DVVaultContainer.TrailingIcon? + } + - private let vaults: [(String, String, DVVaultContainer.TrailingIcon?)] = [ - ("내가 설정한 이름", "2026.04.01", nil), + private let vaults: [VaultPreviewItem] = [ + VaultPreviewItem(name: "내가 설정한 이름", date: "2026.04.01", trailingIcon: nil), ... ]ForEach 부분도 수정:
DVVaultContainer( - name: vaults[index].0, - date: vaults[index].1, - trailingIcon: vaults[index].2, + name: vaults[index].name, + date: vaults[index].date, + trailingIcon: vaults[index].trailingIcon, isSelected: selectedIndex == index )As per coding guidelines: 튜플 반환 시 필드가 3개를 초과하면 struct 사용을 제안하세요.
🤖 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 `@Projects/DVDesign/SampleApp/Sources/DVVaultContainerPreviewView.swift` around lines 10 - 19, Extract the tuple type into a clear struct (e.g., VaultPreviewItem) with fields title: String, date: String, trailingIcon: DVVaultContainer.TrailingIcon?; replace the private let vaults: [(String, String, DVVaultContainer.TrailingIcon?)] declaration with a [VaultPreviewItem] literal; make VaultPreviewItem conform to Identifiable (or add an id: UUID) so the ForEach that currently iterates over vaults can use each item's id and named properties (title, date, trailingIcon) instead of tuple indexes; update any usages in DVVaultContainerPreviewView and the ForEach block to reference the struct properties.
🤖 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.
Nitpick comments:
In `@Projects/DVDesign/SampleApp/Sources/DVProjectContainerPreviewView.swift`:
- Around line 3-4: 파일 상단의 import 정렬과 포맷을 개선하세요: 내장 프레임워크(import SwiftUI)를 프로젝트
모듈(import DVDesign)보다 먼저 유지하되 두 그룹 사이에 빈 줄을 추가해 시각적으로 구분하고, 모듈들이 알파벳 순서로 정렬되어
있는지 확인하세요 (참조 심볼: import SwiftUI, import DVDesign).
In `@Projects/DVDesign/SampleApp/Sources/DVVaultContainerPreviewView.swift`:
- Around line 3-4: 현재 파일의 import 목록에서 시스템 프레임워크(import SwiftUI)와 프로젝트 모듈(import
DVDesign) 사이에 빈 줄이 없으니, import SwiftUI를 먼저 두고 그 다음 줄에 빈 줄을 추가한 후 import
DVDesign를 배치하여 시스템 프레임워크와 프로젝트/서드파티 모듈을 분리하고, 전체 import 블록이 알파벳 순으로 정렬되어 있는지
확인하세요 (참조 심볼: import SwiftUI, import DVDesign).
- Around line 10-19: Extract the tuple type into a clear struct (e.g.,
VaultPreviewItem) with fields title: String, date: String, trailingIcon:
DVVaultContainer.TrailingIcon?; replace the private let vaults: [(String,
String, DVVaultContainer.TrailingIcon?)] declaration with a [VaultPreviewItem]
literal; make VaultPreviewItem conform to Identifiable (or add an id: UUID) so
the ForEach that currently iterates over vaults can use each item's id and named
properties (title, date, trailingIcon) instead of tuple indexes; update any
usages in DVVaultContainerPreviewView and the ForEach block to reference the
struct properties.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 9d3622c2-a3dd-4014-b088-62d7fcaf38b8
📒 Files selected for processing (5)
Projects/DVDesign/SampleApp/Sources/ContentView.swiftProjects/DVDesign/SampleApp/Sources/DVProjectContainerPreviewView.swiftProjects/DVDesign/SampleApp/Sources/DVVaultContainerPreviewView.swiftProjects/DVDesign/Sources/Components/DVProjectContainer.swiftProjects/DVDesign/Sources/Components/DVVaultContainer.swift
# Conflicts: # Projects/DVDesign/SampleApp/Sources/ContentView.swift # Projects/DVDesign/SampleApp/Sources/DVVaultContainerPreviewView.swift
- 우클릭 감지 NSViewRepresentable / hover stroke / border overlay 제거 - 가변 폭 지원: width 고정 제거, minWidth 200 / Spacer(minLength: 8) / 이름·날짜 줄임표 - isSelected 파라미터 추가 - 선택 시 trailing icon 색을 dv.white로 전환 - 프리뷰를 HSplitView 2-column(list + detail) 구조로 재구성 - 컬럼 단위 controlBackgroundColor + scrollContentBackground(.hidden)으로 배경 정상화
- 우클릭 감지 NSViewRepresentable / hover stroke / border overlay 제거 - 가변 폭 지원: width 고정 제거, minWidth 120 / Spacer(minLength: 8) / 이름 줄임표 - 프리뷰를 HSplitView 2-column(list + detail) 구조로 재구성 - controlBackgroundColor + scrollContentBackground(.hidden)로 배경 정상화 - SampleApp 컴포넌트 owner 교체
17a59d1 to
43b5eae
Compare
✨ What's this PR?
📌 관련 이슈 (Related Issue)
🧶 주요 변경 내용 (Summary)
신규 컴포넌트 8개 추가 (
DVDesign/Sources/Components/)DVCategoryDVProjectContainerDVVaultContainerDVButtonDVCheckBoxDVTitleBarDVSecretTypeDVPageControlSampleApp 프리뷰 연동
ContentView라우팅 업데이트구현 상세
// MARK: -섹션 구분,body단일 컨테이너, 하위뷰는extension private var/func분리.contentShape(Rectangle())label 내부 적용 → 빈 영역 클릭 이슈 해결DVVaultContainer.TrailingIconenum으로 만료 상태 타입 안전하게 관리 (.expiringSoon/.expired)DVButton.Styleenum에cornerRadius/height/width/horizontalPadding/font토큰화DVTitleBar검색 필드.regularMaterial배경 적용 (Liquid Glass 대비)📸 스크린샷 (Optional)
🧪 테스트 / 검증 내역
💬 기타 공유 사항
.accessibilityLabel,.accessibilityAddTraits) 미적용 — 후속 PR 예정DVPageControl은 온보딩 전용으로 현재DVDesign에 위치, 추후DVPresentation이동 검토 필요🙇🏻♀️ 리뷰 가이드
DVButton.Styleenum의 토큰 구조 (cornerRadius / height / width / font 일괄 관리 방식)DVVaultContainer.TrailingIconenum 설계.contentShape위치를 Button label 내부에 적용한 이유Summary by CodeRabbit
새 기능
변경