diff --git a/packages/core/application/application-common.ts b/packages/core/application/application-common.ts index 960932081..30412a3f5 100644 --- a/packages/core/application/application-common.ts +++ b/packages/core/application/application-common.ts @@ -15,6 +15,7 @@ import type { ApplicationEventData, CssChangedEventData, DiscardedErrorEventData import { readyInitAccessibilityCssHelper, readyInitFontScale } from '../accessibility/accessibility-common'; import { getAppMainEntry, isAppInBackground, setAppInBackground, setAppMainEntry } from './helpers-common'; import { getNativeScriptGlobals } from '../globals/global-utils'; +import { SDK_VERSION } from '../utils/constants'; // prettier-ignore const ORIENTATION_CSS_CLASSES = [ @@ -29,6 +30,54 @@ const SYSTEM_APPEARANCE_CSS_CLASSES = [ `${CSSUtils.CLASS_PREFIX}${CoreTypes.SystemAppearance.dark}`, ]; +// SDK Version CSS classes +let sdkVersionClasses: string[] = []; + +export function initializeSdkVersionClass(rootView: View): void { + const majorVersion = Math.floor(SDK_VERSION); + sdkVersionClasses = []; + + let platformPrefix = ''; + if (__APPLE__) { + platformPrefix = __VISIONOS__ ? 'ns-visionos' : 'ns-ios'; + } else if (__ANDROID__) { + platformPrefix = 'ns-android'; + } + + if (platformPrefix) { + // Add exact version class (e.g., .ns-ios-26 or .ns-android-36) + // this acts like 'gte' for that major version range + // e.g., if user wants iOS 27, they can add .ns-ios-27 specifiers + sdkVersionClasses.push(`${platformPrefix}-${majorVersion}`); + } + + // Apply the SDK version classes to root views + applySdkVersionClass(rootView); +} + +function applySdkVersionClass(rootView: View): void { + if (!sdkVersionClasses.length) { + return; + } + + if (!rootView) { + return; + } + + // Batch apply all SDK version classes to root view for better performance + const classesToAdd = sdkVersionClasses.filter((className) => !rootView.cssClasses.has(className)); + classesToAdd.forEach((className) => rootView.cssClasses.add(className)); + + // Apply to modal views only if there are any + const rootModalViews = >rootView._getRootModalViews(); + if (rootModalViews.length > 0) { + rootModalViews.forEach((rootModalView) => { + const modalClassesToAdd = sdkVersionClasses.filter((className) => !rootModalView.cssClasses.has(className)); + modalClassesToAdd.forEach((className) => rootModalView.cssClasses.add(className)); + }); + } +} + const globalEvents = getNativeScriptGlobals().events; // helper interface to correctly type Application event handlers diff --git a/packages/core/application/application.android.ts b/packages/core/application/application.android.ts index fb0367171..1f400277e 100644 --- a/packages/core/application/application.android.ts +++ b/packages/core/application/application.android.ts @@ -3,7 +3,7 @@ import type { View } from '../ui/core/view'; import { AndroidActivityCallbacks, NavigationEntry } from '../ui/frame/frame-common'; import { SDK_VERSION } from '../utils/constants'; import { android as androidUtils } from '../utils'; -import { ApplicationCommon } from './application-common'; +import { ApplicationCommon, initializeSdkVersionClass } from './application-common'; import type { AndroidActivityBundleEventData, AndroidActivityEventData, ApplicationEventData } from './application-interfaces'; import { Observable } from '../data/observable'; import { Trace } from '../trace'; @@ -715,6 +715,9 @@ export function ensureClasses() { setFontScaleCssClasses(new Map(VALID_FONT_SCALES.map((fs) => [fs, `a11y-fontscale-${Number(fs * 100).toFixed(0)}`]))); accessibilityServiceObservable = new AccessibilityServiceEnabledObservable(); + + // Initialize SDK version CSS class once + initializeSdkVersionClass(Application.getRootView()); } export function updateCurrentHelperClasses(applyRootCssClass: (cssClasses: string[], newCssClass: string) => void): void { diff --git a/packages/core/application/application.ios.ts b/packages/core/application/application.ios.ts index 8ae527436..874105909 100644 --- a/packages/core/application/application.ios.ts +++ b/packages/core/application/application.ios.ts @@ -6,7 +6,7 @@ import type { NavigationEntry } from '../ui/frame/frame-interfaces'; import { getWindow } from '../utils/native-helper'; import { SDK_VERSION } from '../utils/constants'; import { ios as iosUtils } from '../utils/native-helper'; -import { ApplicationCommon } from './application-common'; +import { ApplicationCommon, initializeSdkVersionClass } from './application-common'; import { ApplicationEventData } from './application-interfaces'; import { Observable } from '../data/observable'; import { Trace } from '../trace'; @@ -441,6 +441,20 @@ export class iOSApplication extends ApplicationCommon { // Observers @profile private didFinishLaunchingWithOptions(notification: NSNotification) { + if (__DEV__) { + /** + * v9+ runtime crash handling + * When crash occurs during boot, we let runtime take over + */ + if (notification.userInfo) { + const isBootCrash = notification.userInfo.objectForKey('NativeScriptBootCrash'); + if (isBootCrash) { + // fatal crash will show in console without app exiting + // allowing hot reload fixes to continue + return; + } + } + } this.setMaxRefreshRate(); // ensures window is assigned to proper window scene setiOSWindow(this.window); @@ -935,6 +949,9 @@ export function ensureClasses() { setFontScaleCssClasses(new Map(VALID_FONT_SCALES.map((fs) => [fs, `a11y-fontscale-${Number(fs * 100).toFixed(0)}`]))); accessibilityServiceObservable = new AccessibilityServiceEnabledObservable(); + + // Initialize SDK version CSS class once + initializeSdkVersionClass(Application.getRootView()); } export function updateCurrentHelperClasses(applyRootCssClass: (cssClasses: string[], newCssClass: string) => void): void {