feat: TypeWhisper Flow sessions, Phase 4 UX, and GitHub Pages privacy site

Migrate keyboard dictation to continuous Flow sessions with auto-start,
tap-to-toggle recording, 60s countdown, five-step onboarding, and App Group
IPC. Add docs/ GitHub Pages site with en/zh privacy policy for App Store compliance.
This commit is contained in:
Rocky
2026-06-19 18:05:50 +08:00
parent 6148d05093
commit 7f059dbd45
48 changed files with 3815 additions and 1065 deletions
@@ -0,0 +1,70 @@
// HostAppLauncher.swift
// OSGKeyboard · Keyboard Extension
//
// Opens the host app via URL using every extension-safe strategy:
// 1. `extensionContext.open` (official)
// 2. Responder-chain `UIApplication.open` (TypeWhisper pattern)
// 3. `sharedApplication` KVC fallback (common in full-access keyboards)
import UIKit
enum HostAppLauncher {
@MainActor
static func open(
url: URL,
from controller: KeyboardViewController,
completion: @escaping @MainActor (Bool) -> Void
) {
if let context = controller.extensionContext {
context.open(url) { success in
Task { @MainActor in
if success {
completion(true)
return
}
completion(openViaFallback(url, from: controller))
}
}
return
}
completion(openViaFallback(url, from: controller))
}
@MainActor
private static func openViaFallback(
_ url: URL,
from controller: KeyboardViewController
) -> Bool {
if openViaResponderChain(url, from: controller) {
return true
}
return openViaSharedApplication(url)
}
@MainActor
private static func openViaResponderChain(
_ url: URL,
from controller: KeyboardViewController
) -> Bool {
var responder: UIResponder? = controller
while let current = responder {
if let application = current as? UIApplication {
application.open(url, options: [:]) { _ in }
return true
}
responder = current.next
}
return false
}
@MainActor
private static func openViaSharedApplication(_ url: URL) -> Bool {
guard
let application = UIApplication.value(forKeyPath: "sharedApplication") as? UIApplication
else {
return false
}
application.open(url, options: [:]) { _ in }
return true
}
}