Files
OSGKeyboard/OSGKeyboardShared/Services/ScenarioStyleDirective.swift
T
Rocky 1bdb8824ac feat: polish scenarios and stabilize keyboard layout height
Add preset-driven polish scenarios (Settings, onboarding, ScenarioChip)
with ScenarioPrompt and style directives; drive keyboard height from
content (240pt) and use viewIsAppearing encapsulated-height offset for
smoother keyboard switches; remove redundant StatusBadge and hide system
dictation via hasDictationKey.
2026-06-29 00:08:00 +08:00

172 lines
7.5 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// ScenarioStyleDirective.swift
// OSGKeyboard · Shared
//
// Shared output-format rules for each polish scenario. Injected into
// both `ScenarioPrompt` (polish-only) and `TranslationPrompt`
// (translate-and-polish) so the two pipelines stay aligned.
//
// Directives emphasize STRUCTURE (bullets, paragraphs, checklists)
// over tone adjectives — structure is what users notice on short ASR
// transcripts.
import Foundation
public enum ScenarioStyleDirective {
/// Format rules for the given scenario, written in the provider's
/// primary instruction language (Chinese-native vs English-native).
public static func make(
scenarioId: String,
providerId: String,
uiLanguage: AppUILanguage? = nil
) -> String {
let id = PolishScenarioCatalog.resolve(scenarioId).id
let lang = uiLanguage ?? AppGroupStore().uiLanguage
let isChineseNative = ["zhipu", "moonshot", "qwen", "deepseek"].contains(providerId)
return isChineseNative
? chinese(id: id, uiLanguage: lang)
: english(id: id, uiLanguage: lang)
}
// MARK: - Chinese directives
private static func chinese(id: String, uiLanguage: AppUILanguage) -> String {
let englishPlatformNames = uiLanguage.resolvedLanguageCode() != "zh-Hans"
switch id {
case "work":
return """
场景:工作沟通(邮件、钉钉、Slack)。
格式(必须):
- 若有 2 个及以上独立事项/请求/问题,必须用 markdown「- 」列表,每条一行;禁止揉进一段。
- 仅 1 件事:可用 1~2 句短段落;必要时「称呼 + 正文」。
- 每条 action 清晰;称呼得体;不过度敬语。
允许:为列表组织内容,不必强行压成单句。
禁止:把多项内容合并成一个长句或一整段散文。
"""
case "todo":
return """
场景:TODO/备忘清单。
格式(必须):
- 输出必须是 markdown「- 」列表,每条一行。
- 每条以动词开头;一条一事;不写称呼、不写解释、不扩写。
禁止:段落 prose、寒暄、背景说明。
"""
case "social_lifestyle":
if englishPlatformNames {
return """
场景:社交网络生活分享帖(Social Network)。
格式:
- 内容≥2 句时必须用空行分段;第一人称;可读性强。
- 可适度 emoji;不写广告腔;不编造体验。
"""
}
return """
场景:小红书生活分享帖。
格式:
- 内容≥2 句时必须用空行分段;第一人称;可读性强。
- 可适度 emoji 与语气词;不写广告腔;不编造体验。
"""
case "social_short":
if englishPlatformNames {
return """
场景:Instagram 短 caption。
格式:句子短、开头抓人、信息密度高;控制总长度;不臆测标签或热点。
"""
}
return """
场景:微博短帖。
格式:句子短、开头抓人、信息密度高;控制总长度;不臆测标签或热点。
"""
case "goofy":
return """
场景:轻松聊天(逗比风格)。
格式:自然短句;措辞略俏皮。
禁止:新增情节、编段子、捏造态度;严肃内容(请假/道歉/投诉)不要强行搞笑。
"""
case "document":
return """
场景:文档/长文笔记。
格式:
- 完整句;≥2 个主题时用空行分段。
- 枚举或步骤用 markdown「- 」列表;可用 `##` 小标题(仅当内容够长)。
- 少网络用语;比工作沟通更适合长文叙述。
"""
case "daily_chat", PolishScenarioCatalog.customId:
fallthrough
default:
return """
场景:日常聊天(IM/私聊)。
格式:自然短句,像真人发消息;标点轻松;可保留极少量口语感。
禁止:公文腔、报告体、强行列表(除非口述本身在枚举)。
"""
}
}
// MARK: - English directives
private static func english(id: String, uiLanguage: AppUILanguage) -> String {
let englishPlatformNames = uiLanguage.resolvedLanguageCode() != "zh-Hans"
switch id {
case "work":
return """
Scenario: workplace message (email, Slack, Teams).
Format (required):
- If there are 2+ distinct items/requests/questions, you MUST use markdown "- " bullets, one per line; never merge into one paragraph.
- Single item only: 12 short sentences; optional greeting + body.
- Clear action per item; polite but not overly formal.
Allowed: list layout instead of forcing a single dense paragraph.
Forbidden: cramming multiple points into one long sentence or prose block.
"""
case "todo":
return """
Scenario: TODO / checklist note.
Format (required):
- Output MUST be markdown "- " bullets, one item per line.
- Each line starts with a verb; one task per line; no greeting, no explanation.
Forbidden: prose paragraphs, filler, background context.
"""
case "social_lifestyle":
if englishPlatformNames {
return """
Scenario: social network lifestyle post.
Format: if ≥2 sentences, separate paragraphs with blank lines; first person; light emoji ok; no ad-speak; do not invent experiences.
"""
}
return """
Scenario: Xiaohongshu-style lifestyle share.
Format: if ≥2 sentences, separate paragraphs with blank lines; first person; light emoji ok; no ad-speak; do not invent experiences.
"""
case "social_short":
if englishPlatformNames {
return """
Scenario: short Instagram caption.
Format: concise, punchy opening; high density; keep brief; no invented hashtags or trends.
"""
}
return """
Scenario: Weibo-style short post.
Format: concise, punchy opening; high density; keep brief; no invented hashtags or trends.
"""
case "goofy":
return """
Scenario: playful chat (goofy tone).
Format: natural short sentences; slightly witty wording only.
Forbidden: new facts, invented jokes, forced humor on serious topics (leave/apology/complaint).
"""
case "document":
return """
Scenario: document / long-form notes.
Format: complete sentences; blank lines between topics when ≥2 themes; use "- " bullets for steps/enumerations; `##` headings only when content is long enough; minimal slang.
"""
case "daily_chat", PolishScenarioCatalog.customId:
fallthrough
default:
return """
Scenario: everyday chat (IM/DM).
Format: natural short sentences like texting; relaxed punctuation; very light colloquial tone ok.
Forbidden: memo/report tone; forced bullets unless the speaker is enumerating.
"""
}
}
}