feat: [iOS] Textarea 업데이트#516
Conversation
- size(large/medium) 속성 추가 및 사이즈별 토큰 적용 - focus ring 추가, 상태별 테두리 색상 정비(negative는 빨강 ring) - resize 높이 정책을 줄 수 기준(최소 2줄, limit 6줄)으로 정밀화, 입력 폰트 사이즈별(body2/label1) 적용 - character count UI 제거 → maxLength(입력 제한·축소 시 기존 텍스트 잘림) + onTextChange API로 대체 - bottom resource 재설계: button/iconButton/icon/contentBadge/segmentedControl/primaryIconButton/slot, 사이즈별 크기·leading 제약·가변 높이 - primaryIconButton을 Button(solid/primary/iconOnly)로 구성 - bottom 전용 아이콘 SegmentedControl 신설(슬라이드 선택 애니메이션) - maxLength 초과 붙여넣기 시 캐럿을 삽입 끝으로 이동 - TextArea heading/requiredBadge/description API 제거 - TextField heading/requiredBadge 및 Status의 description API 제거 - TextArea/TextField Blueprint 프리뷰 룩앤필 정리 및 DocC 문서 재생성 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Hm3CNGafWMFkTDmZ8gWuoT
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
Summary by CodeRabbit
WalkthroughTextArea와 TextField의 공개 API, 렌더링, 프리뷰, 문서, MCP 스키마가 새 size/resize/maxLength/onTextChange 및 단순화된 status 모델에 맞게 갱신됐다. Changes입력 컴포넌트 재구성
Estimated code review effort: 4 (Complex) | ~60 minutes 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 13
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/montage-mcp/data/tokens.json (1)
357-469: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win실제 토큰 식별자가 MCP 데이터에서 빠졌습니다.
이제 공개 API는
Spacing/Opacity의 enum case가 아니라CGFloat.spacing16,CGFloat.opacity52,CGFloat.radius12,CGFloat.primitive24같은 정적 프로퍼티인데, 여기서는 각 네임스페이스의allValues/max/min만 직렬화하고 있습니다. 그 결과 MCP 소비자는 새 토큰 이름을 검색·자동완성·매핑할 수 없고, 기존 enum case 제거 이후에는 개별 토큰을 발견할 경로가 사라집니다.CGFloat확장의 실제 토큰 프로퍼티들도 별도 섹션이나 identifiers로 함께 노출해야 합니다.🤖 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 `@packages/montage-mcp/data/tokens.json` around lines 357 - 469, The MCP token metadata is only serializing aggregate enum-style entries for Spacing, Opacity, Radius, and Primitive, so the real public token identifiers exposed by the CGFloat extension are missing. Update the token JSON generation in the relevant token sections to include the actual CGFloat static properties (for example the spacing/opacity/radius/primitive token names) alongside or instead of the generic allValues/max/min identifiers, so MCP consumers can search, autocomplete, and map the real token symbols. Use the existing Spacing, Opacity, Radius, Dimension, Primitive, and Shadow entries as the place to attach the missing identifiers.
🤖 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 `@documentation/utilities/ios-utility-components/opacity.md`:
- Around line 14-23: The SwiftUI example uses a CGFloat opacity token directly,
which does not match the Double-based API expected by myView.opacity; update the
example in the opacity documentation so the token is wrapped/conveyed as a
Double-compatible value while keeping the CGFloat examples for alphaComponent
and CGFloat variables unchanged. Use the opacity.md SwiftUI snippet as the place
to adjust the myView.opacity usage and ensure the example reflects the correct
type for SwiftUI.
In `@documentation/utilities/ios-utility-components/typography.md`:
- Around line 223-226: The `uiTextStyle` description is incomplete and missing
the referenced source, so update the documentation text near `uiTextStyle` to
explicitly mention `textStyle` and complete the sentence. Keep the same meaning
as the existing `textStyle` explanation, but rewrite the `uiTextStyle` line so
the relationship is clear and the sentence reads naturally.
In `@packages/montage-mcp/data/components.json`:
- Around line 1033-1036: The `interactionColor(_:)` method summary in
`components.json` has a truncated default value, so update the `summary` for the
`interactionColor(Color.Semantic) -> IconButton` entry to match the documented
default from the `IconButton` docs. Use the existing `interactionColor(_:)`
method record as the target and replace the incomplete `(기본값: )` text with the
correct default value so MCP consumers see the full description.
In `@Sources/Blueprint/Sources/Scene/Previews/TextAreaPreview.swift`:
- Around line 162-165: `TextAreaPreview`에서 `leadingResources`에 `suffix(3)`를 직접
대입하는 부분이 타입 불일치로 컴파일되지 않습니다. `leadingResources`와 `trailingResources`가
`[TextArea.Resource]`이므로, `suffix(3)`의 `ArraySlice<TextArea.Resource>` 결과를
`Array(...)`로 감싸도록 `TextAreaPreview`의 리소스 수집 로직을 수정하세요.
In `@Sources/Blueprint/Sources/Scene/Previews/TextFieldPreview.swift`:
- Around line 254-256: The auto-complete toggle in TextFieldPreview only updates
usingSuggestions, so the existing suggestion list can remain visible after
turning it off. Update the toggle handling in the
HStack/toggleRow("autoComplete", $usingSuggestions) flow so that when
usingSuggestions becomes false, autoCompletionDataSource is immediately reset to
nil as well. Use the existing TextFieldPreview state properties to keep the
preview synchronized right away.
In `@Sources/Montage/1` Components/3 Selection And Input/TextArea.swift:
- Around line 37-45: DocC in TextArea.Size is describing fixed minimum heights
that do not match the actual implementation in resolvedMinHeight, so update the
enum documentation to reflect the real height behavior instead of implying 48/44
constants. Adjust the comments on Size.large and Size.medium to align with the
2-line-based min-height calculation used by TextArea so consumers understand the
true contract.
- Around line 645-657: The icon segment in TextArea.segment is only tappable via
onTapGesture, so VoiceOver does not expose it as an interactive control with
name or selected state. Refactor the segment view to use a Button-based control
for each item, and add appropriate accessibilityLabel plus
selected/accessibilityValue or hint so the segmented control in TextArea is
announced and operable by VoiceOver.
- Around line 335-353: The fixed resize path in TextArea’s resolvedMinHeight and
resolvedMaxHeight should normalize the `.fixed(min:max)` bounds before they are
used. Update the `resize` handling so that the `.fixed` case always returns
ordered min/max values from the `TextArea` layout helpers, preventing an invalid
`frame(minHeight:maxHeight:)` when the inputs are reversed. Ensure the
normalization is applied consistently wherever `resolvedMinHeight` and
`resolvedMaxHeight` are derived from `resize`.
In `@Sources/Montage/1` Components/3 Selection And Input/TextField.swift:
- Around line 564-566: TextField의 fieldTextColor가 항상 .semantic(.labelNormal)로
고정되어 있어 disable 상태에서도 본문 텍스트가 활성 색상으로 보입니다. fieldTextColor 로직을 수정해 TextArea와 같은
방식으로 disable 여부를 반영하도록 하고, 비활성일 때는 disabled 톤의 색상을 반환하도록 TextField의 색상 계산을
조정하세요.
- Around line 424-440: `backgroundColor(_:)`가 저장하는 `customBackgroundColor`가
`fieldBackground`에서 전혀 사용되지 않아 공개 API가 무시됩니다. `TextField`의 `fieldBackground`에서
`customBackgroundColor`를 우선 적용하도록 분기하고, 값이 없을 때만 기존 `disable`/`colorScheme` 기반
배경 로직을 사용하도록 수정하세요. `backgroundColor(_:)`, `customBackgroundColor`,
`fieldBackground`를 함께 점검해 리팩터링 전 동작을 복원하면 됩니다.
In `@Sources/Montage/1` Components/5 Loading/Skeleton.swift:
- Around line 221-226: `Skeleton`의 `GeometryReader` 안에서 자동 `lineNumber`를 계산할 때
`round(proxy.size.height / scaledLineHeight)` 때문에 애매한 높이에서 한 줄을 과하게 그려 하단이 잘릴 수
있습니다. `effectiveLineCount` 계산식을 수정해 실제로 들어갈 수 있는 줄 수만 반영하도록 내림 기반 계산으로 바꾸고,
`kind.lineNumber`가 0일 때만 이 자동 계산이 적용되도록 유지하세요.
In `@Sources/Montage/1` Components/6 Navigations/Category.swift:
- Line 124: `Category`의 우측 액션 버튼에서 `iconSize`를 그대로
`IconButton.NormalSize.custom(size:)`에 넘기고 있어 새 계약(컨테이너 한 변 길이)과 맞지 않습니다.
`Category` 내 해당 `IconButton(variant: .normal(size: ...), icon: icon)` 호출을 찾아
`iconSize`(20/22/24)를 직접 전달하지 말고, `NormalSize`의 기존 케이스로 매핑하거나 목표 아이콘 크기에 맞는 컨테이너
크기로 변환해서 넘기도록 수정하세요.
In `@Sources/Montage/1` Components/6 Navigations/Tab.swift:
- Line 175: `Tab`의 아이콘 크기 값을 `IconButton.NormalSize.custom(size:)`에 그대로 넘기면 의미가
바뀌어 탭 버튼이 작게 렌더링됩니다. `Tab`의 `size`에서 계산한 `iconSize`를 직접 쓰지 말고, `Tab.Size`를
`IconButton.NormalSize`로 매핑하는 전용 변환 로직을 추가해 컨테이너 크기 기준으로 전달되도록 수정하세요.
`IconButton(variant:icon:)` 호출부와 `iconButtonSize` 같은 보조 프로퍼티/메서드를 찾아 이 매핑을 적용하면
됩니다.
---
Outside diff comments:
In `@packages/montage-mcp/data/tokens.json`:
- Around line 357-469: The MCP token metadata is only serializing aggregate
enum-style entries for Spacing, Opacity, Radius, and Primitive, so the real
public token identifiers exposed by the CGFloat extension are missing. Update
the token JSON generation in the relevant token sections to include the actual
CGFloat static properties (for example the spacing/opacity/radius/primitive
token names) alongside or instead of the generic allValues/max/min identifiers,
so MCP consumers can search, autocomplete, and map the real token symbols. Use
the existing Spacing, Opacity, Radius, Dimension, Primitive, and Shadow entries
as the place to attach the missing identifiers.
🪄 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: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: debc207d-b199-4403-997d-e263cf6e8acf
📒 Files selected for processing (62)
.gitignoreSources/Blueprint/Blueprint.xcodeproj/project.pbxprojSources/Blueprint/Sources/Scene/ComponentList/ComponentListView.swiftSources/Blueprint/Sources/Scene/Previews/ActionAreaPreview.swiftSources/Blueprint/Sources/Scene/Previews/ButtonPreview.swiftSources/Blueprint/Sources/Scene/Previews/IconButtonPreview.swiftSources/Blueprint/Sources/Scene/Previews/SkeletonPreview.swiftSources/Blueprint/Sources/Scene/Previews/TextAreaPreview.swiftSources/Blueprint/Sources/Scene/Previews/TextFieldPreview.swiftSources/Blueprint/Sources/Scene/Previews/TypographyPreview.swiftSources/Montage/1 Components/2 Actions/Button.swiftSources/Montage/1 Components/2 Actions/IconButton.swiftSources/Montage/1 Components/2 Actions/OutlinedUIButton.swiftSources/Montage/1 Components/2 Actions/SolidUIButton.swiftSources/Montage/1 Components/3 Selection And Input/Control.swiftSources/Montage/1 Components/3 Selection And Input/Select.swiftSources/Montage/1 Components/3 Selection And Input/TextArea.swiftSources/Montage/1 Components/3 Selection And Input/TextField.swiftSources/Montage/1 Components/4 Contents/Accordion.swiftSources/Montage/1 Components/4 Contents/Card.swiftSources/Montage/1 Components/4 Contents/ContentBadgeUIView.swiftSources/Montage/1 Components/4 Contents/Thumbnail.swiftSources/Montage/1 Components/5 Loading/Skeleton.swiftSources/Montage/1 Components/6 Navigations/Category.swiftSources/Montage/1 Components/6 Navigations/Tab.swiftSources/Montage/1 Components/7 Feedback/SnackBar.swiftSources/Montage/1 Components/8 Presentation/Popover.swiftSources/Montage/1 Components/9 Utilities/Color.swiftSources/Montage/1 Components/9 Utilities/Dimension.swiftSources/Montage/1 Components/9 Utilities/InteractionUIView.swiftSources/Montage/1 Components/9 Utilities/ModalNavigation.swiftSources/Montage/1 Components/9 Utilities/Opacity.swiftSources/Montage/1 Components/9 Utilities/Primitive.swiftSources/Montage/1 Components/9 Utilities/Radius.swiftSources/Montage/1 Components/9 Utilities/Spacing.swiftSources/Montage/1 Components/9 Utilities/Typography.swiftSources/Montage/2 Utilities/Extension/SwiftUI/SwiftUI+Debug.swiftSources/Montage/2 Utilities/Extension/UIKit/NSAttributedString+.swiftSources/Montage/Asset/Color.xcassets/Pink/pink100.colorset/Contents.jsonSources/Montage/Asset/Color.xcassets/Violet/violet0.colorset/Contents.jsonSources/Montage/Asset/Color.xcassets/Violet/violet100.colorset/Contents.jsondocumentation/components/actions/button/ios.mddocumentation/components/actions/iconbutton/ios.mddocumentation/components/contents/accordion/ios.mddocumentation/components/loading/skeleton/ios.mddocumentation/components/selection and input/textarea/ios.mddocumentation/components/selection and input/textfield/ios.mddocumentation/utilities/ios-extensions/corefoundation.mddocumentation/utilities/ios-extensions/swift.mddocumentation/utilities/ios-utility-components/color.mddocumentation/utilities/ios-utility-components/dimension.mddocumentation/utilities/ios-utility-components/modalnavigation.mddocumentation/utilities/ios-utility-components/opacity.mddocumentation/utilities/ios-utility-components/primitive.mddocumentation/utilities/ios-utility-components/radius.mddocumentation/utilities/ios-utility-components/spacing.mddocumentation/utilities/ios-utility-components/typography.mdpackages/montage-mcp/data/components.jsonpackages/montage-mcp/data/markdown/coding-guidelines.mdpackages/montage-mcp/data/markdown/color-usage.mdpackages/montage-mcp/data/tokens.jsonscripts/build_mcp_data.js
💤 Files with no reviewable changes (10)
- Sources/Montage/1 Components/2 Actions/SolidUIButton.swift
- Sources/Montage/1 Components/4 Contents/ContentBadgeUIView.swift
- documentation/utilities/ios-extensions/swift.md
- Sources/Montage/1 Components/9 Utilities/InteractionUIView.swift
- documentation/components/contents/accordion/ios.md
- Sources/Montage/1 Components/2 Actions/OutlinedUIButton.swift
- documentation/components/loading/skeleton/ios.md
- Sources/Blueprint/Sources/Scene/Previews/SkeletonPreview.swift
- documentation/utilities/ios-utility-components/modalnavigation.md
- Sources/Montage/1 Components/4 Contents/Accordion.swift
There was a problem hiding this comment.
Caution
Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.
Actionable comments posted: 13
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/montage-mcp/data/tokens.json (1)
357-469: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win실제 토큰 식별자가 MCP 데이터에서 빠졌습니다.
이제 공개 API는
Spacing/Opacity의 enum case가 아니라CGFloat.spacing16,CGFloat.opacity52,CGFloat.radius12,CGFloat.primitive24같은 정적 프로퍼티인데, 여기서는 각 네임스페이스의allValues/max/min만 직렬화하고 있습니다. 그 결과 MCP 소비자는 새 토큰 이름을 검색·자동완성·매핑할 수 없고, 기존 enum case 제거 이후에는 개별 토큰을 발견할 경로가 사라집니다.CGFloat확장의 실제 토큰 프로퍼티들도 별도 섹션이나 identifiers로 함께 노출해야 합니다.🤖 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 `@packages/montage-mcp/data/tokens.json` around lines 357 - 469, The MCP token metadata is only serializing aggregate enum-style entries for Spacing, Opacity, Radius, and Primitive, so the real public token identifiers exposed by the CGFloat extension are missing. Update the token JSON generation in the relevant token sections to include the actual CGFloat static properties (for example the spacing/opacity/radius/primitive token names) alongside or instead of the generic allValues/max/min identifiers, so MCP consumers can search, autocomplete, and map the real token symbols. Use the existing Spacing, Opacity, Radius, Dimension, Primitive, and Shadow entries as the place to attach the missing identifiers.
🤖 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 `@documentation/utilities/ios-utility-components/opacity.md`:
- Around line 14-23: The SwiftUI example uses a CGFloat opacity token directly,
which does not match the Double-based API expected by myView.opacity; update the
example in the opacity documentation so the token is wrapped/conveyed as a
Double-compatible value while keeping the CGFloat examples for alphaComponent
and CGFloat variables unchanged. Use the opacity.md SwiftUI snippet as the place
to adjust the myView.opacity usage and ensure the example reflects the correct
type for SwiftUI.
In `@documentation/utilities/ios-utility-components/typography.md`:
- Around line 223-226: The `uiTextStyle` description is incomplete and missing
the referenced source, so update the documentation text near `uiTextStyle` to
explicitly mention `textStyle` and complete the sentence. Keep the same meaning
as the existing `textStyle` explanation, but rewrite the `uiTextStyle` line so
the relationship is clear and the sentence reads naturally.
In `@packages/montage-mcp/data/components.json`:
- Around line 1033-1036: The `interactionColor(_:)` method summary in
`components.json` has a truncated default value, so update the `summary` for the
`interactionColor(Color.Semantic) -> IconButton` entry to match the documented
default from the `IconButton` docs. Use the existing `interactionColor(_:)`
method record as the target and replace the incomplete `(기본값: )` text with the
correct default value so MCP consumers see the full description.
In `@Sources/Blueprint/Sources/Scene/Previews/TextAreaPreview.swift`:
- Around line 162-165: `TextAreaPreview`에서 `leadingResources`에 `suffix(3)`를 직접
대입하는 부분이 타입 불일치로 컴파일되지 않습니다. `leadingResources`와 `trailingResources`가
`[TextArea.Resource]`이므로, `suffix(3)`의 `ArraySlice<TextArea.Resource>` 결과를
`Array(...)`로 감싸도록 `TextAreaPreview`의 리소스 수집 로직을 수정하세요.
In `@Sources/Blueprint/Sources/Scene/Previews/TextFieldPreview.swift`:
- Around line 254-256: The auto-complete toggle in TextFieldPreview only updates
usingSuggestions, so the existing suggestion list can remain visible after
turning it off. Update the toggle handling in the
HStack/toggleRow("autoComplete", $usingSuggestions) flow so that when
usingSuggestions becomes false, autoCompletionDataSource is immediately reset to
nil as well. Use the existing TextFieldPreview state properties to keep the
preview synchronized right away.
In `@Sources/Montage/1` Components/3 Selection And Input/TextArea.swift:
- Around line 37-45: DocC in TextArea.Size is describing fixed minimum heights
that do not match the actual implementation in resolvedMinHeight, so update the
enum documentation to reflect the real height behavior instead of implying 48/44
constants. Adjust the comments on Size.large and Size.medium to align with the
2-line-based min-height calculation used by TextArea so consumers understand the
true contract.
- Around line 645-657: The icon segment in TextArea.segment is only tappable via
onTapGesture, so VoiceOver does not expose it as an interactive control with
name or selected state. Refactor the segment view to use a Button-based control
for each item, and add appropriate accessibilityLabel plus
selected/accessibilityValue or hint so the segmented control in TextArea is
announced and operable by VoiceOver.
- Around line 335-353: The fixed resize path in TextArea’s resolvedMinHeight and
resolvedMaxHeight should normalize the `.fixed(min:max)` bounds before they are
used. Update the `resize` handling so that the `.fixed` case always returns
ordered min/max values from the `TextArea` layout helpers, preventing an invalid
`frame(minHeight:maxHeight:)` when the inputs are reversed. Ensure the
normalization is applied consistently wherever `resolvedMinHeight` and
`resolvedMaxHeight` are derived from `resize`.
In `@Sources/Montage/1` Components/3 Selection And Input/TextField.swift:
- Around line 564-566: TextField의 fieldTextColor가 항상 .semantic(.labelNormal)로
고정되어 있어 disable 상태에서도 본문 텍스트가 활성 색상으로 보입니다. fieldTextColor 로직을 수정해 TextArea와 같은
방식으로 disable 여부를 반영하도록 하고, 비활성일 때는 disabled 톤의 색상을 반환하도록 TextField의 색상 계산을
조정하세요.
- Around line 424-440: `backgroundColor(_:)`가 저장하는 `customBackgroundColor`가
`fieldBackground`에서 전혀 사용되지 않아 공개 API가 무시됩니다. `TextField`의 `fieldBackground`에서
`customBackgroundColor`를 우선 적용하도록 분기하고, 값이 없을 때만 기존 `disable`/`colorScheme` 기반
배경 로직을 사용하도록 수정하세요. `backgroundColor(_:)`, `customBackgroundColor`,
`fieldBackground`를 함께 점검해 리팩터링 전 동작을 복원하면 됩니다.
In `@Sources/Montage/1` Components/5 Loading/Skeleton.swift:
- Around line 221-226: `Skeleton`의 `GeometryReader` 안에서 자동 `lineNumber`를 계산할 때
`round(proxy.size.height / scaledLineHeight)` 때문에 애매한 높이에서 한 줄을 과하게 그려 하단이 잘릴 수
있습니다. `effectiveLineCount` 계산식을 수정해 실제로 들어갈 수 있는 줄 수만 반영하도록 내림 기반 계산으로 바꾸고,
`kind.lineNumber`가 0일 때만 이 자동 계산이 적용되도록 유지하세요.
In `@Sources/Montage/1` Components/6 Navigations/Category.swift:
- Line 124: `Category`의 우측 액션 버튼에서 `iconSize`를 그대로
`IconButton.NormalSize.custom(size:)`에 넘기고 있어 새 계약(컨테이너 한 변 길이)과 맞지 않습니다.
`Category` 내 해당 `IconButton(variant: .normal(size: ...), icon: icon)` 호출을 찾아
`iconSize`(20/22/24)를 직접 전달하지 말고, `NormalSize`의 기존 케이스로 매핑하거나 목표 아이콘 크기에 맞는 컨테이너
크기로 변환해서 넘기도록 수정하세요.
In `@Sources/Montage/1` Components/6 Navigations/Tab.swift:
- Line 175: `Tab`의 아이콘 크기 값을 `IconButton.NormalSize.custom(size:)`에 그대로 넘기면 의미가
바뀌어 탭 버튼이 작게 렌더링됩니다. `Tab`의 `size`에서 계산한 `iconSize`를 직접 쓰지 말고, `Tab.Size`를
`IconButton.NormalSize`로 매핑하는 전용 변환 로직을 추가해 컨테이너 크기 기준으로 전달되도록 수정하세요.
`IconButton(variant:icon:)` 호출부와 `iconButtonSize` 같은 보조 프로퍼티/메서드를 찾아 이 매핑을 적용하면
됩니다.
---
Outside diff comments:
In `@packages/montage-mcp/data/tokens.json`:
- Around line 357-469: The MCP token metadata is only serializing aggregate
enum-style entries for Spacing, Opacity, Radius, and Primitive, so the real
public token identifiers exposed by the CGFloat extension are missing. Update
the token JSON generation in the relevant token sections to include the actual
CGFloat static properties (for example the spacing/opacity/radius/primitive
token names) alongside or instead of the generic allValues/max/min identifiers,
so MCP consumers can search, autocomplete, and map the real token symbols. Use
the existing Spacing, Opacity, Radius, Dimension, Primitive, and Shadow entries
as the place to attach the missing identifiers.
🪄 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: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: debc207d-b199-4403-997d-e263cf6e8acf
📒 Files selected for processing (62)
.gitignoreSources/Blueprint/Blueprint.xcodeproj/project.pbxprojSources/Blueprint/Sources/Scene/ComponentList/ComponentListView.swiftSources/Blueprint/Sources/Scene/Previews/ActionAreaPreview.swiftSources/Blueprint/Sources/Scene/Previews/ButtonPreview.swiftSources/Blueprint/Sources/Scene/Previews/IconButtonPreview.swiftSources/Blueprint/Sources/Scene/Previews/SkeletonPreview.swiftSources/Blueprint/Sources/Scene/Previews/TextAreaPreview.swiftSources/Blueprint/Sources/Scene/Previews/TextFieldPreview.swiftSources/Blueprint/Sources/Scene/Previews/TypographyPreview.swiftSources/Montage/1 Components/2 Actions/Button.swiftSources/Montage/1 Components/2 Actions/IconButton.swiftSources/Montage/1 Components/2 Actions/OutlinedUIButton.swiftSources/Montage/1 Components/2 Actions/SolidUIButton.swiftSources/Montage/1 Components/3 Selection And Input/Control.swiftSources/Montage/1 Components/3 Selection And Input/Select.swiftSources/Montage/1 Components/3 Selection And Input/TextArea.swiftSources/Montage/1 Components/3 Selection And Input/TextField.swiftSources/Montage/1 Components/4 Contents/Accordion.swiftSources/Montage/1 Components/4 Contents/Card.swiftSources/Montage/1 Components/4 Contents/ContentBadgeUIView.swiftSources/Montage/1 Components/4 Contents/Thumbnail.swiftSources/Montage/1 Components/5 Loading/Skeleton.swiftSources/Montage/1 Components/6 Navigations/Category.swiftSources/Montage/1 Components/6 Navigations/Tab.swiftSources/Montage/1 Components/7 Feedback/SnackBar.swiftSources/Montage/1 Components/8 Presentation/Popover.swiftSources/Montage/1 Components/9 Utilities/Color.swiftSources/Montage/1 Components/9 Utilities/Dimension.swiftSources/Montage/1 Components/9 Utilities/InteractionUIView.swiftSources/Montage/1 Components/9 Utilities/ModalNavigation.swiftSources/Montage/1 Components/9 Utilities/Opacity.swiftSources/Montage/1 Components/9 Utilities/Primitive.swiftSources/Montage/1 Components/9 Utilities/Radius.swiftSources/Montage/1 Components/9 Utilities/Spacing.swiftSources/Montage/1 Components/9 Utilities/Typography.swiftSources/Montage/2 Utilities/Extension/SwiftUI/SwiftUI+Debug.swiftSources/Montage/2 Utilities/Extension/UIKit/NSAttributedString+.swiftSources/Montage/Asset/Color.xcassets/Pink/pink100.colorset/Contents.jsonSources/Montage/Asset/Color.xcassets/Violet/violet0.colorset/Contents.jsonSources/Montage/Asset/Color.xcassets/Violet/violet100.colorset/Contents.jsondocumentation/components/actions/button/ios.mddocumentation/components/actions/iconbutton/ios.mddocumentation/components/contents/accordion/ios.mddocumentation/components/loading/skeleton/ios.mddocumentation/components/selection and input/textarea/ios.mddocumentation/components/selection and input/textfield/ios.mddocumentation/utilities/ios-extensions/corefoundation.mddocumentation/utilities/ios-extensions/swift.mddocumentation/utilities/ios-utility-components/color.mddocumentation/utilities/ios-utility-components/dimension.mddocumentation/utilities/ios-utility-components/modalnavigation.mddocumentation/utilities/ios-utility-components/opacity.mddocumentation/utilities/ios-utility-components/primitive.mddocumentation/utilities/ios-utility-components/radius.mddocumentation/utilities/ios-utility-components/spacing.mddocumentation/utilities/ios-utility-components/typography.mdpackages/montage-mcp/data/components.jsonpackages/montage-mcp/data/markdown/coding-guidelines.mdpackages/montage-mcp/data/markdown/color-usage.mdpackages/montage-mcp/data/tokens.jsonscripts/build_mcp_data.js
💤 Files with no reviewable changes (10)
- Sources/Montage/1 Components/2 Actions/SolidUIButton.swift
- Sources/Montage/1 Components/4 Contents/ContentBadgeUIView.swift
- documentation/utilities/ios-extensions/swift.md
- Sources/Montage/1 Components/9 Utilities/InteractionUIView.swift
- documentation/components/contents/accordion/ios.md
- Sources/Montage/1 Components/2 Actions/OutlinedUIButton.swift
- documentation/components/loading/skeleton/ios.md
- Sources/Blueprint/Sources/Scene/Previews/SkeletonPreview.swift
- documentation/utilities/ios-utility-components/modalnavigation.md
- Sources/Montage/1 Components/4 Contents/Accordion.swift
🛑 Comments failed to post (13)
documentation/utilities/ios-utility-components/opacity.md (1)
14-23: 📐 Maintainability & Code Quality | 🟡 Minor
🧩 Analysis chain
🏁 Script executed:
git ls-files documentation/utilities/ios-utility-components/opacity.md documentation | sed -n '1,120p'Repository: wanteddev/montage-ios
Length of output: 3501
🏁 Script executed:
rg -n "opacity88|opacity52|opacity43|withAlphaComponent|\.opacity\(" documentation -SRepository: wanteddev/montage-ios
Length of output: 1263
🏁 Script executed:
rg -n "static let opacity(43|52|88)|extension CGFloat|typealias CGFloat|func opacity\\(_ opacity: Double\\)" . -SRepository: wanteddev/montage-ios
Length of output: 1351
🏁 Script executed:
rg -n "static let opacity(43|52|88)|extension CGFloat|typealias CGFloat|func opacity\\(_ opacity: Double\\)" . -SRepository: wanteddev/montage-ios
Length of output: 1351
🏁 Script executed:
sed -n '1,120p' 'Sources/Montage/1 Components/9 Utilities/Opacity.swift' && printf '\n---\n' && sed -n '1,80p' documentation/utilities/ios-utility-components/opacity.mdRepository: wanteddev/montage-ios
Length of output: 3365
🏁 Script executed:
sed -n '1,120p' 'Sources/Montage/1 Components/9 Utilities/Opacity.swift' && printf '\n---\n' && sed -n '1,80p' documentation/utilities/ios-utility-components/opacity.mdRepository: wanteddev/montage-ios
Length of output: 3365
🏁 Script executed:
sed -n '1,120p' 'Sources/Montage/1 Components/9 Utilities/Opacity.swift' && printf '\n---\n' && sed -n '1,80p' documentation/utilities/ios-utility-components/opacity.mdRepository: wanteddev/montage-ios
Length of output: 3365
🏁 Script executed:
sed -n '1,120p' 'Sources/Montage/1 Components/9 Utilities/Opacity.swift' && printf '\n---\n' && sed -n '1,80p' documentation/utilities/ios-utility-components/opacity.mdRepository: wanteddev/montage-ios
Length of output: 3365
SwiftUI 예제는
Double로 감싸주세요.
CGFloat.opacity{N}토큰만 노출되므로myView.opacity(.opacity88)는 그대로는 타입이 맞지 않습니다.수정 예시
- myView.opacity(.opacity88) + myView.opacity(Double(.opacity88))📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.🤖 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 `@documentation/utilities/ios-utility-components/opacity.md` around lines 14 - 23, The SwiftUI example uses a CGFloat opacity token directly, which does not match the Double-based API expected by myView.opacity; update the example in the opacity documentation so the token is wrapped/conveyed as a Double-compatible value while keeping the CGFloat examples for alphaComponent and CGFloat variables unchanged. Use the opacity.md SwiftUI snippet as the place to adjust the myView.opacity usage and ensure the example reflects the correct type for SwiftUI.documentation/utilities/ios-utility-components/typography.md (1)
223-226: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
uiTextStyle설명 문장이 끊겨 있습니다.
"Dynamic Type 스케일의 기준이 되는 UIKit 텍스트 스타일. 과 동일한 논리..."는 참조 대상이 빠져 의미가 불분명합니다.textStyle을 명시해서 문장을 완성하는 편이 좋겠습니다.🤖 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 `@documentation/utilities/ios-utility-components/typography.md` around lines 223 - 226, The `uiTextStyle` description is incomplete and missing the referenced source, so update the documentation text near `uiTextStyle` to explicitly mention `textStyle` and complete the sentence. Keep the same meaning as the existing `textStyle` explanation, but rewrite the `uiTextStyle` line so the relationship is clear and the sentence reads naturally.packages/montage-mcp/data/components.json (1)
1033-1036: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
interactionColor(_:)요약의 기본값이 잘려 있습니다.여기 요약은
(기본값: ).로 저장돼 있어서 MCP 소비자 쪽 문서가 불완전하게 노출됩니다. 같은 PR의documentation/components/actions/iconbutton/ios.md에는 기본값이.labelNormal로 적혀 있으니 JSON도 동일하게 맞춰야 합니다.수정 예시
{ "name": "interactionColor(_:)", "signature": "func interactionColor(Color.Semantic) -> IconButton", - "summary": "hover / press 시 인터랙션 영역에 사용할 색상을 설정합니다(기본값: ).", + "summary": "hover / press 시 인터랙션 영역에 사용할 색상을 설정합니다(기본값: `.labelNormal`).", "kind": "method" },📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements."name": "interactionColor(_:)", "signature": "func interactionColor(Color.Semantic) -> IconButton", "summary": "hover / press 시 인터랙션 영역에 사용할 색상을 설정합니다(기본값: `.labelNormal`).", "kind": "method"🤖 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 `@packages/montage-mcp/data/components.json` around lines 1033 - 1036, The `interactionColor(_:)` method summary in `components.json` has a truncated default value, so update the `summary` for the `interactionColor(Color.Semantic) -> IconButton` entry to match the documented default from the `IconButton` docs. Use the existing `interactionColor(_:)` method record as the target and replace the incomplete `(기본값: )` text with the correct default value so MCP consumers see the full description.Sources/Blueprint/Sources/Scene/Previews/TextAreaPreview.swift (1)
162-165: 🎯 Functional Correctness | 🔴 Critical
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash set -euo pipefail printf '\n== TextAreaPreview.swift (relevant lines) ==\n' nl -ba Sources/Blueprint/Sources/Scene/Previews/TextAreaPreview.swift | sed -n '130,210p' printf '\n== Search for leadingResources / trailingResources declarations ==\n' rg -n "leadingResources|trailingResources" Sources/Blueprint/Sources/Scene/Previews/TextAreaPreview.swift Sources/Blueprint/Sources -g '*.swift' printf '\n== Search for TextArea.Resource definition ==\n' rg -n "struct Resource|enum Resource|typealias Resource|protocol Resource" Sources/Blueprint/Sources -g '*.swift'Repository: wanteddev/montage-ios
Length of output: 245
🏁 Script executed:
#!/bin/bash set -euo pipefail printf '\n== TextAreaPreview.swift (relevant lines) ==\n' awk 'NR>=130 && NR<=210 { printf "%4d %s\n", NR, $0 }' Sources/Blueprint/Sources/Scene/Previews/TextAreaPreview.swift printf '\n== Search for leadingResources / trailingResources declarations ==\n' rg -n "leadingResources|trailingResources" Sources/Blueprint/Sources/Scene/Previews/TextAreaPreview.swift Sources/Blueprint/Sources -g '*.swift' || true printf '\n== Search for TextArea.Resource definition ==\n' rg -n "struct Resource|enum Resource|typealias Resource|protocol Resource" Sources/Blueprint/Sources -g '*.swift' || trueRepository: wanteddev/montage-ios
Length of output: 6937
🏁 Script executed:
#!/bin/bash set -euo pipefail printf '\n== TextAreaPreview.swift (relevant lines) ==\n' awk 'NR>=130 && NR<=210 { printf "%4d %s\n", NR, $0 }' Sources/Blueprint/Sources/Scene/Previews/TextAreaPreview.swift printf '\n== Search for leadingResources / trailingResources declarations ==\n' rg -n "leadingResources|trailingResources" Sources/Blueprint/Sources/Scene/Previews/TextAreaPreview.swift Sources/Blueprint/Sources -g '*.swift' || true printf '\n== Search for TextArea.Resource definition ==\n' rg -n "struct Resource|enum Resource|typealias Resource|protocol Resource" Sources/Blueprint/Sources -g '*.swift' || trueRepository: wanteddev/montage-ios
Length of output: 6937
suffix(3)결과를Array로 감싸세요
leadingResources와trailingResources는[TextArea.Resource]인데,suffix(3)는ArraySlice<TextArea.Resource>를 반환합니다. 현재 코드는 타입이 맞지 않아 컴파일되지 않습니다.Array(...)로 변환하세요.수정 예시
- leadingResources = leadingResources.suffix(3) + leadingResources = Array(leadingResources.suffix(3)) ... - trailingResources = trailingResources.suffix(3) + trailingResources = Array(trailingResources.suffix(3))📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.if let resource = resources[index] { leadingResources.append(resource) leadingResources = Array(leadingResources.suffix(3)) }🤖 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 `@Sources/Blueprint/Sources/Scene/Previews/TextAreaPreview.swift` around lines 162 - 165, `TextAreaPreview`에서 `leadingResources`에 `suffix(3)`를 직접 대입하는 부분이 타입 불일치로 컴파일되지 않습니다. `leadingResources`와 `trailingResources`가 `[TextArea.Resource]`이므로, `suffix(3)`의 `ArraySlice<TextArea.Resource>` 결과를 `Array(...)`로 감싸도록 `TextAreaPreview`의 리소스 수집 로직을 수정하세요.Sources/Blueprint/Sources/Scene/Previews/TextFieldPreview.swift (1)
254-256: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
자동완성 토글을 끌 때 기존 제안도 바로 지워야 합니다.
지금은
usingSuggestions만false가 되고autoCompletionDataSource는 유지돼서, 이미 열린 제안 목록이 다음 입력 전까지 남을 수 있습니다. 토글 off 시nil로 리셋해 프리뷰 상태를 즉시 맞춰 주세요.수정 예시
.transparentChecking(isPresented: showTransparentChecker, checkerSize: 201, checkerColor: .red) + .onChange(of: usingSuggestions) { enabled in + if !enabled { + autoCompletionDataSource = nil + } + }🤖 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 `@Sources/Blueprint/Sources/Scene/Previews/TextFieldPreview.swift` around lines 254 - 256, The auto-complete toggle in TextFieldPreview only updates usingSuggestions, so the existing suggestion list can remain visible after turning it off. Update the toggle handling in the HStack/toggleRow("autoComplete", $usingSuggestions) flow so that when usingSuggestions becomes false, autoCompletionDataSource is immediately reset to nil as well. Use the existing TextFieldPreview state properties to keep the preview synchronized right away.Sources/Montage/1 Components/3 Selection And Input/TextArea.swift (3)
37-45: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
공개 DocC 설명의 최소 높이 수치가 구현과 다릅니다.
지금 기본 높이는
resolvedMinHeight에서 2줄 기준으로 계산되는데,Size.large/.medium설명은 각각 48/44의 고정 최소 높이처럼 안내하고 있습니다. 이 상태로 DocC가 생성되면 소비자가 높이 계약을 잘못 이해하게 됩니다.🤖 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 `@Sources/Montage/1` Components/3 Selection And Input/TextArea.swift around lines 37 - 45, DocC in TextArea.Size is describing fixed minimum heights that do not match the actual implementation in resolvedMinHeight, so update the enum documentation to reflect the real height behavior instead of implying 48/44 constants. Adjust the comments on Size.large and Size.medium to align with the 2-line-based min-height calculation used by TextArea so consumers understand the true contract.
335-353: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
.fixed(min:max)범위를 정규화해야 합니다.
min > max가 들어오면 그대로frame(minHeight:maxHeight:)에 전달되어 레이아웃이 비정상적으로 동작할 수 있습니다. 공개 API인 만큼 여기서 최소/최대값을 정렬해 두는 편이 안전합니다.수정 예시
private var resolvedMinHeight: CGFloat? { _ = dynamicTypeSize // Dynamic Type 변경 시 높이 재계산을 위한 의존성 등록 switch resize { case .normal, .limit: return height(forRows: minRows) - case .fixed(let min, _): - return min + case .fixed(let min, let max): + return Swift.min(min, max) } } private var resolvedMaxHeight: CGFloat? { _ = dynamicTypeSize switch resize { case .normal: return .infinity case .limit: return height(forRows: maxRows) - case .fixed(_, let max): return max + case .fixed(let min, let max): return Swift.max(min, max) } }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.private var resolvedMinHeight: CGFloat? { _ = dynamicTypeSize // Dynamic Type 변경 시 높이 재계산을 위한 의존성 등록 switch resize { case .normal, .limit: return height(forRows: minRows) case .fixed(let min, let max): return Swift.min(min, max) } } /// resize 방식에 따른 최대 높이. `.limit`은 폰트 줄높이 기준 6줄로 계산되어 Dynamic Type 변경에도 /// 항상 정확히 6줄을 유지한다. private var resolvedMaxHeight: CGFloat? { _ = dynamicTypeSize switch resize { case .normal: return .infinity case .limit: return height(forRows: maxRows) case .fixed(let min, let max): return Swift.max(min, max) } }🤖 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 `@Sources/Montage/1` Components/3 Selection And Input/TextArea.swift around lines 335 - 353, The fixed resize path in TextArea’s resolvedMinHeight and resolvedMaxHeight should normalize the `.fixed(min:max)` bounds before they are used. Update the `resize` handling so that the `.fixed` case always returns ordered min/max values from the `TextArea` layout helpers, preventing an invalid `frame(minHeight:maxHeight:)` when the inputs are reversed. Ensure the normalization is applied consistently wherever `resolvedMinHeight` and `resolvedMaxHeight` are derived from `resize`.
645-657: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
아이콘 세그먼트가 VoiceOver에서 조작 가능한 컨트롤로 노출되지 않습니다.
지금은
Image에onTapGesture만 붙어 있어서 버튼 역할, 접근 가능한 이름, 선택 상태가 전달되지 않습니다. Bottom segmented control은 아이콘만 보이기 때문에Button기반으로 바꾸고 각 세그먼트에accessibilityLabel/선택 상태를 함께 제공해야 합니다. As per path instructions,VoiceOver 지원,적절한 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 `@Sources/Montage/1` Components/3 Selection And Input/TextArea.swift around lines 645 - 657, The icon segment in TextArea.segment is only tappable via onTapGesture, so VoiceOver does not expose it as an interactive control with name or selected state. Refactor the segment view to use a Button-based control for each item, and add appropriate accessibilityLabel plus selected/accessibilityValue or hint so the segmented control in TextArea is announced and operable by VoiceOver.Source: Path instructions
Sources/Montage/1 Components/3 Selection And Input/TextField.swift (2)
424-440: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
backgroundColor(_:)가 현재 완전히 무시됩니다.
backgroundColor(_:)는customBackgroundColor를 저장하지만fieldBackground에서 그 값을 전혀 읽지 않아, 공개 modifier가 더 이상 동작하지 않습니다. 이번 리팩터링으로 API 계약이 깨졌습니다.수정 예시
`@ViewBuilder` var fieldBackground: some View { Group { - if disable { + if disable { SwiftUI.Color.semantic(.fillAlternative) + } else if let customBackgroundColor { + customBackgroundColor } else if colorScheme == .light { SwiftUI.Color.atomic(.common100) .opacity(0.8) .background(.ultraThinMaterial) } else {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.`@ViewBuilder` var fieldBackground: some View { Group { if disable { SwiftUI.Color.semantic(.fillAlternative) } else if let customBackgroundColor { customBackgroundColor } else if colorScheme == .light { SwiftUI.Color.atomic(.common100) .opacity(0.8) .background(.ultraThinMaterial) } else { SwiftUI.Color.atomic(.coolNeutral17) .opacity(0.61) .background(.ultraThinMaterial) } } .clipShape(RoundedRectangle(cornerRadius: size.cornerRadius)) }🤖 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 `@Sources/Montage/1` Components/3 Selection And Input/TextField.swift around lines 424 - 440, `backgroundColor(_:)`가 저장하는 `customBackgroundColor`가 `fieldBackground`에서 전혀 사용되지 않아 공개 API가 무시됩니다. `TextField`의 `fieldBackground`에서 `customBackgroundColor`를 우선 적용하도록 분기하고, 값이 없을 때만 기존 `disable`/`colorScheme` 기반 배경 로직을 사용하도록 수정하세요. `backgroundColor(_:)`, `customBackgroundColor`, `fieldBackground`를 함께 점검해 리팩터링 전 동작을 복원하면 됩니다.
564-566: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
비활성 상태의 입력 텍스트가 활성 색상으로 남아 있습니다.
disable == true여도 본문 텍스트가 항상.labelNormal이라서, 배경/placeholder/트레일링 버튼과 달리 비활성 상태가 시각적으로 전달되지 않습니다. TextArea와 같은 disabled 톤으로 맞추는 편이 자연스럽습니다.🤖 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 `@Sources/Montage/1` Components/3 Selection And Input/TextField.swift around lines 564 - 566, TextField의 fieldTextColor가 항상 .semantic(.labelNormal)로 고정되어 있어 disable 상태에서도 본문 텍스트가 활성 색상으로 보입니다. fieldTextColor 로직을 수정해 TextArea와 같은 방식으로 disable 여부를 반영하도록 하고, 비활성일 때는 disabled 톤의 색상을 반환하도록 TextField의 색상 계산을 조정하세요.Sources/Montage/1 Components/5 Loading/Skeleton.swift (1)
221-226: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
자동 줄 수 계산에서
round는 마지막 라인을 잘리게 만들 수 있습니다.Line 225는 높이가 1.5줄처럼 애매한 경우 반올림으로 한 줄을 더 그려서 하단 막대가 clipping 됩니다. 여기서는 실제로 들어가는 줄 수만 계산하도록 내림을 써야 합니다.
제안 수정
- let effectiveLineCount = kind.lineNumber > 0 - ? kind.lineNumber - : max(1, Int(round(proxy.size.height / scaledLineHeight))) + let effectiveLineCount = kind.lineNumber > 0 + ? kind.lineNumber + : max(1, Int(floor(proxy.size.height / scaledLineHeight)))📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.GeometryReader { proxy in let spacing = scaledLineSpacing let effectiveLineCount = kind.lineNumber > 0 ? kind.lineNumber : max(1, Int(floor(proxy.size.height / scaledLineHeight))) let barHeight = max(0, scaledLineHeight - spacing)🤖 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 `@Sources/Montage/1` Components/5 Loading/Skeleton.swift around lines 221 - 226, `Skeleton`의 `GeometryReader` 안에서 자동 `lineNumber`를 계산할 때 `round(proxy.size.height / scaledLineHeight)` 때문에 애매한 높이에서 한 줄을 과하게 그려 하단이 잘릴 수 있습니다. `effectiveLineCount` 계산식을 수정해 실제로 들어갈 수 있는 줄 수만 반영하도록 내림 기반 계산으로 바꾸고, `kind.lineNumber`가 0일 때만 이 자동 계산이 적용되도록 유지하세요.Sources/Montage/1 Components/6 Navigations/Category.swift (1)
124-124: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
iconSize를 그대로.custom(size:)에 넘기면 새 계약과 단위가 어긋납니다.여기서의
iconSize는 기존 아이콘 크기 값(20/22/24)인데, 새IconButton.NormalSize.custom(size:)는 컨테이너 한 변 길이를 받습니다. 그래서20/22는 24pt로 클램프되어 아이콘이 더 작게 렌더링되고, 카테고리 우측 액션 버튼 크기가 의도보다 줄어듭니다. 이 값은 새NormalSize케이스로 매핑하거나, 목표 아이콘 크기에 맞는 컨테이너 크기로 변환해서 넘겨야 합니다.🤖 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 `@Sources/Montage/1` Components/6 Navigations/Category.swift at line 124, `Category`의 우측 액션 버튼에서 `iconSize`를 그대로 `IconButton.NormalSize.custom(size:)`에 넘기고 있어 새 계약(컨테이너 한 변 길이)과 맞지 않습니다. `Category` 내 해당 `IconButton(variant: .normal(size: ...), icon: icon)` 호출을 찾아 `iconSize`(20/22/24)를 직접 전달하지 말고, `NormalSize`의 기존 케이스로 매핑하거나 목표 아이콘 크기에 맞는 컨테이너 크기로 변환해서 넘기도록 수정하세요.Sources/Montage/1 Components/6 Navigations/Tab.swift (1)
175-175: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
iconSize를.custom(size:)에 그대로 넘기면 단위가 바뀝니다.Line 175에서
iconSize는 기존 의미대로 아이콘 크기(20/22/24)인데,IconButton.NormalSize.custom(size:)는 컨테이너 한 변 길이를 받습니다. 지금처럼 넘기면 20/22/24가 24pt로 클램프되고 아이콘도 재계산돼서, 탭의 small/medium/large가 거의 같은 작은 버튼으로 렌더링됩니다.Tab.Size를NormalSize로 직접 매핑하거나, 컨테이너 크기로 변환한 값을 넘겨야 합니다.예시 수정안
- if resize == .hug, let icon, let iconButtonAction { - IconButton(variant: .normal(size: .custom(size: iconSize)), icon: icon) { + if resize == .hug, let icon, let iconButtonAction { + IconButton(variant: .normal(size: iconButtonSize), icon: icon) { iconButtonAction() } .padding(.trailing, horizontalPadding ? 16 : 0) }private var iconButtonSize: IconButton.NormalSize { switch size { case .small: .large // 20pt icon case .medium: .custom(size: 33) // convert desired icon size to container size case .large: .xlarge // 24pt icon } }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.IconButton(variant: .normal(size: iconButtonSize), icon: icon) { iconButtonAction() } .padding(.trailing, horizontalPadding ? 16 : 0) } private var iconButtonSize: IconButton.NormalSize { switch size { case .small: .large case .medium: .custom(size: 33) case .large: .xlarge } }🤖 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 `@Sources/Montage/1` Components/6 Navigations/Tab.swift at line 175, `Tab`의 아이콘 크기 값을 `IconButton.NormalSize.custom(size:)`에 그대로 넘기면 의미가 바뀌어 탭 버튼이 작게 렌더링됩니다. `Tab`의 `size`에서 계산한 `iconSize`를 직접 쓰지 말고, `Tab.Size`를 `IconButton.NormalSize`로 매핑하는 전용 변환 로직을 추가해 컨테이너 크기 기준으로 전달되도록 수정하세요. `IconButton(variant:icon:)` 호출부와 `iconButtonSize` 같은 보조 프로퍼티/메서드를 찾아 이 매핑을 적용하면 됩니다.
|
@coderabbitai PR 타겟 브랜치를 잘못 설정해서 수정했어. 처음부터 다시 리뷰해줘 |
|
✅ Action performedFull review finished. |
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
Sources/Montage/1 Components/3 Selection And Input/TextField.swift (1)
372-382: 🎯 Functional Correctness | 🟠 Major접근성 이름과 상태 값을 분리하세요.
TextField("", ...)는 필드 목적을 명확히 읽을 라벨이 없고,.accessibilityValue(accessibilityStatusDescription)가 입력값 대신"오류"또는 빈 문자열을 덮어씁니다. 필드 목적은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 `@Sources/Montage/1` Components/3 Selection And Input/TextField.swift around lines 372 - 382, The TextField accessibility setup currently hides the field purpose and overwrites the value with status text; update the SwiftUI.TextField in TextField.swift so its purpose is exposed via accessibilityLabel and the status message from accessibilityStatusDescription is moved to accessibilityHint instead of accessibilityValue. Keep the actual text value available to assistive technologies, and use the existing TextField initializer and modifiers near focused(_:) and accessibilityValue(...) to make the change cleanly.Source: Path instructions
🤖 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 `@Sources/Blueprint/Sources/Scene/Previews/TextFieldPreview.swift`:
- Around line 184-188: `TextFieldPreview`의 `onChange(of: text)`만으로는
`usingSuggestions` 토글 시 `autoCompletionDataSource`가 동기화되지 않습니다.
`usingSuggestions`(또는 `autoComplete`) 상태가 변경될 때도 현재 텍스트 상태를 기준으로 자동완성 데이터를 즉시
갱신하거나 nil로 정리하도록 별도의 상태 변경 처리 로직을 추가하세요. `text`를 다루는 기존 갱신 경로와 동일한 규칙을 재사용해, 켜질
때는 즉시 제안이 준비되고 꺼질 때는 기존 `autoCompletionDataSource`가 남지 않게 맞춰주세요.
- Around line 141-146: The toggleRow view currently renders Text(title) next to
Switch, but the switch has no explicit accessibility label, so VoiceOver may not
announce the option name. Update the toggleRow(_:_: ) helper in TextFieldPreview
to attach an appropriate accessibilityLabel to the Switch using the title value,
and add a suitable accessibilityHint if needed so the control is properly
described for VoiceOver support.
In `@Sources/Montage/1` Components/3 Selection And Input/TextArea.swift:
- Around line 124-133: 아이콘 전용 세그먼트에 VoiceOver용 접근성 정보를 추가해야 합니다. `TextArea`의
`segmentedControl` API와 이를 렌더링하는 `BottomSegmentedControl`에서 세그먼트별
`accessibilityLabel`을 받을 수 있는 모델을 도입하고, 각 아이콘에 해당 label을 연결하세요. 또한 `Image` +
`onTapGesture`만 쓰는 현재 구현을 수정해 세그먼트에 button trait와 적절한 `accessibilityHint`를 부여해
조작 가능함을 명확히 표시하세요.
- Around line 217-219: `TextArea.maxLength(_:)` should normalize negative limits
at the entry point so `updateUIView` never receives an invalid value that can
crash via `String.prefix(_:)`. Update the `maxLength(_:)` method to clamp any
non-nil `limit` to zero or greater before storing it in `self.maxLength`, and
keep the fix localized to the `TextArea` configuration path.
---
Outside diff comments:
In `@Sources/Montage/1` Components/3 Selection And Input/TextField.swift:
- Around line 372-382: The TextField accessibility setup currently hides the
field purpose and overwrites the value with status text; update the
SwiftUI.TextField in TextField.swift so its purpose is exposed via
accessibilityLabel and the status message from accessibilityStatusDescription is
moved to accessibilityHint instead of accessibilityValue. Keep the actual text
value available to assistive technologies, and use the existing TextField
initializer and modifiers near focused(_:) and accessibilityValue(...) to make
the change cleanly.
🪄 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: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 804ff47f-056c-4cf0-babb-dad146f767fa
📒 Files selected for processing (7)
Sources/Blueprint/Sources/Scene/Previews/TextAreaPreview.swiftSources/Blueprint/Sources/Scene/Previews/TextFieldPreview.swiftSources/Montage/1 Components/3 Selection And Input/TextArea.swiftSources/Montage/1 Components/3 Selection And Input/TextField.swiftdocumentation/components/selection and input/textarea/ios.mddocumentation/components/selection and input/textfield/ios.mdpackages/montage-mcp/data/components.json
- TextArea.maxLength: 음수 입력을 0 이상으로 정규화해 String.prefix 트랩 방지 - TextArea 세그먼트 컨트롤: accessibilityLabels 추가 + 버튼/선택 trait 부여 - TextField: 실제 텍스트 노출 유지, 상태 문구를 value 대신 hint로 이동, placeholder를 label로 - Blueprint 프리뷰: Switch 접근성 라벨 추가, autoComplete 토글 시 자동완성 동기화 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D8gTT3K3Hci759WS9qMf8r
|
CodeRabbit 리뷰 코멘트 반영 완료 (f94afa7)
Montage·Blueprint 빌드 통과, |
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D8gTT3K3Hci759WS9qMf8r
resize 변경 시 높이 갱신용으로 text에 공백을 주입하던 꼼수가 placeholder 표시 조건(text.isEmpty)을 깨뜨려 Limit에서 placeholder가 사라졌다. text를 건드리는 대신 .id(resize)로 뷰를 재생성해 높이를 재계산하도록 변경한다. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KWRQ1qMUwA73goLx5eHnhP
개요
수정사항
미리보기