diff --git a/ios/AGENTS.md b/ios/AGENTS.md index 53c322b..c6d45dd 100644 --- a/ios/AGENTS.md +++ b/ios/AGENTS.md @@ -8,8 +8,19 @@ Instructions for work under `/Users/buzzert/src/sybil-2/ios`. - `just build` will: 1. generate `Sybil.xcodeproj` with `xcodegen` if missing, 2. build scheme `Sybil` for `iPhone 16e` simulator. +- Preferred test command: `just test` +- `just test` runs the Swift package tests through `xcodebuild test` on the `iPhone 16e` iOS simulator from `ios/Packages/Sybil`. +- `just test` disables Xcode parallel testing because the current async view-model tests use timing-sensitive selection tasks. +- Do not use plain `swift test` for this package; it runs as host macOS and hits a deployment mismatch with `MarkdownUI`. - If `xcbeautify` is installed it is used automatically; otherwise raw `xcodebuild` output is used. +## Simulator Workflow +- Run the app in the simulator with `just run` from `/Users/buzzert/src/sybil-2/ios`. +- `just run` boots the `iPhone 16e` simulator if needed, builds with a stable derived data path, installs `Sybil.app`, and launches bundle id `net.buzzert.sybil2`. +- Capture a simulator screenshot with `just screenshot` from `/Users/buzzert/src/sybil-2/ios`; it writes `build/sybil-screenshot.png` by default. +- To choose a screenshot path, run `just screenshot path=build/name.png`. +- The underlying screenshot command is `xcrun simctl io booted screenshot ` and requires a booted simulator. + ## App Structure - App target entry: `/Users/buzzert/src/sybil-2/ios/Apps/Sybil/Sources/SybilApp.swift` - Shared iOS app code lives in Swift package: diff --git a/ios/Packages/Sybil/Sources/Sybil/SybilWorkspaceView.swift b/ios/Packages/Sybil/Sources/Sybil/SybilWorkspaceView.swift index b55dafc..81099c4 100644 --- a/ios/Packages/Sybil/Sources/Sybil/SybilWorkspaceView.swift +++ b/ios/Packages/Sybil/Sources/Sybil/SybilWorkspaceView.swift @@ -62,6 +62,14 @@ struct SybilWorkspaceView: View { return "chat:none" } + private var shouldAutoFocusComposer: Bool { + viewModel.displayedMessages.isEmpty + } + + private var composerFocusPolicyID: String { + "\(transcriptScrollContextID):\(composerFocusRequest):\(shouldAutoFocusComposer)" + } + private var canSwipeToCreateChat: Bool { guard onRequestNewChat != nil else { return false @@ -124,8 +132,8 @@ struct SybilWorkspaceView: View { } resetNewChatSwipe(animated: false) } - .task(id: composerFocusRequest) { - await focusComposerIfRequested() + .task(id: composerFocusPolicyID) { + await applyComposerFocusPolicy() } } @@ -346,15 +354,13 @@ struct SybilWorkspaceView: View { } @MainActor - private func focusComposerIfRequested() async { - guard composerFocusRequest > 0 else { + private func applyComposerFocusPolicy() async { + guard shouldAutoFocusComposer else { + composerFocused = false return } - await Task.yield() - try? await Task.sleep(for: .milliseconds(80)) - - guard viewModel.showsComposer, !viewModel.isSearchMode else { + guard shouldAutoFocusComposer, viewModel.showsComposer else { return } composerFocused = true diff --git a/ios/justfile b/ios/justfile index 40b2976..8575b62 100644 --- a/ios/justfile +++ b/ios/justfile @@ -1,10 +1,28 @@ +simulator := "platform=iOS Simulator,name=iPhone 16e,OS=latest" +simulator_name := "iPhone 16e" +derived_data := "build/DerivedData" + default: @just build build: if [ ! -d "Sybil.xcodeproj" ]; then xcodegen --spec project.yml; fi if command -v xcbeautify >/dev/null 2>&1; then \ - xcodebuild -scheme Sybil -destination 'platform=iOS Simulator,name=iPhone 16e,OS=latest' | xcbeautify; \ + xcodebuild -scheme Sybil -destination '{{simulator}}' | xcbeautify; \ else \ - xcodebuild -scheme Sybil -destination 'platform=iOS Simulator,name=iPhone 16e,OS=latest'; \ + xcodebuild -scheme Sybil -destination '{{simulator}}'; \ fi + +test: + cd Packages/Sybil && xcodebuild test -scheme Sybil -destination '{{simulator}}' -parallel-testing-enabled NO + +run: + if [ ! -d "Sybil.xcodeproj" ]; then xcodegen --spec project.yml; fi + xcrun simctl boot '{{simulator_name}}' 2>/dev/null || true + xcodebuild -scheme Sybil -destination '{{simulator}}' -derivedDataPath '{{derived_data}}' + xcrun simctl install booted '{{derived_data}}/Build/Products/Debug-iphonesimulator/Sybil.app' + xcrun simctl launch booted net.buzzert.sybil2 + +screenshot path="build/sybil-screenshot.png": + mkdir -p "$(dirname '{{path}}')" + xcrun simctl io booted screenshot '{{path}}'