clauditate
Version:
A menubar meditation app that helps you stay mindful while Claude Code works
290 lines • 9.79 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
const menubar_1 = require("menubar");
const electron_1 = require("electron");
const path = __importStar(require("path"));
const fs = __importStar(require("fs"));
const net = __importStar(require("net"));
const os = __importStar(require("os"));
const preferences_1 = require("./preferences");
const mb = (0, menubar_1.menubar)({
index: `file://${path.join(__dirname, "../index.html")}`,
icon: "☯",
tooltip: "Breathe - Meditation",
showDockIcon: false,
browserWindow: {
width: 300,
height: 480,
resizable: false,
show: false,
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
preload: path.join(__dirname, "preload.js"),
},
},
});
// Data storage path
const userDataPath = electron_1.app.getPath("userData");
const settingsPath = path.join(userDataPath, "settings.json");
const statsPath = path.join(userDataPath, "stats.json");
const dailySessionsPath = path.join(userDataPath, "dailySessions.json");
// Helper functions for file operations
const readJsonFile = (filePath) => {
try {
if (fs.existsSync(filePath)) {
const data = fs.readFileSync(filePath, "utf8");
return JSON.parse(data);
}
}
catch (error) {
console.error("Error reading file:", error);
}
return null;
};
const writeJsonFile = (filePath, data) => {
try {
fs.writeFileSync(filePath, JSON.stringify(data, null, 2));
return true;
}
catch (error) {
console.error("Error writing file:", error);
return false;
}
};
// IPC handlers
electron_1.ipcMain.handle("show-notification", async (event, { title, body }) => {
if (electron_1.Notification.isSupported()) {
new electron_1.Notification({
title,
body,
icon: path.join(__dirname, "../assets/icon.png"),
silent: false,
}).show();
}
});
electron_1.ipcMain.handle("save-settings", async (event, settings) => {
return writeJsonFile(settingsPath, settings);
});
electron_1.ipcMain.handle("load-settings", async (event) => {
return readJsonFile(settingsPath);
});
electron_1.ipcMain.handle("save-stats", async (event, stats) => {
return writeJsonFile(statsPath, stats);
});
electron_1.ipcMain.handle("load-stats", async (event) => {
return readJsonFile(statsPath);
});
electron_1.ipcMain.handle("save-daily-sessions", async (event, sessions) => {
return writeJsonFile(dailySessionsPath, sessions);
});
electron_1.ipcMain.handle("load-daily-sessions", async (event) => {
return readJsonFile(dailySessionsPath);
});
electron_1.ipcMain.handle("open-external", async (event, url) => {
await electron_1.shell.openExternal(url);
});
electron_1.ipcMain.handle("quit-app", async (event) => {
electron_1.app.quit();
});
electron_1.ipcMain.handle("toggle-snooze", async (event) => {
try {
const isSnooze = await preferencesManager.toggleSnooze();
return { success: true, isSnooze };
}
catch (error) {
console.error("Failed to toggle snooze:", error);
return {
success: false,
error: error instanceof Error ? error.message : String(error),
};
}
});
electron_1.ipcMain.handle("get-snooze-status", async (event) => {
try {
const isSnooze = await preferencesManager.isSnooze();
return { success: true, isSnooze };
}
catch (error) {
console.error("Failed to get snooze status:", error);
return {
success: false,
error: error instanceof Error ? error.message : String(error),
};
}
});
electron_1.ipcMain.handle("save-preferences", async (event, preferences) => {
try {
await preferencesManager.updatePreferences(preferences);
return { success: true };
}
catch (error) {
console.error("Failed to save preferences:", error);
return {
success: false,
error: error instanceof Error ? error.message : String(error),
};
}
});
electron_1.ipcMain.handle("load-preferences", async (event) => {
try {
const data = await preferencesManager.loadPreferences();
return { success: true, preferences: data.preferences };
}
catch (error) {
console.error("Failed to load preferences:", error);
return {
success: false,
error: error instanceof Error ? error.message : String(error),
};
}
});
// Initialize preferences manager
const preferencesManager = new preferences_1.PreferencesManager();
mb.on("ready", () => {
console.log("Menubar app is ready");
setupIPCServer();
});
// Track window shown for dismissal detection
mb.on("show", async () => {
console.log("Window shown");
await preferencesManager.recordWindowShown();
});
// Track window hidden and check for dismissal
mb.on("hide", async () => {
console.log("Window hidden");
await preferencesManager.checkForDismissal();
});
// Only open dev tools in development
if (process.env.NODE_ENV === "development") {
mb.on("after-create-window", () => {
mb.window?.webContents.openDevTools({ mode: "detach" });
});
}
// IPC Server for CLI communication
let ipcServer = null;
const getSocketPath = () => {
const tmpDir = os.tmpdir();
return path.join(tmpDir, "clauditate.sock");
};
const setupIPCServer = () => {
const socketPath = getSocketPath();
// Remove existing socket file if it exists
try {
if (fs.existsSync(socketPath)) {
fs.unlinkSync(socketPath);
}
}
catch (error) {
console.log("Could not remove existing socket:", error);
}
ipcServer = net.createServer((socket) => {
console.log("CLI client connected");
const safeWrite = (data) => {
if (!socket.destroyed && socket.writable) {
try {
socket.write(data);
}
catch (error) {
console.log("Socket write failed (client disconnected):", error instanceof Error ? error.message : String(error));
}
}
};
socket.on("data", (data) => {
try {
const command = data.toString().trim();
console.log("Received command:", command);
switch (command) {
case "show":
try {
mb.showWindow();
safeWrite("shown\n");
}
catch (error) {
safeWrite("error: failed to show\n");
}
break;
case "hide":
try {
mb.hideWindow();
safeWrite("hidden\n");
}
catch (error) {
safeWrite("error: failed to hide\n");
}
break;
case "ping":
safeWrite("pong\n");
break;
default:
safeWrite("error: unknown command\n");
}
}
catch (error) {
console.error("Error processing command:", error);
safeWrite("error: processing failed\n");
}
});
socket.on("error", (error) => {
console.log("Socket error:", error);
});
socket.on("close", () => {
console.log("CLI client disconnected");
});
});
ipcServer.listen(socketPath, () => {
console.log("IPC server listening on:", socketPath);
});
ipcServer.on("error", (error) => {
console.error("IPC server error:", error);
});
};
// Cleanup on app quit
electron_1.app.on("before-quit", () => {
if (ipcServer) {
ipcServer.close();
try {
const socketPath = getSocketPath();
if (fs.existsSync(socketPath)) {
fs.unlinkSync(socketPath);
}
}
catch (error) {
console.log("Could not cleanup socket:", error);
}
}
});
//# sourceMappingURL=main.js.map