@@ -317,35 +317,57 @@ struct ContentView: View {
317317 }
318318
319319 private var bothDetectionsList : some View {
320- VStack ( spacing: 0 ) {
321- unifiedMapView ( showAircraft: true )
322- . frame ( height: mapHeight)
323- . padding ( . vertical, 4 )
320+ GeometryReader { geo in
321+ if useSideBySideMapList ( for: geo. size) {
322+ HStack ( spacing: 0 ) {
323+ unifiedMapView ( showAircraft: true )
324+ . frame ( maxWidth: . infinity, maxHeight: . infinity)
325+ . padding ( . vertical, 4 )
326+ . padding ( . leading, 4 )
327+ . layoutPriority ( 0 )
324328
325- List {
326- if hasDrones {
327- Section ( " Drones ( \( cotViewModel. parsedMessages. count) ) " ) {
328- ForEach ( cotViewModel. parsedMessages) { item in
329- MessageRow (
330- message: item,
331- cotViewModel: cotViewModel,
332- isCompact: true
333- )
334- . id ( item. uid)
335- }
329+ Divider ( )
330+
331+ bothList
332+ . frame ( minWidth: 340 , idealWidth: 420 , maxWidth: 520 )
333+ . layoutPriority ( 1 )
334+ }
335+ } else {
336+ VStack ( spacing: 0 ) {
337+ unifiedMapView ( showAircraft: true )
338+ . frame ( height: mapHeight)
339+ . padding ( . vertical, 4 )
340+
341+ bothList
342+ }
343+ }
344+ }
345+ }
346+
347+ private var bothList : some View {
348+ List {
349+ if hasDrones {
350+ Section ( " Drones ( \( cotViewModel. parsedMessages. count) ) " ) {
351+ ForEach ( cotViewModel. parsedMessages) { item in
352+ MessageRow (
353+ message: item,
354+ cotViewModel: cotViewModel,
355+ isCompact: true
356+ )
357+ . id ( item. uid)
336358 }
337359 }
360+ }
338361
339- if hasAircraft {
340- Section ( " Aircraft ( \( cotViewModel. aircraftTracks. count) ) " ) {
341- ForEach ( cotViewModel. aircraftTracks) { aircraft in
342- AircraftRow ( aircraft: aircraft)
343- }
362+ if hasAircraft {
363+ Section ( " Aircraft ( \( cotViewModel. aircraftTracks. count) ) " ) {
364+ ForEach ( cotViewModel. aircraftTracks) { aircraft in
365+ AircraftRow ( aircraft: aircraft)
344366 }
345367 }
346368 }
347- . scrollContentBackground ( . visible)
348369 }
370+ . scrollContentBackground ( . visible)
349371 }
350372
351373 private func getValidFlightPath( for uid: String ) -> [ CLLocationCoordinate2D ] {
@@ -516,17 +538,23 @@ struct ContentView: View {
516538 #if targetEnvironment(macCatalyst)
517539 return 200
518540 #else
519- // Check if we're on iPad in landscape
520- let isIPad = UIDevice . current. userInterfaceIdiom == . pad
521- let isLandscape = UIApplication . shared. connectedScenes
522- . compactMap { $0 as? UIWindowScene }
523- . first? . interfaceOrientation. isLandscape ?? false
524-
525- if isIPad && isLandscape {
526- return 280 // Fits map + bottom controls + "Tap to view full map" capsule
527- } else {
528- return 320 // Portrait: extra room for stats panel + map controls + capsule
529- }
541+ // On iPad-landscape we render map+list side-by-side, so height is
542+ // driven by the container, not this constant. Vertical layouts only.
543+ return 320
544+ #endif
545+ }
546+
547+ /// True when we should render map and list side-by-side instead of stacked.
548+ /// Decision is geometry-driven so it reacts to rotation, split-view, and
549+ /// Stage Manager resizing in real time.
550+ private func useSideBySideMapList( for size: CGSize ) -> Bool {
551+ #if targetEnvironment(macCatalyst)
552+ return false
553+ #else
554+ // Wider than tall AND wide enough to fit both panes comfortably.
555+ // 900pt threshold filters out iPhone-Plus landscape (~736pt) and
556+ // narrow Stage-Manager iPad windows.
557+ return size. width > size. height && size. width >= 900
530558 #endif
531559 }
532560
@@ -608,41 +636,65 @@ struct ContentView: View {
608636
609637 private var droneList : some View {
610638 ScrollViewReader { proxy in
611- VStack ( spacing: 0 ) {
612- unifiedMapView ( showAircraft: false )
613- . frame ( height: mapHeight)
614- . padding ( . vertical, 4 )
639+ GeometryReader { geo in
640+ Group {
641+ if useSideBySideMapList ( for: geo. size) {
642+ HStack ( spacing: 0 ) {
643+ unifiedMapView ( showAircraft: false )
644+ . frame ( maxWidth: . infinity, maxHeight: . infinity)
645+ . padding ( . vertical, 4 )
646+ . padding ( . leading, 4 )
647+ . layoutPriority ( 0 )
615648
616- List {
617- Section {
618- ForEach ( cotViewModel. parsedMessages) { item in
619- MessageRow (
620- message: item,
621- cotViewModel: cotViewModel,
622- isCompact: true
623- )
624- . id ( item. uid)
649+ Divider ( )
650+
651+ droneOnlyList
652+ . frame ( minWidth: 340 , idealWidth: 420 , maxWidth: 520 )
653+ . layoutPriority ( 1 )
654+ }
655+ } else {
656+ VStack ( spacing: 0 ) {
657+ unifiedMapView ( showAircraft: false )
658+ . frame ( height: mapHeight)
659+ . padding ( . vertical, 4 )
660+
661+ droneOnlyList
625662 }
626663 }
627664 }
628- . listStyle ( . inset)
629- . scrollContentBackground ( . visible)
630- }
631- . onChange ( of: cotViewModel. parsedMessages) { oldMessages, newMessages in
632- if oldMessages. count < newMessages. count {
633- if let latest = newMessages. last {
634- if !oldMessages. contains ( where: { $0. id == latest. id } ) {
635- latestMessage = latest
636- showAlert = false
637- withAnimation {
638- proxy. scrollTo ( latest. id, anchor: . bottom)
665+ . onChange ( of: cotViewModel. parsedMessages) { oldMessages, newMessages in
666+ if oldMessages. count < newMessages. count {
667+ if let latest = newMessages. last {
668+ if !oldMessages. contains ( where: { $0. id == latest. id } ) {
669+ latestMessage = latest
670+ showAlert = false
671+ withAnimation {
672+ proxy. scrollTo ( latest. id, anchor: . bottom)
673+ }
639674 }
640675 }
641676 }
642677 }
643678 }
644679 }
645680 }
681+
682+ private var droneOnlyList : some View {
683+ List {
684+ Section {
685+ ForEach ( cotViewModel. parsedMessages) { item in
686+ MessageRow (
687+ message: item,
688+ cotViewModel: cotViewModel,
689+ isCompact: true
690+ )
691+ . id ( item. uid)
692+ }
693+ }
694+ }
695+ . listStyle ( . inset)
696+ . scrollContentBackground ( . visible)
697+ }
646698
647699 private func unifiedMapView( showAircraft: Bool = true ) -> some View {
648700 LiveMapPreview ( cotViewModel: cotViewModel, droneCount: cotViewModel. parsedMessages. count, showAircraft: showAircraft, showUnifiedMap: $showUnifiedMap)
0 commit comments