mirror of
https://github.com/FBukovina/Atoll.git
synced 2026-10-27 15:14:33 +00:00
Merge branch 'main' into feature/configurable-media-key-steps
This commit is contained in:
commit
d6e833e724
8 changed files with 7444 additions and 346 deletions
|
|
@ -247,6 +247,7 @@
|
||||||
hasScannedForEncodings = 0;
|
hasScannedForEncodings = 0;
|
||||||
knownRegions = (
|
knownRegions = (
|
||||||
en,
|
en,
|
||||||
|
tr,
|
||||||
Base,
|
Base,
|
||||||
hi,
|
hi,
|
||||||
ta,
|
ta,
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -113,6 +113,7 @@ struct MinimalisticMusicPlayerView: View {
|
||||||
// Compact progress bar
|
// Compact progress bar
|
||||||
progressBar
|
progressBar
|
||||||
.padding(.top, 6)
|
.padding(.top, 6)
|
||||||
|
.clipped()
|
||||||
|
|
||||||
// Compact playback controls
|
// Compact playback controls
|
||||||
if shouldShowControlHUDRow {
|
if shouldShowControlHUDRow {
|
||||||
|
|
@ -133,7 +134,7 @@ struct MinimalisticMusicPlayerView: View {
|
||||||
reminderList
|
reminderList
|
||||||
}
|
}
|
||||||
.padding(.horizontal, 12)
|
.padding(.horizontal, 12)
|
||||||
.padding(.top, -15)
|
.padding(.top, 12)
|
||||||
.padding(.bottom, ReminderLiveActivityManager.baselineMinimalisticBottomPadding)
|
.padding(.bottom, ReminderLiveActivityManager.baselineMinimalisticBottomPadding)
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
.frame(height: calculateDynamicHeight(), alignment: .top)
|
.frame(height: calculateDynamicHeight(), alignment: .top)
|
||||||
|
|
@ -254,7 +255,7 @@ struct MinimalisticMusicPlayerView: View {
|
||||||
height += 6 + 4 // progress bar + top padding
|
height += 6 + 4 // progress bar + top padding
|
||||||
|
|
||||||
// Add playback controls height
|
// Add playback controls height
|
||||||
height += 40 + 2 // controls + top padding
|
height += 54 + 2 // controls + top padding
|
||||||
|
|
||||||
// Add lyrics height if enabled in settings (reserve space even while loading)
|
// Add lyrics height if enabled in settings (reserve space even while loading)
|
||||||
if enableLyrics {
|
if enableLyrics {
|
||||||
|
|
@ -706,7 +707,7 @@ private struct MinimalisticReminderDetailsView: View {
|
||||||
// MARK: - Playback Controls (Larger)
|
// MARK: - Playback Controls (Larger)
|
||||||
|
|
||||||
private var playbackControls: some View {
|
private var playbackControls: some View {
|
||||||
HStack(spacing: 16) {
|
HStack(spacing: 10) {
|
||||||
ForEach(Array(displayedSlots.enumerated()), id: \.offset) { _, slot in
|
ForEach(Array(displayedSlots.enumerated()), id: \.offset) { _, slot in
|
||||||
slotView(for: slot)
|
slotView(for: slot)
|
||||||
}
|
}
|
||||||
|
|
@ -834,10 +835,10 @@ private struct MinimalisticReminderDetailsView: View {
|
||||||
private var playPauseButton: some View {
|
private var playPauseButton: some View {
|
||||||
MinimalisticSquircircleButton(
|
MinimalisticSquircircleButton(
|
||||||
icon: musicManager.isPlaying ? (musicManager.isLiveStream ? "stop.fill" : "pause.fill") : "play.fill",
|
icon: musicManager.isPlaying ? (musicManager.isLiveStream ? "stop.fill" : "pause.fill") : "play.fill",
|
||||||
fontSize: 28,
|
fontSize: 26,
|
||||||
fontWeight: .semibold,
|
fontWeight: .semibold,
|
||||||
frameSize: CGSize(width: 60, height: 60),
|
frameSize: CGSize(width: 54, height: 54),
|
||||||
cornerRadius: 24,
|
cornerRadius: 22,
|
||||||
foregroundColor: .white,
|
foregroundColor: .white,
|
||||||
pressEffect: .none,
|
pressEffect: .none,
|
||||||
symbolEffectStyle: .replace,
|
symbolEffectStyle: .replace,
|
||||||
|
|
@ -867,8 +868,8 @@ private struct MinimalisticReminderDetailsView: View {
|
||||||
icon: icon,
|
icon: icon,
|
||||||
fontSize: size,
|
fontSize: size,
|
||||||
fontWeight: .medium,
|
fontWeight: .medium,
|
||||||
frameSize: CGSize(width: 40, height: 40),
|
frameSize: CGSize(width: 36, height: 36),
|
||||||
cornerRadius: 16,
|
cornerRadius: 14,
|
||||||
foregroundColor: isActive ? resolvedActiveColor : .white.opacity(0.85),
|
foregroundColor: isActive ? resolvedActiveColor : .white.opacity(0.85),
|
||||||
pressEffect: pressEffect,
|
pressEffect: pressEffect,
|
||||||
symbolEffectStyle: symbolEffect,
|
symbolEffectStyle: symbolEffect,
|
||||||
|
|
@ -991,8 +992,8 @@ private struct MinimalisticReminderDetailsView: View {
|
||||||
icon: routeManager.activeDevice?.iconName ?? "speaker.wave.2",
|
icon: routeManager.activeDevice?.iconName ?? "speaker.wave.2",
|
||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
fontWeight: .medium,
|
fontWeight: .medium,
|
||||||
frameSize: CGSize(width: 40, height: 40),
|
frameSize: CGSize(width: 36, height: 36),
|
||||||
cornerRadius: 16,
|
cornerRadius: 14,
|
||||||
foregroundColor: .white.opacity(0.85),
|
foregroundColor: .white.opacity(0.85),
|
||||||
symbolEffectStyle: .replace
|
symbolEffectStyle: .replace
|
||||||
) {
|
) {
|
||||||
|
|
@ -1051,8 +1052,8 @@ private struct MinimalisticReminderDetailsView: View {
|
||||||
icon: "airplayaudio",
|
icon: "airplayaudio",
|
||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
fontWeight: .medium,
|
fontWeight: .medium,
|
||||||
frameSize: CGSize(width: 40, height: 40),
|
frameSize: CGSize(width: 36, height: 36),
|
||||||
cornerRadius: 16,
|
cornerRadius: 14,
|
||||||
foregroundColor: .white.opacity(0.85),
|
foregroundColor: .white.opacity(0.85),
|
||||||
symbolEffectStyle: .replace
|
symbolEffectStyle: .replace
|
||||||
) {
|
) {
|
||||||
|
|
@ -1144,17 +1145,17 @@ struct MinimalisticAlbumArtView: View {
|
||||||
)
|
)
|
||||||
.clipped()
|
.clipped()
|
||||||
.clipShape(RoundedRectangle(cornerRadius: albumArtCornerRadius))
|
.clipShape(RoundedRectangle(cornerRadius: albumArtCornerRadius))
|
||||||
.scaleEffect(x: 1.3, y: 1.4)
|
.scaleEffect(x: 1.08, y: 1.1)
|
||||||
.rotationEffect(.degrees(92))
|
.rotationEffect(.degrees(92))
|
||||||
.blur(radius: 35)
|
.blur(radius: 16)
|
||||||
.opacity(
|
.opacity(
|
||||||
usesLiveCanvasArtwork
|
usesLiveCanvasArtwork
|
||||||
? (musicManager.isPlaying ? 0.68 : 0.22)
|
? (musicManager.isPlaying ? 0.55 : 0.18)
|
||||||
: min(0.6, 1 - max(musicManager.albumArt.getBrightness(), 0.3))
|
: min(0.45, 1 - max(musicManager.albumArt.getBrightness(), 0.3))
|
||||||
)
|
)
|
||||||
.shadow(
|
.shadow(
|
||||||
color: Color(nsColor: musicManager.avgColor).opacity(usesLiveCanvasArtwork ? 0.28 : 0.16),
|
color: Color(nsColor: musicManager.avgColor).opacity(usesLiveCanvasArtwork ? 0.22 : 0.12),
|
||||||
radius: usesLiveCanvasArtwork ? 18 : 12,
|
radius: usesLiveCanvasArtwork ? 10 : 7,
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0
|
y: 0
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -632,10 +632,10 @@ struct SettingsView: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
VStack(alignment: .leading, spacing: 2) {
|
VStack(alignment: .leading, spacing: 2) {
|
||||||
Text(suggestion.title)
|
Text(LocalizedStringKey(suggestion.title))
|
||||||
.font(.system(size: 13, weight: .semibold))
|
.font(.system(size: 13, weight: .semibold))
|
||||||
.foregroundStyle(Color.primary)
|
.foregroundStyle(Color.primary)
|
||||||
Text(suggestion.tab.title)
|
Text(LocalizedStringKey(suggestion.tab.title))
|
||||||
.font(.system(size: 11))
|
.font(.system(size: 11))
|
||||||
.foregroundStyle(Color.secondary)
|
.foregroundStyle(Color.secondary)
|
||||||
}
|
}
|
||||||
|
|
@ -2239,12 +2239,12 @@ private struct SystemControlsView: View {
|
||||||
case .betterDisplay:
|
case .betterDisplay:
|
||||||
if betterDisplayManager.isRunning { return "Running" }
|
if betterDisplayManager.isRunning { return "Running" }
|
||||||
if betterDisplayManager.isDetected { return "Not running" }
|
if betterDisplayManager.isDetected { return "Not running" }
|
||||||
return "Not detected"
|
return String(localized: "Not detected")
|
||||||
case .lunar:
|
case .lunar:
|
||||||
if lunarManager.isConnected { return "Connected" }
|
if lunarManager.isConnected { return "Connected" }
|
||||||
if lunarManager.isRunning { return "Running" }
|
if lunarManager.isRunning { return "Running" }
|
||||||
if lunarManager.isDetected { return "Not running" }
|
if lunarManager.isDetected { return "Not running" }
|
||||||
return "Not detected"
|
return String(localized: "Not detected")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2266,23 +2266,23 @@ private struct SystemControlsView: View {
|
||||||
switch thirdPartyDDCProvider {
|
switch thirdPartyDDCProvider {
|
||||||
case .betterDisplay:
|
case .betterDisplay:
|
||||||
if !betterDisplayManager.isDetected {
|
if !betterDisplayManager.isDetected {
|
||||||
return "Install [BetterDisplay](https://betterdisplay.pro) to control external display brightness (and optional volume) through Atoll's HUD."
|
return String(localized: "Install [BetterDisplay](https://betterdisplay.pro) to control external display brightness (and optional volume) through Atoll's HUD.")
|
||||||
}
|
}
|
||||||
if !betterDisplayManager.isRunning {
|
if !betterDisplayManager.isRunning {
|
||||||
return "BetterDisplay is installed but not currently running. Launch BetterDisplay to enable integration."
|
return String(localized: "BetterDisplay is installed but not currently running. Launch BetterDisplay to enable integration.")
|
||||||
}
|
}
|
||||||
return "BetterDisplay OSD events will be routed through Atoll's active HUD style. Brightness is always routed; volume is routed when external volume control listener is enabled below. Make sure BetterDisplay's OSD integration is enabled in Settings › Application › Integration."
|
return String(localized: "BetterDisplay OSD events will be routed through Atoll's active HUD style. Brightness is always routed; volume is routed when external volume control listener is enabled below. Make sure BetterDisplay's OSD integration is enabled in Settings › Application › Integration.")
|
||||||
case .lunar:
|
case .lunar:
|
||||||
if !lunarManager.isDetected {
|
if !lunarManager.isDetected {
|
||||||
return "Install [Lunar](https://lunar.fyi) to control external display brightness, contrast, and optional volume through Atoll's HUD via DDC."
|
return String(localized: "Install [Lunar](https://lunar.fyi) to control external display brightness, contrast, and optional volume through Atoll's HUD via DDC.")
|
||||||
}
|
}
|
||||||
if !lunarManager.isRunning {
|
if !lunarManager.isRunning {
|
||||||
return "Lunar is installed but not currently running. Launch Lunar to enable integration."
|
return String(localized: "Lunar is installed but not currently running. Launch Lunar to enable integration.")
|
||||||
}
|
}
|
||||||
if lunarManager.isConnected {
|
if lunarManager.isConnected {
|
||||||
return "Connected to Lunar's DDC socket. Brightness and contrast adjustments are shown through Atoll's HUD; volume follows when external volume control listener is enabled below."
|
return String(localized: "Connected to Lunar's DDC socket. Brightness and contrast adjustments are shown through Atoll's HUD; volume follows when external volume control listener is enabled below.")
|
||||||
}
|
}
|
||||||
return "Lunar is running but the socket connection is not yet established. It will connect automatically."
|
return String(localized: "Lunar is running but the socket connection is not yet established. It will connect automatically.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3125,14 +3125,14 @@ struct CalendarSettings: View {
|
||||||
|
|
||||||
var title: String {
|
var title: String {
|
||||||
switch self {
|
switch self {
|
||||||
case .mins15: return "15 mins"
|
case .mins15: return String(localized: "15 mins")
|
||||||
case .mins30: return "30 mins"
|
case .mins30: return String(localized: "30 mins")
|
||||||
case .hour1: return "1 hour"
|
case .hour1: return String(localized: "1 hour")
|
||||||
case .hours3: return "3 hours"
|
case .hours3: return String(localized: "3 hours")
|
||||||
case .hours6: return "6 hours"
|
case .hours6: return String(localized: "6 hours")
|
||||||
case .hours12: return "12 hours"
|
case .hours12: return String(localized: "12 hours")
|
||||||
case .restOfDay: return "Rest of the day"
|
case .restOfDay: return String(localized: "Rest of the day")
|
||||||
case .allTime: return "All time"
|
case .allTime: return String(localized: "All time")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3623,9 +3623,9 @@ private extension DevicesSettingsView {
|
||||||
var title: String {
|
var title: String {
|
||||||
switch self {
|
switch self {
|
||||||
case .symbol:
|
case .symbol:
|
||||||
return "Symbol"
|
return String(localized: "Symbol")
|
||||||
case .threeD:
|
case .threeD:
|
||||||
return "3D"
|
return String(localized: "3D")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4961,14 +4961,14 @@ struct LockScreenSettings: View {
|
||||||
|
|
||||||
var title: String {
|
var title: String {
|
||||||
switch self {
|
switch self {
|
||||||
case .mins15: return "15 mins"
|
case .mins15: return String(localized: "15 mins")
|
||||||
case .mins30: return "30 mins"
|
case .mins30: return String(localized: "30 mins")
|
||||||
case .hour1: return "1 hour"
|
case .hour1: return String(localized: "1 hour")
|
||||||
case .hours3: return "3 hours"
|
case .hours3: return String(localized: "3 hours")
|
||||||
case .hours6: return "6 hours"
|
case .hours6: return String(localized: "6 hours")
|
||||||
case .hours12: return "12 hours"
|
case .hours12: return String(localized: "12 hours")
|
||||||
case .restOfDay: return "Rest of the day"
|
case .restOfDay: return String(localized: "Rest of the day")
|
||||||
case .allTime: return "All time"
|
case .allTime: return String(localized: "All time")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4982,9 +4982,9 @@ struct LockScreenSettings: View {
|
||||||
|
|
||||||
var title: String {
|
var title: String {
|
||||||
switch self {
|
switch self {
|
||||||
case .leading: return "Left"
|
case .leading: return String(localized: "Left")
|
||||||
case .center: return "Center"
|
case .center: return String(localized: "Center")
|
||||||
case .trailing: return "Right"
|
case .trailing: return String(localized: "Right")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -7057,11 +7057,11 @@ struct StatsSettings: View {
|
||||||
private var formattedUpdateInterval: String {
|
private var formattedUpdateInterval: String {
|
||||||
let seconds = Int(statsUpdateInterval.rounded())
|
let seconds = Int(statsUpdateInterval.rounded())
|
||||||
if seconds >= 60 {
|
if seconds >= 60 {
|
||||||
return "60 s (1 min)"
|
return String(localized: "60 s (1 min)")
|
||||||
} else if seconds == 1 {
|
} else if seconds == 1 {
|
||||||
return "1 s"
|
return String(localized: "1 s")
|
||||||
} else {
|
} else {
|
||||||
return "\(seconds) s"
|
return String(localized: "\(seconds) s")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -7453,16 +7453,16 @@ struct ClipboardSettings: View {
|
||||||
let interval = Date().timeIntervalSince(date)
|
let interval = Date().timeIntervalSince(date)
|
||||||
|
|
||||||
if interval < 60 {
|
if interval < 60 {
|
||||||
return "Just now"
|
return String(localized: "Just now")
|
||||||
} else if interval < 3600 {
|
} else if interval < 3600 {
|
||||||
let minutes = Int(interval / 60)
|
let minutes = Int(interval / 60)
|
||||||
return "\(minutes)m ago"
|
return String(localized: "\(minutes)m ago")
|
||||||
} else if interval < 86400 {
|
} else if interval < 86400 {
|
||||||
let hours = Int(interval / 3600)
|
let hours = Int(interval / 3600)
|
||||||
return "\(hours)h ago"
|
return String(localized: "\(hours)h ago")
|
||||||
} else {
|
} else {
|
||||||
let days = Int(interval / 86400)
|
let days = Int(interval / 86400)
|
||||||
return "\(days)d ago"
|
return String(localized: "\(days)d ago")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -7631,16 +7631,16 @@ struct ScreenAssistantSettings: View {
|
||||||
let interval = Date().timeIntervalSince(date)
|
let interval = Date().timeIntervalSince(date)
|
||||||
|
|
||||||
if interval < 60 {
|
if interval < 60 {
|
||||||
return "Just now"
|
return String(localized: "Just now")
|
||||||
} else if interval < 3600 {
|
} else if interval < 3600 {
|
||||||
let minutes = Int(interval / 60)
|
let minutes = Int(interval / 60)
|
||||||
return "\(minutes)m ago"
|
return String(localized: "\(minutes)m ago")
|
||||||
} else if interval < 86400 {
|
} else if interval < 86400 {
|
||||||
let hours = Int(interval / 3600)
|
let hours = Int(interval / 3600)
|
||||||
return "\(hours)h ago"
|
return String(localized: "\(hours)h ago")
|
||||||
} else {
|
} else {
|
||||||
let days = Int(interval / 86400)
|
let days = Int(interval / 86400)
|
||||||
return "\(days)d ago"
|
return String(localized: "\(days)d ago")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -8004,21 +8004,21 @@ struct SettingsPermissionCallout: View {
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(alignment: .leading, spacing: 8) {
|
VStack(alignment: .leading, spacing: 8) {
|
||||||
Label(title, systemImage: icon)
|
Label(LocalizedStringKey(title), systemImage: icon)
|
||||||
.font(.subheadline.weight(.semibold))
|
.font(.subheadline.weight(.semibold))
|
||||||
.foregroundStyle(iconColor)
|
.foregroundStyle(iconColor)
|
||||||
|
|
||||||
Text(message)
|
Text(LocalizedStringKey(message))
|
||||||
.font(.caption)
|
.font(.caption)
|
||||||
.foregroundStyle(.secondary)
|
.foregroundStyle(.secondary)
|
||||||
|
|
||||||
HStack(spacing: 8) {
|
HStack(spacing: 8) {
|
||||||
Button(requestButtonTitle) {
|
Button(LocalizedStringKey(requestButtonTitle)) {
|
||||||
requestAction()
|
requestAction()
|
||||||
}
|
}
|
||||||
.buttonStyle(.borderedProminent)
|
.buttonStyle(.borderedProminent)
|
||||||
|
|
||||||
Button(openSettingsButtonTitle) {
|
Button(LocalizedStringKey(openSettingsButtonTitle)) {
|
||||||
openSettingsAction()
|
openSettingsAction()
|
||||||
}
|
}
|
||||||
.buttonStyle(.bordered)
|
.buttonStyle(.bordered)
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@ class LockScreenManager: ObservableObject {
|
||||||
private var debounceIdleTask: Task<Void, Never>?
|
private var debounceIdleTask: Task<Void, Never>?
|
||||||
private var collapseTask: Task<Void, Never>?
|
private var collapseTask: Task<Void, Never>?
|
||||||
private var postUnlockMusicHUDTask: Task<Void, Never>?
|
private var postUnlockMusicHUDTask: Task<Void, Never>?
|
||||||
|
private var lockStatePollTask: Task<Void, Never>?
|
||||||
|
|
||||||
// MARK: - Helpers
|
// MARK: - Helpers
|
||||||
|
|
||||||
|
|
@ -65,9 +66,11 @@ class LockScreenManager: ObservableObject {
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
DistributedNotificationCenter.default().removeObserver(self)
|
DistributedNotificationCenter.default().removeObserver(self)
|
||||||
|
NSWorkspace.shared.notificationCenter.removeObserver(self)
|
||||||
debounceIdleTask?.cancel()
|
debounceIdleTask?.cancel()
|
||||||
collapseTask?.cancel()
|
collapseTask?.cancel()
|
||||||
postUnlockMusicHUDTask?.cancel()
|
postUnlockMusicHUDTask?.cancel()
|
||||||
|
lockStatePollTask?.cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Setup
|
// MARK: - Setup
|
||||||
|
|
@ -80,7 +83,7 @@ class LockScreenManager: ObservableObject {
|
||||||
name: .init("com.apple.screenIsLocked"),
|
name: .init("com.apple.screenIsLocked"),
|
||||||
object: nil
|
object: nil
|
||||||
)
|
)
|
||||||
|
|
||||||
// Observe screen unlocked event
|
// Observe screen unlocked event
|
||||||
DistributedNotificationCenter.default().addObserver(
|
DistributedNotificationCenter.default().addObserver(
|
||||||
self,
|
self,
|
||||||
|
|
@ -88,7 +91,18 @@ class LockScreenManager: ObservableObject {
|
||||||
name: .init("com.apple.screenIsUnlocked"),
|
name: .init("com.apple.screenIsUnlocked"),
|
||||||
object: nil
|
object: nil
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Fallback: macOS sometimes delays `com.apple.screenIsUnlocked`, leaving
|
||||||
|
// lock-screen widgets visible after the user-perceived unlock.
|
||||||
|
// The workspace session-active notification typically fires earlier; the
|
||||||
|
// guard at the top of `screenUnlocked` makes the call idempotent.
|
||||||
|
NSWorkspace.shared.notificationCenter.addObserver(
|
||||||
|
self,
|
||||||
|
selector: #selector(screenUnlocked),
|
||||||
|
name: NSWorkspace.sessionDidBecomeActiveNotification,
|
||||||
|
object: nil
|
||||||
|
)
|
||||||
|
|
||||||
print("LockScreenManager: ✅ Observers registered for lock/unlock events")
|
print("LockScreenManager: ✅ Observers registered for lock/unlock events")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -141,9 +155,11 @@ class LockScreenManager: ObservableObject {
|
||||||
print("[\(timestamp())] LockScreenManager: ⏭️ Lock icon disabled in settings")
|
print("[\(timestamp())] LockScreenManager: ⏭️ Lock icon disabled in settings")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
startLockStatePolling()
|
||||||
|
|
||||||
print("[\(timestamp())] LockScreenManager: ✅ Lock screen activated")
|
print("[\(timestamp())] LockScreenManager: ✅ Lock screen activated")
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc private func screenUnlocked() {
|
@objc private func screenUnlocked() {
|
||||||
guard isLocked else {
|
guard isLocked else {
|
||||||
print("[\(timestamp())] LockScreenManager: 🔁 Unlock event ignored (already unlocked)")
|
print("[\(timestamp())] LockScreenManager: 🔁 Unlock event ignored (already unlocked)")
|
||||||
|
|
@ -156,6 +172,7 @@ class LockScreenManager: ObservableObject {
|
||||||
lastUpdated = Date()
|
lastUpdated = Date()
|
||||||
updateIdleState(locked: false)
|
updateIdleState(locked: false)
|
||||||
isLocked = false
|
isLocked = false
|
||||||
|
stopLockStatePolling()
|
||||||
postUnlockMusicHUDTask?.cancel()
|
postUnlockMusicHUDTask?.cancel()
|
||||||
shouldDelayPostUnlockMusicHUD = Defaults[.enableLockScreenLiveActivity]
|
shouldDelayPostUnlockMusicHUD = Defaults[.enableLockScreenLiveActivity]
|
||||||
|
|
||||||
|
|
@ -207,8 +224,46 @@ class LockScreenManager: ObservableObject {
|
||||||
print("[\(self.timestamp())] LockScreenManager: ✅ Lock screen deactivated")
|
print("[\(self.timestamp())] LockScreenManager: ✅ Lock screen deactivated")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Lock State Polling
|
||||||
|
|
||||||
|
// Defensive fallback against late/missed `com.apple.screenIsUnlocked` and
|
||||||
|
// `NSWorkspace.sessionDidBecomeActiveNotification` notifications, which
|
||||||
|
// macOS sometimes delivers well after the user-perceived unlock — leaving
|
||||||
|
// lock-screen widgets visible for an extra moment. While we believe we are
|
||||||
|
// locked, poll the canonical session-lock state and fire `screenUnlocked()`
|
||||||
|
// the moment the OS flips. The handler's duplicate-event guard makes this
|
||||||
|
// safe to call alongside any later-arriving notification.
|
||||||
|
private static func isSessionScreenLocked() -> Bool {
|
||||||
|
guard let session = CGSessionCopyCurrentDictionary() as? [String: Any] else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return session["CGSSessionScreenIsLocked"] as? Bool ?? false
|
||||||
|
}
|
||||||
|
|
||||||
|
private func startLockStatePolling() {
|
||||||
|
lockStatePollTask?.cancel()
|
||||||
|
lockStatePollTask = Task { [weak self] in
|
||||||
|
while !Task.isCancelled {
|
||||||
|
try? await Task.sleep(for: .milliseconds(500))
|
||||||
|
if Task.isCancelled { return }
|
||||||
|
await MainActor.run {
|
||||||
|
guard let self, self.isLocked else { return }
|
||||||
|
if !Self.isSessionScreenLocked() {
|
||||||
|
print("[\(self.timestamp())] LockScreenManager: 🔓 Polling detected unlock ahead of notification")
|
||||||
|
self.screenUnlocked()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func stopLockStatePolling() {
|
||||||
|
lockStatePollTask?.cancel()
|
||||||
|
lockStatePollTask = nil
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Idle State Management
|
// MARK: - Idle State Management
|
||||||
|
|
||||||
/// Copy EXACT logic from ScreenRecordingManager
|
/// Copy EXACT logic from ScreenRecordingManager
|
||||||
private func updateIdleState(locked: Bool) {
|
private func updateIdleState(locked: Bool) {
|
||||||
if locked {
|
if locked {
|
||||||
|
|
|
||||||
|
|
@ -49,8 +49,10 @@ final class LockScreenWeatherPanelManager {
|
||||||
|
|
||||||
func hide() {
|
func hide() {
|
||||||
guard let window else { return }
|
guard let window else { return }
|
||||||
|
window.alphaValue = 0
|
||||||
window.orderOut(nil)
|
window.orderOut(nil)
|
||||||
window.contentView = nil
|
window.contentView = nil
|
||||||
|
window.alphaValue = 1
|
||||||
latestFrame = nil
|
latestFrame = nil
|
||||||
lastSnapshot = nil
|
lastSnapshot = nil
|
||||||
lastContentSize = nil
|
lastContentSize = nil
|
||||||
|
|
|
||||||
14
DynamicIsland/tr.lproj/InfoPlist.strings
Normal file
14
DynamicIsland/tr.lproj/InfoPlist.strings
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
"CFBundleDisplayName" = "Atoll";
|
||||||
|
"NSAppleEventsUsageDescription" = "Atoll, Spotify ve Apple Music'i kontrol etmek için AppleScript kullanır.";
|
||||||
|
"NSBluetoothAlwaysUsageDescription" = "Atoll, ses aygıtları bağlandığında bunları algılamak ve HUD'da pil durumlarını göstermek için Bluetooth erişimine ihtiyaç duyar.";
|
||||||
|
"NSCalendarsFullAccessUsageDescription" = "Takvim etkinliklerini çentik içinde gösterir.";
|
||||||
|
"NSCalendarsUsageDescription" = "Takvim modu için gereklidir.";
|
||||||
|
"NSCameraUsageDescription" = "Ayna modu için kameranıza ihtiyaç duyuyoruz.";
|
||||||
|
"NSDesktopFolderUsageDescription" = "Atoll, ekran görüntülerini masaüstünüze kaydetmek için erişime ihtiyaç duyar.";
|
||||||
|
"NSDocumentsFolderUsageDescription" = "Atoll, rafa sürüklenen dosyaları kaydetmek ve okumak için erişime ihtiyaç duyar.";
|
||||||
|
"NSDownloadsFolderUsageDescription" = "Atoll, İndirilenler klasöründeki dosyaları okumak için erişime ihtiyaç duyar.";
|
||||||
|
"NSLocationUsageDescription" = "Atoll, doğru kilit ekranı hava durumunu göstermek için konumunuzu kullanır.";
|
||||||
|
"NSLocationWhenInUseUsageDescription" = "Atoll, doğru kilit ekranı hava durumunu göstermek için konumunuzu kullanır.";
|
||||||
|
"NSRemindersUsageDescription" = "Atoll, anımsatıcılarınızı göstermek için Anımsatıcılar erişimine ihtiyaç duyar.";
|
||||||
|
"NSAudioCaptureUsageDescription" = "Atoll, gerçek zamanlı müzik dalga biçimi görselleştiricisini göstermek için ses yakalama iznine ihtiyaç duyar.";
|
||||||
|
"NSScreenCaptureUsageDescription" = "Atoll, yapay zeka asistanı özelliği için ekran görüntüleri yakalamak üzere ekran kaydı iznine ihtiyaç duyar.";
|
||||||
|
|
@ -11994,8 +11994,8 @@
|
||||||
},
|
},
|
||||||
"zh-Hans": {
|
"zh-Hans": {
|
||||||
"stringUnit": {
|
"stringUnit": {
|
||||||
"state": "new",
|
"state": "translated",
|
||||||
"value": ""
|
"value": "Amazon Music"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -36021,6 +36021,12 @@
|
||||||
"value": "Включить медиа ",
|
"value": "Включить медиа ",
|
||||||
"state": "translated"
|
"state": "translated"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"zh-Hans": {
|
||||||
|
"stringUnit": {
|
||||||
|
"state": "translated",
|
||||||
|
"value": "启用媒体 "
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -60469,8 +60475,8 @@
|
||||||
},
|
},
|
||||||
"zh-Hans": {
|
"zh-Hans": {
|
||||||
"stringUnit": {
|
"stringUnit": {
|
||||||
"state": "new",
|
"state": "translated",
|
||||||
"value": ""
|
"value": "仅应用图标"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -60599,8 +60605,8 @@
|
||||||
},
|
},
|
||||||
"zh-Hans": {
|
"zh-Hans": {
|
||||||
"stringUnit": {
|
"stringUnit": {
|
||||||
"state": "new",
|
"state": "translated",
|
||||||
"value": ""
|
"value": "仅下载图标"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -60729,8 +60735,8 @@
|
||||||
},
|
},
|
||||||
"zh-Hans": {
|
"zh-Hans": {
|
||||||
"stringUnit": {
|
"stringUnit": {
|
||||||
"state": "new",
|
"state": "translated",
|
||||||
"value": ""
|
"value": "透明度:"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -60865,8 +60871,8 @@
|
||||||
},
|
},
|
||||||
"zh-Hans": {
|
"zh-Hans": {
|
||||||
"stringUnit": {
|
"stringUnit": {
|
||||||
"state": "new",
|
"state": "translated",
|
||||||
"value": ""
|
"value": "打开 Google AI Studio"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -60996,8 +61002,8 @@
|
||||||
},
|
},
|
||||||
"zh-Hans": {
|
"zh-Hans": {
|
||||||
"stringUnit": {
|
"stringUnit": {
|
||||||
"state": "new",
|
"state": "translated",
|
||||||
"value": ""
|
"value": "在日历中打开"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -61132,8 +61138,8 @@
|
||||||
},
|
},
|
||||||
"zh-Hans": {
|
"zh-Hans": {
|
||||||
"stringUnit": {
|
"stringUnit": {
|
||||||
"state": "new",
|
"state": "translated",
|
||||||
"value": ""
|
"value": "打开模型设置"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -61152,6 +61158,12 @@
|
||||||
"value": "Открывать вырез при наведении",
|
"value": "Открывать вырез при наведении",
|
||||||
"state": "translated"
|
"state": "translated"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"zh-Hans": {
|
||||||
|
"stringUnit": {
|
||||||
|
"state": "translated",
|
||||||
|
"value": "悬停时打开刘海"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -61280,8 +61292,8 @@
|
||||||
},
|
},
|
||||||
"zh-Hans": {
|
"zh-Hans": {
|
||||||
"stringUnit": {
|
"stringUnit": {
|
||||||
"state": "new",
|
"state": "translated",
|
||||||
"value": ""
|
"value": "打开设置"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -61558,8 +61570,8 @@
|
||||||
},
|
},
|
||||||
"zh-Hans": {
|
"zh-Hans": {
|
||||||
"stringUnit": {
|
"stringUnit": {
|
||||||
"state": "new",
|
"state": "translated",
|
||||||
"value": ""
|
"value": "打开 AI 助手面板,进行文件分析与对话。默认为 Cmd+Shift+A。仅在启用屏幕助手功能时有效。"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -61700,8 +61712,8 @@
|
||||||
},
|
},
|
||||||
"zh-Hans": {
|
"zh-Hans": {
|
||||||
"stringUnit": {
|
"stringUnit": {
|
||||||
"state": "new",
|
"state": "translated",
|
||||||
"value": ""
|
"value": "打开剪贴板历史记录面板。默认为 Cmd+Shift+V(类似于 PC 上的 Windows+V)。仅在启用剪贴板功能时有效。"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -61842,8 +61854,8 @@
|
||||||
},
|
},
|
||||||
"zh-Hans": {
|
"zh-Hans": {
|
||||||
"stringUnit": {
|
"stringUnit": {
|
||||||
"state": "new",
|
"state": "translated",
|
||||||
"value": ""
|
"value": "打开取色器面板进行屏幕颜色捕获。默认为 Cmd+Shift+P。仅在启用取色器功能时有效。"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -61973,8 +61985,8 @@
|
||||||
},
|
},
|
||||||
"zh-Hans": {
|
"zh-Hans": {
|
||||||
"stringUnit": {
|
"stringUnit": {
|
||||||
"state": "new",
|
"state": "translated",
|
||||||
"value": ""
|
"value": "输出设备"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -62104,8 +62116,8 @@
|
||||||
},
|
},
|
||||||
"zh-Hans": {
|
"zh-Hans": {
|
||||||
"stringUnit": {
|
"stringUnit": {
|
||||||
"state": "new",
|
"state": "translated",
|
||||||
"value": ""
|
"value": "输出音量"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -62246,8 +62258,8 @@
|
||||||
},
|
},
|
||||||
"zh-Hans": {
|
"zh-Hans": {
|
||||||
"stringUnit": {
|
"stringUnit": {
|
||||||
"state": "new",
|
"state": "translated",
|
||||||
"value": ""
|
"value": "面板模式会在刘海附近的浮动窗口中显示剪贴板。弹出模式会将剪贴板显示为附加在剪贴板按钮上的下拉菜单。"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -62388,8 +62400,8 @@
|
||||||
},
|
},
|
||||||
"zh-Hans": {
|
"zh-Hans": {
|
||||||
"stringUnit": {
|
"stringUnit": {
|
||||||
"state": "new",
|
"state": "translated",
|
||||||
"value": ""
|
"value": "面板模式会在浮动窗口中显示取色器。弹出模式会将取色器显示为附加在取色器按钮上的下拉菜单。"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -62530,8 +62542,8 @@
|
||||||
},
|
},
|
||||||
"zh-Hans": {
|
"zh-Hans": {
|
||||||
"stringUnit": {
|
"stringUnit": {
|
||||||
"state": "new",
|
"state": "translated",
|
||||||
"value": ""
|
"value": "面板模式会在刘海附近的浮动窗口中显示助手。弹出模式会将助手显示为附加在 AI 按钮上的下拉菜单。"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -62790,8 +62802,8 @@
|
||||||
},
|
},
|
||||||
"zh-Hans": {
|
"zh-Hans": {
|
||||||
"stringUnit": {
|
"stringUnit": {
|
||||||
"state": "new",
|
"state": "translated",
|
||||||
"value": ""
|
"value": "已暂停"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -63310,8 +63322,8 @@
|
||||||
},
|
},
|
||||||
"zh-Hans": {
|
"zh-Hans": {
|
||||||
"stringUnit": {
|
"stringUnit": {
|
||||||
"state": "new",
|
"state": "translated",
|
||||||
"value": ""
|
"value": "已选取 %@"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -63440,8 +63452,8 @@
|
||||||
},
|
},
|
||||||
"zh-Hans": {
|
"zh-Hans": {
|
||||||
"stringUnit": {
|
"stringUnit": {
|
||||||
"state": "new",
|
"state": "translated",
|
||||||
"value": ""
|
"value": "拾取的颜色"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -63570,8 +63582,8 @@
|
||||||
},
|
},
|
||||||
"zh-Hans": {
|
"zh-Hans": {
|
||||||
"stringUnit": {
|
"stringUnit": {
|
||||||
"state": "new",
|
"state": "translated",
|
||||||
"value": ""
|
"value": "拾取状态"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -63700,8 +63712,8 @@
|
||||||
},
|
},
|
||||||
"zh-Hans": {
|
"zh-Hans": {
|
||||||
"stringUnit": {
|
"stringUnit": {
|
||||||
"state": "new",
|
"state": "translated",
|
||||||
"value": ""
|
"value": "从历史记录中固定项目以保存在此处"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -63830,8 +63842,8 @@
|
||||||
},
|
},
|
||||||
"zh-Hans": {
|
"zh-Hans": {
|
||||||
"stringUnit": {
|
"stringUnit": {
|
||||||
"state": "new",
|
"state": "translated",
|
||||||
"value": ""
|
"value": "固定项目以添加收藏"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -63960,8 +63972,8 @@
|
||||||
},
|
},
|
||||||
"zh-Hans": {
|
"zh-Hans": {
|
||||||
"stringUnit": {
|
"stringUnit": {
|
||||||
"state": "new",
|
"state": "translated",
|
||||||
"value": ""
|
"value": "固定项目以将它们添加到收藏夹"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -64090,8 +64102,8 @@
|
||||||
},
|
},
|
||||||
"zh-Hans": {
|
"zh-Hans": {
|
||||||
"stringUnit": {
|
"stringUnit": {
|
||||||
"state": "new",
|
"state": "translated",
|
||||||
"value": ""
|
"value": "已固定项目"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -64226,8 +64238,8 @@
|
||||||
},
|
},
|
||||||
"zh-Hans": {
|
"zh-Hans": {
|
||||||
"stringUnit": {
|
"stringUnit": {
|
||||||
"state": "new",
|
"state": "translated",
|
||||||
"value": ""
|
"value": "请在模型设置中为所选的 AI 提供商配置您的 API 密钥。"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -64521,8 +64533,8 @@
|
||||||
},
|
},
|
||||||
"zh-Hans": {
|
"zh-Hans": {
|
||||||
"stringUnit": {
|
"stringUnit": {
|
||||||
"state": "new",
|
"state": "translated",
|
||||||
"value": ""
|
"value": "弹出模式会将剪贴板显示为附加在剪贴板按钮上的下拉菜单。面板模式会在刘海附近的浮动窗口中显示剪贴板。"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -64663,8 +64675,8 @@
|
||||||
},
|
},
|
||||||
"zh-Hans": {
|
"zh-Hans": {
|
||||||
"stringUnit": {
|
"stringUnit": {
|
||||||
"state": "new",
|
"state": "translated",
|
||||||
"value": ""
|
"value": "弹出模式会将取色器显示为附加在取色器按钮上的下拉菜单。面板模式会在浮动窗口中显示取色器。"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -64805,8 +64817,8 @@
|
||||||
},
|
},
|
||||||
"zh-Hans": {
|
"zh-Hans": {
|
||||||
"stringUnit": {
|
"stringUnit": {
|
||||||
"state": "new",
|
"state": "translated",
|
||||||
"value": ""
|
"value": "弹出模式会将助手显示为附加在 AI 按钮上的下拉菜单。面板模式会在刘海附近的浮动窗口中显示助手。"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -65340,8 +65352,8 @@
|
||||||
},
|
},
|
||||||
"zh-Hans": {
|
"zh-Hans": {
|
||||||
"stringUnit": {
|
"stringUnit": {
|
||||||
"state": "new",
|
"state": "translated",
|
||||||
"value": ""
|
"value": "预设将显示在计时器弹出窗口中,包括配置的名称、持续时间和重音颜色。重新排序以更改显示顺序。"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -65471,8 +65483,8 @@
|
||||||
},
|
},
|
||||||
"zh-Hans": {
|
"zh-Hans": {
|
||||||
"stringUnit": {
|
"stringUnit": {
|
||||||
"state": "new",
|
"state": "translated",
|
||||||
"value": ""
|
"value": "气压"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -65731,8 +65743,8 @@
|
||||||
},
|
},
|
||||||
"zh-Hans": {
|
"zh-Hans": {
|
||||||
"stringUnit": {
|
"stringUnit": {
|
||||||
"state": "new",
|
"state": "translated",
|
||||||
"value": ""
|
"value": "预览缩放:"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -89120,8 +89132,8 @@
|
||||||
},
|
},
|
||||||
"zh-Hans": {
|
"zh-Hans": {
|
||||||
"stringUnit": {
|
"stringUnit": {
|
||||||
"state": "new",
|
"state": "translated",
|
||||||
"value": ""
|
"value": "当 Amazon Music 应用是活动媒体源时使用 macOS 正在播放。播放控制遵循系统正在播放目标。如果 Amazon Music 应用不支持远程拖拽,在时间轴上拖动可能无效。"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -92636,6 +92648,266 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"15 mins": {
|
||||||
|
"localizations": {
|
||||||
|
"zh-Hans": {
|
||||||
|
"stringUnit": {
|
||||||
|
"state": "translated",
|
||||||
|
"value": "15 分钟"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"30 mins": {
|
||||||
|
"localizations": {
|
||||||
|
"zh-Hans": {
|
||||||
|
"stringUnit": {
|
||||||
|
"state": "translated",
|
||||||
|
"value": "30 分钟"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"1 hour": {
|
||||||
|
"localizations": {
|
||||||
|
"zh-Hans": {
|
||||||
|
"stringUnit": {
|
||||||
|
"state": "translated",
|
||||||
|
"value": "1 小时"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"3 hours": {
|
||||||
|
"localizations": {
|
||||||
|
"zh-Hans": {
|
||||||
|
"stringUnit": {
|
||||||
|
"state": "translated",
|
||||||
|
"value": "3 小时"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"6 hours": {
|
||||||
|
"localizations": {
|
||||||
|
"zh-Hans": {
|
||||||
|
"stringUnit": {
|
||||||
|
"state": "translated",
|
||||||
|
"value": "6 小时"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"12 hours": {
|
||||||
|
"localizations": {
|
||||||
|
"zh-Hans": {
|
||||||
|
"stringUnit": {
|
||||||
|
"state": "translated",
|
||||||
|
"value": "12 小时"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Rest of the day": {
|
||||||
|
"localizations": {
|
||||||
|
"zh-Hans": {
|
||||||
|
"stringUnit": {
|
||||||
|
"state": "translated",
|
||||||
|
"value": "今天余下的时间"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"All time": {
|
||||||
|
"localizations": {
|
||||||
|
"zh-Hans": {
|
||||||
|
"stringUnit": {
|
||||||
|
"state": "translated",
|
||||||
|
"value": "所有时间"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Symbol": {
|
||||||
|
"localizations": {
|
||||||
|
"zh-Hans": {
|
||||||
|
"stringUnit": {
|
||||||
|
"state": "translated",
|
||||||
|
"value": "符号"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"3D": {
|
||||||
|
"localizations": {
|
||||||
|
"zh-Hans": {
|
||||||
|
"stringUnit": {
|
||||||
|
"state": "translated",
|
||||||
|
"value": "3D"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Left": {
|
||||||
|
"localizations": {
|
||||||
|
"zh-Hans": {
|
||||||
|
"stringUnit": {
|
||||||
|
"state": "translated",
|
||||||
|
"value": "左对齐"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Right": {
|
||||||
|
"localizations": {
|
||||||
|
"zh-Hans": {
|
||||||
|
"stringUnit": {
|
||||||
|
"state": "translated",
|
||||||
|
"value": "右对齐"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Enable Minimalistic UI": {
|
||||||
|
"localizations": {
|
||||||
|
"zh-Hans": {
|
||||||
|
"stringUnit": {
|
||||||
|
"state": "translated",
|
||||||
|
"value": "启用极简 UI"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Hide Dynamic Island during screenshots & recordings": {
|
||||||
|
"localizations": {
|
||||||
|
"zh-Hans": {
|
||||||
|
"stringUnit": {
|
||||||
|
"state": "translated",
|
||||||
|
"value": "截屏和录屏时隐藏灵动岛"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Close gesture": {
|
||||||
|
"localizations": {
|
||||||
|
"zh-Hans": {
|
||||||
|
"stringUnit": {
|
||||||
|
"state": "translated",
|
||||||
|
"value": "关闭手势"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Reverse swipe gestures": {
|
||||||
|
"localizations": {
|
||||||
|
"zh-Hans": {
|
||||||
|
"stringUnit": {
|
||||||
|
"state": "translated",
|
||||||
|
"value": "反向滑动"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Reverse scroll gestures": {
|
||||||
|
"localizations": {
|
||||||
|
"zh-Hans": {
|
||||||
|
"stringUnit": {
|
||||||
|
"state": "translated",
|
||||||
|
"value": "反向滚动"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Extend hover area": {
|
||||||
|
"localizations": {
|
||||||
|
"zh-Hans": {
|
||||||
|
"stringUnit": {
|
||||||
|
"state": "translated",
|
||||||
|
"value": "扩展悬停区域"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"External display style": {
|
||||||
|
"localizations": {
|
||||||
|
"zh-Hans": {
|
||||||
|
"stringUnit": {
|
||||||
|
"state": "translated",
|
||||||
|
"value": "外接显示器样式"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Hide until hovered": {
|
||||||
|
"localizations": {
|
||||||
|
"zh-Hans": {
|
||||||
|
"stringUnit": {
|
||||||
|
"state": "translated",
|
||||||
|
"value": "悬停时显示"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Enable Focus Detection": {
|
||||||
|
"localizations": {
|
||||||
|
"zh-Hans": {
|
||||||
|
"stringUnit": {
|
||||||
|
"state": "translated",
|
||||||
|
"value": "启用专注模式检测"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Accessibility permission lets Dynamic Island replace the native volume, brightness, and keyboard HUDs.": {
|
||||||
|
"localizations": {
|
||||||
|
"zh-Hans": {
|
||||||
|
"stringUnit": {
|
||||||
|
"state": "translated",
|
||||||
|
"value": "辅助功能权限允许灵动岛替换原生的音量、亮度和键盘提示框。"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Full Disk Access for global mode": {
|
||||||
|
"localizations": {
|
||||||
|
"zh-Hans": {
|
||||||
|
"stringUnit": {
|
||||||
|
"state": "translated",
|
||||||
|
"value": "全局模式需要完全磁盘访问权限"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Without Full Disk Access, Shelf can only read files from Documents and Downloads. Grant Full Disk Access to make Shelf work globally.": {
|
||||||
|
"localizations": {
|
||||||
|
"zh-Hans": {
|
||||||
|
"stringUnit": {
|
||||||
|
"state": "translated",
|
||||||
|
"value": "如果没有完全磁盘访问权限,暂存器只能读取文档和下载目录的文件。授予完全磁盘访问权限以使暂存器能在全局范围工作。"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Request Full Disk Access": {
|
||||||
|
"localizations": {
|
||||||
|
"zh-Hans": {
|
||||||
|
"stringUnit": {
|
||||||
|
"state": "translated",
|
||||||
|
"value": "请求完全磁盘访问权限"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Open Privacy & Security": {
|
||||||
|
"localizations": {
|
||||||
|
"zh-Hans": {
|
||||||
|
"stringUnit": {
|
||||||
|
"state": "translated",
|
||||||
|
"value": "打开隐私与安全性"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"version": "1.1"
|
"version": "1.1"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue