@@ -49,6 +49,10 @@ struct AdaptiveCardView: View {
4949 self . renderImageSet ( imgSet)
5050 case . actionSet( let actSet) :
5151 self . renderActions ( actSet. actions)
52+ case . icon( let ico) :
53+ self . renderIcon ( ico)
54+ case . list( let lst) :
55+ self . renderList ( lst)
5256 case . unknown:
5357 EmptyView ( )
5458 }
@@ -239,6 +243,89 @@ struct AdaptiveCardView: View {
239243 }
240244 }
241245
246+ // MARK: - Icon rendering
247+
248+ @ViewBuilder
249+ private func renderIcon( _ icon: ACIcon ) -> some View {
250+ self . iconView ( icon)
251+ }
252+
253+ /// Builds a view for an ACIcon: SF Symbol when the name matches, otherwise a text label.
254+ @ViewBuilder
255+ private func iconView( _ icon: ACIcon ) -> some View {
256+ let iconSize = self . iconFontSize ( icon. size)
257+ let iconColor = self . iconColor ( icon. color)
258+
259+ // Attempt SF Symbol lookup; fall back to a text label
260+ if self . sfSymbolExists ( icon. name) {
261+ Image ( systemName: icon. name)
262+ . font ( . system( size: iconSize) )
263+ . foregroundStyle ( iconColor)
264+ } else {
265+ Text ( icon. name)
266+ . font ( . system( size: iconSize) )
267+ . foregroundStyle ( iconColor)
268+ }
269+ }
270+
271+ /// Map Adaptive Card icon size tokens to point sizes.
272+ private func iconFontSize( _ size: String ? ) -> CGFloat {
273+ switch size? . lowercased ( ) {
274+ case " xxs " : return 10
275+ case " xs " : return 12
276+ case " sm " , " small " : return 14
277+ case " md " , " medium " : return 18
278+ case " lg " , " large " : return 24
279+ case " xl " : return 30
280+ case " xxl " : return 38
281+ default : return 16
282+ }
283+ }
284+
285+ /// Resolve Adaptive Card color tokens to SwiftUI colors.
286+ private func iconColor( _ color: String ? ) -> Color {
287+ switch color? . lowercased ( ) {
288+ case " accent " : return . accentColor
289+ case " good " : return . green
290+ case " warning " : return . orange
291+ case " attention " : return . red
292+ case " light " : return . secondary
293+ case " dark " : return . primary
294+ default : return . primary
295+ }
296+ }
297+
298+ /// Check whether an SF Symbol name is valid at runtime.
299+ private func sfSymbolExists( _ name: String ) -> Bool {
300+ #if canImport(UIKit)
301+ return UIImage ( systemName: name) != nil
302+ #elseif canImport(AppKit)
303+ return NSImage ( systemSymbolName: name, accessibilityDescription: nil ) != nil
304+ #else
305+ return false
306+ #endif
307+ }
308+
309+ // MARK: - List rendering
310+
311+ @ViewBuilder
312+ private func renderList( _ list: ACList ) -> some View {
313+ let ordered = list. style? . lowercased ( ) == " ordered "
314+ VStack ( alignment: . leading, spacing: 4 ) {
315+ ForEach ( list. items. indices, id: \. self) { idx in
316+ HStack ( alignment: . top, spacing: 4 ) {
317+ if let icon = list. items [ idx] . icon {
318+ self . iconView ( icon)
319+ }
320+ let prefix = ordered ? " \( idx + 1 ) . " : " \u{2022} "
321+ Text ( " \( prefix) \( list. items [ idx] . text) " )
322+ . font ( . subheadline)
323+ . fixedSize ( horizontal: false , vertical: true )
324+ }
325+ }
326+ }
327+ }
328+
242329 private func imageMaxHeight( _ size: String ? ) -> CGFloat {
243330 switch size? . lowercased ( ) {
244331 case " small " : return 60
0 commit comments