chore(release): v1.3.1-17 — Mac native select + 胶囊白底升 0.85 + ASR 行清理

[SelectLite]
- macOS 走原生 <select>(NSPopUpButton),不再用自定义 portal popover——
  WKWebView 里 portal+position:fixed 的位置漂移问题反复修不干净
- Win/Linux 仍用自定义 popover:popover 锁到 trigger 宽(width 不再用 minWidth),
  避免内容撑大跑出 trigger 范围
- positionPopover 横向 maxLeft 用 rect.width,两帧 left 一致

[Capsule]
- pill 白底 alpha 0.62 → 0.85(用户:"让白色更白一些")
- cancel/confirm 按钮保持 1.3.0 stable 取值;thinking 字号 17/weight 600/cap-shine
  动画保留
- 此前 v1.3.1-9 ~ v1.3.1-15 多轮 alpha 微调 / NSVisualEffectView 实验全部回退

[Settings ASR 行]
- 下拉只保留云端 provider,删 disabled 的本地引擎条目
- 本地引擎激活时下拉锁住 + 显示一行"ASR 提供商已被接管"提示
- i18n 五语言加 settings.providers.asrProviderTakenOver

[版本]
1.3.1-8 → 1.3.1-17,四处版本号同步
This commit is contained in:
baiqing committed 2026-05-12 21:58:51 +08:00
1 parent 40c39e11d3
commit c5bdb02595
13 files changed
+71 -54

No files matched your search

