···11+import {readPackageUp} from 'read-pkg-up';
22+33+export const {packageJson} = await readPackageUp({
44+ cwd: new URL('.', import.meta.url),
55+});
-38
dark-mode-notify.swift
···11-#!/usr/bin/env swift
22-33-// Run as ./notify.swift <program to run when dark mode changes>
44-// The program will have the DARKMODE env flag set to 1 or 0
55-// You can also compile with:
66-// swiftc notify.swift -o notify
77-// And run the binary directly
88-// Most credit goes to https://github.com/mnewt/dotemacs/blob/master/bin/dark-mode-notifier.swift
99-1010-import Cocoa
1111-1212-@discardableResult
1313-func shell(_ args: [String]) -> Int32 {
1414- let task = Process()
1515- let isDark = UserDefaults.standard.string(forKey: "AppleInterfaceStyle") == "Dark"
1616- var env = ProcessInfo.processInfo.environment
1717- env["DARKMODE"] = isDark ? "1" : "0"
1818- task.environment = env
1919- task.launchPath = "/usr/bin/env"
2020- task.arguments = args
2121- task.standardError = FileHandle.standardError
2222- task.standardOutput = FileHandle.standardOutput
2323- task.launch()
2424- task.waitUntilExit()
2525- return task.terminationStatus
2626-}
2727-2828-let args = Array(CommandLine.arguments.suffix(from: 1))
2929-shell(args)
3030-3131-DistributedNotificationCenter.default.addObserver(
3232- forName: Notification.Name("AppleInterfaceThemeChangedNotification"),
3333- object: nil,
3434- queue: nil) { (notification) in
3535- shell(args)
3636-}
3737-3838-NSApplication.shared.run()
+9
functions/disable-automatic-switching.js
···11+import {unlink} from 'node:fs/promises';
22+import {execa} from 'execa';
33+import {launchAgentPlistFilePath} from '../constants/index.js';
44+55+export async function disableAutomaticSwitching() {
66+ await execa('launchctl', ['unload', '-w', launchAgentPlistFilePath]);
77+88+ await unlink(launchAgentPlistFilePath);
99+}
+13
functions/enable-automatic-switching.js
···11+import {writeFile} from 'node:fs/promises';
22+import {execa} from 'execa';
33+import {launchAgentPlistFilePath} from '../constants/index.js';
44+import {getLaunchAgentPlistFileContents} from './get-launch-agent-plist-file-contents.js';
55+66+export async function enableAutomaticSwitching() {
77+ await writeFile(
88+ launchAgentPlistFilePath,
99+ await getLaunchAgentPlistFileContents(),
1010+ );
1111+1212+ await execa('launchctl', ['load', '-w', launchAgentPlistFilePath]);
1313+}
+24
functions/get-launch-agent-plist-file-contents.js
···11+import {readFile} from 'node:fs/promises';
22+import {fileURLToPath} from 'node:url';
33+import process from 'node:process';
44+import pupa from 'pupa';
55+import envPaths from 'env-paths';
66+import {packageJson} from '../constants/index.js';
77+88+export async function getLaunchAgentPlistFileContents() {
99+ return pupa(
1010+ await readFile(new URL('../templates/launch-agent.xml', import.meta.url), {
1111+ encoding: 'utf8',
1212+ }),
1313+ {
1414+ autoTerminalProfilePath: fileURLToPath(
1515+ new URL('../cli.js', import.meta.url),
1616+ ),
1717+ darkModeNotifyPath: fileURLToPath(
1818+ new URL('../dark-mode-notify/dark-mode-notify.swift', import.meta.url),
1919+ ),
2020+ logPath: envPaths(packageJson.name).log,
2121+ path: process.env.PATH,
2222+ },
2323+ );
2424+}
+4
functions/index.js
···11+export {enableAutomaticSwitching} from './enable-automatic-switching.js';
22+export {disableAutomaticSwitching} from './disable-automatic-switching.js';
33+export {getLaunchAgentPlistFileContents} from './get-launch-agent-plist-file-contents.js';
44+export {isAutomaticSwitchingEnabled} from './is-automatic-switching-enabled.js';
+32
functions/is-automatic-switching-enabled.js
···11+import {existsSync as exists} from 'node:fs';
22+import {execa} from 'execa';
33+import {launchAgentPlistFilePath} from '../constants/index.js';
44+55+/**
66+ * @return {Promise<boolean>}
77+ */
88+async function isDarkModeNotifyRunning() {
99+ const {stdout} = await execa('launchctl', ['list']);
1010+1111+ return stdout.includes('ke.bou.dark-mode-notify');
1212+}
1313+1414+/**
1515+ * @return {Promise<boolean>}
1616+ */
1717+export async function isAutomaticSwitchingEnabled() {
1818+ const _isDarkModeNotifyRunning = await isDarkModeNotifyRunning();
1919+ const plistFileExists = exists(launchAgentPlistFilePath);
2020+2121+ if (_isDarkModeNotifyRunning && !plistFileExists) {
2222+ throw new Error(
2323+ `Automatic switching is running, but the launch agent plist file (${launchAgentPlistFilePath}) doesn't exist`,
2424+ );
2525+ } else if (!_isDarkModeNotifyRunning && plistFileExists) {
2626+ throw new Error(
2727+ `Automatic switching is not running, but the launch agent plist file (${launchAgentPlistFilePath}) exists`,
2828+ );
2929+ }
3030+3131+ return _isDarkModeNotifyRunning && plistFileExists;
3232+}