1diff --git a/build/Widgets.types.d.ts b/build/Widgets.types.d.ts
2index c9f4dfe1f10a3a65ab72bd7e639f6f6d148e5f95..dc0792e1cd7b6762394de5e3fefa0863a253f70c 100644
3--- a/build/Widgets.types.d.ts
4+++ b/build/Widgets.types.d.ts
5@@ -93,6 +93,10 @@ export type LiveActivityEnvironment = {
6 * The color scheme of the activity's environment.
7 */
8 colorScheme: 'light' | 'dark';
9+ /** The rendering mode supplied by WidgetKit for this presentation. */
10+ widgetRenderingMode?: WidgetRenderingMode;
11+ /** Whether the presentation OS supports Liquid Glass. @platform iOS 26+ */
12+ isLiquidGlassAvailable?: boolean;
13 /**
14 * Whether the activity is displayed in a context with reduced luminance.
15 * @platform iOS 16+
16diff --git a/ios/LiveActivityFactory.swift b/ios/LiveActivityFactory.swift
17index 29e4f1566d188665e301d32bc1ad85640f83e68c..7143f9a1c06245d16649b981d7f5cde1d8868217 100644
18--- a/ios/LiveActivityFactory.swift
19+++ b/ios/LiveActivityFactory.swift
20@@ -19,6 +19,14 @@ final class LiveActivityFactory: SharedObject {
21 throw LiveActivitiesNotSupportedException()
22 }
23
24+ // iOS ends an activity after 8 hours but leaves it frozen on the Lock Screen for up to
25+ // 4 more. getInstances() can't see it, so a caller starting a replacement would leave
26+ // both cards visible. Dismiss the ended ones now.
27+ for activity in Activity<LiveActivityAttributes>.activities
28+ where activity.content.state.name == name && activity.activityState == .ended {
29+ Task { await activity.end(nil, dismissalPolicy: .immediate) }
30+ }
31+
32 do {
33 let initialState = LiveActivityAttributes.ContentState(name: name, props: props)
34 let activity = try Activity.request(
35diff --git a/ios/Widgets/Utils.swift b/ios/Widgets/Utils.swift
36index 76b2199c2d3f879463fed5d2bd49038b0428a2e7..6cb52c3de4ad9696bc0c51ad62401cce96f405e7 100644
37--- a/ios/Widgets/Utils.swift
38+++ b/ios/Widgets/Utils.swift
39@@ -125,6 +125,10 @@ func getLiveActivityEnvironment(for environment: EnvironmentValues, in context:
40 "isStale": context.isStale
41 ]
42
43+ if #available(iOS 26.0, *) {
44+ env["isLiquidGlassAvailable"] = true
45+ }
46+ env["widgetRenderingMode"] = environment.widgetRenderingMode.description
47 if #available(iOS 18.0, *) {
48 env["isActivityUpdateReduced"] = environment.isActivityUpdateReduced
49 env["activityFamily"] = "\(environment.activityFamily)"
50diff --git a/ios/Widgets/WidgetLiveActivity.swift b/ios/Widgets/WidgetLiveActivity.swift
51index 4d135c4e4eb1f846315654ed9d835a935d120d42..dae47445b27516fa2464a25b68bddc8f6dec3e1e 100644
52--- a/ios/Widgets/WidgetLiveActivity.swift
53+++ b/ios/Widgets/WidgetLiveActivity.swift
54@@ -19,53 +19,41 @@ struct LiveActivityAttributes: ActivityAttributes {
55 }
56
57 public struct WidgetLiveActivity: Widget {
58- @Environment(\.self) var env
59-
60 let widgetContext: AppContext = AppContext()
61
62 public init() {}
63
64 public var body: some WidgetConfiguration {
65 ActivityConfiguration(for: LiveActivityAttributes.self) { context in
66- let nodes = getLiveActivityNodes(
67- forName: context.state.name,
68- props: context.state.props,
69- environment: getLiveActivityEnvironment(for: env, in: context)
70- )
71 // Only apply widgetURL when the activity has one: a hierarchy with more than one
72 // widgetURL modifier is undefined behavior, and layouts can set their own through
73 // the widgetURL modifier from @expo/ui.
74- let banner = LiveActivityBannerView(context: context, nodes: nodes)
75+ let banner = LiveActivityBannerView(context: context)
76 if let url = context.attributes.url.flatMap(URL.init(string:)) {
77 banner.widgetURL(url)
78 } else {
79 banner
80 }
81 } dynamicIsland: { context in
82- let nodes = getLiveActivityNodes(
83- forName: context.state.name,
84- props: context.state.props,
85- environment: getLiveActivityEnvironment(for: env, in: context)
86- )
87 let island = DynamicIsland {
88 DynamicIslandExpandedRegion(.center) {
89- LiveActivitySectionView(context: context, nodes: nodes, sectionName: "expandedCenter")
90+ LiveActivitySectionView(context: context, sectionName: "expandedCenter")
91 }
92 DynamicIslandExpandedRegion(.leading) {
93- LiveActivitySectionView(context: context, nodes: nodes, sectionName: "expandedLeading")
94+ LiveActivitySectionView(context: context, sectionName: "expandedLeading")
95 }
96 DynamicIslandExpandedRegion(.trailing) {
97- LiveActivitySectionView(context: context, nodes: nodes, sectionName: "expandedTrailing")
98+ LiveActivitySectionView(context: context, sectionName: "expandedTrailing")
99 }
100 DynamicIslandExpandedRegion(.bottom) {
101- LiveActivitySectionView(context: context, nodes: nodes, sectionName: "expandedBottom")
102+ LiveActivitySectionView(context: context, sectionName: "expandedBottom")
103 }
104 } compactLeading: {
105- LiveActivitySectionView(context: context, nodes: nodes, sectionName: "compactLeading")
106+ LiveActivitySectionView(context: context, sectionName: "compactLeading")
107 } compactTrailing: {
108- LiveActivitySectionView(context: context, nodes: nodes, sectionName: "compactTrailing")
109+ LiveActivitySectionView(context: context, sectionName: "compactTrailing")
110 } minimal: {
111- LiveActivitySectionView(context: context, nodes: nodes, sectionName: "minimal")
112+ LiveActivitySectionView(context: context, sectionName: "minimal")
113 }
114 if let url = context.attributes.url.flatMap(URL.init(string:)) {
115 return island.widgetURL(url)
116@@ -77,11 +65,16 @@ public struct WidgetLiveActivity: Widget {
117 }
118
119 private struct LiveActivitySectionView: View {
120+ @Environment(\.self) var env
121 let context: ActivityViewContext<LiveActivityAttributes>
122- let nodes: [String: Any]
123 let sectionName: String
124
125 var body: some View {
126+ let nodes = getLiveActivityNodes(
127+ forName: context.state.name,
128+ props: context.state.props,
129+ environment: getLiveActivityEnvironment(for: env, in: context)
130+ )
131 if let node = nodes[sectionName] as? [String: Any] {
132 WidgetsDynamicView(name: context.activityID, kind: .liveActivity, node: node)
133 } else {
134@@ -91,10 +84,17 @@ private struct LiveActivitySectionView: View {
135 }
136
137 private struct LiveActivityBannerView: View {
138+ // Read inside the rendered view: Widget-level values do not include the
139+ // presentation's color scheme, rendering mode, or reduced-luminance state.
140+ @Environment(\.self) var env
141 var context: ActivityViewContext<LiveActivityAttributes>
142- let nodes: [String: Any]
143
144 var body: some View {
145+ let nodes = getLiveActivityNodes(
146+ forName: context.state.name,
147+ props: context.state.props,
148+ environment: getLiveActivityEnvironment(for: env, in: context)
149+ )
150 if #available(iOS 18.0, *) {
151 LiveActivityBanner(context: context, nodes: nodes)
152 } else if let node = nodes["banner"] as? [String: Any] {
153diff --git a/scripts/build-layout-registry.mjs b/scripts/build-layout-registry.mjs
154index d52a60e342c8e0a8a1d78f7174da5e60e6bd1c53..fa9bd77c5c04d459038a5b4a00e1391780b406fa 100644
155--- a/scripts/build-layout-registry.mjs
156+++ b/scripts/build-layout-registry.mjs
157@@ -154,6 +154,8 @@ function createLayoutRegistry(expectedWidgets, capturedLayouts) {
158 return registry;
159 }
160
161-if (process.argv[1] && path.resolve(process.argv[1]) === __filename) {
162+// Compare real paths: pnpm exposes the package through a symlink, so the
163+// invoked path and import.meta.url differ while naming the same file.
164+if (process.argv[1] && fs.realpathSync(path.resolve(process.argv[1])) === fs.realpathSync(__filename)) {
165 await main();
166 }
167diff --git a/src/Widgets.types.ts b/src/Widgets.types.ts
168index 07706139379384dae27befef4e3e5ae09d04a0db..924fa0029243664e369ce08c81e504b6300bfc7f 100644
169--- a/src/Widgets.types.ts
170+++ b/src/Widgets.types.ts
171@@ -107,6 +107,10 @@ export type LiveActivityEnvironment = {
172 * The color scheme of the activity's environment.
173 */
174 colorScheme: 'light' | 'dark';
175+ /** The rendering mode supplied by WidgetKit for this presentation. */
176+ widgetRenderingMode?: WidgetRenderingMode;
177+ /** Whether the presentation OS supports Liquid Glass. @platform iOS 26+ */
178+ isLiquidGlassAvailable?: boolean;
179 /**
180 * Whether the activity is displayed in a context with reduced luminance.
181 * @platform iOS 16+