Rename app to PrivateVoice Dictation
| | |
| | | |
| | | ### 10.5 后续 UI 与品牌命名调整 |
| | | |
| | | 以下需求先记录,等进入合适的 UI/品牌整理节点再实现,不作为 Round 3 Moonshine 验收阻断项: |
| | | 以下需求已进入 `v2.1.24 build 20260603.1934` 品牌命名调整: |
| | | |
| | | - 左侧 Logo 区只显示 `PrivateVoice`,下方小字显示 `Private. Offline.`。 |
| | | - App 正式名称从 `PrivateVoice Input` 改为 `PrivateVoice Dictation`。该调整需要同步处理界面显示、macOS App bundle 名称、DMG 文件名、Info.plist、release manifest、测试文档和用户可见文案。 |
| | | - App 正式名称从 `PrivateVoice Input` 改为 `PrivateVoice Dictation`。 |
| | | - 本轮不迁移用户数据目录,继续使用既有 `Application Support/PrivateVoice Input`,避免影响历史记录、模型文件和系统权限。 |
| | | |
| | | ## 11. 总体建议 |
| | | |
| | |
| | | - [ ] 用户验收候选包 |
| | | - [ ] 用户验收 |
| | | |
| | | ## 6. 暂缓记录 |
| | | ## 6. UI/品牌命名调整 |
| | | |
| | | 以下事项已进入 `v2.1.24 build 20260603.1934`: |
| | | |
| | | - [x] 左侧 Logo 区只显示 `PrivateVoice`,下方小字显示 `Private. Offline.`。 |
| | | - [x] App 正式名称从 `PrivateVoice Input` 改为 `PrivateVoice Dictation`。 |
| | | - [x] macOS App bundle 名称、DMG 文件名、Info.plist、Wails 构建配置和用户可见文案同步。 |
| | | - [x] 不迁移用户数据目录,继续使用既有 `Application Support/PrivateVoice Input`。 |
| | | |
| | | ## 7. 暂缓记录 |
| | | |
| | | 以下事项不作为 Round 5 阻断项: |
| | | |
| | | - 左侧 Logo 区只显示 `PrivateVoice`,下方小字显示 `Private. Offline.`。 |
| | | - App 正式名称从 `PrivateVoice Input` 改为 `PrivateVoice Dictation`。 |
| | | - 粤语作为独立语言版本。 |
| | | - 欧洲语言 UI。 |
| | | - 模型删除 UI 的完整成熟化。 |
| | |
| | | # Changelog |
| | | |
| | | ## v2.1.24 (2026-06-03) |
| | | |
| | | ### QA 候选包 |
| | | |
| | | - **品牌命名调整**:App 正式名称从 `PrivateVoice Input` 改为 `PrivateVoice Dictation`,同步 macOS App bundle、DMG 文件名、Info.plist、Wails 构建配置和用户可见文案。 |
| | | - **侧栏 Logo 区调整**:左侧品牌区只显示 `PrivateVoice`,下方小字显示 `Private. Offline.`。 |
| | | - **升级兼容保护**:本轮不迁移用户数据目录,继续使用既有 `Application Support/PrivateVoice Input`,避免影响历史记录、模型文件和系统权限。 |
| | | |
| | | ### 构建 |
| | | |
| | | - build: `20260603.1934` |
| | | - 说明: 本版本为 Round 5 后追加的 UI/品牌命名验收候选包。 |
| | | |
| | | --- |
| | | |
| | | ## v2.1.23 (2026-06-03) |
| | | |
| | | ### QA 候选包 |
| | |
| | | ) |
| | | |
| | | const ( |
| | | appVersion = "2.1.23" |
| | | appBuild = "20260603.1904" |
| | | appVersion = "2.1.24" |
| | | appBuild = "20260603.1934" |
| | | appDisplayVersion = appVersion + " (build " + appBuild + ")" |
| | | appName = "PrivateVoice Input" |
| | | appName = "PrivateVoice Dictation" |
| | | |
| | | silenceThreshold = 0.05 // RMS below this = silence (matches HasVoiceActivity) |
| | | silenceTimeoutDuration = 3 * time.Second // auto-stop after this much silence in free-talk |
| | |
| | | |
| | | info: |
| | | companyName: "PrivateVoice" |
| | | productName: "PrivateVoice Input" |
| | | productName: "PrivateVoice Dictation" |
| | | productIdentifier: "com.privatevoice.input" |
| | | description: "Private offline voice input tool" |
| | | copyright: "(c) 2026, PrivateVoice" |
| | | comments: "Private.Offline" |
| | | version: "2.1.23" |
| | | comments: "Private. Offline." |
| | | version: "2.1.24" |
| | | |
| | | dev_mode: |
| | | root_path: . |
| | |
| | | <plist version="1.0"> |
| | | <dict> |
| | | <key>CFBundleExecutable</key> |
| | | <string>PrivateVoice Input</string> |
| | | <string>PrivateVoice Dictation</string> |
| | | <key>CFBundleIconFile</key> |
| | | <string>icon.icns</string> |
| | | <key>CFBundleIdentifier</key> |
| | | <string>com.privatevoice.input</string> |
| | | <key>CFBundleName</key> |
| | | <string>PrivateVoice Input</string> |
| | | <string>PrivateVoice Dictation</string> |
| | | <key>CFBundleDisplayName</key> |
| | | <string>PrivateVoice Input</string> |
| | | <string>PrivateVoice Dictation</string> |
| | | <key>CFBundlePackageType</key> |
| | | <string>APPL</string> |
| | | <key>CFBundleShortVersionString</key> |
| | | <string>2.1.23</string> |
| | | <string>2.1.24</string> |
| | | <key>CFBundleVersion</key> |
| | | <string>20260603.1904</string> |
| | | <string>20260603.1934</string> |
| | | <key>LSMinimumSystemVersion</key> |
| | | <string>11.0</string> |
| | | <key>NSMicrophoneUsageDescription</key> |
| | | <string>PrivateVoice Input needs microphone access for voice-to-text input.</string> |
| | | <string>PrivateVoice Dictation needs microphone access for voice-to-text input.</string> |
| | | <key>NSHighResolutionCapable</key> |
| | | <true/> |
| | | <key>LSUIElement</key> |
| | |
| | | [Desktop Entry] |
| | | Type=Application |
| | | Name=PrivateVoice Input |
| | | Name=PrivateVoice Dictation |
| | | Comment=Private offline voice input tool |
| | | Exec=privatevoice-input |
| | | Icon=privatevoice-input |
| | |
| | | <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> |
| | | <assemblyIdentity |
| | | type="win32" |
| | | name="PrivateVoice.Input" |
| | | version="2.1.23.0" |
| | | name="PrivateVoice.Dictation" |
| | | version="2.1.24.0" |
| | | processorArchitecture="*"/> |
| | | <dependency> |
| | | <dependentAssembly> |
| | |
| | | <head> |
| | | <meta charset="UTF-8" /> |
| | | <meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
| | | <title>PrivateVoice Input</title> |
| | | <title>PrivateVoice Dictation</title> |
| | | <link rel="stylesheet" href="/src/styles/global.css" /> |
| | | </head> |
| | | <body> |
| | |
| | | { |
| | | "name": "privatevoice-input-frontend", |
| | | "version": "2.1.23", |
| | | "name": "privatevoice-dictation-frontend", |
| | | "version": "2.1.24", |
| | | "lockfileVersion": 3, |
| | | "requires": true, |
| | | "packages": { |
| | | "": { |
| | | "name": "privatevoice-input-frontend", |
| | | "version": "2.1.23", |
| | | "name": "privatevoice-dictation-frontend", |
| | | "version": "2.1.24", |
| | | "dependencies": { |
| | | "@wailsio/runtime": "latest" |
| | | }, |
| | |
| | | { |
| | | "name": "privatevoice-input-frontend", |
| | | "name": "privatevoice-dictation-frontend", |
| | | "private": true, |
| | | "version": "2.1.23", |
| | | "version": "2.1.24", |
| | | "type": "module", |
| | | "scripts": { |
| | | "dev": "vite dev", |
| | |
| | | <nav class="sidebar"> |
| | | <div class="sidebar-header"> |
| | | <AppIcon size={24} /> |
| | | <span class="app-name">{t('app.name')}</span> |
| | | <div class="brand-copy"> |
| | | <span class="brand-name">{t('app.brandName')}</span> |
| | | <span class="brand-tagline">{t('app.brandTagline')}</span> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="nav-items"> |
| | |
| | | -webkit-app-region: no-drag; |
| | | } |
| | | |
| | | .app-name { |
| | | .brand-copy { |
| | | display: flex; |
| | | min-width: 0; |
| | | flex-direction: column; |
| | | gap: 2px; |
| | | } |
| | | |
| | | .brand-name { |
| | | font-weight: 600; |
| | | font-size: var(--font-size-lg); |
| | | line-height: 1.15; |
| | | color: var(--color-label); |
| | | } |
| | | |
| | | .brand-tagline { |
| | | font-size: var(--font-size-xs); |
| | | line-height: 1.2; |
| | | color: var(--color-secondary-label); |
| | | } |
| | | |
| | | .nav-items { |
| | |
| | | |
| | | let { |
| | | size = 56, |
| | | label = 'PrivateVoice Input', |
| | | label = 'PrivateVoice Dictation', |
| | | elevated = false, |
| | | }: Props = $props() |
| | | </script> |
| | |
| | | { |
| | | "app": { |
| | | "name": "PrivateVoice Input", |
| | | "secondaryName": "私语输入" |
| | | "name": "PrivateVoice Dictation", |
| | | "brandName": "PrivateVoice", |
| | | "brandTagline": "Private. Offline.", |
| | | "secondaryName": "Private. Offline." |
| | | }, |
| | | "nav": { |
| | | "home": "Home", |
| | |
| | | "done": "Typed" |
| | | }, |
| | | "home": { |
| | | "tagline": "Private.Offline", |
| | | "tagline": "Private. Offline.", |
| | | "description": "Hold {key} to speak, release to recognize and type", |
| | | "hotkeyLabel": "Trigger Hotkey", |
| | | "modeHint": "Hold to record, release to type · Tap to start free talk, tap again to stop", |
| | |
| | | "copyToClipboard": "Save Recognition to Clipboard", |
| | | "copyToClipboardDesc": "Keep recognized text in the clipboard after input; off uses it only temporarily", |
| | | "startAtLogin": "Start at Login", |
| | | "startAtLoginDesc": "Launch PrivateVoice Input when you log in", |
| | | "startAtLoginDesc": "Launch PrivateVoice Dictation when you log in", |
| | | "hideDockIcon": "Hide Dock Icon", |
| | | "hideDockIconDesc": "Show PrivateVoice Input only in the menu bar" |
| | | "hideDockIconDesc": "Show PrivateVoice Dictation only in the menu bar" |
| | | }, |
| | | "models": { |
| | | "sensevoiceDesc": "Lightweight Chinese offline model for Chinese and mixed Chinese-English input.", |
| | |
| | | }, |
| | | "permissions": { |
| | | "title": "Permission Check", |
| | | "subtitle": "PrivateVoice Input needs these permissions for recording, global hotkeys, and automatic text input", |
| | | "subtitle": "PrivateVoice Dictation needs these permissions for recording, global hotkeys, and automatic text input", |
| | | "refresh": "Refresh", |
| | | "grant": "Grant", |
| | | "granted": "Granted", |
| | |
| | | }, |
| | | "backgroundActivity": { |
| | | "title": "Background Activity / Login Item", |
| | | "desc": "Starts PrivateVoice Input after login and allows it to run as a menu bar app in the background; it does not affect the currently running voice input." |
| | | "desc": "Starts PrivateVoice Dictation after login and allows it to run as a menu bar app in the background; it does not affect the currently running voice input." |
| | | } |
| | | }, |
| | | "hotkeys": { |
| | |
| | | "checkingUpdate": "Checking...", |
| | | "isLatestVersion": "Up to date", |
| | | "checkFailed": "Check failed", |
| | | "tagline": "Private.Offline", |
| | | "slogan": "Private.Offline", |
| | | "tagline": "Private. Offline.", |
| | | "slogan": "Private. Offline.", |
| | | "legalText": "This app is a secondary development based on the open-source VoiceSnap project and follows the MIT License.", |
| | | "legalDetails": "For complete copyright notices, license text, and improvement details, visit:", |
| | | "supportUrl": "https://shanghai3168.github.io/privatevoice-support/", |
| | | "openSupport": "Open copyright and license details", |
| | | "copyright": "© {year} PrivateVoice Input · MIT License" |
| | | "copyright": "© {year} PrivateVoice Dictation · MIT License" |
| | | }, |
| | | "history": { |
| | | "title": "Recognition History", |
| | |
| | | "from": "Recognized As", |
| | | "to": "Replace With", |
| | | "fromPlaceholder": "e.g. voice snap", |
| | | "toPlaceholder": "e.g. PrivateVoice Input", |
| | | "toPlaceholder": "e.g. PrivateVoice Dictation", |
| | | "add": "Add", |
| | | "edit": "Edit", |
| | | "save": "Save", |
| | |
| | | { |
| | | "app": { |
| | | "name": "私语输入", |
| | | "secondaryName": "PrivateVoice Input" |
| | | "name": "PrivateVoice Dictation", |
| | | "brandName": "PrivateVoice", |
| | | "brandTagline": "Private. Offline.", |
| | | "secondaryName": "Private. Offline." |
| | | }, |
| | | "nav": { |
| | | "home": "首页", |
| | |
| | | "copyToClipboard": "识别内容保存到剪贴板", |
| | | "copyToClipboardDesc": "输入文字后仍保留在剪贴板,关闭后仅临时用于上屏", |
| | | "startAtLogin": "开机自启动", |
| | | "startAtLoginDesc": "登录系统时自动启动私语输入", |
| | | "startAtLoginDesc": "登录系统时自动启动 PrivateVoice Dictation", |
| | | "hideDockIcon": "隐藏 Dock 图标", |
| | | "hideDockIconDesc": "只在菜单栏显示私语输入图标" |
| | | "hideDockIconDesc": "只在菜单栏显示 PrivateVoice Dictation 图标" |
| | | }, |
| | | "models": { |
| | | "sensevoiceDesc": "轻量中文离线模型,适合中文和中英混输。", |
| | |
| | | }, |
| | | "permissions": { |
| | | "title": "权限检查", |
| | | "subtitle": "私语输入需要这些权限才能稳定完成录音、全局热键和自动上屏", |
| | | "subtitle": "PrivateVoice Dictation 需要这些权限才能稳定完成录音、全局热键和自动上屏", |
| | | "refresh": "刷新", |
| | | "grant": "去授权", |
| | | "granted": "已授权", |
| | |
| | | }, |
| | | "inputMonitoring": { |
| | | "title": "输入监控", |
| | | "desc": "用于在私语输入窗口不可见、其他 App 正在前台时监听右 Alt 全局热键。" |
| | | "desc": "用于在 PrivateVoice Dictation 窗口不可见、其他 App 正在前台时监听右 Alt 全局热键。" |
| | | }, |
| | | "backgroundActivity": { |
| | | "title": "后台活动 / 登录项", |
| | | "desc": "用于登录系统后自动启动私语输入,并允许它作为菜单栏应用在后台运行;不影响当前已经打开的语音输入。" |
| | | "desc": "用于登录系统后自动启动 PrivateVoice Dictation,并允许它作为菜单栏应用在后台运行;不影响当前已经打开的语音输入。" |
| | | } |
| | | }, |
| | | "hotkeys": { |
| | |
| | | "isLatestVersion": "已是最新版本", |
| | | "checkFailed": "检查失败", |
| | | "tagline": "永远离线·稳定极速", |
| | | "slogan": "Private.Offline", |
| | | "slogan": "Private. Offline.", |
| | | "legalText": "本应用基于开源项目 VoiceSnap 进行二次开发,遵循 MIT License。", |
| | | "legalDetails": "完整版权声明、许可文本及改进详情,请访问:", |
| | | "supportUrl": "https://shanghai3168.github.io/privatevoice-support/", |
| | | "openSupport": "打开版权与许可说明", |
| | | "copyright": "© {year} PrivateVoice Input · MIT License" |
| | | "copyright": "© {year} PrivateVoice Dictation · MIT License" |
| | | }, |
| | | "history": { |
| | | "title": "识别历史", |
| | |
| | | "from": "识别结果", |
| | | "to": "替换为", |
| | | "fromPlaceholder": "例如:私语输入", |
| | | "toPlaceholder": "例如:PrivateVoice Input", |
| | | "toPlaceholder": "例如:PrivateVoice Dictation", |
| | | "add": "添加", |
| | | "edit": "编辑", |
| | | "save": "保存", |
| | |
| | | if (g_activityToken != nil) return; |
| | | g_activityToken = [[NSProcessInfo processInfo] |
| | | beginActivityWithOptions:NSActivityUserInitiatedAllowingIdleSystemSleep |
| | | reason:@"PrivateVoice Input global hotkey listener"]; |
| | | reason:@"PrivateVoice Dictation global hotkey listener"]; |
| | | } |
| | | |
| | | static void* eventTapThreadMain(void* arg) { |
| | |
| | | C.beginHotkeyActivity() |
| | | trusted := C.ensureAccessibility() |
| | | if trusted == 0 { |
| | | logger.Info("Accessibility permission not granted for current app identity — hotkey won't work until PrivateVoice Input is removed and re-added in System Settings > Privacy & Security > Accessibility") |
| | | logger.Info("Accessibility permission not granted for current app identity — hotkey won't work until PrivateVoice Dictation is removed and re-added in System Settings > Privacy & Security > Accessibility") |
| | | } else { |
| | | logger.Info("Accessibility permission granted") |
| | | } |
| | |
| | | |
| | | const desktopEntry = `[Desktop Entry] |
| | | Type=Application |
| | | Name=PrivateVoice Input |
| | | Name=PrivateVoice Dictation |
| | | Exec=%s |
| | | Hidden=false |
| | | X-GNOME-Autostart-enabled=true |
| | |
| | | ) |
| | | |
| | | const registryKey = `SOFTWARE\Microsoft\Windows\CurrentVersion\Run` |
| | | const appName = "PrivateVoice Input" |
| | | const appName = "PrivateVoice Dictation" |
| | | |
| | | var legacyAppNames = []string{"VoiceSnap"} |
| | | |
| | |
| | | |
| | | func main() { |
| | | if err := paths.MigrateFromLegacyExecutableDir(); err != nil { |
| | | os.Stderr.WriteString("PrivateVoice Input data migration failed: " + err.Error() + "\n") |
| | | os.Stderr.WriteString("PrivateVoice Dictation data migration failed: " + err.Error() + "\n") |
| | | } |
| | | |
| | | // Initialize logger |
| | | logger.Init() |
| | | exePath, _ := os.Executable() |
| | | logger.Info("PrivateVoice Input starting... version=%s build=%s executable=%s dataDir=%s", appVersion, appBuild, exePath, paths.AppSupportDir()) |
| | | logger.Info("PrivateVoice Dictation starting... version=%s build=%s executable=%s dataDir=%s", appVersion, appBuild, exePath, paths.AppSupportDir()) |
| | | |
| | | // Single instance check |
| | | lock, err := singleinstance.Acquire() |
| | |
| | | # |
| | | set -euo pipefail |
| | | |
| | | APP_NAME="PrivateVoice Input" |
| | | APP_NAME="PrivateVoice Dictation" |
| | | DEVELOPER_IDENTITY="${PRIVATEVOICE_DEVELOPER_IDENTITY:-Developer ID Application: Ningbo Zhuozhi Innovation Network Technology Co., Ltd. (3HY4G7M2AL)}" |
| | | KEYCHAIN_PROFILE="${PRIVATEVOICE_NOTARY_PROFILE:-privatevoice}" |
| | | SIGNING_KEYCHAIN="${PRIVATEVOICE_SIGNING_KEYCHAIN:-}" |
| | |
| | | # |
| | | set -euo pipefail |
| | | |
| | | APP_NAME="PrivateVoice Input" |
| | | APP_NAME="PrivateVoice Dictation" |
| | | LOCAL_IDENTITY="${PRIVATEVOICE_LOCAL_SIGN_IDENTITY:-PrivateVoice Local Code Signing}" |
| | | KEYCHAIN="${PRIVATEVOICE_LOCAL_KEYCHAIN:-$HOME/Library/Keychains/login.keychain-db}" |
| | | APPSTORE_MIN_SYSTEM_VERSION="${PRIVATEVOICE_APPSTORE_MIN_SYSTEM_VERSION:-13.4}" |
| | |
| | | #!/usr/bin/env bash |
| | | # |
| | | # build-local-macos.sh - Build a locally signed macOS app and DMG for PrivateVoice Input. |
| | | # build-local-macos.sh - Build a locally signed macOS app and DMG for PrivateVoice Dictation. |
| | | # |
| | | # This script intentionally does not notarize. It creates or reuses a stable |
| | | # local Code Signing identity so macOS TCC permissions, such as Accessibility, |
| | |
| | | # |
| | | set -euo pipefail |
| | | |
| | | APP_NAME="PrivateVoice Input" |
| | | APP_NAME="PrivateVoice Dictation" |
| | | DEFAULT_LOCAL_IDENTITY="PrivateVoice Local Code Signing" |
| | | LEGACY_LOCAL_IDENTITY="VoiceSnap Local Code Signing" |
| | | LOCAL_IDENTITY="${PRIVATEVOICE_LOCAL_SIGN_IDENTITY:-${VOICESNAP_LOCAL_SIGN_IDENTITY:-}}" |
| | |
| | | #!/usr/bin/env bash |
| | | # |
| | | # build-release-macos.sh — Build a signed & notarized macOS DMG for PrivateVoice Input |
| | | # build-release-macos.sh — Build a signed & notarized macOS DMG for PrivateVoice Dictation |
| | | # |
| | | # Prerequisites (one-time): |
| | | # 1. Install Developer ID certificate in Keychain |
| | |
| | | IDENTITY="Developer ID Application: Ningbo Zhuozhi Innovation Network Technology Co., Ltd. (3HY4G7M2AL)" |
| | | KEYCHAIN_PROFILE="privatevoice" |
| | | |
| | | APP_NAME="PrivateVoice Input" |
| | | APP_NAME="PrivateVoice Dictation" |
| | | BUNDLE_ID="com.privatevoice.input" |
| | | |
| | | SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" |
| | |
| | | |
| | | // GetAppName returns the application name. |
| | | func (s *AppService) GetAppName() string { |
| | | return "PrivateVoice Input" |
| | | return "PrivateVoice Dictation" |
| | | } |
| | | |
| | | // OpenURL opens a URL in the default browser. |
| | |
| | | { |
| | | "$schema": "https://wails.io/schemas/config.v3.json", |
| | | "name": "PrivateVoice Input", |
| | | "outputfilename": "PrivateVoice Input", |
| | | "name": "PrivateVoice Dictation", |
| | | "outputfilename": "PrivateVoice Dictation", |
| | | "frontend:install": "npm install", |
| | | "frontend:build": "npm run build", |
| | | "frontend:dev:watcher": "npm run dev", |