Vibe 3.1.6 更新解读:转录滚动跟随交互重构与 Google Meet 检测的屏幕录制权限修复
Vibe 3.1.6 更新解读转录滚动跟随交互重构与 Google Meet 检测的屏幕录制权限修复【免费下载链接】vibeTranscribe on your own!项目地址: https://gitcode.com/GitHub_Trending/vib/vibe本篇基于 Vibe 开源仓库的 3.1.6 版本更新记录website/changelog/3.1.6.md展开聚焦两个改动一是“边转录边回看”场景下滚动跟随follow机制的彻底重构——让读者真正能自由翻阅仍在实时生成的转录文本二是 macOS 上 Google Meet 会议检测首次主动请求屏幕录制Screen Recording权限并在用户拒绝时直达系统设置。读完本文你将理解这两处改动的设计动机、实现细节与源码证据并掌握 Vibe 转录视图与会议检测模块的内部工作机制。版本概览3.1.6 于 2026-08-28 发布版本主题为Scroll back through a transcript while it is still being written在转录仍持续写入时回翻已生成的内容。本次更新包含一项体验改进Improved和一项修复Fixed分类内容摘要Improved 转录生成过程中的滚动跟随重构跟随实时文本不再“锁死”在底部滚轮、触控板、滚动条、Page Up 等真实操作即可脱离跟随滚回底部后自动恢复跟随Fixed Google Meet 检测请求它所需的权限该改动曾出现在 3.1.5 的更新说明中但实际代码在 3.1.5 打标签之后才落地3.1.6 是首个包含它的构建。macOS 上读取浏览器窗口标题需要屏幕录制权限Vibe 此前只检查、从不请求现在会请求一次并在用户拒绝时直链系统设置一、滚动跟随重构让“正在生成的转录”真正可以回看1.1 旧机制的问题在实时转录过程中转录视图默认会“跟随”最新生成的文本——每来一个新段落就自动滚到底部。这个设计本身合理但旧实现有一个致命缺陷跟随实时文本曾几乎无法脱离向上滚动或点击某一行进行播放都会在下个段落到达时被瞬间拉回底部。旧实现的关键在于“把控制权交还给读者”的判断方式它监听一个scroll 事件并设置了一个700 ms 的忽略窗口——凡是 Vibe 自己引发的滚动在其后的 700 ms 内的滚动事件都被忽略。然而只要转录仍在进行每个到达的新段落都会刷新这个窗口。结果就是在一次真实的转录过程中这个窗口永远不会关闭读者向上翻页后总是被拽回末尾回看行为形同虚设。1.2 新机制由“输入事件”而非“滚动事件”驱动脱离3.1.6 的核心思路是不再猜测“这个滚动是不是用户发起的”而是直接监听用户发起滚动的输入事件本身。驱动逻辑位于 desktop/src/pages/main/components/transcript-view.tsx/** Keys that scroll the transcript, so pressing one means the reader is steering. */ const SCROLL_KEYS [ArrowUp, ArrowDown, PageUp, PageDown, Home, End] /** How close to the tail counts as being back at the bottom, in pixels. */ const BOTTOM_STICK_PX 96releaseFollow是“脱离跟随”的核心回调transcript-view.tsx它会先清除following状态并且如果此时正在播放则显示一个“跳转到播放位置”的悬浮胶囊jump pill3 秒后自动淡出。const releaseFollow useCallback(() { if (followingRef.current) { followingRef.current false setFollowing(false) } if (!playingRef.current) return setJumpVisible(true) window.clearTimeout(jumpTimer.current) jumpTimer.current window.setTimeout(() setJumpVisible(false), 3000) }, [])脱离跟随的触发条件全部是真实输入transcript-view.tsx滚轮滚动element.addEventListener(wheel, releaseFollow, { passive: true })触控板触屏滑动element.addEventListener(touchmove, releaseFollow, { passive: true })拖动滚动条pointerdown且event.target element——注释明确说明只有滚动条本身命中滚动容器时才算用户干预点击文本行不算键盘翻页ArrowUp / ArrowDown / PageUp / PageDown / Home / End即SCROLL_KEYS按下即脱离INPUT / TEXTAREA / SELECT或可编辑元素内的按键会被排除避免在编辑文本时误触发与旧方案“700 ms 窗口 监听 scroll”的根本区别在于Vibe 自身滚动产生的 scroll 事件不会再被误判为用户操作因此流式转录导致的连续滚动不会与用户的上翻动作互相干扰。1.3 滚回底部自动恢复跟随脱离跟随之后用户滚回末尾时 Vibe 会像“日志查看器粘回底部”一样自动重新进入跟随状态。恢复逻辑同样基于 scroll 事件但结合了运行状态与阈值判断transcript-view.tsxconst onScroll useCallback(() { const element scrollRef.current if (!running || followingRef.current || !element) return if (element.scrollHeight - element.scrollTop - element.clientHeight BOTTOM_STICK_PX) return followingRef.current true setFollowing(true) }, [running])即仅当转录仍在运行running、当前未跟随、且距离底部不超过BOTTOM_STICK_PX96 像素时才恢复跟随。这里特意“在 scroll 回调中实时测量”而非延迟到下一帧——否则流式段落已经撑高了列表用户在视觉上不再处于底部恢复判定就会失效。1.4 跟随状态的两种滚动行为恢复或保持跟随后视图存在两条滚动路径transcript-view.tsx播放同步当播放器广播时间推进PLAYER_TIME_EVENT时activeIndex指向正在朗读的段落若following为真则调用rowVirtualizer.scrollToIndex(position, { align: center })将当前行居中转录跟随转录运行中且未编辑、未脱离时scrollToIndex(visible.length - 1, { align: end })将最新一行对齐到底部。两者都受following、editing正在编辑某一行等状态门控编辑状态下不会强制滚动。1.5 配套交互跳转播放胶囊当读者已脱离跟随、播放器正在播放时视图底部中央会出现一个“跳转到播放位置”的胶囊按钮jumpToPlaying见 transcript-view.tsx点击后恢复跟随并scrollToIndex到当前朗读行同时立即隐藏胶囊。这使得“翻回去阅读 → 点击胶囊回到正在播放的位置”成为完整的闭环操作。值得注意的细节是点击转录行的时间戳即可播放该行PLAYER_SEEK_EVENT按segment.start / 100换算秒数这也是 changelog 中“clicking a line to play it”所指的交互。二、Google Meet 检测请求它所需的权限2.1 背景为什么只有 macOS 需要这个权限Vibe 的会议检测模块 crates/meeting-detect 支持 Zoom、Teams、Google Meet 三种会议来源。检测链路分为两层进程层通过操作系统 API 枚举正在运行的进程按进程名 / 可执行文件路径 / Bundle ID 匹配 Zoom、Teams 与浏览器详见 crates/meeting-detect/src/process.rs 中的ZOOM_BUNDLE_IDS、TEAMS_BUNDLE_IDS、BROWSER_BUNDLE_IDS及ZOOM_PROCESS_NAMES、TEAMS_PROCESS_NAMES、BROWSER_PROCESS_NAMES窗口标题层仅用于 Google Meet。当麦克风被浏览器占用时读取浏览器的窗口标题通过is_meet_title判断是否正在开会crates/meeting-detect/src/window_title.rspub(crate) fn is_meet_title(title: str) - bool { let title title.trim(); title Google Meet || title.starts_with(Meet – ) || title.starts_with(Meet - ) }在 macOS 上读取其他应用浏览器的窗口标题受屏幕录制权限Screen Recording管控。权限缺失时CGWindowListCopyWindowInfo返回的窗口列表完全没有标题系统层面无法区分“没有会议”和“有会议但读不到标题”。这正是问题的根源——Zoom 和 Teams 走进程列表即可识别从不依赖此权限唯独 Google Meet 检测在权限缺失时会“静默失效”。2.2 3.1.5/3.1.6 的修复3.1.5 的更新说明中曾写入“Google Meet detection asks for the permission it needs”但代码实际落在该版本 tag 之后因此 3.1.6 才是首个包含该行为的构建changelog 原文明确说明了这一点。修复包含三个层次① 检查与请求分离的权限 APIcrates/meeting-detect/src/lib.rspub fn screen_recording_granted() - bool { #[cfg(target_os macos)] { window_title::screen_recording_granted() } #[cfg(not(target_os macos))] { true } } pub fn request_screen_recording() - bool { #[cfg(target_os macos)] { window_title::request_screen_recording() } #[cfg(not(target_os macos))] { true } }底层实现位于 crates/meeting-detect/src/window_title.rsScreenCaptureAccess.preflight()做无副作用的检查ScreenCaptureAccess.request()弹出系统授权框并注册到系统设置macOS 对同一应用身份只弹一次授权框之后返回既有结果。非 macOS 平台直接返回true不适用。② Tauri 命令层desktop/src-tauri/src/cmd/permissions.rs暴露三个命令给前端get_screen_recording_permission_statusmacOS 上granted则返回Granted否则返回NotDetermined——因为preflight无法区分“从未询问”与“曾被拒绝”而 macOS 授权框只能弹一次所以 UI 侧先提供“请求”按钮、被拒后降级为“打开系统设置”request_screen_recording_permission通过tokio::task::spawn_blocking在阻塞线程中调用request_screen_recording避免阻塞主线程open_screen_recording_settings使用深链open x-apple.systempreferences:com.apple.preference.security?Privacy_ScreenCapture直达“隐私与安全性 → 屏幕录制”设置面板。③ 前端设置面板desktop/src/pages/settings/sections/recording.tsx中的MeetPermissionRow组件启用会议检测后查询权限状态并监听窗口focus事件刷新macOS 在授权后需要应用重新获得焦点才会返回新答案状态为not_determined时自动发起一次请求——注释说明这是唯一一次展示系统授权框的机会状态为已拒绝时渲染一行“打开系统设置”链接点击调用open_screen_recording_settings深链已授权或非 macOSnot_applicable时整行隐藏。最终效果正如 changelog 所述“It now requests it once and links straight to the settings pane if you decline. Zoom and Teams never needed it.”2.3 三平台行为对照平台Meet 识别方式是否需要额外权限macOSCGWindowListCopyWindowInfocore_graphics::window::copy_window_infokCGWindowListOptionOnScreenOnly读取窗口标题需要屏幕录制权限缺失时记录tracing::warn!并返回NoneWindowsEnumWindowsGetWindowTextWGetWindowThreadProcessId按 PID 匹配浏览器窗口不需要Linuxx11rb 读取_NET_CLIENT_LIST/_NET_WM_PID/_NET_WM_NAMEWayland 会话下标题不可读直接返回None不需要三个平台的窗口枚举实现均位于 crates/meeting-detect/src/window_title.rs并配有单元测试覆盖标题识别规则如Meet – Daily standup命中、Google Meet - Google Chrome不命中见同文件tests模块。三、纵深会议检测的完整判定链路与防抖机制为更好地理解“权限只是其中一环”这里把检测链路串起来。核心入口在 crates/meeting-detect/src/lib.rs麦克风状态mic::current_usage()返回当前麦克风是否活跃及占用者macOS 系统 API 可能只报活跃不报占用者processes为空此时退化为process::running_candidates()枚举已知候选进程归属分类classify_active按Zoom → Teams → Meet浏览器标题的优先级判定。只要进程列表中有 Zoom 即归属 ZoomTeams 次之两者都没有且麦克风被浏览器占用时才扫描窗口标题确认 Meetcrates/meeting-detect/src/lib.rs防抖与轮询watch(interval)在后台线程轮询只在状态稳定变化时才向上游发送MeetingState。三档防抖阈值定义在源码常量中常量默认值含义DEFAULT_ONSET_DEBOUNCE400 ms会议开始进入录音需持续确认的时长DEFAULT_RELEASE_DEBOUNCE600 ms麦克风关闭导致退出会议时需持续确认的时长DEFAULT_ATTRIBUTION_DEBOUNCE5 s麦克风仍开、仅“归属”丢失如会议中短暂切走时需持续确认的时长UNSETTLED_INTERVAL150 ms信号未稳定时的加速轮询间隔TITLE_SCAN_INTERVAL1000 ms窗口标题扫描的限流间隔枚举窗口开销大稳定态下不每轮都扫MIN_INTERVAL50 ms轮询间隔下限Meet 粘性保持hold_meet一旦 Meet 通过防抖确认只要麦克风会话未结束且浏览器进程仍在即使浏览器切走标签导致标题暂时不可读归属也不会降级crates/meeting-detect/src/lib.rs并且此状态下可跳过昂贵的窗口扫描。相关行为均有测试覆盖例如watcher_holds_confirmed_meet_until_mic_release验证“标题丢失被抑制、直到麦克风释放才发出非录音状态”a_held_meet_stops_paying_for_the_window_scan验证粘性状态下不再持续扫描窗口。总结3.1.6 的两处改动恰好代表了桌面录音工具的两类典型问题交互层的“自动化过度”与系统层的“权限缺失”。滚动跟随的重构说明了一个通用设计原则当程序行为与用户意图冲突时与其靠时间窗口猜测“这次滚动是不是用户发的”不如直接监听用户发起滚动的输入事件wheel / touchmove / 滚动条 / 翻页键再配合“回到底部自动恢复”的阈值BOTTOM_STICK_PX 96实现无感衔接。Google Meet 权限修复则展示了跨平台检测中“进程识别优先、窗口标题兜底”的架构取舍Zoom 与 Teams 永不依赖屏幕录制权限而 Meet 在 macOS 上不仅会主动请求一次权限被拒后还能一键直达系统设置面板。若需深入代码可从 desktop/src/pages/main/components/transcript-view.tsx 与 crates/meeting-detect/src/lib.rs 两个文件入手其内嵌的注释与单元测试对理解这两套机制极具参考价值。【免费下载链接】vibeTranscribe on your own!项目地址: https://gitcode.com/GitHub_Trending/vib/vibe创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
上一篇/下一篇内容由系统自动关联
返回资讯列表 →