+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "openless-app",
"version": "1.3.1-8",
"version": "1.3.1-17",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "openless-app",
"version": "1.3.1-8",
"version": "1.3.1-17",
"dependencies": {
"@tauri-apps/api": "^2.1.1",
"@tauri-apps/plugin-autostart": "^2.5.1",
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "openless-app",
"private": true,
"version": "1.3.1-8",
"version": "1.3.1-17",
"type": "module",
"scripts": {
"dev": "vite",
+1 -1
View File
@@ -3751,7 +3751,7 @@ dependencies = [
[[package]]
name = "openless"
version = "1.3.1-8"
version = "1.3.1-17"
dependencies = [
"anyhow",
"arboard",
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "openless"
version = "1.3.1-8"
version = "1.3.1-17"
description = "OpenLess — local voice input that types where your cursor is"
authors = ["OpenLess"]
edition = "2021"
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "OpenLess",
"version": "1.3.1-8",
"version": "1.3.1-17",
"identifier": "com.openless.app",
"build": {
"beforeDevCommand": "npm run dev",
+2 -5
View File
@@ -99,9 +99,7 @@ interface CircleButtonProps {
function CircleButton({ variant, enabled, onClick }: CircleButtonProps) {
const { t } = useTranslation();
const isCancel = variant === 'cancel';
const os = detectOS();
// v1.3.1-7 用户反馈"Windows 胶囊磨砂玻璃没了" —— 之前 Windows 强制关掉 backdrop-filter,
// 现在 WebView2 支持,打开让玻璃感对齐 macOS。仍只 cancel 按钮加磨砂(confirm 按钮纯白底)。
// confirm 是主操作锚点,纯白;cancel 半透 + 自带 backdrop blur 跟 pill 拉开层级。
const useBackdrop = isCancel;
return (
<button
@@ -246,7 +244,6 @@ function Pill({ os, state, level, insertedChars, message, onCancel, onConfirm }:
const ambient = state === 'recording' ? Math.min(1, Math.max(0, level)) : 0;
const scale = os === 'win' ? 1 : 1 + ambient * 0.018;
const shadowAlpha = 0.20 + ambient * 0.10;
// v1.3.1-7 用户反馈"Windows 胶囊磨砂玻璃没了" —— 打开 Windows backdrop-filter。
const useBackdrop = true;
return (
@@ -261,7 +258,7 @@ function Pill({ os, state, level, insertedChars, message, onCancel, onConfirm }:
height: metrics.height,
boxSizing: metrics.boxSizing,
borderRadius: 999,
background: 'rgba(255, 255, 255, 0.62)',
background: 'rgba(255, 255, 255, 0.85)',
backdropFilter: useBackdrop ? 'blur(28px) saturate(180%)' : 'none',
WebkitBackdropFilter: useBackdrop ? 'blur(28px) saturate(180%)' : 'none',
border: '1px solid rgba(255, 255, 255, 0.55)',
@@ -1,8 +1,9 @@
// SelectLite — 受控的下拉组件,替代 native <select> 以避开 Windows Win32 ComboBox
// 弹框的直角丑框(issue #418)。
// SelectLite — Win/Linux 用自定义 popover 替代 native <select> 避开 Win32 ComboBox
// 的直角丑框(issue #418)。macOS 走原生 <select>(NSPopUpButton),WKWebView 里
// portal+position:fixed 的自定义 popover 位置漂移反复修不干净。
//
// 设计:
// - 触发器是一个 button(chevron + 当前值标签),样式可被 `style` 覆盖
// Win/Linux 自定义分支设计:
// - 触发器是 button(chevron + 当前值),样式可被 `style` 覆盖
// - popover 用 portal 渲染到 document.body,避开父容器 overflow:hidden
// - 键盘:ArrowDown/ArrowUp 切换高亮,Enter 确认,Esc 关闭
// - 点击外部 / 滚动外部容器都会关闭(popover 内部 scroll 不关闭)
@@ -20,6 +21,7 @@ import {
} from 'react';
import { createPortal } from 'react-dom';
import { Icon } from '../Icon';
import { detectOS } from '../WindowChrome';
export interface SelectOption {
value: string;
@@ -85,17 +87,16 @@ export function SelectLite({
const trigger = triggerRef.current;
if (!trigger) return;
const rect = trigger.getBoundingClientRect();
const popoverRect = popoverRef.current?.getBoundingClientRect();
const popoverHeight = popoverRect?.height ?? 280;
// popover 宽度优先用真实测量值(>= trigger 宽),fallback 才用 trigger 宽。
const popoverWidth = Math.max(popoverRect?.width ?? 0, rect.width);
// popover 高度只用真实测量;fallback 280 仅在首帧 popover 还没挂载时用。
const popoverHeight = popoverRef.current?.getBoundingClientRect().height ?? 280;
// 纵向:默认在触发器下方;若下方空间放不下 popover,翻转向上避免被视口裁剪。
const spaceBelow = window.innerHeight - rect.bottom;
const flipUp = spaceBelow < popoverHeight + 8 && rect.top > popoverHeight + 8;
const top = flipUp ? rect.top - popoverHeight - 4 : rect.bottom + 4;
// 横向:窗口右边的 select 可能让 popover 溢出屏幕;clamp 到 [8, viewport-width-8]。
// popover 强制 width=trigger.width(见下方 style),所以 maxLeft 用 rect.width 算;
// popover 没挂载和挂载后两帧 left 一致,避免 first-paint 跳位。
const minLeft = 8;
const maxLeft = Math.max(minLeft, window.innerWidth - popoverWidth - 8);
const maxLeft = Math.max(minLeft, window.innerWidth - rect.width - 8);
const left = Math.min(Math.max(rect.left, minLeft), maxLeft);
setAnchor({ left, top, width: rect.width });
}, []);
@@ -241,6 +242,37 @@ export function SelectLite({
cursor: disabled ? 'not-allowed' : 'default',
};
// macOS 走原生 select(理由见文件头)。继承 triggerStyle 视觉,paddingRight 22 给
// 系统 chevron 留位;appearance:auto 关掉全局 button reset 让原生外观回来。
if (detectOS() === 'mac') {
return (
<select
className="ol-focus-ring"
aria-label={ariaLabel}
value={value}
disabled={disabled}
onChange={e => onChange(e.target.value)}
style={{
...triggerStyle,
appearance: 'auto',
WebkitAppearance: 'menulist',
paddingRight: 22,
}}
>
{placeholder && !selected && (
<option value="" disabled>
{placeholder}
</option>
)}
{options.map(opt => (
<option key={opt.value || `__opt_${opt.label}`} value={opt.value} disabled={opt.disabled}>
{opt.label}
</option>
))}
</select>
);
}
return (
<>
<button
@@ -279,7 +311,9 @@ export function SelectLite({
position: 'fixed',
left: anchor.left,
top: anchor.top,
minWidth: anchor.width,
// 锁到 trigger 宽(不是 minWidth),避免 content 撑大让 popover 跑出
// trigger 范围;长 label 走 textOverflow:ellipsis 截断。
width: anchor.width,
maxHeight: 280,
overflowY: 'auto',
padding: 4,
+1
View File
@@ -391,6 +391,7 @@ export const en: typeof zhCN = {
volcengineMappingNote: 'Secret Key is not required right now. Resource ID defaults to volc.bigasr.sauc.duration.',
localAsrActiveNotice: 'Local ASR ({{name}}) is currently active. Switch or disable it from the Advanced tab.',
localAsrTakeoverHint: 'Once "{{name}}" is enabled, the ASR provider will be taken over.',
asrProviderTakenOver: 'ASR provider taken over',
localAsrHint: 'Local Qwen3-ASR runs entirely on this machine. No API key needed — just download the model from HuggingFace.',
foundryLocalAsrHint: 'Windows local Whisper runs on this device and does not need an ASR API key. First use downloads Foundry Local runtime components and a Whisper model; LLM polishing still uses your configured LLM provider.',
localAsrPerformanceWarning: 'Local inference runs on CPU + Apple Silicon Accelerate; each transcription takes **several seconds longer than cloud ASR**, and Chinese / dialect accuracy is **typically lower** than Volcengine or Whisper turbo. Use it for offline, privacy-sensitive, or no-cloud-API scenarios.',
+1
View File
@@ -393,6 +393,7 @@ export const ja: typeof zhCN = {
volcengineMappingNote: 'Secret Key は現在不要。Resource ID のデフォルトは volc.bigasr.sauc.duration。',
localAsrActiveNotice: '現在「{{name}}」を使用中。「詳細設定」タブから切り替えまたは無効化できます。',
localAsrTakeoverHint: '「{{name}}」を有効化すると ASR プロバイダーが引き継がれます。',
asrProviderTakenOver: 'ASR プロバイダーは引き継ぎ済み',
localAsrHint: 'ローカル Qwen3-ASR は本機で実行されるため API Key 不要。HuggingFace からモデルをダウンロードすればすぐに利用できます。',
foundryLocalAsrHint: 'Windows ローカル Whisper は本機で実行され、ASR API Key は不要です。初回使用時に Foundry Local ランタイムコンポーネントと Whisper モデルをダウンロードします。LLM 整文は引き続き設定済みの LLM プロバイダーを使用します。',
localAsrPerformanceWarning: 'ローカル推論は CPU + Apple Silicon Accelerate で動作するため、1 回の転写時間は **クラウド ASR より数秒長くなります**。中国語認識精度や方言/訛り対応も **通常は** Volcengine / Whisper turbo に劣ります。ネットワーク制限下またはプライバシー重視の場合に選択してください。',
+1
View File
@@ -393,6 +393,7 @@ export const ko: typeof zhCN = {
volcengineMappingNote: 'Secret Key 는 현재 입력 불필요. Resource ID 기본값은 volc.bigasr.sauc.duration.',
localAsrActiveNotice: '현재 "{{name}}" 사용 중. "고급" 탭에서 전환 또는 비활성화할 수 있습니다.',
localAsrTakeoverHint: '"{{name}}" 활성화 시 ASR 프로바이더가 인수됩니다.',
asrProviderTakenOver: 'ASR 프로바이더 인수 완료',
localAsrHint: '로컬 Qwen3-ASR 은 본 기기에서 실행되며 API Key 가 필요 없습니다. HuggingFace 에서 모델을 로컬로 다운로드하면 즉시 사용 가능합니다.',
foundryLocalAsrHint: 'Windows 로컬 Whisper 는 이 기기에서 실행되며 ASR API Key 가 필요 없습니다. 첫 사용 시 Foundry Local 런타임 구성 요소와 Whisper 모델을 다운로드합니다. LLM 정리는 계속 설정된 LLM 공급자를 사용합니다.',
localAsrPerformanceWarning: '로컬 추론은 CPU + Apple Silicon Accelerate 에서 동작하므로, 한 번의 전사 시간이 **클라우드 ASR 보다 몇 초 더 걸립니다**. 중국어 인식 정확도와 방언/억양 대응도 **일반적으로** Volcengine / Whisper turbo 에 미치지 못합니다. 네트워크 제한 또는 프라이버시가 중요한 경우에 사용하세요.',
+1
View File
@@ -389,6 +389,7 @@ export const zhCN = {
volcengineMappingNote: 'Secret Key 当前无需填写。Resource ID 默认使用 volc.bigasr.sauc.duration。',
localAsrActiveNotice: '当前已启用「{{name}}」,可在「高级」中切换或禁用。',
localAsrTakeoverHint: '启动「{{name}}」后,ASR 提供商将被接管。',
asrProviderTakenOver: 'ASR 提供商已被接管',
localAsrHint: '本地 Qwen3-ASR 在本机运行,无需 API Key。模型从 HuggingFace 下载到本地后即可使用。',
foundryLocalAsrHint: 'Windows 本地 Whisper 在本机运行,无需 ASR API Key。首次使用会下载 Foundry Local 运行组件和 Whisper 模型;LLM 润色仍按你配置的 LLM 提供商调用。',
localAsrPerformanceWarning: '本地推理跑在 CPU + Apple Silicon Accelerate 上,单次转写时间会**比云端 ASR 长几秒**;中文识别准确率与方言/口音表现也**通常不如**火山引擎 / Whisper turbo。请按需取舍:网络受限或对隐私敏感时再用本地。',
+1
View File
@@ -391,6 +391,7 @@ export const zhTW: typeof zhCN = {
volcengineMappingNote: 'Secret Key 當前無需填寫。Resource ID 默認使用 volc.bigasr.sauc.duration。',
localAsrActiveNotice: '當前已啓用「{{name}}」,可在「高級」中切換或停用。',
localAsrTakeoverHint: '啓動「{{name}}」後,ASR 提供商將被接管。',
asrProviderTakenOver: 'ASR 提供商已被接管',
localAsrHint: '本地 Qwen3-ASR 在本機運行,無需 API Key。模型從 HuggingFace 下載到本地後即可使用。',
foundryLocalAsrHint: 'Windows 本地 Whisper 在本機運行,無需 ASR API Key。首次使用會下載 Foundry Local 運行組件和 Whisper 模型;LLM 潤色仍按你配置的 LLM 提供商調用。',
localAsrPerformanceWarning: '本地推理跑在 CPU + Apple Silicon Accelerate 上,**首次轉寫需要加載模型(數秒)**,之後單次轉寫也會比雲端 ASR 慢若干秒;中文識別準確率與方言/口音表現通常不如火山引擎 / Whisper turbo。適用場景:離線 / 隱私敏感 / 不願付費雲 API。',
+13 -32
View File
@@ -1428,30 +1428,20 @@ function ProvidersSection() {
<div style={{ fontSize: 13, fontWeight: 600 }}>{t('settings.providers.asrTitle')}</div>
<div style={{ fontSize: 11.5, color: 'var(--ol-ink-4)', marginTop: 2 }}>{t('settings.providers.asrDesc')}</div>
</div>
{/* desc 同 LLM 行去掉,避免 180px label 列里换行。 */}
{/* 下拉只放云端选项;本地引擎激活时锁住 + 在下方放一行"ASR 提供商已被接管"提示,
未激活时不显示提示。 */}
<SettingRow label={t('settings.providers.providerLabel')}>
{(() => {
// 平台本机引擎:macOS=Qwen3,Windows=Foundry。本机项始终在下拉里露出,
// 当用户未启用本地推理时呈 disabled——用户能看到"它存在 + 提示",
// 但只能在 Advanced 启用;启用后整下拉锁住,**实际激活的** local 项被选中
// (pr_agent #403 fix:跨机器同步导致 macOS profile = foundry / Windows
// profile = qwen3 时,UI 不能假装是平台本机引擎而要忠实显示)。
const platformLocalAsr: AsrPresetId | null =
os === 'mac' ? 'local-qwen3' : os === 'win' ? 'foundry-local-whisper' : null;
const platformLocalNameKey = platformLocalAsr === 'local-qwen3'
? 'asrLocalQwen3'
: platformLocalAsr === 'foundry-local-whisper'
? 'asrFoundryLocalWhisper'
: null;
const isLocked = committedAsrProvider === 'local-qwen3' || committedAsrProvider === 'foundry-local-whisper';
// active 是本地时,selectedValue = 实际 committed 的 provider(不强制
// 覆盖成 platformLocalAsr);非 active 时按 asrProvider(云端选项)显示。
const isLocked =
committedAsrProvider === 'local-qwen3' ||
committedAsrProvider === 'foundry-local-whisper';
const selectedValue: AsrPresetId = isLocked ? committedAsrProvider : asrProvider;
// 异常 local:当前激活的 local 不是本平台本机引擎(跨机器配置同步)。
// 必须把它也作为 disabled <option> 加进列表,否则受控 <select> 会回退
// 到第一项造成视觉假象。
// 跨机器同步异常兜底:committed 是本地但不在 visibleAsrPresets 里时,受控
// select 会回退到首项造成假象 —— 补一个 disabled option 让 select 找到当前值。
const anomalousLocal: AsrPresetId | null =
isLocked && committedAsrProvider !== platformLocalAsr ? committedAsrProvider : null;
isLocked && !visibleAsrPresets.some(p => p.id === committedAsrProvider)
? committedAsrProvider
: null;
const anomalousNameKey = anomalousLocal === 'local-qwen3'
? 'asrLocalQwen3'
: anomalousLocal === 'foundry-local-whisper'
@@ -1468,13 +1458,6 @@ function ProvidersSection() {
value: p.id,
label: t(`settings.providers.presets.${p.nameKey}`),
})),
...(platformLocalAsr && platformLocalNameKey
? [{
value: platformLocalAsr,
label: t(`settings.providers.presets.${platformLocalNameKey}`),
disabled: true,
}]
: []),
...(anomalousLocal && anomalousNameKey
? [{
value: anomalousLocal,
@@ -1486,11 +1469,9 @@ function ProvidersSection() {
ariaLabel={t('settings.providers.providerLabel')}
style={{ ...inputStyle, maxWidth: 200 }}
/>
{platformLocalAsr && platformLocalNameKey && (
<div style={{ fontSize: 11, color: 'var(--ol-ink-4)', lineHeight: 1.5, maxWidth: 320 }}>
{t('settings.providers.localAsrTakeoverHint', {
name: t(`settings.providers.presets.${platformLocalNameKey}`),
})}
{isLocked && (
<div style={{ fontSize: 11, color: 'var(--ol-ink-4)', lineHeight: 1.5 }}>
{t('settings.providers.asrProviderTakenOver')}
</div>
)}
</div>