expo-dev-menu 57.0.0 → 57.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -10,6 +10,20 @@
10
10
 
11
11
  ### 💡 Others
12
12
 
13
+ ## 57.0.2 — 2026-06-27
14
+
15
+ _This version does not introduce any user-facing changes._
16
+
17
+ ## 57.0.1 — 2026-06-25
18
+
19
+ ### 🎉 New features
20
+
21
+ - Add a `tryToLaunchLastBundle` preference backing the dev launcher auto-launch toggle. ([#47131](https://github.com/expo/expo/pull/47131) by [@alanjhughes](https://github.com/alanjhughes))
22
+
23
+ ### 🐛 Bug fixes
24
+
25
+ - [iOS] Show the floating Tools button at its final position instead of animating in from the top-left corner. ([#46762](https://github.com/expo/expo/pull/46762) by [@alanjhughes](https://github.com/alanjhughes))
26
+
13
27
  ## 57.0.0 — 2026-06-25
14
28
 
15
29
  ### 🐛 Bug fixes
@@ -12,7 +12,7 @@ apply plugin: 'expo-module-gradle-plugin'
12
12
  apply plugin: 'org.jetbrains.kotlin.plugin.compose'
13
13
 
14
14
  group = 'host.exp.exponent'
15
- version = '57.0.0'
15
+ version = '57.0.2'
16
16
 
17
17
  def hasDevLauncher = findProject(":expo-dev-launcher") != null
18
18
  def configureInRelease = findProperty("expo.devmenu.configureInRelease") == "true"
@@ -29,7 +29,7 @@ android {
29
29
 
30
30
  defaultConfig {
31
31
  versionCode 10
32
- versionName '57.0.0'
32
+ versionName '57.0.2'
33
33
  }
34
34
 
35
35
  buildTypes {
@@ -49,6 +49,11 @@ interface DevMenuPreferences {
49
49
  * Whether to show a floating action button that pulls up the DevMenu at launch.
50
50
  */
51
51
  var showFab: Boolean
52
+
53
+ /**
54
+ * Whether to try to launch the most recently opened project at startup instead of showing the launcher.
55
+ */
56
+ var tryToLaunchLastBundle: Boolean
52
57
  }
53
58
 
54
59
  class DevMenuDefaultPreferences(
@@ -68,6 +73,7 @@ class DevMenuDefaultPreferences(
68
73
  private val fabDefault = metaDataBool("EXDevMenuShowFloatingActionButton", true)
69
74
  private val showsAtLaunchDefault = metaDataBool("EXDevMenuShowsAtLaunch", true)
70
75
  private val isOnboardingFinishedDefault = metaDataBool("EXDevMenuIsOnboardingFinished", false)
76
+ private val tryToLaunchLastBundleDefault = metaDataBool("DEV_CLIENT_TRY_TO_LAUNCH_LAST_BUNDLE", true)
71
77
 
72
78
  private val listeners = mutableListOf<() -> Unit>()
73
79
 
@@ -108,4 +114,7 @@ class DevMenuDefaultPreferences(
108
114
 
109
115
  override var showFab: Boolean
110
116
  by preferences(sharedPreferences, fabDefault)
117
+
118
+ override var tryToLaunchLastBundle: Boolean
119
+ by preferences(sharedPreferences, tryToLaunchLastBundleDefault)
111
120
  }
@@ -134,6 +134,7 @@ struct DevMenuFABView: View {
134
134
  @State private var isDragging = false
135
135
  @State private var isPressed = false
136
136
  @State private var dragStartPosition: CGPoint = .zero
137
+ @State private var didPosition = false
137
138
 
138
139
  // Get safe area from window since .ignoresSafeArea() may zero out geometry values
139
140
  private var windowSafeArea: UIEdgeInsets {
@@ -169,23 +170,7 @@ struct DevMenuFABView: View {
169
170
  .position(x: currentFrame.midX, y: currentFrame.midY)
170
171
  .gesture(dragGesture(bounds: geometry.size, safeArea: safeArea))
171
172
  .onAppear {
172
- let initialPos: CGPoint
173
- if let storedPos = Self.loadStoredPosition() {
174
- let margin = FABConstants.margin
175
- let minX = margin / 2
176
- let maxX = geometry.size.width - fabSize.width - margin / 2
177
- let minY = safeArea.top + FABConstants.verticalPadding
178
- let maxY = geometry.size.height - fabSize.height - safeArea.bottom - FABConstants.verticalPadding
179
-
180
- initialPos = CGPoint(
181
- x: storedPos.x.clamped(to: minX...maxX),
182
- y: storedPos.y.clamped(to: minY...maxY)
183
- )
184
- } else {
185
- initialPos = defaultPosition(bounds: geometry.size, safeArea: safeArea)
186
- }
187
- position = initialPos
188
- onFrameChange(CGRect(origin: initialPos, size: fabSize))
173
+ placeInitially(bounds: geometry.size, safeArea: safeArea)
189
174
  }
190
175
  .onChange(of: geometry.size) { newSize in
191
176
  let newPos = snapToEdge(
@@ -197,7 +182,7 @@ struct DevMenuFABView: View {
197
182
  position = newPos
198
183
  onFrameChange(CGRect(origin: newPos, size: fabSize))
199
184
  }
200
- .animation(isDragging ? dragSpring : FABConstants.snapAnimation, value: position)
185
+ .animation(didPosition ? (isDragging ? dragSpring : FABConstants.snapAnimation) : nil, value: position)
201
186
  }
202
187
  .ignoresSafeArea()
203
188
  }
@@ -251,6 +236,31 @@ struct DevMenuFABView: View {
251
236
  }
252
237
  }
253
238
 
239
+ private func placeInitially(bounds: CGSize, safeArea: EdgeInsets) {
240
+ let initialPos: CGPoint
241
+ if let storedPos = Self.loadStoredPosition() {
242
+ let margin = FABConstants.margin
243
+ let minX = margin / 2
244
+ let maxX = bounds.width - fabSize.width - margin / 2
245
+ let minY = safeArea.top + FABConstants.verticalPadding
246
+ let maxY = bounds.height - fabSize.height - safeArea.bottom - FABConstants.verticalPadding
247
+
248
+ initialPos = CGPoint(
249
+ x: storedPos.x.clamped(to: minX...maxX),
250
+ y: storedPos.y.clamped(to: minY...maxY)
251
+ )
252
+ } else {
253
+ initialPos = defaultPosition(bounds: bounds, safeArea: safeArea)
254
+ }
255
+ position = initialPos
256
+ onFrameChange(CGRect(origin: initialPos, size: fabSize))
257
+ // Enable position animations only after the initial placement so the
258
+ // button appears at its final spot instead of sliding in from (0, 0).
259
+ DispatchQueue.main.async {
260
+ didPosition = true
261
+ }
262
+ }
263
+
254
264
  private func defaultPosition(bounds: CGSize, safeArea: EdgeInsets) -> CGPoint {
255
265
  CGPoint(
256
266
  x: bounds.width - fabSize.width - FABConstants.margin / 2,
@@ -30,12 +30,13 @@ struct DevMenuAppInfo: View {
30
30
  label: {
31
31
  HStack {
32
32
  Text(viewModel.clipboardMessage ?? "Copy system info")
33
- .foregroundColor(.blue)
33
+ .foregroundColor(.primary)
34
34
  Spacer()
35
- Image(systemName: "document.on.clipboard")
35
+ Image(systemName: "doc.on.clipboard")
36
36
  .resizable()
37
+ .scaledToFit()
37
38
  .frame(width: 16, height: 16)
38
- .opacity(0.6)
39
+ .foregroundColor(.secondary)
39
40
  }
40
41
  .padding(.vertical)
41
42
  .disabled(viewModel.clipboardMessage != nil)
@@ -33,6 +33,7 @@ struct DevMenuRootView: View {
33
33
  .id(navigationId)
34
34
  .onReceive(DevMenuManager.shared.menuWillShowPublisher) { _ in
35
35
  navigationId = UUID()
36
+ viewModel.refresh()
36
37
  }
37
38
  #else
38
39
  NavigationStack {
@@ -41,6 +42,7 @@ struct DevMenuRootView: View {
41
42
  .id(navigationId)
42
43
  .onReceive(DevMenuManager.shared.menuWillShowPublisher) { _ in
43
44
  navigationId = UUID()
45
+ viewModel.refresh()
44
46
  }
45
47
  #endif
46
48
  }
@@ -32,6 +32,10 @@ class DevMenuViewModel: ObservableObject {
32
32
  loadFloatingActionButtonState()
33
33
  }
34
34
 
35
+ func refresh() {
36
+ loadData()
37
+ }
38
+
35
39
  private func loadAppInfo() {
36
40
  let appInfoDict = devMenuManager.getAppInfo()
37
41
 
@@ -114,7 +118,7 @@ class DevMenuViewModel: ObservableObject {
114
118
  func copyToClipboard(_ content: String) {
115
119
  #if !os(tvOS) && !os(macOS)
116
120
  UIPasteboard.general.string = content
117
- hostUrlCopiedMessage = "Copied!"
121
+ hostUrlCopiedMessage = "Copied to clipboard"
118
122
 
119
123
  DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
120
124
  self.hostUrlCopiedMessage = nil
@@ -145,7 +149,7 @@ class DevMenuViewModel: ObservableObject {
145
149
  let jsonString = jsonData.flatMap { String(data: $0, encoding: .utf8) } ?? "Unable to serialize app info"
146
150
 
147
151
  UIPasteboard.general.string = jsonString
148
- clipboardMessage = "Copied to clipboard!"
152
+ clipboardMessage = "Copied to clipboard"
149
153
 
150
154
  DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
151
155
  self.clipboardMessage = nil
@@ -24,7 +24,10 @@ struct HostUrl: View {
24
24
  Spacer()
25
25
 
26
26
  Image(systemName: "doc.on.clipboard")
27
- .foregroundColor(.secondary.opacity(0.7))
27
+ .resizable()
28
+ .scaledToFit()
29
+ .frame(width: 16, height: 16)
30
+ .foregroundColor(.secondary)
28
31
  }
29
32
  .padding()
30
33
  .background(Color.expoSecondarySystemBackground)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-dev-menu",
3
- "version": "57.0.0",
3
+ "version": "57.0.2",
4
4
  "description": "Expo/React Native module with the developer menu.",
5
5
  "main": "build/DevMenu.js",
6
6
  "types": "build/DevMenu.d.ts",
@@ -32,15 +32,15 @@
32
32
  "@types/node": "^22.14.0",
33
33
  "react": "19.2.3",
34
34
  "react-native": "0.86.0",
35
- "babel-preset-expo": "57.0.0",
36
- "expo": "57.0.0-preview.0",
37
- "expo-module-scripts": "56.0.3"
35
+ "expo": "57.0.0-preview.1",
36
+ "expo-module-scripts": "56.0.3",
37
+ "babel-preset-expo": "57.0.0"
38
38
  },
39
39
  "peerDependencies": {
40
40
  "expo": "*",
41
41
  "react-native": "*"
42
42
  },
43
- "gitHead": "e3eb896c5fdcd89e0cded98ff4e35c9db12cc9c0",
43
+ "gitHead": "b134c8d1eb906156ef7d5f6fee1a70ff0d9a8f22",
44
44
  "scripts": {
45
45
  "build": "expo-module build",
46
46
  "clean": "expo-module clean",
@@ -1 +0,0 @@
1
- {"root":["./src/index.ts","./src/withdevmenu.ts"],"version":"6.0.3"}