ios: fix keyboard behavior

This commit is contained in:
2026-05-03 21:52:49 -07:00
parent d1140d21d4
commit 4bc0773d35
3 changed files with 45 additions and 10 deletions

View File

@@ -8,8 +8,19 @@ Instructions for work under `/Users/buzzert/src/sybil-2/ios`.
- `just build` will: - `just build` will:
1. generate `Sybil.xcodeproj` with `xcodegen` if missing, 1. generate `Sybil.xcodeproj` with `xcodegen` if missing,
2. build scheme `Sybil` for `iPhone 16e` simulator. 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. - 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 <path>` and requires a booted simulator.
## App Structure ## App Structure
- App target entry: `/Users/buzzert/src/sybil-2/ios/Apps/Sybil/Sources/SybilApp.swift` - App target entry: `/Users/buzzert/src/sybil-2/ios/Apps/Sybil/Sources/SybilApp.swift`
- Shared iOS app code lives in Swift package: - Shared iOS app code lives in Swift package:

View File

@@ -62,6 +62,14 @@ struct SybilWorkspaceView: View {
return "chat:none" return "chat:none"
} }
private var shouldAutoFocusComposer: Bool {
viewModel.displayedMessages.isEmpty
}
private var composerFocusPolicyID: String {
"\(transcriptScrollContextID):\(composerFocusRequest):\(shouldAutoFocusComposer)"
}
private var canSwipeToCreateChat: Bool { private var canSwipeToCreateChat: Bool {
guard onRequestNewChat != nil else { guard onRequestNewChat != nil else {
return false return false
@@ -124,8 +132,8 @@ struct SybilWorkspaceView: View {
} }
resetNewChatSwipe(animated: false) resetNewChatSwipe(animated: false)
} }
.task(id: composerFocusRequest) { .task(id: composerFocusPolicyID) {
await focusComposerIfRequested() await applyComposerFocusPolicy()
} }
} }
@@ -346,15 +354,13 @@ struct SybilWorkspaceView: View {
} }
@MainActor @MainActor
private func focusComposerIfRequested() async { private func applyComposerFocusPolicy() async {
guard composerFocusRequest > 0 else { guard shouldAutoFocusComposer else {
composerFocused = false
return return
} }
await Task.yield() guard shouldAutoFocusComposer, viewModel.showsComposer else {
try? await Task.sleep(for: .milliseconds(80))
guard viewModel.showsComposer, !viewModel.isSearchMode else {
return return
} }
composerFocused = true composerFocused = true

View File

@@ -1,10 +1,28 @@
simulator := "platform=iOS Simulator,name=iPhone 16e,OS=latest"
simulator_name := "iPhone 16e"
derived_data := "build/DerivedData"
default: default:
@just build @just build
build: build:
if [ ! -d "Sybil.xcodeproj" ]; then xcodegen --spec project.yml; fi if [ ! -d "Sybil.xcodeproj" ]; then xcodegen --spec project.yml; fi
if command -v xcbeautify >/dev/null 2>&1; then \ 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 \ else \
xcodebuild -scheme Sybil -destination 'platform=iOS Simulator,name=iPhone 16e,OS=latest'; \ xcodebuild -scheme Sybil -destination '{{simulator}}'; \
fi 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}}'