- TypeScript 57.9%
- Swift 35.5%
- Shell 3.4%
- JavaScript 2.2%
- Nix 0.5%
- Other 0.5%
| .eas/workflows | ||
| .maestro | ||
| .pi | ||
| .plans | ||
| assets | ||
| modules/focus-only-timer-core | ||
| scripts | ||
| src | ||
| targets | ||
| .dir-locals.el | ||
| .envrc | ||
| .gitignore | ||
| .prettierignore | ||
| .prettierrc.json | ||
| AGENTS.md | ||
| app.config.ts | ||
| babel.config.js | ||
| bun.lock | ||
| eas.json | ||
| eslint.config.js | ||
| flake.lock | ||
| flake.nix | ||
| index.ts | ||
| lingui.config.js | ||
| metro.config.js | ||
| package.json | ||
| README.md | ||
| skills-lock.json | ||
| tsconfig.json | ||
Focus Only
Agent skills
Agent skill files are kept out of Git. Restore the skills listed in
skills-lock.json after cloning the repository or when the lock file changes:
npx skills experimental_install
Create a TestFlight build
TestFlight builds use the production EAS profile and the production bundle identifier. Update version in app.config.ts to the release version, such as 1.12.0. Commit the version change and create its matching Git tag before building.
Build the iOS app and submit it to App Store Connect:
bunx eas-cli@latest build \
--platform ios \
--profile production \
--auto-submit
The production profile sets APP_VARIANT=production and automatically increments the remote build number. During the first submission, EAS may prompt you for Apple credentials and App Store Connect access.
After Apple processes the upload, open App Store Connect → Focus Only → TestFlight and assign the build to an internal testing group. Don't use the preview or development profiles for TestFlight because they use internal distribution rather than App Store distribution.
App Store screenshots
Generate the English and German App Store screenshots with:
bun run screenshots:app-store
The script builds the test app once, then captures dark-mode portrait screenshots on an iPhone 17 Pro Max and a 13-inch iPad Pro. It writes three screenshots for each locale and device:
assets/screenshots/
iPhone/
en-US/
de-DE/
iPad/
en-US/
de-DE/
Each set contains the running timer at 24:42, statistics populated from the last-ten-days fixture, and the default settings screen. The script fixes the status bar at 9:41, removes PNG alpha channels, and checks the output dimensions against App Store Connect's accepted 6.9-inch iPhone and 13-inch iPad sizes.
Reuse the existing screenshot build while editing the Maestro flow:
./scripts/capture-app-store-screenshots.sh --skip-build
Override SCREENSHOTS_IOS_RUNTIME, SCREENSHOTS_IPHONE_NAME, or SCREENSHOTS_IPAD_NAME when the local simulator names or runtime differ from the defaults.
Fixture links remain available in development and test builds. For example, seed the ten-day statistics fixture in the currently booted development simulator with:
xcrun simctl openurl \
booted \
"focusonly-dev://e2e-setup?sessionHistoryFixture=last-ten-days"
Maestro end-to-end tests
Run a device suite
Run the complete build and test sequence for either device with:
bun run maestro:test:iphone
bun run maestro:test:ipad
The script boots the selected simulator, creates a clean native project for the test variant, builds and installs the Release configuration, and runs the flows configured in .maestro/config.yaml against de.totap.focustimer.test on that exact simulator.
The defaults are iPhone 17 Pro with iOS 26.0 and a dedicated Focus Only iPad Pro 13 with iOS 26.4. The script creates either simulator when it does not exist. The iPhone suite runs 43 flows; the iPad suite excludes the swipe-to-dismiss flow because that gesture is intentionally disabled on iPad.
To reuse an already installed test build while iterating on the Maestro flows, run:
./scripts/run-maestro-ios.sh iphone --skip-build
./scripts/run-maestro-ios.sh ipad --skip-build
Override MAESTRO_IPHONE_NAME, MAESTRO_IPAD_NAME, or MAESTRO_IOS_RUNTIME when the local simulator uses a different name or runtime.
The lower-level bun run ios:test command remains available for manual builds. bun run maestro:test is the low-level iPhone-filtered suite; use the device-specific commands above to apply the correct flow tags and simulator UDID automatically.