diff --git a/Projects/DVDesign/Resources/Assets.xcassets/ServiceLogos/Contents.json b/Projects/DVDesign/Resources/Assets.xcassets/ServiceLogos/Contents.json
new file mode 100644
index 00000000..73c00596
--- /dev/null
+++ b/Projects/DVDesign/Resources/Assets.xcassets/ServiceLogos/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Projects/DVDesign/Resources/Assets.xcassets/ServiceLogos/github.imageset/Contents.json b/Projects/DVDesign/Resources/Assets.xcassets/ServiceLogos/github.imageset/Contents.json
new file mode 100644
index 00000000..58cccfd3
--- /dev/null
+++ b/Projects/DVDesign/Resources/Assets.xcassets/ServiceLogos/github.imageset/Contents.json
@@ -0,0 +1,16 @@
+{
+ "images" : [
+ {
+ "filename" : "github.svg",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "preserves-vector-representation" : true,
+ "template-rendering-intent" : "template"
+ }
+}
diff --git a/Projects/DVDesign/Resources/Assets.xcassets/ServiceLogos/github.imageset/github.svg b/Projects/DVDesign/Resources/Assets.xcassets/ServiceLogos/github.imageset/github.svg
new file mode 100644
index 00000000..23349765
--- /dev/null
+++ b/Projects/DVDesign/Resources/Assets.xcassets/ServiceLogos/github.imageset/github.svg
@@ -0,0 +1 @@
+
diff --git a/Projects/DVDesign/Resources/Assets.xcassets/ServiceLogos/google.imageset/Contents.json b/Projects/DVDesign/Resources/Assets.xcassets/ServiceLogos/google.imageset/Contents.json
new file mode 100644
index 00000000..18bfb84f
--- /dev/null
+++ b/Projects/DVDesign/Resources/Assets.xcassets/ServiceLogos/google.imageset/Contents.json
@@ -0,0 +1,16 @@
+{
+ "images" : [
+ {
+ "filename" : "google.svg",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "preserves-vector-representation" : true,
+ "template-rendering-intent" : "template"
+ }
+}
diff --git a/Projects/DVDesign/Resources/Assets.xcassets/ServiceLogos/google.imageset/google.svg b/Projects/DVDesign/Resources/Assets.xcassets/ServiceLogos/google.imageset/google.svg
new file mode 100644
index 00000000..75543e0c
--- /dev/null
+++ b/Projects/DVDesign/Resources/Assets.xcassets/ServiceLogos/google.imageset/google.svg
@@ -0,0 +1 @@
+
diff --git a/Projects/DVDesign/Resources/Assets.xcassets/ServiceLogos/kakaotalk.imageset/Contents.json b/Projects/DVDesign/Resources/Assets.xcassets/ServiceLogos/kakaotalk.imageset/Contents.json
new file mode 100644
index 00000000..5bd102af
--- /dev/null
+++ b/Projects/DVDesign/Resources/Assets.xcassets/ServiceLogos/kakaotalk.imageset/Contents.json
@@ -0,0 +1,16 @@
+{
+ "images" : [
+ {
+ "filename" : "kakaotalk.svg",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "preserves-vector-representation" : true,
+ "template-rendering-intent" : "original"
+ }
+}
diff --git a/Projects/DVDesign/Resources/Assets.xcassets/ServiceLogos/kakaotalk.imageset/kakaotalk.svg b/Projects/DVDesign/Resources/Assets.xcassets/ServiceLogos/kakaotalk.imageset/kakaotalk.svg
new file mode 100644
index 00000000..b9dd9b4e
--- /dev/null
+++ b/Projects/DVDesign/Resources/Assets.xcassets/ServiceLogos/kakaotalk.imageset/kakaotalk.svg
@@ -0,0 +1 @@
+
diff --git a/Projects/DVDesign/Resources/Assets.xcassets/ServiceLogos/naver.imageset/Contents.json b/Projects/DVDesign/Resources/Assets.xcassets/ServiceLogos/naver.imageset/Contents.json
new file mode 100644
index 00000000..f1283617
--- /dev/null
+++ b/Projects/DVDesign/Resources/Assets.xcassets/ServiceLogos/naver.imageset/Contents.json
@@ -0,0 +1,16 @@
+{
+ "images" : [
+ {
+ "filename" : "naver.svg",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "preserves-vector-representation" : true,
+ "template-rendering-intent" : "template"
+ }
+}
diff --git a/Projects/DVDesign/Resources/Assets.xcassets/ServiceLogos/naver.imageset/naver.svg b/Projects/DVDesign/Resources/Assets.xcassets/ServiceLogos/naver.imageset/naver.svg
new file mode 100644
index 00000000..a977bf67
--- /dev/null
+++ b/Projects/DVDesign/Resources/Assets.xcassets/ServiceLogos/naver.imageset/naver.svg
@@ -0,0 +1 @@
+
diff --git a/Projects/DVDesign/SampleApp/Sources/DVVaultContainerPreviewView.swift b/Projects/DVDesign/SampleApp/Sources/DVVaultContainerPreviewView.swift
index 27310648..772e4f57 100644
--- a/Projects/DVDesign/SampleApp/Sources/DVVaultContainerPreviewView.swift
+++ b/Projects/DVDesign/SampleApp/Sources/DVVaultContainerPreviewView.swift
@@ -7,15 +7,22 @@ struct DVVaultContainerPreviewView: View {
@State private var selectedIndex: Int? = nil
- private let vaults: [(String, String, DVVaultContainer.TrailingIcon?)] = [
- ("내가 설정한 이름", "2026.04.01", nil),
- ("이름이름이름", "2026.04.01", nil),
- ("Example", "2026.03.27", nil),
- ("LongLongNameExampleVault","2026.04.01", nil),
- ("만료 임박 항목", "2026.04.01", .expiringSoon),
- ("이름이름이름", "2026.04.01", .expiringSoon),
- ("만료된 항목", "2026.03.27", .expired),
- ("LongLongNameExampleVault","2025.04.01", .expired),
+ /// `typeIcon`은 DVDesign이 알 필요 없는 실제 secretType과 무관한 값이라, 폴백 렌더링 확인용으로 하나만 재사용한다.
+ private let placeholderTypeIcon = Image(systemName: "key.fill")
+
+ private let vaults: [(String, String, DVVaultContainer.TrailingIcon?, String?, Bool)] = [
+ ("내가 설정한 이름", "2026.04.01", nil, nil, true),
+ ("이름이름이름", "2026.04.01", nil, nil, true),
+ ("Example", "2026.03.27", nil, nil, true),
+ ("LongLongNameExampleVault","2026.04.01", nil, nil, true),
+ ("Google 계정", "2026.04.01", nil, "google", false),
+ ("GitHub 계정", "2026.04.01", nil, "github", false),
+ ("네이버 계정", "2026.04.01", nil, "naver", false),
+ ("카카오톡 계정", "2026.04.01", nil, "kakaotalk", false),
+ ("만료 임박 항목", "2026.04.01", .expiringSoon, "google", false),
+ ("이름이름이름", "2026.04.01", .expiringSoon, nil, true),
+ ("만료된 항목", "2026.03.27", .expired, "github", false),
+ ("LongLongNameExampleVault","2025.04.01", .expired, nil, true),
]
var body: some View {
@@ -60,6 +67,8 @@ extension DVVaultContainerPreviewView {
DVVaultContainer(
name: vaults[index].0,
date: vaults[index].1,
+ service: vaults[index].3,
+ typeIcon: vaults[index].4 ? placeholderTypeIcon : nil,
trailingIcon: vaults[index].2,
isSelected: selectedIndex == index
)
diff --git a/Projects/DVDesign/Sources/Components/DVVaultContainer.swift b/Projects/DVDesign/Sources/Components/DVVaultContainer.swift
index 9ee02416..43ae7f6a 100644
--- a/Projects/DVDesign/Sources/Components/DVVaultContainer.swift
+++ b/Projects/DVDesign/Sources/Components/DVVaultContainer.swift
@@ -17,6 +17,9 @@ public struct DVVaultContainer: View {
public let name: String
public let date: String
+ public let service: String?
+ /// `service`가 비어 있을 때만 쓰이는 최종 폴백. 호출부가 자신의 타입 분류에 맞는 SF Symbol 등을 넘긴다.
+ public let typeIcon: Image?
public let trailingIcon: TrailingIcon?
public let isSelected: Bool
@@ -25,11 +28,15 @@ public struct DVVaultContainer: View {
public init(
name: String,
date: String,
+ service: String? = nil,
+ typeIcon: Image? = nil,
trailingIcon: TrailingIcon? = nil,
isSelected: Bool = false
) {
self.name = name
self.date = date
+ self.service = service
+ self.typeIcon = typeIcon
self.trailingIcon = trailingIcon
self.isSelected = isSelected
}
@@ -52,10 +59,71 @@ public struct DVVaultContainer: View {
extension DVVaultContainer {
+ @ViewBuilder
private var avatarCircle: some View {
+ if let logo = ServiceLogoCatalog.logo(forService: service) {
+ logoAvatar(logo)
+ } else if let serviceInitial {
+ initialAvatar(serviceInitial)
+ } else {
+ typeIconAvatar
+ }
+ }
+
+ private func logoAvatar(_ logo: DVServiceLogo) -> some View {
+ Circle()
+ .fill(logo.brandColor)
+ .frame(width: 44, height: 44)
+ .overlay {
+ if logo.rendersAsTemplate {
+ Image(logo.assetName, bundle: .module)
+ .renderingMode(.template)
+ .resizable()
+ .scaledToFit()
+ .foregroundStyle(Color.dv(.white))
+ .frame(width: 22, height: 22)
+ } else {
+ // 배경+글리프가 한 path로 합쳐진 에셋: template 틴트를 걸지 않고 원본 색상 그대로 그린다.
+ Image(logo.assetName, bundle: .module)
+ .resizable()
+ .scaledToFit()
+ .frame(width: 22, height: 22)
+ }
+ }
+ }
+
+ private func initialAvatar(_ initial: String) -> some View {
Circle()
.fill(Color.dv(.gray300))
.frame(width: 44, height: 44)
+ .overlay(
+ Text(initial)
+ .dvFont(.headingLG)
+ .foregroundStyle(Color.dv(.gray600))
+ )
+ }
+
+ @ViewBuilder
+ private var typeIconAvatar: some View {
+ Circle()
+ .fill(Color.dv(.gray300))
+ .frame(width: 44, height: 44)
+ .overlay {
+ if let typeIcon {
+ typeIcon
+ .resizable()
+ .scaledToFit()
+ .frame(width: 20, height: 20)
+ .foregroundStyle(Color.dv(.gray600))
+ }
+ }
+ }
+
+ /// 로고 매칭에 실패했지만 `service` 자체는 채워져 있을 때 쓰는 중간 폴백.
+ private var serviceInitial: String? {
+ guard let service else { return nil }
+ guard let first = service.trimmingCharacters(in: .whitespacesAndNewlines).first else { return nil }
+ return String(first).uppercased()
}
private var textStack: some View {
diff --git a/Projects/DVDesign/Sources/Foundations/Color/Color+Hex.swift b/Projects/DVDesign/Sources/Foundations/Color/Color+Hex.swift
new file mode 100644
index 00000000..73080821
--- /dev/null
+++ b/Projects/DVDesign/Sources/Foundations/Color/Color+Hex.swift
@@ -0,0 +1,14 @@
+// Copyright © 2026 Devault. All rights reserved
+
+import SwiftUI
+
+extension Color {
+ /// 0xRRGGBB 형식의 브랜드 컬러 하드코딩(예: 서비스 로고 색상)에 쓴다.
+ init(hex: UInt32) {
+ self.init(
+ red: Double((hex >> 16) & 0xFF) / 255,
+ green: Double((hex >> 8) & 0xFF) / 255,
+ blue: Double(hex & 0xFF) / 255
+ )
+ }
+}
diff --git a/Projects/DVDesign/Sources/Foundations/ServiceLogo/ServiceLogoCatalog.swift b/Projects/DVDesign/Sources/Foundations/ServiceLogo/ServiceLogoCatalog.swift
new file mode 100644
index 00000000..12273340
--- /dev/null
+++ b/Projects/DVDesign/Sources/Foundations/ServiceLogo/ServiceLogoCatalog.swift
@@ -0,0 +1,57 @@
+// Copyright © 2026 Devault. All rights reserved
+
+import SwiftUI
+
+public struct DVServiceLogo {
+ public let assetName: String
+ public let brandColor: Color
+ /// `false`면 SVG가 배경과 글리프를 한 path로 합쳐 그린다(예: KakaoTalk의 사각 배경+말풍선).
+ /// 이런 에셋은 `.template` 틴트를 걸면 배경까지 단색으로 뭉개지므로 원본 색상 그대로 그려야 한다.
+ public let rendersAsTemplate: Bool
+}
+
+/// `Secret.service` 자유 텍스트를 정규화해 번들된 브랜드 로고를 찾는다.
+/// 매칭되는 항목이 없으면 `nil`을 반환하며, 호출부는 이니셜 플레이스홀더로 폴백해야 한다.
+public enum ServiceLogoCatalog {
+
+ public static func logo(forService service: String?) -> DVServiceLogo? {
+ guard let service else { return nil }
+ return logosByAlias[normalize(service)]
+ }
+
+ /// 영문/숫자만 남기고 비교한다. "Google Cloud"처럼 알려지지 않은 표기는
+ /// `aliases`에 미리 추가해두지 않는 한 조용히 매칭 실패해 이니셜 폴백으로 빠진다.
+ private static func normalize(_ raw: String) -> String {
+ raw.lowercased().filter { $0.isLetter || $0.isNumber }
+ }
+
+ private struct Entry {
+ let logo: DVServiceLogo
+ let aliases: [String]
+ }
+
+ private static let entries: [Entry] = [
+ Entry(
+ logo: DVServiceLogo(assetName: "google", brandColor: Color(hex: 0x4285F4), rendersAsTemplate: true),
+ aliases: ["google", "gmail", "googleworkspace"]
+ ),
+ Entry(
+ logo: DVServiceLogo(assetName: "github", brandColor: Color(hex: 0x181717), rendersAsTemplate: true),
+ aliases: ["github"]
+ ),
+ Entry(
+ logo: DVServiceLogo(assetName: "naver", brandColor: Color(hex: 0x03C75A), rendersAsTemplate: true),
+ aliases: ["naver"]
+ ),
+ Entry(
+ logo: DVServiceLogo(assetName: "kakaotalk", brandColor: Color(hex: 0xFFCD00), rendersAsTemplate: false),
+ aliases: ["kakao", "kakaotalk"]
+ ),
+ ]
+
+ private static let logosByAlias: [String: DVServiceLogo] = entries.reduce(into: [:]) { result, entry in
+ for alias in entry.aliases {
+ result[alias] = entry.logo
+ }
+ }
+}
diff --git a/Projects/DVPresentation/Sources/Dependencies/SecretClient+Preview.swift b/Projects/DVPresentation/Sources/Dependencies/SecretClient+Preview.swift
new file mode 100644
index 00000000..b9cffaba
--- /dev/null
+++ b/Projects/DVPresentation/Sources/Dependencies/SecretClient+Preview.swift
@@ -0,0 +1,91 @@
+// Copyright © 2026 Devault. All rights reserved
+
+import Foundation
+
+import DVDomain
+
+// MARK: - Preview Fixtures
+
+extension Secret {
+ static let preview = Secret(
+ id: UUID(),
+ name: "GitHub API Key",
+ secretType: .apiKeyToken,
+ service: "github",
+ createdAt: .now,
+ updatedAt: .now,
+ payload: SecretPayload(encryptedData: Data(), keyTag: "preview", schemaVersion: 1)
+ )
+}
+
+extension [Secret] {
+ /// 아바타 폴백 3단계(로고 매칭 → service 첫 글자 → secretType 아이콘)를
+ /// 타입 6종에 걸쳐 전부 확인하기 위한 프리뷰 픽스처.
+ public static let preview: [Secret] = previewFixtures.map(\.secret)
+
+ /// `.project` 컬렉션 필터링용. `Secret`엔 프로젝트 소속 필드가 없어(실제로는 별도 조인 테이블) 프리뷰에서만 매핑을 들고 있는다.
+ public static func preview(in projectID: Project.ID) -> [Secret] {
+ previewFixtures
+ .filter { $0.secret.deletedAt == nil && $0.projectIDs.contains(projectID) }
+ .map(\.secret)
+ }
+
+ private static let previewFixtures: [(secret: Secret, projectIDs: Set)] = {
+ let cheerLotID = [Project].preview[0].id
+
+ return [
+ // 1) 로고 매칭
+ (.preview, [cheerLotID]), // GitHub API Key · service: "github"
+ (secret(name: "Google 계정", type: .oauth, service: "google"), [cheerLotID]),
+ (secret(name: "네이버 계정", type: .oauth, service: "naver"), []),
+ (secret(name: "카카오톡 계정", type: .oauth, service: "kakaotalk"), []),
+
+ // 2) 매칭 실패, service 첫 글자로 폴백
+ (secret(name: "Stripe API Key", type: .apiKeyToken, service: "Stripe"), []),
+ (secret(name: "CheerLot DB", type: .database, service: "CheerLot"), [cheerLotID]),
+
+ // 3) service 없음, secretType 아이콘으로 폴백
+ (secret(name: "이름 없는 API 키", type: .apiKeyToken, expiresAt: .now.addingTimeInterval(-86_400)), []),
+ (secret(name: "사내 SSO", type: .oauth), []),
+ (secret(name: "운영 DB", type: .database, expiresAt: .now.addingTimeInterval(3 * 86_400)), []),
+ (secret(name: "배포 서버 SSH 키", type: .sshAndCredentials, expiresAt: .now.addingTimeInterval(20 * 86_400)), []),
+ (secret(name: ".env 모음", type: .environmentVariableSet), []),
+ (secret(name: "라이선스 키", type: .etc), []),
+
+ // 4) Deleted 컬렉션 전용
+ (secret(name: "삭제된 API 키", type: .apiKeyToken, deletedAt: .now), []),
+
+ // 5) Star(liked) 컬렉션 전용
+ (secret(name: "즐겨찾는 API 키", type: .apiKeyToken, liked: true), []),
+ ]
+ }()
+
+ private static func secret(
+ name: String,
+ type: SecretType,
+ service: String? = nil,
+ expiresAt: Date? = nil,
+ liked: Bool = false,
+ deletedAt: Date? = nil
+ ) -> Secret {
+ Secret(
+ id: UUID(),
+ name: name,
+ secretType: type,
+ service: service,
+ expiresAt: expiresAt,
+ liked: liked,
+ deletedAt: deletedAt,
+ createdAt: .now,
+ updatedAt: .now,
+ payload: SecretPayload(encryptedData: Data(), keyTag: "preview", schemaVersion: 1)
+ )
+ }
+}
+
+extension [Project] {
+ public static let preview: [Project] = [
+ Project(id: UUID(), name: "CheerLot", createdAt: .now, updatedAt: .now),
+ Project(id: UUID(), name: "Mobile", createdAt: .now, updatedAt: .now),
+ ]
+}
diff --git a/Projects/DVPresentation/Sources/Dependencies/SecretClient.swift b/Projects/DVPresentation/Sources/Dependencies/SecretClient.swift
index 1c61f104..e077e9ec 100644
--- a/Projects/DVPresentation/Sources/Dependencies/SecretClient.swift
+++ b/Projects/DVPresentation/Sources/Dependencies/SecretClient.swift
@@ -28,7 +28,22 @@ extension SecretClient: TestDependencyKey {
public static let testValue = SecretClient()
public static let previewValue = SecretClient(
- fetchByQuery: { _ in [.preview] },
+ fetchByQuery: { query in
+ switch query.collection {
+ case .expired(let referenceDate):
+ return [Secret].preview.filter {
+ $0.deletedAt == nil && ($0.expiresAt.map { $0 < referenceDate } ?? false)
+ }
+ case .deleted:
+ return [Secret].preview.filter { $0.deletedAt != nil }
+ case .liked:
+ return [Secret].preview.filter { $0.deletedAt == nil && $0.liked }
+ case .project(let id):
+ return [Secret].preview(in: id)
+ case .all:
+ return [Secret].preview.filter { $0.deletedAt == nil }
+ }
+ },
softDelete: { _ in .preview },
restore: { _ in .preview },
permanentlyDelete: { _ in },
@@ -46,23 +61,3 @@ public extension DependencyValues {
set { self[SecretClient.self] = newValue }
}
}
-
-// MARK: - Preview Fixtures
-
-extension Secret {
- static let preview = Secret(
- id: UUID(),
- name: "GitHub API Key",
- secretType: .apiKeyToken,
- createdAt: .now,
- updatedAt: .now,
- payload: SecretPayload(encryptedData: Data(), keyTag: "preview", schemaVersion: 1)
- )
-}
-
-public extension [Project] {
- static let preview: [Project] = [
- Project(id: UUID(), name: "CheerLot", createdAt: .now, updatedAt: .now),
- Project(id: UUID(), name: "Mobile", createdAt: .now, updatedAt: .now),
- ]
-}
diff --git a/Projects/DVPresentation/Sources/Features/CreateSecret/Model/CreatableSecretType.swift b/Projects/DVPresentation/Sources/Features/CreateSecret/Model/CreatableSecretType.swift
index 4d35646b..081d2349 100644
--- a/Projects/DVPresentation/Sources/Features/CreateSecret/Model/CreatableSecretType.swift
+++ b/Projects/DVPresentation/Sources/Features/CreateSecret/Model/CreatableSecretType.swift
@@ -48,19 +48,8 @@ enum CreatableSecretType: String, CaseIterable, Hashable {
}
}
- /// 타입 선택 화면의 카드 아이콘.
+ /// 타입 선택 화면의 카드 아이콘. SecretList 아바타 폴백과 동일한 소스(`SecretType.icon`)를 쓴다.
var icon: Image {
- Image(systemName: iconSystemName)
- }
-
- private var iconSystemName: String {
- switch self {
- case .apiKeyToken: return "key.fill"
- case .oauth: return "person.badge.key.fill"
- case .database: return "cylinder.split.1x2.fill"
- case .sshAndCredentials: return "terminal.fill"
- case .environmentVariableSet: return "curlybraces"
- case .etc: return "ellipsis.circle.fill"
- }
+ domainType.icon
}
}
diff --git a/Projects/DVPresentation/Sources/Features/SecretList/SecretListView.swift b/Projects/DVPresentation/Sources/Features/SecretList/SecretListView.swift
index e3864272..fd7fb112 100644
--- a/Projects/DVPresentation/Sources/Features/SecretList/SecretListView.swift
+++ b/Projects/DVPresentation/Sources/Features/SecretList/SecretListView.swift
@@ -109,6 +109,8 @@ extension SecretListView {
DVVaultContainer(
name: secret.name,
date: SecretDateFormatter.string(from: secret.updatedAt),
+ service: secret.service,
+ typeIcon: secret.secretType.icon,
trailingIcon: trailingIcon(for: secret),
isSelected: secret.id == store.selectedSecretID
)
diff --git a/Projects/DVPresentation/Sources/Support/SecretType+Icon.swift b/Projects/DVPresentation/Sources/Support/SecretType+Icon.swift
new file mode 100644
index 00000000..25404d28
--- /dev/null
+++ b/Projects/DVPresentation/Sources/Support/SecretType+Icon.swift
@@ -0,0 +1,23 @@
+// Copyright © 2026 Devault. All rights reserved
+
+import SwiftUI
+
+import DVDomain
+
+/// 타입 선택 화면(SelectSecretType)과 목록 화면(SecretList) 아바타 폴백이 공유하는 단일 아이콘 소스.
+extension SecretType {
+ var icon: Image {
+ Image(systemName: iconSystemName)
+ }
+
+ private var iconSystemName: String {
+ switch self {
+ case .apiKeyToken: return "key.fill"
+ case .oauth: return "checkmark.shield.fill"
+ case .database: return "cylinder.split.1x2.fill"
+ case .sshAndCredentials: return "terminal.fill"
+ case .environmentVariableSet: return "curlybraces"
+ case .etc: return "ellipsis.circle.fill"
+ }
+ }
+}