chore(previews): record English feature previews and add App Store creative asset scripts

This commit is contained in:
2026-10-20 20:06:37 +02:00
parent ad79c2ea13
commit a4c6102f5c
9 changed files with 1415 additions and 23 deletions
@@ -4,7 +4,8 @@
// What's New 1.7.0 recording host. Voice chrome + clipboard panel are the
// **real** extension views (`KeyboardTopControls`, `RecordButton`,
// `KeyboardTranslationMenuButton`, `ClipboardHistoryPanelView`, …) driven by
// scripted `KeyboardState`. Launch with `--clipboard-demo`.
// scripted `KeyboardState`. Launch with `--clipboard-demo` and optional
// `--whats-new-lang=zh|en`.
#if DEBUG
import OSGKeyboardShared
@@ -35,10 +36,17 @@ struct ClipboardHistoryDemoView: View {
/// Closing beat: hand the copied text to the real AI skill row.
@State private var showsSkills = false
private static let language = FeaturePreviewFlags.language
private static var isEnglish: Bool { language == .en }
private var palette: ThemePalette {
colorScheme == .dark ? Palette.dark : Palette.light
}
init() {
FeaturePreviewFlags.applyRecordingLanguage()
}
var body: some View {
ZStack {
if FeaturePreviewFlags.isFullscreen {
@@ -48,13 +56,10 @@ struct ClipboardHistoryDemoView: View {
}
VStack(spacing: 0) {
if FeaturePreviewFlags.isFullscreen {
// Chinese-only recording — see the `zh-Hans` locale pin on
// this view's body; the sample conversation has no English
// variant, so the host chrome stays Chinese to match.
FeaturePreviewHostDocument(
kind: .messages,
title: "信息",
language: .zh,
title: Self.isEnglish ? "Messages" : "信息",
language: Self.language,
text: hostDraft,
incoming: Self.incomingMessage
)
@@ -71,7 +76,7 @@ struct ClipboardHistoryDemoView: View {
}
}
.environment(\.themePalette, palette)
.environment(\.locale, Locale(identifier: "zh-Hans"))
.environment(\.locale, Locale(identifier: Self.isEnglish ? "en" : "zh-Hans"))
.preferredColorScheme(.light)
.task { await runTimeline() }
}
@@ -290,7 +295,9 @@ struct ClipboardHistoryDemoView: View {
}) else { return }
await runReplySkill(
skill: generic,
answer: "好的,预算版我整理一下,明天上午发你。"
answer: Self.isEnglish
? "Sure, I'll pull the budget version together and send it tomorrow morning."
: "好的,预算版我整理一下,明天上午发你。"
)
state.pendingClipboardSkillID = nil
try? await sleep(0.6)
@@ -337,6 +344,7 @@ struct ClipboardHistoryDemoView: View {
/// Demo copy per reply style. Keyed by skill so the answer matches whatever
/// chip the ranker put in the row.
private static func answer(for skillID: String) -> String {
if isEnglish { return englishAnswer(for: skillID) }
switch skillID {
case AIClipboardSkillCatalog.playfulReplyID:
return "行嘞,预算表这就去梳妆打扮,明早准时上桌。"
@@ -353,6 +361,23 @@ struct ClipboardHistoryDemoView: View {
}
}
private static func englishAnswer(for skillID: String) -> String {
switch skillID {
case AIClipboardSkillCatalog.playfulReplyID:
return "On it! The budget sheet is getting dressed up and will be on your desk by morning."
case AIClipboardSkillCatalog.businessReplyID:
return "Noted. I'll finalize the budget version tonight and send it over tomorrow morning."
case AIClipboardSkillCatalog.empathyReplyID:
return "Totally get that you need this soon. I'll prioritize it and send it first thing tomorrow."
case AIClipboardSkillCatalog.acceptTaskID:
return "Got it, I'll take this on and send the budget version tomorrow morning."
case AIClipboardSkillCatalog.clarifyRequestID:
return "Sure. Do you need the version with staffing costs, or just procurement?"
default:
return "Sure, I'll put it together and send it tomorrow morning."
}
}
/// One tap on a reply-style chip: the row gives way to the thinking state,
/// the answer streams in, then it is offered for insertion.
private func runReplySkill(skill: AIClipboardSkill, answer: String) async {
@@ -394,6 +419,8 @@ struct ClipboardHistoryDemoView: View {
state.layoutWidth = 390
state.usesIPadLayoutMetrics = false
state.showsSystemGlobeKey = false
// The skill ranker localizes chip titles off this.
state.uiLanguage = Self.isEnglish ? .english : .chinese
// `AIKeyboardView.showsClipboardSkills` gates on this — without it the
// real skill row never appears no matter what the ranker returns.
state.clipboardHistoryEnabled = true
@@ -414,7 +441,10 @@ struct ClipboardHistoryDemoView: View {
private func seedHistory() {
history.clearAll()
_ = history.ingest(rawText: "订单号 OSG-20260811-8842", changeCount: 3)
_ = history.ingest(
rawText: Self.isEnglish ? "Order OSG-20260811-8842" : "订单号 OSG-20260811-8842",
changeCount: 3
)
_ = history.ingest(rawText: "https://osglab.com", changeCount: 2)
_ = history.ingest(rawText: Self.incomingMessage, changeCount: 1)
history.reload()
@@ -422,7 +452,11 @@ struct ClipboardHistoryDemoView: View {
/// The message the clip replies to. Copying it is what puts the reply
/// skills in the row, so it has to be the newest clipboard entry.
static let incomingMessage = "方案我看了,明天能不能把预算那一版也发我?"
static var incomingMessage: String {
isEnglish
? "Read the proposal. Could you also send me the budget version tomorrow?"
: "方案我看了,明天能不能把预算那一版也发我?"
}
private func sleep(_ seconds: Double) async throws {
try await Task.sleep(nanoseconds: UInt64(seconds * 2.4 * 1_000_000_000))
+22 -9
View File
@@ -4,15 +4,27 @@
// What's New "Edit last input" recording host. Uses the **real**
// `LastInputEditView` (compiled into the app target) driven by a scripted
// `KeyboardState` — no ASR / LLM. Opening beat shows the voice mic + hint.
// Launch with `--edit-demo`. Not shipped in Release.
// Launch with `--edit-demo` and optional `--whats-new-lang=zh|en`.
// Not shipped in Release.
#if DEBUG
import OSGKeyboardShared
import SwiftUI
struct EditDemoView: View {
private static let originalText = "明天下午三点开会讨论方案"
private static let editedText = "各位好,明天下午三点在 A 会议室召开方案讨论会,请准时参加。"
private static let language = FeaturePreviewFlags.language
private static var originalText: String {
language == .en
? "meeting tomorrow at 3 to discuss the plan"
: "明天下午三点开会讨论方案"
}
private static var editedText: String {
language == .en
? "Hi all, we'll review the proposal tomorrow at 3 PM in Room A. Please be on time."
: "各位好,明天下午三点在 A 会议室召开方案讨论会,请准时参加。"
}
private enum Scene: Equatable {
case hint
@@ -24,6 +36,10 @@ struct EditDemoView: View {
/// Mirrors the host document for `--preview-fullscreen` recordings.
@State private var hostText: String = EditDemoView.originalText
init() {
FeaturePreviewFlags.applyRecordingLanguage()
}
private var source: EditSessionSource {
let reference = EditableInputReference(
displayText: Self.originalText,
@@ -45,13 +61,10 @@ struct EditDemoView: View {
}
VStack(spacing: 0) {
if FeaturePreviewFlags.isFullscreen {
// Chinese-only recording: the sample text below has no
// English variant, so the host chrome is pinned to match
// (see the `zh-Hans` locale pin on this view's body).
FeaturePreviewHostDocument(
kind: .notes,
title: "备忘录",
language: .zh,
title: Self.language == .en ? "Notes" : "备忘录",
language: Self.language,
text: hostText
)
} else {
@@ -74,7 +87,7 @@ struct EditDemoView: View {
}
}
.environment(\.themePalette, palette)
.environment(\.locale, Locale(identifier: "zh-Hans"))
.environment(\.locale, Locale(identifier: Self.language == .en ? "en" : "zh-Hans"))
.preferredColorScheme(.light)
.task { await runTimeline() }
}
@@ -18,6 +18,26 @@ enum FeaturePreviewFlags {
static var isFullscreen: Bool {
ProcessInfo.processInfo.arguments.contains("--preview-fullscreen")
}
/// Recording language from `--whats-new-lang=zh|en`; Chinese when absent.
static var language: WhatsNewDemoScenario.Language {
let prefix = "--whats-new-lang="
guard let argument = ProcessInfo.processInfo.arguments.first(
where: { $0.hasPrefix(prefix) }
) else {
return .zh
}
return WhatsNewDemoScenario.Language(
rawValue: String(argument.dropFirst(prefix.count))
) ?? .zh
}
/// Points the extension strings (`ExtL10n` reads the App Group setting)
/// at the recording language before the first frame renders.
@MainActor
static func applyRecordingLanguage() {
ProviderConfig.shared.uiLanguage = language == .en ? .english : .chinese
}
}
/// A Notes- or Messages-like document that reacts to the demo timeline.