Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- Configurable global keyboard shortcut to toggle Caffeine from anywhere. Set it in Preferences. No default shortcut is provided to avoid conflicts with other apps.

### Changed

- Improved Ukrainian translation.
Expand Down
17 changes: 17 additions & 0 deletions src/Caffeine.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
objects = {

/* Begin PBXBuildFile section */
B3CF99362FC71C2A00929FE3 /* KeyboardShortcuts in Frameworks */ = {isa = PBXBuildFile; productRef = B3CF99352FC71C2A00929FE3 /* KeyboardShortcuts */; };
F23FEFAF2F9623DB00C445B2 /* DZFoundation in Embed Frameworks */ = {isa = PBXBuildFile; productRef = F2634EAA2F375A5200C56D92 /* DZFoundation */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
F26346032F27633300C56D92 /* Sparkle in Frameworks */ = {isa = PBXBuildFile; productRef = F26346022F27633300C56D92 /* Sparkle */; };
F2634EAB2F375A5200C56D92 /* DZFoundation in Frameworks */ = {isa = PBXBuildFile; productRef = F2634EAA2F375A5200C56D92 /* DZFoundation */; };
Expand Down Expand Up @@ -58,6 +59,7 @@
files = (
F26346032F27633300C56D92 /* Sparkle in Frameworks */,
F2634EAB2F375A5200C56D92 /* DZFoundation in Frameworks */,
B3CF99362FC71C2A00929FE3 /* KeyboardShortcuts in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -103,6 +105,7 @@
packageProductDependencies = (
F26346022F27633300C56D92 /* Sparkle */,
F2634EAA2F375A5200C56D92 /* DZFoundation */,
B3CF99352FC71C2A00929FE3 /* KeyboardShortcuts */,
);
productName = Caffeine;
productReference = F2F53BA42EC354AE008A08B6 /* Caffeine.app */;
Expand Down Expand Up @@ -147,6 +150,7 @@
packageReferences = (
F26346012F27633300C56D92 /* XCRemoteSwiftPackageReference "Sparkle" */,
F2634EA92F375A5200C56D92 /* XCRemoteSwiftPackageReference "DZFoundation" */,
B3CF99342FC71C2A00929FE3 /* XCRemoteSwiftPackageReference "KeyboardShortcuts" */,
);
preferredProjectObjectVersion = 77;
productRefGroup = F2F53BA52EC354AE008A08B6 /* Products */;
Expand Down Expand Up @@ -393,6 +397,14 @@
/* End XCConfigurationList section */

/* Begin XCRemoteSwiftPackageReference section */
B3CF99342FC71C2A00929FE3 /* XCRemoteSwiftPackageReference "KeyboardShortcuts" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/sindresorhus/KeyboardShortcuts";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 2.0.0;
};
};
F26346012F27633300C56D92 /* XCRemoteSwiftPackageReference "Sparkle" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/sparkle-project/Sparkle";
Expand All @@ -412,6 +424,11 @@
/* End XCRemoteSwiftPackageReference section */

/* Begin XCSwiftPackageProductDependency section */
B3CF99352FC71C2A00929FE3 /* KeyboardShortcuts */ = {
isa = XCSwiftPackageProductDependency;
package = B3CF99342FC71C2A00929FE3 /* XCRemoteSwiftPackageReference "KeyboardShortcuts" */;
productName = KeyboardShortcuts;
};
F26346022F27633300C56D92 /* Sparkle */ = {
isa = XCSwiftPackageProductDependency;
package = F26346012F27633300C56D92 /* XCRemoteSwiftPackageReference "Sparkle" */;
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions src/Caffeine/Classes/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
//

import Cocoa
import DZFoundation
import KeyboardShortcuts
import Sparkle
import SwiftUI

Expand All @@ -25,6 +27,17 @@ class AppDelegate: NSObject, NSApplicationDelegate, SPUStandardUserDriverDelegat

// Hide the dock icon - this is a menu bar only app
NSApp.setActivationPolicy(.accessory)

self.registerGlobalShortcut()
}

private func registerGlobalShortcut() {
KeyboardShortcuts.onKeyUp(for: .toggleActive) { [weak self] in
Task { @MainActor in
self?.menuBarController?.toggleActive()
}
}
DZLog("Registered global toggle shortcut")
}

func applicationWillTerminate(_: Notification) {
Expand Down
10 changes: 10 additions & 0 deletions src/Caffeine/Classes/Models/KeyboardShortcutsName+Caffeine.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
//
// KeyboardShortcutsName+Caffeine.swift
// Caffeine
//

import KeyboardShortcuts

extension KeyboardShortcuts.Name {
static let toggleActive = Self("toggleActive")
}
26 changes: 26 additions & 0 deletions src/Caffeine/Classes/Views/MenuBarController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import Cocoa
import Combine
import KeyboardShortcuts
import Sparkle
import SwiftUI

Expand Down Expand Up @@ -37,6 +38,10 @@ class MenuBarController: NSObject {
}
}

func toggleActive() {
self.viewModel.toggleActive()
}

private func setupMenuBar() {
self.statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)

Expand Down Expand Up @@ -90,6 +95,22 @@ class MenuBarController: NSObject {
private func showContextMenu() {
let menu = NSMenu()

// Toggle activate/deactivate
let toggleTitle = self.viewModel.isActive
? String(localized: "Deactivate")
: String(localized: "Activate")
let toggleItem = NSMenuItem(
title: toggleTitle,
action: #selector(self.toggleActiveFromMenu(_:)),
keyEquivalent: ""
)
toggleItem.target = self
// Informational only: NSMenu key equivalents fire only while the menu is open.
// Real global triggering is via the KeyboardShortcuts listener in AppDelegate.
toggleItem.setShortcut(for: .toggleActive)
menu.addItem(toggleItem)
menu.addItem(NSMenuItem.separator())

// Status info (only show if active)
if self.viewModel.isActive, let timeString = viewModel.formattedTimeRemaining() {
let infoItem = NSMenuItem(title: timeString, action: nil, keyEquivalent: "")
Expand Down Expand Up @@ -187,6 +208,11 @@ class MenuBarController: NSObject {
self.viewModel.activate(withTimeout: seconds)
}

@objc
private func toggleActiveFromMenu(_: NSMenuItem) {
self.viewModel.toggleActive()
}

@objc
private func showPreferences(_: Any?) {
self.showPreferencesWindow()
Expand Down
23 changes: 23 additions & 0 deletions src/Caffeine/Classes/Views/PreferencesView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
// Created by Dominic Rodemer on 11.11.25.
//

import KeyboardShortcuts
import SwiftUI

struct PreferencesView: View {
Expand Down Expand Up @@ -97,6 +98,28 @@ struct PreferencesView: View {
.padding(.leading, 20)
}

Divider()
.padding(.vertical, 8)

VStack(alignment: .leading, spacing: 8) {
Text("Keyboard Shortcut")
.font(.system(size: 13, weight: .semibold))

HStack(spacing: 8) {
Text("Toggle Caffeine:")
.font(.system(size: 13))
KeyboardShortcuts.Recorder(for: .toggleActive)
Spacer()
}

Text(
"Press a key combination to toggle Caffeine from anywhere. The shortcut is registered system-wide. Click the field and press Delete to clear."
)
.font(.system(size: 11))
.foregroundColor(.secondary)
.fixedSize(horizontal: false, vertical: true)
}

Spacer()
.frame(height: 30)

Expand Down
8 changes: 8 additions & 0 deletions src/Caffeine/Resources/de.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,11 @@
/* System messages */
"Caffeine prevents sleep" = "Caffeine verhindert den Ruhezustand";

/* Keyboard shortcut */
"Keyboard Shortcut" = "Tastaturkurzbefehl";
"Toggle Caffeine:" = "Caffeine umschalten:";
"Press a key combination to toggle Caffeine from anywhere. The shortcut is registered system-wide. Click the field and press Delete to clear." = "Drücke eine Tastenkombination, um Caffeine von überall aus umzuschalten. Der Kurzbefehl wird systemweit registriert. Klicke in das Feld und drücke die Entf-Taste, um ihn zu löschen.";

/* Context menu activation */
"Activate" = "Aktivieren";
"Deactivate" = "Deaktivieren";
8 changes: 8 additions & 0 deletions src/Caffeine/Resources/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,11 @@
/* System messages */
"Caffeine prevents sleep" = "Caffeine prevents sleep";

/* Keyboard shortcut */
"Keyboard Shortcut" = "Keyboard Shortcut";
"Toggle Caffeine:" = "Toggle Caffeine:";
"Press a key combination to toggle Caffeine from anywhere. The shortcut is registered system-wide. Click the field and press Delete to clear." = "Press a key combination to toggle Caffeine from anywhere. The shortcut is registered system-wide. Click the field and press Delete to clear.";

/* Context menu activation */
"Activate" = "Activate";
"Deactivate" = "Deactivate";
8 changes: 8 additions & 0 deletions src/Caffeine/Resources/es.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,11 @@
/* System messages */
"Caffeine prevents sleep" = "Caffeine impide el reposo";

/* Keyboard shortcut */
"Keyboard Shortcut" = "Atajo de teclado";
"Toggle Caffeine:" = "Alternar Caffeine:";
"Press a key combination to toggle Caffeine from anywhere. The shortcut is registered system-wide. Click the field and press Delete to clear." = "Pulsa una combinación de teclas para alternar Caffeine desde cualquier lugar. El atajo se registra en todo el sistema. Haz clic en el campo y pulsa Suprimir para borrarlo.";

/* Context menu activation */
"Activate" = "Activar";
"Deactivate" = "Desactivar";
8 changes: 8 additions & 0 deletions src/Caffeine/Resources/fr.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,11 @@
/* System messages */
"Caffeine prevents sleep" = "Caffeine empêche la mise en veille";

/* Keyboard shortcut */
"Keyboard Shortcut" = "Raccourci clavier";
"Toggle Caffeine:" = "Activer/désactiver Caffeine :";
"Press a key combination to toggle Caffeine from anywhere. The shortcut is registered system-wide. Click the field and press Delete to clear." = "Appuyez sur une combinaison de touches pour activer ou désactiver Caffeine depuis n'importe où. Le raccourci est enregistré à l'échelle du système. Cliquez sur le champ et appuyez sur Suppr pour l'effacer.";

/* Context menu activation */
"Activate" = "Activer";
"Deactivate" = "Désactiver";
8 changes: 8 additions & 0 deletions src/Caffeine/Resources/it.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,11 @@

/* System messages */
"Caffeine prevents sleep" = "Caffeine impedisce lo stop";
/* Keyboard shortcut */
"Keyboard Shortcut" = "Abbreviazione da tastiera";
"Toggle Caffeine:" = "Attiva/disattiva Caffeine:";
"Press a key combination to toggle Caffeine from anywhere. The shortcut is registered system-wide. Click the field and press Delete to clear." = "Premi una combinazione di tasti per attivare o disattivare Caffeine da qualsiasi punto. L'abbreviazione viene registrata a livello di sistema. Fai clic sul campo e premi Cancella per cancellarla.";

/* Context menu activation */
"Activate" = "Attiva";
"Deactivate" = "Disattiva";
8 changes: 8 additions & 0 deletions src/Caffeine/Resources/ja.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,11 @@
/* System messages */
"Caffeine prevents sleep" = "Caffeine がスリープを防ぎます";

/* Keyboard shortcut */
"Keyboard Shortcut" = "キーボードショートカット";
"Toggle Caffeine:" = "Caffeine の切り替え:";
"Press a key combination to toggle Caffeine from anywhere. The shortcut is registered system-wide. Click the field and press Delete to clear." = "キーの組み合わせを押すと、どこからでも Caffeine を切り替えられます。ショートカットはシステム全体に登録されます。フィールドをクリックして Delete キーを押すと消去できます。";

/* Context menu activation */
"Activate" = "有効化";
"Deactivate" = "無効化";
8 changes: 8 additions & 0 deletions src/Caffeine/Resources/ko.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,11 @@

/* System messages */
"Caffeine prevents sleep" = "Caffeine이 잠자기를 방지합니다";
/* Keyboard shortcut */
"Keyboard Shortcut" = "키보드 단축키";
"Toggle Caffeine:" = "Caffeine 전환:";
"Press a key combination to toggle Caffeine from anywhere. The shortcut is registered system-wide. Click the field and press Delete to clear." = "키 조합을 누르면 어디서든 Caffeine을 전환할 수 있습니다. 단축키는 시스템 전체에 등록됩니다. 필드를 클릭한 후 Delete 키를 누르면 지워집니다.";

/* Context menu activation */
"Activate" = "활성화";
"Deactivate" = "비활성화";
8 changes: 8 additions & 0 deletions src/Caffeine/Resources/nl.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,11 @@

/* System messages */
"Caffeine prevents sleep" = "Caffeine voorkomt sluimerstand";
/* Keyboard shortcut */
"Keyboard Shortcut" = "Toetscombinatie";
"Toggle Caffeine:" = "Caffeine in- of uitschakelen:";
"Press a key combination to toggle Caffeine from anywhere. The shortcut is registered system-wide. Click the field and press Delete to clear." = "Druk op een toetscombinatie om Caffeine vanaf elke plek in of uit te schakelen. De toetscombinatie wordt systeembreed geregistreerd. Klik op het veld en druk op Delete om hem te wissen.";

/* Context menu activation */
"Activate" = "Inschakelen";
"Deactivate" = "Uitschakelen";
8 changes: 8 additions & 0 deletions src/Caffeine/Resources/pt-BR.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,11 @@

/* System messages */
"Caffeine prevents sleep" = "O Caffeine impede o repouso";
/* Keyboard shortcut */
"Keyboard Shortcut" = "Atalho de teclado";
"Toggle Caffeine:" = "Alternar o Caffeine:";
"Press a key combination to toggle Caffeine from anywhere. The shortcut is registered system-wide. Click the field and press Delete to clear." = "Pressione uma combinação de teclas para alternar o Caffeine de qualquer lugar. O atalho é registrado em todo o sistema. Clique no campo e pressione a tecla Delete para apagá-lo.";

/* Context menu activation */
"Activate" = "Ativar";
"Deactivate" = "Desativar";
8 changes: 8 additions & 0 deletions src/Caffeine/Resources/pt.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,11 @@

/* System messages */
"Caffeine prevents sleep" = "O Caffeine impede o repouso";
/* Keyboard shortcut */
"Keyboard Shortcut" = "Atalho de teclado";
"Toggle Caffeine:" = "Alternar o Caffeine:";
"Press a key combination to toggle Caffeine from anywhere. The shortcut is registered system-wide. Click the field and press Delete to clear." = "Prima uma combinação de teclas para alternar o Caffeine a partir de qualquer lado. O atalho é registado em todo o sistema. Clique no campo e prima a tecla Apagar para o limpar.";

/* Context menu activation */
"Activate" = "Ativar";
"Deactivate" = "Desativar";
8 changes: 8 additions & 0 deletions src/Caffeine/Resources/ru.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,11 @@

/* System messages */
"Caffeine prevents sleep" = "Caffeine предотвращает переход в сон";
/* Keyboard shortcut */
"Keyboard Shortcut" = "Сочетание клавиш";
"Toggle Caffeine:" = "Переключение Caffeine:";
"Press a key combination to toggle Caffeine from anywhere. The shortcut is registered system-wide. Click the field and press Delete to clear." = "Нажмите сочетание клавиш, чтобы переключать Caffeine из любого приложения. Сочетание регистрируется во всей системе. Щёлкните поле и нажмите Delete, чтобы очистить.";

/* Context menu activation */
"Activate" = "Активировать";
"Deactivate" = "Деактивировать";
8 changes: 8 additions & 0 deletions src/Caffeine/Resources/uk.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,11 @@
/* System messages */
"Caffeine prevents sleep" = "Caffeine запобігає переходу в режим сну";

/* Keyboard shortcut */
"Keyboard Shortcut" = "Сполучення клавіш";
"Toggle Caffeine:" = "Перемикання Caffeine:";
"Press a key combination to toggle Caffeine from anywhere. The shortcut is registered system-wide. Click the field and press Delete to clear." = "Натисніть сполучення клавіш, щоб перемикати Caffeine з будь-якого застосунку. Сполучення реєструється в усій системі. Клацніть поле та натисніть Delete, щоб очистити.";

/* Context menu activation */
"Activate" = "Увімкнути";
"Deactivate" = "Вимкнути";
8 changes: 8 additions & 0 deletions src/Caffeine/Resources/zh-Hans.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,11 @@
/* System messages */
"Caffeine prevents sleep" = "Caffeine 会阻止睡眠";

/* Keyboard shortcut */
"Keyboard Shortcut" = "键盘快捷键";
"Toggle Caffeine:" = "切换 Caffeine:";
"Press a key combination to toggle Caffeine from anywhere. The shortcut is registered system-wide. Click the field and press Delete to clear." = "按下一组按键即可从任何位置切换 Caffeine。该快捷键将在系统范围内注册。点按字段并按 Delete 键即可清除。";

/* Context menu activation */
"Activate" = "激活";
"Deactivate" = "停用";