Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
c4e31df
[widgets] Add widget RedBox (#45402)
jakex7 May 6, 2026
aaca88c
[ui] document community bottom sheet (#45412)
Kudo May 6, 2026
e9f9b76
chore: Replace incorrect `workspace:*` specifiers in `peerDependencie…
kitten May 6, 2026
10fa49b
feat(metro-config,metro-file-map): Add project-level crawl ignore pat…
kitten May 6, 2026
d323ecd
[tools] Migrate to pnpm (#45427)
alanjhughes May 6, 2026
485c37a
[tools] Update npm commands to pnpm (#45428)
alanjhughes May 6, 2026
964f25d
[tools] Drop remaining yarn and npm refs (#45429)
alanjhughes May 6, 2026
084055e
[ui] TextField selection fixes (#45424)
intergalacticspacehighway May 6, 2026
5aaa8cb
[expo-router] pin screens version to exact beta.1 (#45421)
Ubax May 6, 2026
cb5a77f
[ui][docs] Update TextField docs for SwiftUI and Jetpack Compose (#45…
intergalacticspacehighway May 6, 2026
326540f
fix(precompile): fix log warning being treated as error in pod instal…
chrfalch May 6, 2026
f4b5910
[docs] Fix indentations in the tutorial code piece (#45436)
masiama May 6, 2026
36b015d
[app-metrics] Add device/network params to TTI metric (#45414)
tsapeta May 6, 2026
897e82a
[docs] Update maestro cloud job options (#45442)
hSATAC May 6, 2026
19b7c3f
fix(precompile): reduce prominence of 3rd party fallback logs (#45435)
chrfalch May 6, 2026
15c6f72
[tools] Use workspace:* for @expo packages (#45432)
alanjhughes May 6, 2026
a8cdc17
fix(cli): Remove remaining startup delay from `DevelopmentSession` an…
kitten May 6, 2026
1b0052a
[tools] Fix canary publishing (#45444)
alanjhughes May 6, 2026
1a4c17d
fix(cli): Prevent timeouts/intervals or own timeout from preventing f…
kitten May 6, 2026
1c87783
[docs] Update EmDash Vale rule (#45437)
amandeepmittal May 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import {
TextFieldKeyboardType,
TextFieldImeAction,
TextFieldCapitalization,
TextFieldValue,
OutlinedTextField,
Button,
Host,
Expand All @@ -28,10 +27,8 @@ export default function TextFieldScreen() {
const [lastAction, setLastAction] = React.useState('');
const textRef = React.useRef<TextFieldRef>(null);

const maskedPhone = useNativeState<TextFieldValue>({
text: '',
selection: { start: 0, end: 0 },
});
const maskedPhoneText = useNativeState('');
const maskedPhoneSelection = useNativeState({ start: 0, end: 0 });

const imperativeText = useNativeState('Select me!');
const imperativeSelection = useNativeState<{ start: number; end: number }>({ start: 0, end: 0 });
Expand Down Expand Up @@ -151,12 +148,13 @@ export default function TextFieldScreen() {
<Column modifiers={[p]} verticalArrangement={{ spacedBy: 8 }}>
<ComposeText style={{ typography: 'labelLarge' }}>Worklet Phone Masking</ComposeText>
<TextField
value={maskedPhone}
value={maskedPhoneText}
selection={maskedPhoneSelection}
keyboardOptions={{ keyboardType: 'phone' }}
modifiers={[fillMaxWidth()]}
onValueChange={(v) => {
'worklet';
const digits = v.text.replace(/\D/g, '').slice(0, 10);
const digits = v.replace(/\D/g, '').slice(0, 10);
let formatted: string;
if (digits.length === 0) {
formatted = '';
Expand All @@ -167,11 +165,9 @@ export default function TextFieldScreen() {
} else {
formatted = `(${digits.slice(0, 3)}) ${digits.slice(3, 6)}-${digits.slice(6)}`;
}
if (formatted !== v.text) {
maskedPhone.value = {
text: formatted,
selection: { start: formatted.length, end: formatted.length },
};
if (formatted !== v) {
maskedPhoneText.value = formatted;
maskedPhoneSelection.value = { start: formatted.length, end: formatted.length };
}
}}>
<TextField.Placeholder>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import {
foregroundStyle,
} from '@expo/ui/swift-ui/modifiers';
import * as React from 'react';
import { runOnJS } from 'react-native-worklets';

export default function TextFieldScreen() {
const textRef = React.useRef<TextFieldRef>(null);
Expand All @@ -41,13 +40,6 @@ export default function TextFieldScreen() {
const maskedPhone = useNativeState('');
const phoneSelection = useNativeState<TextFieldSelection>({ start: 0, end: 0 });

const setPhoneCursor = React.useCallback(
(position: number) => {
phoneSelection.value = { start: position, end: position };
},
[phoneSelection]
);

const submitLabelOptions = [
'continue',
'done',
Expand Down Expand Up @@ -128,8 +120,7 @@ export default function TextFieldScreen() {
}
if (formatted !== v) {
maskedPhone.value = formatted;
// To keep selection at the end of the input while typing
runOnJS(setPhoneCursor)(formatted.length);
phoneSelection.value = { start: formatted.length, end: formatted.length };
}
}}
/>
Expand Down
6 changes: 4 additions & 2 deletions docs/.vale/writing-styles/expo-docs/EmDash.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
extends: substitution
message: "Instead of '-' or '--', use '&mdash;'. Markdown renders this nicely."
message: "Use '%s' instead. Markdown renders this nicely."
link: 'https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md#use-mdash'
level: suggestion
scope: raw
# Look for ' - ' (space, hyphen, space) or ' -- ' (space, two hyphens, space)
nonword: true
# Look for ' - ' (space, hyphen, space), ' -- ' (space, two hyphens, space), or '—' (Unicode em-dash, U+2014)
swap:
' - ': ' &mdash; '
' -- ': ' &mdash; '
'—': '&mdash;'
6 changes: 6 additions & 0 deletions docs/pages/eas/observe/reference/metrics.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,12 @@ Each TTI event includes extra params to help triage issues:
- **`expo.frameRate.slowFrames`** (count): Frames that took 17ms or longer to render. If this is high relative to the startup duration, the main thread was consistently busy during launch. Points to heavy layout work, synchronous bridge calls, or too many components rendering at once.
- **`expo.frameRate.frozenFrames`** (count): Frames that took 700ms or longer to render. These are hard freezes where the app visibly hung. Even one during startup is a serious issue. Usually caused by synchronous I/O, large JSON parsing, or blocking network calls on the main thread.
- **`expo.frameRate.totalDelay`** (seconds): Total accumulated time all frames exceeded their target duration. This is the single best "smoothness" number. Compare it to TTI: if TTI is 2.5s and `totalDelay` is 0.1s, startup was slow but smooth (the time was spent on legitimate work). If `totalDelay` is 1.5s, the app was janky for most of the startup, and the user was staring at a stuttering screen.
- **`expo.device.lowPowerMode`** (boolean): Whether the OS power-saver mode (Low Power Mode on iOS, Battery Saver on Android) was active when TTI was reported. Power-saver mode throttles CPU, GPU, and background activity, so a TTI regression that disappears once this flag is filtered out is environmental rather than a code change.
- **`expo.device.batteryLevel`** (number, 0–1): Fractional battery charge at TTI. Useful for ruling out thermal/throttling effects on devices that aggressively manage performance at low charge. Omitted when the OS does not report a value.
- **`expo.device.batteryCharging`** (boolean): Whether the device was plugged in or wirelessly charging. Charging tends to raise sustained CPU performance ceilings on iOS and some Android OEMs, so non-charging samples are the more conservative population to compare against.
- **`expo.device.thermalState`** (string): One of `nominal`, `fair`, `serious`, `critical`, `unknown`. Sustained `serious`/`critical` states cause the OS to throttle the CPU/GPU and can dramatically slow startup independent of any app change.
- **`expo.network.connected`** (boolean): Whether the device had an internet-capable network at TTI. If TTI degrades only when this is `true`, the cause is likely a network-bound startup path; if it degrades when `false`, the app is doing more work than it should before showing cached content.
- **`expo.network.type`** (string): One of `wifi`, `cellular`, `ethernet`, `none`, `other`, `unknown`. Use to compare cellular versus Wi-Fi populations &mdash; large gaps usually point to network-bound startup work. VPN traffic is reported as the underlying transport (typically `wifi` or `cellular`) since the VPN tunnels over it. The value set is intentionally the same on Android and iOS so dashboards don't need per-platform branching.

**How to interpret them:**

Expand Down
12 changes: 5 additions & 7 deletions docs/pages/eas/workflows/pre-packaged-jobs.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -1012,11 +1012,10 @@ jobs:
include_tags: string | string[] # optional - tags to include in the tests. Will be passed to Maestro as `--include-tags`.
exclude_tags: string | string[] # optional - tags to exclude from the tests. Will be passed to Maestro as `--exclude-tags`.
maestro_version: string # optional - version of Maestro to use for the tests. If not provided, the latest version will be used.
android_api_level: string # optional - Android API level to use for the tests. Will be passed to Maestro as `--android-api-level`.
maestro_config: string # optional - path to the Maestro `config.yaml` file to use for the tests. Will be passed to Maestro as `--config`.
device_locale: string # optional - device locale to use for the tests. Will be passed to Maestro as `--device-locale`. Run `maestro cloud --help` for a list of supported values.
device_model: string # optional - model of the device to use for the tests. Will be passed to Maestro as `--device-model`. Run `maestro cloud --help` for a list of supported values.
device_os: string # optional - OS of the device to use for the tests. Will be passed to Maestro as `--device-os`. Run `maestro cloud --help` for a list of supported values.
device_locale: string # optional - device locale to use for the tests. Will be passed to Maestro as `--device-locale`.
device_model: string # optional - model of the device to use for the tests. Will be passed to Maestro as `--device-model`. Run `maestro list-cloud-devices` for a list of supported values.
device_os: string # optional - OS of the device to use for the tests. Will be passed to Maestro as `--device-os`. Run `maestro list-cloud-devices` for a list of supported values.
name: string # optional - name for the Maestro Cloud upload. Corresponds to `--name` param to `maestro cloud`.
branch: string # optional - override for the branch the Maestro Cloud upload originated from. By default, if the workflow run has been triggered from GitHub, the branch of the workflow run will be used. Corresponds to `--branch` param to `maestro cloud`.
async: boolean # optional - run the Maestro Cloud tests asynchronously. If true, the status of the job will only denote whether the upload was successful, _not_ whether the tests succeeded. Corresponds to `--async` param to `maestro cloud`.
Expand All @@ -1035,11 +1034,10 @@ You can pass the following parameters into the `params` list:
| include_tags | string | Optional. The tags to include in the tests. Corresponds to `--include-tags` param to `maestro cloud`. Example: `"pull,push"`. |
| exclude_tags | string | Optional. The tags to exclude from the tests. Corresponds to `--exclude-tags` param to `maestro cloud`. Example: `"disabled"`. |
| maestro_version | string | Optional. The version of Maestro to use. Example: `1.30.0`. |
| android_api_level | string | Optional. The Android API level to use. Corresponds to `--android-api-level` param to `maestro cloud`. Example: `29`. |
| maestro_config | string | Optional. The path to the Maestro `config.yaml` file to use. Corresponds to `--config` param to `maestro cloud`. Example: `.maestro/config.yaml`. |
| device_locale | string | Optional. The locale that will be set on devices used for the tests. Corresponds to `--device-locale` param to `maestro cloud`. Example: `pl_PL`. |
| device_model | string | Optional. The model of the device to use for the tests. Corresponds to `--device-model` param to `maestro cloud`. Example: `iPhone-11`. Run `maestro cloud --help` for a list of supported values. |
| device_os | string | Optional. The OS of the device to use for the tests. Corresponds to `--device-os` param to `maestro cloud`. Example: `iOS-18-2`. Run `maestro cloud --help` for a list of supported values. |
| device_model | string | Optional. The model of the device to use for the tests. Corresponds to `--device-model` param to `maestro cloud`. Example: `iPhone-11`. Run `maestro list-cloud-devices` for a list of supported values. |
| device_os | string | Optional. The OS of the device to use for the tests. Corresponds to `--device-os` param to `maestro cloud`. Example: `iOS-18-2`. Run `maestro list-cloud-devices` for a list of supported values. |
| name | string | Optional. Name for the Maestro Cloud upload. Corresponds to `--name` param to `maestro cloud`. |
| branch | string | Optional. Override for the branch the Maestro Cloud upload originated from. By default, if the workflow run has been triggered from GitHub, the branch of the workflow run will be used. Corresponds to `--branch` param to `maestro cloud`. |
| async | boolean | Optional. Run the Maestro Cloud tests asynchronously. If true, the status of the job will only denote whether the upload was successful, _not_ whether the tests succeeded. Corresponds to `--async` param to `maestro cloud`. |
Expand Down
7 changes: 3 additions & 4 deletions docs/pages/eas/workflows/syntax.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -1271,11 +1271,10 @@ jobs:
include_tags: string | string[] # optional. Tags to include in the tests. Will be passed to Maestro as `--include-tags`.
exclude_tags: string | string[] # optional. Tags to exclude from the tests. Will be passed to Maestro as `--exclude-tags`.
maestro_version: string # optional. Version of Maestro to use for the tests. If not provided, the latest version will be used.
android_api_level: string # optional. Android API level to use for the tests. Will be passed to Maestro as `--android-api-level`.
maestro_config: string # optional. Path to the Maestro `config.yaml` file to use for the tests. Will be passed to Maestro as `--config`.
device_locale: string # optional. Device locale to use for the tests. Will be passed to Maestro as `--device-locale`. Run `maestro cloud --help` for a list of supported values.
device_model: string # optional. Model of the device to use for the tests. Will be passed to Maestro as `--device-model`. Run `maestro cloud --help` for a list of supported values.
device_os: string # optional. OS of the device to use for the tests. Will be passed to Maestro as `--device-os`. Run `maestro cloud --help` for a list of supported values.
device_locale: string # optional. Device locale to use for the tests. Will be passed to Maestro as `--device-locale`.
device_model: string # optional. Model of the device to use for the tests. Will be passed to Maestro as `--device-model`. Run `maestro list-cloud-devices` for a list of supported values.
device_os: string # optional. OS of the device to use for the tests. Will be passed to Maestro as `--device-os`. Run `maestro list-cloud-devices` for a list of supported values.
name: string # optional. Name for the Maestro Cloud upload. Corresponds to `--name` param to `maestro cloud`.
branch: string # optional. Override for the branch the Maestro Cloud upload originated from. By default, if the workflow run has been triggered from GitHub, the branch of the workflow run will be used. Corresponds to `--branch` param to `maestro cloud`.
async: boolean # optional. Run the Maestro Cloud tests asynchronously. If true, the status of the job will only denote whether the upload was successful, *not* whether the tests succeeded. Corresponds to `--async` param to `maestro cloud`.
Expand Down
20 changes: 10 additions & 10 deletions docs/pages/tutorial/create-a-modal.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -346,17 +346,17 @@ type Props = PropsWithChildren<{
export default function EmojiPicker({ isVisible, children, onClose }: Props) {
return (
<View>
<Modal animationType="slide" transparent={true} visible={isVisible}>
<View style={styles.modalContent}>
<View style={styles.titleContainer}>
<Text style={styles.title}>Choose a sticker</Text>
<Pressable onPress={onClose}>
<MaterialIcons name="close" color="#fff" size={22} />
</Pressable>
<Modal animationType="slide" transparent={true} visible={isVisible}>
<View style={styles.modalContent}>
<View style={styles.titleContainer}>
<Text style={styles.title}>Choose a sticker</Text>
<Pressable onPress={onClose}>
<MaterialIcons name="close" color="#fff" size={22} />
</Pressable>
</View>
{children}
</View>
{children}
</View>
</Modal>
</Modal>
</View>
);
}
Expand Down
Loading
Loading