Files
openless/Sources/OpenLessApp/Capsule/CapsuleWindowController.swift
T
baiqing 88a5a97812 feat: 1.0.03 — 侧边栏重构 + 词汇表 chip 布局 + 风格页 + 关掉自动学词
- 侧边栏 4 → 6 项:首页 / 历史记录 / 词汇表 / 风格 / 帮助中心 / 设置
- 词典 → 词汇表,整页改 chip 流式布局:phrase + hitCount,hover ✕ 删除,
  批量输入(每行一个),重置统计 / 清除全部
- DictionaryEntry 加 hitCount + 兼容旧 dictionary.json 解码
- DictationCoordinator 在润色后扫描最终文本递增命中数;
  删除 learnTerms 自动学词路径
- 润色模式 → 风格 (StyleTab):4 张模式卡 + 启用开关;
  关闭时跳过 Ark 直接插入原文 (UserPreferences.polishEnabled)
- 帮助中心:快速上手 4 步、快捷键速查、FAQ、GitHub 外链
- 设置 Tab 合并 Volc ASR + Ark 凭据、录音、授权、隐私、关于 + 检查更新
- 胶囊:NSPanel 220×96, hasShadow=false;macOS 26 分支去掉 SwiftUI 阴影
  → 解决"红绿灯外圈黑边"
- 侧边栏视觉:strokeBorder 0.08 → 0.04、阴影 radius 16 → 6
  → 解决"灰色边栏 + 黑色卡顿"
- 首页底部新增今日概览 + 风格预览
2026-04-28 11:16:40 +08:00

75 lines
2.8 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.
import AppKit
import SwiftUI
import OpenLessUI
@MainActor
final class CapsuleWindowController {
private let window: NSPanel
private let hostingView: NSHostingView<CapsuleView>
private var currentState: CapsuleState = .hidden
private var currentLevel: Float = 0
// 比 CapsuleView (176×42) 留出足够空白,让 SwiftUI 的 .shadow(radius 18, y:8)
// 在 NSHostingView 内自然 fade 到透明,避免被 panel bounds 裁出一圈暗矩形。
private let panelSize = NSSize(width: 220, height: 96)
var onCancel: () -> Void = {}
var onConfirm: () -> Void = {}
init() {
let initialView = CapsuleView(state: .hidden, level: 0)
hostingView = NSHostingView(rootView: initialView)
hostingView.frame = NSRect(origin: .zero, size: panelSize)
let panel = NSPanel(
contentRect: NSRect(origin: .zero, size: panelSize),
styleMask: [.borderless, .nonactivatingPanel],
backing: .buffered,
defer: false
)
panel.isOpaque = false
panel.backgroundColor = .clear
// 系统阴影按 contentView 矩形 bounds 算,会和 SwiftUI 自带的胶囊阴影叠加成深色矩形边。
// SwiftUI 那一层已经够看,关掉系统阴影。
panel.hasShadow = false
panel.level = .statusBar
panel.collectionBehavior = [.canJoinAllSpaces, .stationary, .ignoresCycle, .fullScreenAuxiliary]
// 必须接受鼠标点击,否则胶囊上的叉/勾按钮无法响应。
// .nonactivatingPanel 保证点击不会抢走前台 app 的焦点。
panel.ignoresMouseEvents = false
panel.contentView = hostingView
self.window = panel
repositionToBottomCenter()
}
func update(state: CapsuleState, level: Float = 0) {
currentState = state
currentLevel = level
hostingView.rootView = CapsuleView(
state: state,
level: level,
onCancel: { [weak self] in self?.onCancel() },
onConfirm: { [weak self] in self?.onConfirm() }
)
if state == .hidden {
window.orderOut(nil)
} else {
if !window.isVisible {
repositionToBottomCenter()
window.orderFrontRegardless()
}
}
}
private func repositionToBottomCenter() {
guard let screen = NSScreen.main else { return }
let screenFrame = screen.visibleFrame
let size = window.frame.size
let x = screenFrame.midX - size.width / 2
// 旧版 panel(196×56) 时胶囊视觉中心位于 visibleFrame.minY + 52。
// 改大 panel 后保持视觉位置不变:y = 52 - panelHeight/2。
let y = screenFrame.minY + 52 - size.height / 2
window.setFrameOrigin(NSPoint(x: x, y: y))
}
}