Refine adaptive polish and cross-platform account support
Improve learned-style and clipboard skill presentation while keeping shared account clients buildable in the source-composed macOS target.
This commit is contained in:
@@ -29,7 +29,7 @@
|
||||
<ul>
|
||||
<li><strong>Voice audio</strong> — captured only while you actively record. On a local engine, audio is transcribed on-device and raw audio is not uploaded. With a user-configured cloud engine, recordings are sent directly to that ASR provider. If you explicitly choose managed credits, recordings are sent through the OSGKeyboard managed gateway at <code>account.osglab.com</code> to the managed ASR provider for transcription. OSGKeyboard does not keep raw recordings after the request is processed.</li>
|
||||
<li><strong>Transcribed text and cursor context</strong> — after ASR, the transcript (not audio) may be sent for polish when you use a configured LLM or managed credits. A small amount of text around the cursor may be included to continue naturally. Secure fields are never captured, and cursor context is not written to logs or voice history. User-configured requests go directly to that provider; managed-credit requests go through <code>account.osglab.com</code>.</li>
|
||||
<li><strong>Speaking-style learning</strong> — only after you tap “Generate My Style,” eligible pairs of pre-polish and final text, together with the polish-style prompts that produced those examples, are sent to your configured AI service. Native speech and explicit user edits are used to infer recurring habits; historical prompts are supplied only to distinguish and remove AI-added style. User-configured requests go directly to that provider; managed-credit requests go through <code>account.osglab.com</code>. The generated prompt is shown for review and is not saved or activated until you confirm it.</li>
|
||||
<li><strong>Speaking-style learning</strong> — only after you tap “Generate My Style,” eligible pairs of pre-polish and final text, together with the polish-style prompts that produced those examples, are sent to your configured AI service. Native speech and explicit user edits are used to infer recurring habits; historical prompts are supplied only to distinguish and remove AI-added style. The generated prompt is shown for review and is not saved or activated until you confirm it. When you later invoke a conversational clipboard-reply skill while a reviewed user-created style is active, that style prompt is sent with the copied text so the reply can follow your wording and rhythm; built-in polish personalities are not attached. User-configured requests go directly to that provider; managed-credit requests go through <code>account.osglab.com</code>.</li>
|
||||
<li><strong>AI mode questions</strong> — in AI keyboard mode, your spoken question text—or a question you tap from an idle suggestion—is sent to the selected AI service to generate an answer. User-configured requests go directly to that provider; managed-credit requests go through <code>account.osglab.com</code>. When the selected service supports server-side web search, it may retrieve public web results for time-sensitive answers.</li>
|
||||
<li><strong>Anonymous onboarding AI trial</strong> — before you sign in, onboarding can issue a short-lived, feature-limited credential after Apple App Attest verifies a genuine app installation. The four fixed lessons may send one voice transcript, an explicitly copied lesson sample for translation and reply, and one transcribed AI question through <code>account.osglab.com</code>. This flow does not create an account, process an Apple identity token, use credits, or upload raw voice audio. The copied lesson sample is temporary and does not enable or populate clipboard history. The service keeps only pseudonymous integrity, request-state, feature, and metering records needed to enforce the one-use limits and investigate abuse; it does not retain request or response bodies.</li>
|
||||
<li><strong>AI suggestions and OSGKeyboard web pages</strong> — the main app may request public suggestion titles from <code>key.osglab.com</code>; no voice, transcript, clipboard body, API key, or ordinary keystroke content is included. Opening the update page requests public content from <code>download.osglab.com</code>. Those hosts may process ordinary connection metadata such as IP address and request time.</li>
|
||||
@@ -82,7 +82,7 @@
|
||||
<ul>
|
||||
<li><strong>语音音频</strong> — 仅在你主动录音时采集。使用本地引擎时,音频在设备端转写且原始录音不会上传。使用用户自备云端服务时,录音会直接发送到你配置的识别服务商;若你明确选择「使用积分」,录音会经 <code>account.osglab.com</code> 的 OSGKeyboard 托管网关发送到托管识别服务商。请求处理完成后,OSGKeyboard 不保留原始录音。</li>
|
||||
<li><strong>转写文字与光标上下文</strong> — 使用已配置的 LLM 或托管积分时,转写文字(非音频)可能发送润色,并可能包含光标附近少量文字以自然衔接。密码框不会采集,光标上下文不会写入日志或语音历史。用户自备请求直达所选服务商;托管积分请求经 <code>account.osglab.com</code> 处理。</li>
|
||||
<li><strong>说话风格学习</strong> — 只有当你主动点击「生成我的风格」后,符合条件的润色前后文本对及产出这些示例时使用的润色风格 Prompt,才会发送给你配置的 AI 服务。原生口述与用户明确编辑用于归纳反复出现的习惯;历史 Prompt 只用于识别并排除 AI 附加风格。用户自备请求直达所选服务商;托管积分请求经 <code>account.osglab.com</code> 处理。生成结果会先展示供你检查,确认保存前不会写入或启用。</li>
|
||||
<li><strong>说话风格学习</strong> — 只有当你主动点击「生成我的风格」后,符合条件的润色前后文本对及产出这些示例时使用的润色风格 Prompt,才会发送给你配置的 AI 服务。原生口述与用户明确编辑用于归纳反复出现的习惯;历史 Prompt 只用于识别并排除 AI 附加风格。生成结果会先展示供你检查,确认保存前不会写入或启用。之后,当你启用了经检查的用户自定义风格并主动使用口语回复类剪贴板技能时,该风格 Prompt 会随复制文本一起发送,用于贴近你的用词与节奏;内置润色人格不会附加。用户自备请求直达所选服务商;托管积分请求经 <code>account.osglab.com</code> 处理。</li>
|
||||
<li><strong>AI 模式问题</strong> — 在 AI 键盘模式下,语音转写后的问题文字或你点选的建议会发送到当前选择的 AI 服务。用户自备请求直达所选服务商;托管积分请求经 <code>account.osglab.com</code> 处理。所选服务支持联网搜索时,可能检索公开网页结果回答时效性问题。</li>
|
||||
<li><strong>匿名首次体验</strong> — 登录前,开机引导可在 Apple App Attest 验证正版 App 后签发短时、限功能的匿名凭证。四项固定教学会经 <code>account.osglab.com</code> 分别发送一次语音转写文字、由你明确复制的教学示例(用于翻译与回复),以及一次语音转写后的 AI 问题;不会创建账号、处理 Apple 身份令牌、使用积分或上传原始语音。教学示例仅短时使用,不会开启或写入剪贴板历史。服务端只保留执行一次性限制和调查滥用所需的匿名完整性、请求状态、功能类型及计量记录,不保留请求或回答正文。</li>
|
||||
<li><strong>AI 建议与 OSGKeyboard 网页</strong> — 主 App 可能从 <code>key.osglab.com</code> 请求公开建议标题;请求不包含语音、转写、剪贴板正文、API Key 或普通击键内容。打开更新页时会从 <code>download.osglab.com</code> 请求公开内容。相关主机可能处理 IP 地址、请求时间等普通连接元数据。</li>
|
||||
|
||||
@@ -109,10 +109,12 @@ struct PolishStylesView: View {
|
||||
let corpus = styleLearningCorpus
|
||||
let required = PolishStyleLearningCorpusBuilder.requiredEffectiveCharacterCount
|
||||
let reachedLimit = catalog.entries.count >= PolishStyleLimits.maximumUserPacks
|
||||
let isActionAvailable = corpus.isReady && !reachedLimit
|
||||
let canGenerate = isActionAvailable && !isGeneratingLearnedStyle
|
||||
|
||||
return VStack(alignment: .leading, spacing: Spacing.md) {
|
||||
HStack(alignment: .top, spacing: Spacing.md) {
|
||||
Image(systemName: "waveform.badge.sparkles")
|
||||
Image(systemName: "waveform")
|
||||
.font(.system(size: 20, weight: .semibold))
|
||||
.foregroundStyle(palette.accent)
|
||||
.frame(width: 42, height: 42)
|
||||
@@ -170,6 +172,7 @@ struct PolishStylesView: View {
|
||||
if isGeneratingLearnedStyle {
|
||||
ProgressView()
|
||||
.controlSize(.small)
|
||||
.tint(isActionAvailable ? palette.textOnAccent : palette.textSecondary)
|
||||
} else {
|
||||
Image(systemName: "sparkles")
|
||||
}
|
||||
@@ -179,11 +182,26 @@ struct PolishStylesView: View {
|
||||
: AppL10n.string("polishStyles.learn.action")
|
||||
)
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
.font(TypeStyle.bodyEmph)
|
||||
.foregroundStyle(
|
||||
isActionAvailable ? palette.textOnAccent : palette.textSecondary
|
||||
)
|
||||
.frame(maxWidth: .infinity, minHeight: 48)
|
||||
.background(
|
||||
isActionAvailable ? palette.accent : palette.surfaceElevated,
|
||||
in: RoundedRectangle(cornerRadius: Radius.large, style: .continuous)
|
||||
)
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: Radius.large, style: .continuous)
|
||||
.stroke(
|
||||
isActionAvailable ? Color.clear : palette.dividerStrong,
|
||||
lineWidth: 0.5
|
||||
)
|
||||
)
|
||||
.contentShape(RoundedRectangle(cornerRadius: Radius.large, style: .continuous))
|
||||
}
|
||||
.buttonStyle(.borderedProminent)
|
||||
.tint(palette.accent)
|
||||
.disabled(!corpus.isReady || isGeneratingLearnedStyle || reachedLimit)
|
||||
.buttonStyle(.plain)
|
||||
.disabled(!canGenerate)
|
||||
|
||||
Text(
|
||||
reachedLimit
|
||||
|
||||
@@ -641,14 +641,14 @@
|
||||
"polishStyles.custom.section" = "My styles";
|
||||
"polishStyles.intro.title" = "Choose a writing personality";
|
||||
"polishStyles.intro.body" = "The selected style shapes every polished dictation. Your custom styles sync through iCloud when settings sync is enabled.";
|
||||
"polishStyles.learn.title" = "Learn and generate my speaking style";
|
||||
"polishStyles.learn.body" = "Learn your recurring speaking habits while using earlier polish prompts only to filter out AI-added style.";
|
||||
"polishStyles.learn.progress" = "%lld / %lld effective characters";
|
||||
"polishStyles.learn.remaining" = "%lld more needed";
|
||||
"polishStyles.learn.ready" = "Ready to generate";
|
||||
"polishStyles.learn.action" = "Generate My Style";
|
||||
"polishStyles.learn.generating" = "Learning…";
|
||||
"polishStyles.learn.privacy" = "Only after you tap Generate, eligible before-and-after text and prior style prompts are sent to your configured AI service. You can review the result before saving.";
|
||||
"polishStyles.learn.title" = "Generate Your Style";
|
||||
"polishStyles.learn.body" = "Learn how you naturally express yourself from past dictation.";
|
||||
"polishStyles.learn.progress" = "%lld / %lld characters";
|
||||
"polishStyles.learn.remaining" = "%lld to go";
|
||||
"polishStyles.learn.ready" = "Ready";
|
||||
"polishStyles.learn.action" = "Generate Style";
|
||||
"polishStyles.learn.generating" = "Generating…";
|
||||
"polishStyles.learn.privacy" = "Sent to your configured AI only when you generate. Review and edit before saving.";
|
||||
"polishStyles.learn.limit" = "Delete a custom style before generating another one.";
|
||||
"polishStyles.learn.error.insufficient" = "Keep dictating until 5,000 effective characters are available.";
|
||||
"polishStyles.learn.error.invalidResponse" = "The AI did not return a valid writing style. Please try again.";
|
||||
|
||||
@@ -640,14 +640,14 @@
|
||||
"polishStyles.custom.section" = "我的风格";
|
||||
"polishStyles.intro.title" = "选择完整写作人格";
|
||||
"polishStyles.intro.body" = "选中的风格会影响每次听写润色。开启设置同步后,自定义风格会通过 iCloud 保存。";
|
||||
"polishStyles.learn.title" = "学习并生成我的说话风格";
|
||||
"polishStyles.learn.body" = "学习你反复出现的说话习惯;历史润色 Prompt 只用于排除 AI 附加的风格。";
|
||||
"polishStyles.learn.progress" = "%lld / %lld 个有效字符";
|
||||
"polishStyles.learn.remaining" = "还需 %lld 个";
|
||||
"polishStyles.learn.ready" = "可以开始生成";
|
||||
"polishStyles.learn.action" = "生成我的风格";
|
||||
"polishStyles.learn.generating" = "正在学习…";
|
||||
"polishStyles.learn.privacy" = "只有点击生成后,符合条件的润色前后文本和历史风格 Prompt 才会发送给你配置的 AI 服务;保存前可以先检查和修改。";
|
||||
"polishStyles.learn.title" = "生成专属风格";
|
||||
"polishStyles.learn.body" = "从历史听写中学习你的表达习惯。";
|
||||
"polishStyles.learn.progress" = "%lld / %lld 字";
|
||||
"polishStyles.learn.remaining" = "还差 %lld 字";
|
||||
"polishStyles.learn.ready" = "可以生成";
|
||||
"polishStyles.learn.action" = "生成风格";
|
||||
"polishStyles.learn.generating" = "生成中…";
|
||||
"polishStyles.learn.privacy" = "仅在生成时发送给你配置的 AI,保存前可预览和修改。";
|
||||
"polishStyles.learn.limit" = "请先删除一个自定义风格,再生成新风格。";
|
||||
"polishStyles.learn.error.insufficient" = "请继续听写,累积到 5,000 个有效字符后再生成。";
|
||||
"polishStyles.learn.error.invalidResponse" = "AI 没有返回有效的润色风格,请重试。";
|
||||
|
||||
Reference in New Issue
Block a user