/* THIS IS A GENERATED/BUNDLED FILE BY ESBUILD if you want to view the source, please visit the github repository of this plugin */ var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/main.ts var main_exports = {}; __export(main_exports, { default: () => ImmersePlugin }); module.exports = __toCommonJS(main_exports); var import_obsidian4 = require("obsidian"); // src/types.ts var DEFAULT_SETTINGS = { pomodoroWorkMinutes: 25, pomodoroBreakMinutes: 5, longBreakMinutes: 15, longBreakInterval: 4, enableSounds: true, enableCelebrations: true, defaultEstimateMinutes: 30, lists: [ { id: "work", name: "Work", color: "#6366f1", icon: "\u{1F4BC}" }, { id: "personal", name: "Personal", color: "#22c55e", icon: "\u{1F3E0}" }, { id: "learning", name: "Learning", color: "#f59e0b", icon: "\u{1F4DA}" } ], autoStartBreak: true, tickSoundEnabled: false, // Daily note logging logToDaily: false, // Task reminders enableReminders: true, defaultReminderMinutes: 15 }; var DEFAULT_DATA = { tasks: [], completedToday: 0, totalFocusMinutesToday: 0, streak: 0, lastActiveDate: "", pomodorosCompleted: 0, dailyStats: [], completedTasksArchive: [] }; var VIEW_TYPE_IMMERSE = "immerse-view"; var CELEBRATION_MESSAGES = [ { emoji: "\u{1F4A5}", message: "Crushed it!" }, { emoji: "\u{1F525}", message: "On fire!" }, { emoji: "\u26A1", message: "Lightning fast!" }, { emoji: "\u{1F3AF}", message: "Bullseye!" }, { emoji: "\u{1F680}", message: "Blasting through!" }, { emoji: "\u{1F4AA}", message: "Strong work!" }, { emoji: "\u{1F31F}", message: "Stellar!" }, { emoji: "\u2728", message: "Brilliant!" }, { emoji: "\u{1F3C6}", message: "Champion!" }, { emoji: "\u{1F389}", message: "Well done!" } ]; var EARLY_FINISH_MESSAGES = [ { emoji: "\u26A1", message: "Speed demon! Finished early!" }, { emoji: "\u{1F3CE}\uFE0F", message: "Faster than expected!" }, { emoji: "\u{1F3AF}", message: "Under budget! Nice work!" } ]; var OVERTIME_MESSAGES = [ { emoji: "\u{1F4AA}", message: "Persistence pays off!" }, { emoji: "\u{1F3C3}", message: "Marathon runner!" }, { emoji: "\u{1F525}", message: "The grind is real!" } ]; // src/view.ts var import_obsidian2 = require("obsidian"); // src/modals.ts var import_obsidian = require("obsidian"); var QuickAddTaskModal = class extends import_obsidian.Modal { constructor(app, plugin) { super(app); this.taskText = ""; this.selectedList = "work"; this.scheduledDate = ""; this.scheduledTime = ""; this.reminderMinutes = 0; this.plugin = plugin; this.estimatedMinutes = plugin.settings.defaultEstimateMinutes; this.reminderMinutes = plugin.settings.defaultReminderMinutes; if (plugin.settings.lists.length > 0) { this.selectedList = plugin.settings.lists[0].id; } } onOpen() { const { contentEl } = this; contentEl.addClass("immerse-modal"); contentEl.createEl("h2", { text: "\u26A1 Add New Task" }); new import_obsidian.Setting(contentEl).setName("Task").addText((text) => { text.setPlaceholder("What do you need to do?").onChange((value) => this.taskText = value); text.inputEl.focus(); text.inputEl.addEventListener("keydown", (e) => { if (e.key === "Enter" && this.taskText.trim()) { this.submitTask(); } }); }); new import_obsidian.Setting(contentEl).setName("Estimated Time").setDesc("How long do you think this will take?").addDropdown((dropdown) => { const options = { "5": "5 min", "10": "10 min", "15": "15 min", "20": "20 min", "25": "25 min (1 pomodoro)", "30": "30 min", "45": "45 min", "50": "50 min (2 pomodoros)", "60": "1 hour", "90": "1.5 hours", "120": "2 hours", "180": "3 hours" }; Object.entries(options).forEach(([value, label]) => { dropdown.addOption(value, label); }); dropdown.setValue(this.estimatedMinutes.toString()); dropdown.onChange((value) => this.estimatedMinutes = parseInt(value)); }); new import_obsidian.Setting(contentEl).setName("List").addDropdown((dropdown) => { this.plugin.settings.lists.forEach((list) => { dropdown.addOption(list.id, `${list.icon} ${list.name}`); }); dropdown.setValue(this.selectedList); dropdown.onChange((value) => this.selectedList = value); }); new import_obsidian.Setting(contentEl).setName("\u{1F4C5} Scheduled Date").setDesc("Optional: When do you plan to work on this?").addText((text) => { text.setPlaceholder("YYYY-MM-DD").setValue(this.scheduledDate).onChange((value) => this.scheduledDate = value); text.inputEl.type = "date"; }); new import_obsidian.Setting(contentEl).setName("\u23F0 Scheduled Time").setDesc("Optional: What time?").addText((text) => { text.setPlaceholder("HH:mm").setValue(this.scheduledTime).onChange((value) => this.scheduledTime = value); text.inputEl.type = "time"; }); if (this.plugin.settings.enableReminders) { new import_obsidian.Setting(contentEl).setName("\u{1F514} Reminder").setDesc("Remind me before the scheduled time").addDropdown((dropdown) => { dropdown.addOption("0", "No reminder"); dropdown.addOption("5", "5 minutes before"); dropdown.addOption("10", "10 minutes before"); dropdown.addOption("15", "15 minutes before"); dropdown.addOption("30", "30 minutes before"); dropdown.addOption("60", "1 hour before"); dropdown.setValue(this.reminderMinutes.toString()); dropdown.onChange((value) => this.reminderMinutes = parseInt(value)); }); } const buttonContainer = contentEl.createEl("div", { cls: "immerse-modal-buttons" }); const cancelBtn = buttonContainer.createEl("button", { text: "Cancel", cls: "immerse-btn" }); cancelBtn.addEventListener("click", () => this.close()); const addBtn = buttonContainer.createEl("button", { text: "Add Task", cls: "immerse-btn immerse-btn-primary" }); addBtn.addEventListener("click", () => this.submitTask()); } submitTask() { if (this.taskText.trim()) { const task = this.plugin.createTask(this.taskText, this.estimatedMinutes, this.selectedList); if (this.scheduledDate) { task.scheduledDate = this.scheduledDate; } if (this.scheduledTime) { task.scheduledTime = this.scheduledTime; } if (this.reminderMinutes > 0 && this.scheduledDate && this.scheduledTime) { task.reminderMinutes = this.reminderMinutes; } this.plugin.addTask(task); new import_obsidian.Notice("\u2705 Task added!"); this.close(); } else { new import_obsidian.Notice("Please enter a task description"); } } onClose() { const { contentEl } = this; contentEl.empty(); } }; var EditTaskModal = class extends import_obsidian.Modal { constructor(app, plugin, task) { super(app); this.plugin = plugin; this.task = { ...task }; } onOpen() { const { contentEl } = this; contentEl.addClass("immerse-modal"); contentEl.createEl("h2", { text: "\u270F\uFE0F Edit Task" }); new import_obsidian.Setting(contentEl).setName("Task").addText((text) => text.setValue(this.task.text).onChange((value) => this.task.text = value)); new import_obsidian.Setting(contentEl).setName("Estimated Time").addDropdown((dropdown) => { const options = { "5": "5 min", "10": "10 min", "15": "15 min", "20": "20 min", "25": "25 min", "30": "30 min", "45": "45 min", "50": "50 min", "60": "1 hour", "90": "1.5 hours", "120": "2 hours", "180": "3 hours" }; Object.entries(options).forEach(([value, label]) => { dropdown.addOption(value, label); }); dropdown.setValue(this.task.estimatedMinutes.toString()); dropdown.onChange((value) => this.task.estimatedMinutes = parseInt(value)); }); new import_obsidian.Setting(contentEl).setName("List").addDropdown((dropdown) => { this.plugin.settings.lists.forEach((list) => { dropdown.addOption(list.id, `${list.icon} ${list.name}`); }); dropdown.setValue(this.task.list); dropdown.onChange((value) => this.task.list = value); }); new import_obsidian.Setting(contentEl).setName("Notes").setDesc("Add any additional details or links").addTextArea((textarea) => { textarea.setValue(this.task.notes).onChange((value) => this.task.notes = value); textarea.inputEl.rows = 4; }); new import_obsidian.Setting(contentEl).setName("\u{1F4C5} Scheduled Date").setDesc("Optional: When do you plan to work on this?").addText((text) => { text.setPlaceholder("YYYY-MM-DD").setValue(this.task.scheduledDate || "").onChange((value) => this.task.scheduledDate = value || void 0); text.inputEl.type = "date"; }); new import_obsidian.Setting(contentEl).setName("\u23F0 Scheduled Time").setDesc("Optional: What time?").addText((text) => { text.setPlaceholder("HH:mm").setValue(this.task.scheduledTime || "").onChange((value) => this.task.scheduledTime = value || void 0); text.inputEl.type = "time"; }); if (this.plugin.settings.enableReminders) { new import_obsidian.Setting(contentEl).setName("\u{1F514} Reminder").setDesc("Remind me before the scheduled time").addDropdown((dropdown) => { dropdown.addOption("0", "No reminder"); dropdown.addOption("5", "5 minutes before"); dropdown.addOption("10", "10 minutes before"); dropdown.addOption("15", "15 minutes before"); dropdown.addOption("30", "30 minutes before"); dropdown.addOption("60", "1 hour before"); dropdown.setValue((this.task.reminderMinutes || 0).toString()); dropdown.onChange((value) => { const minutes = parseInt(value); this.task.reminderMinutes = minutes > 0 ? minutes : void 0; }); }); } if (this.task.actualMinutes > 0) { new import_obsidian.Setting(contentEl).setName("Time Tracked").setDesc(`You've worked on this task for ${this.plugin.formatTimeHuman(this.task.actualMinutes)}`); } const buttonContainer = contentEl.createEl("div", { cls: "immerse-modal-buttons" }); const cancelBtn = buttonContainer.createEl("button", { text: "Cancel", cls: "immerse-btn" }); cancelBtn.addEventListener("click", () => this.close()); const saveBtn = buttonContainer.createEl("button", { text: "Save", cls: "immerse-btn immerse-btn-primary" }); saveBtn.addEventListener("click", () => { this.plugin.updateTask(this.task.id, this.task); new import_obsidian.Notice("\u2705 Task updated!"); this.close(); }); } onClose() { const { contentEl } = this; contentEl.empty(); } }; // src/view.ts var ImmerseView = class extends import_obsidian2.ItemView { constructor(leaf, plugin) { super(leaf); this.currentFilter = "all"; // References to elements that need frequent updates this.timerTimeEl = null; this.progressBarEl = null; this.actualTimeEl = null; this.pauseBtnEl = null; this.plugin = plugin; } getViewType() { return VIEW_TYPE_IMMERSE; } getDisplayText() { return "Immerse"; } getIcon() { return "zap"; } async onOpen() { this.refresh(); } // Light update - only updates timer display without rebuilding DOM updateTimerDisplay() { if (!this.timerTimeEl) return; this.timerTimeEl.textContent = this.plugin.formatTime(this.plugin.currentTimerSeconds); if (this.progressBarEl) { let progressPercent = 0; if (this.plugin.isBreakMode) { const breakDuration = this.plugin.pomodoroCount % this.plugin.settings.longBreakInterval === 0 ? this.plugin.settings.longBreakMinutes * 60 : this.plugin.settings.pomodoroBreakMinutes * 60; progressPercent = (breakDuration - this.plugin.currentTimerSeconds) / breakDuration * 100; } else { const workDuration = this.plugin.settings.pomodoroWorkMinutes * 60; progressPercent = (workDuration - this.plugin.currentTimerSeconds) / workDuration * 100; } this.progressBarEl.style.width = `${Math.min(Math.max(progressPercent, 0), 100)}%`; } if (this.actualTimeEl && this.plugin.activeTaskId) { const task = this.plugin.data.tasks.find((t) => t.id === this.plugin.activeTaskId); if (task) { this.actualTimeEl.textContent = `Actual: ${this.plugin.formatTimeHuman(task.actualMinutes)}`; } } } refresh() { const container = this.containerEl.children[1]; container.empty(); container.addClass("immerse-container"); this.timerTimeEl = null; this.progressBarEl = null; this.actualTimeEl = null; this.pauseBtnEl = null; this.renderHeader(container); this.renderStatsBar(container); this.renderActiveTask(container); this.renderTaskList(container); } renderHeader(container) { const header = container.createEl("div", { cls: "immerse-header" }); const titleSection = header.createEl("div", { cls: "immerse-title-section" }); titleSection.createEl("h2", { text: "\u26A1 Immerse", cls: "immerse-title" }); const today = new Date(); const dateStr = today.toLocaleDateString("en-US", { weekday: "long", month: "short", day: "numeric" }); titleSection.createEl("div", { text: dateStr, cls: "immerse-date" }); const actions = header.createEl("div", { cls: "immerse-header-actions" }); const reportsBtn = actions.createEl("button", { cls: "immerse-btn" }); reportsBtn.innerHTML = "\u{1F4CA} Reports"; reportsBtn.addEventListener("click", () => { this.plugin.activateReportView(); }); const addBtn = actions.createEl("button", { cls: "immerse-btn immerse-btn-primary" }); addBtn.innerHTML = "+ Add Task"; addBtn.addEventListener("click", () => { new QuickAddTaskModal(this.app, this.plugin).open(); }); } renderStatsBar(container) { const stats = this.plugin.getStats(); const statsBar = container.createEl("div", { cls: "immerse-stats-bar" }); const statItems = [ { label: "Pending", value: stats.pendingCount.toString(), icon: "\u{1F4CB}" }, { label: "Done Today", value: stats.completedToday.toString(), icon: "\u2705" }, { label: "Today's Focus", value: this.plugin.formatTimeHuman(stats.totalFocusMinutesToday), icon: "\u23F1\uFE0F" }, { label: "Streak", value: `${stats.streak} days`, icon: "\u{1F525}" } ]; statItems.forEach((stat) => { const item = statsBar.createEl("div", { cls: "immerse-stat-item" }); item.createEl("div", { cls: "immerse-stat-icon", text: stat.icon }); item.createEl("div", { cls: "immerse-stat-value", text: stat.value }); item.createEl("div", { cls: "immerse-stat-label", text: stat.label }); }); } renderActiveTask(container) { const activeSection = container.createEl("div", { cls: "immerse-active-section" }); if (this.plugin.activeTaskId) { const task = this.plugin.data.tasks.find((t) => t.id === this.plugin.activeTaskId); if (task) { activeSection.addClass("immerse-has-active"); const activeCard = activeSection.createEl("div", { cls: "immerse-active-card" }); if (this.plugin.isBreakMode) { activeCard.addClass("immerse-break-card"); const breakLabel = this.plugin.currentTimerSeconds > 0 ? "\u2615 BREAK TIME" : "\u2728 BREAK COMPLETE"; activeCard.createEl("div", { cls: "immerse-active-label", text: breakLabel }); } else { const workLabel = this.plugin.currentTimerSeconds > 0 ? "\u{1F3AF} FOCUSING ON" : "\u{1F345} POMODORO COMPLETE"; activeCard.createEl("div", { cls: "immerse-active-label", text: workLabel }); } activeCard.createEl("div", { cls: "immerse-active-task-name", text: task.text }); const timerDisplay = activeCard.createEl("div", { cls: "immerse-timer-display" }); this.timerTimeEl = timerDisplay.createEl("span", { cls: "immerse-timer-time", text: this.plugin.formatTime(this.plugin.currentTimerSeconds) }); const progressWrap = activeCard.createEl("div", { cls: "immerse-progress-wrap" }); this.progressBarEl = progressWrap.createEl("div", { cls: "immerse-progress-bar" }); let progressPercent = 0; if (this.plugin.isBreakMode) { const breakDuration = this.plugin.pomodoroCount % this.plugin.settings.longBreakInterval === 0 ? this.plugin.settings.longBreakMinutes * 60 : this.plugin.settings.pomodoroBreakMinutes * 60; progressPercent = (breakDuration - this.plugin.currentTimerSeconds) / breakDuration * 100; } else { const workDuration = this.plugin.settings.pomodoroWorkMinutes * 60; progressPercent = (workDuration - this.plugin.currentTimerSeconds) / workDuration * 100; } this.progressBarEl.style.width = `${Math.min(Math.max(progressPercent, 0), 100)}%`; if (progressPercent >= 100) this.progressBarEl.addClass("immerse-overtime"); if (!this.plugin.isBreakMode) { const timeInfo = activeCard.createEl("div", { cls: "immerse-time-info" }); timeInfo.createEl("span", { text: `Est: ${this.plugin.formatTimeHuman(task.estimatedMinutes)}` }); this.actualTimeEl = timeInfo.createEl("span", { text: `Actual: ${this.plugin.formatTimeHuman(task.actualMinutes)}` }); } const controls = activeCard.createEl("div", { cls: "immerse-active-controls" }); if (this.plugin.isBreakMode) { if (this.plugin.currentTimerSeconds > 0) { this.pauseBtnEl = controls.createEl("button", { cls: "immerse-btn immerse-btn-secondary" }); this.pauseBtnEl.innerHTML = this.plugin.isTimerRunning ? "\u23F8 Pause" : "\u25B6 Resume"; this.pauseBtnEl.addEventListener("click", () => this.plugin.toggleTimer()); const skipBreakBtn = controls.createEl("button", { cls: "immerse-btn immerse-btn-primary" }); skipBreakBtn.innerHTML = "\u23ED Skip Break"; skipBreakBtn.addEventListener("click", () => { this.plugin.isBreakMode = false; this.plugin.startPomodoro(task.id); }); const stopBtn = controls.createEl("button", { cls: "immerse-btn immerse-btn-danger" }); stopBtn.innerHTML = "\u2715 Stop"; stopBtn.addEventListener("click", () => { this.plugin.isBreakMode = false; this.plugin.stopTimer(); }); } else { const resumeWorkBtn = controls.createEl("button", { cls: "immerse-btn immerse-btn-success" }); resumeWorkBtn.innerHTML = "\u25B6 Resume Work"; resumeWorkBtn.addEventListener("click", () => { this.plugin.isBreakMode = false; this.plugin.startPomodoro(task.id); }); const stopBtn = controls.createEl("button", { cls: "immerse-btn immerse-btn-danger" }); stopBtn.innerHTML = "\u2715 Stop"; stopBtn.addEventListener("click", () => { this.plugin.isBreakMode = false; this.plugin.stopTimer(); }); } } else { if (this.plugin.currentTimerSeconds > 0) { this.pauseBtnEl = controls.createEl("button", { cls: "immerse-btn immerse-btn-secondary" }); this.pauseBtnEl.innerHTML = this.plugin.isTimerRunning ? "\u23F8 Pause" : "\u25B6 Resume"; this.pauseBtnEl.addEventListener("click", () => this.plugin.toggleTimer()); const completeBtn = controls.createEl("button", { cls: "immerse-btn immerse-btn-success" }); completeBtn.innerHTML = "\u2713 Complete"; completeBtn.addEventListener("click", () => this.plugin.completeTask(task.id)); const stopBtn = controls.createEl("button", { cls: "immerse-btn immerse-btn-danger" }); stopBtn.innerHTML = "\u2715 Stop"; stopBtn.addEventListener("click", () => this.plugin.stopTimer()); } else { const startBreakBtn = controls.createEl("button", { cls: "immerse-btn immerse-btn-secondary" }); startBreakBtn.innerHTML = "\u2615 Start Break"; startBreakBtn.addEventListener("click", () => this.plugin.startBreak()); const continueBtn = controls.createEl("button", { cls: "immerse-btn immerse-btn-primary" }); continueBtn.innerHTML = "\u25B6 Continue Working"; continueBtn.addEventListener("click", () => this.plugin.startPomodoro(task.id)); const completeBtn = controls.createEl("button", { cls: "immerse-btn immerse-btn-success" }); completeBtn.innerHTML = "\u2713 Complete"; completeBtn.addEventListener("click", () => this.plugin.completeTask(task.id)); const stopBtn = controls.createEl("button", { cls: "immerse-btn immerse-btn-danger" }); stopBtn.innerHTML = "\u2715 Stop"; stopBtn.addEventListener("click", () => this.plugin.stopTimer()); } } } } else { const startPrompt = activeSection.createEl("div", { cls: "immerse-start-prompt" }); startPrompt.createEl("div", { cls: "immerse-prompt-icon", text: "\u26A1" }); startPrompt.createEl("div", { cls: "immerse-prompt-text", text: "Ready to focus?" }); startPrompt.createEl("div", { cls: "immerse-prompt-hint", text: "Click \u25B6 on a task to start a Pomodoro session" }); } } renderTaskList(container) { const listSection = container.createEl("div", { cls: "immerse-list-section" }); const filters = listSection.createEl("div", { cls: "immerse-filters" }); const filterOptions = [ { id: "all", label: "All Tasks" }, { id: "today", label: "Today" }, { id: "completed", label: "Completed" }, ...this.plugin.settings.lists.map((l) => ({ id: l.id, label: `${l.icon} ${l.name}` })) ]; filterOptions.forEach((opt) => { const btn = filters.createEl("button", { cls: `immerse-filter-btn ${this.currentFilter === opt.id ? "active" : ""}`, text: opt.label }); btn.addEventListener("click", () => { this.currentFilter = opt.id; this.refresh(); }); }); const taskList = listSection.createEl("div", { cls: "immerse-task-list" }); let tasks = this.plugin.data.tasks; if (this.currentFilter === "today") { tasks = this.plugin.getTodaysTasks(); } else if (this.currentFilter === "completed") { tasks = this.plugin.data.tasks.filter((t) => t.completed); } else if (this.currentFilter !== "all") { tasks = this.plugin.getTasksByList(this.currentFilter); } tasks = [...tasks].sort((a, b) => { if (a.completed !== b.completed) return a.completed ? 1 : -1; return b.createdAt - a.createdAt; }); if (tasks.length === 0) { const emptyState = taskList.createEl("div", { cls: "immerse-empty-state" }); emptyState.createEl("div", { cls: "immerse-empty-icon", text: "\u{1F4DD}" }); emptyState.createEl("div", { cls: "immerse-empty-text", text: "No tasks yet" }); emptyState.createEl("div", { cls: "immerse-empty-hint", text: "Add a task to get started!" }); } else { tasks.forEach((task) => this.renderTaskItem(taskList, task)); } } renderTaskItem(container, task) { const list = this.plugin.settings.lists.find((l) => l.id === task.list); const isOverdue = !task.completed && task.scheduledDate && task.scheduledTime && new Date(`${task.scheduledDate}T${task.scheduledTime}`).getTime() < Date.now(); const taskEl = container.createEl("div", { cls: `immerse-task-item ${task.completed ? "completed" : ""} ${task.isActive ? "active" : ""} ${isOverdue ? "overdue" : ""}` }); const checkbox = taskEl.createEl("div", { cls: "immerse-checkbox" }); checkbox.innerHTML = task.completed ? "\u2713" : ""; checkbox.style.borderColor = (list == null ? void 0 : list.color) || "#6366f1"; if (task.completed) { checkbox.style.backgroundColor = (list == null ? void 0 : list.color) || "#6366f1"; checkbox.style.color = "white"; } checkbox.addEventListener("click", (e) => { e.stopPropagation(); if (!task.completed) { this.plugin.completeTask(task.id); } }); const content = taskEl.createEl("div", { cls: "immerse-task-content" }); const taskHeader = content.createEl("div", { cls: "immerse-task-header" }); taskHeader.createEl("span", { cls: "immerse-task-text", text: task.text }); if (list) { const listBadge = taskHeader.createEl("span", { cls: "immerse-list-badge", text: `${list.icon} ${list.name}` }); listBadge.style.backgroundColor = list.color + "20"; listBadge.style.color = list.color; } const taskMeta = content.createEl("div", { cls: "immerse-task-meta" }); taskMeta.createEl("span", { text: `Est: ${this.plugin.formatTimeHuman(task.estimatedMinutes)}` }); if (task.actualMinutes > 0) { const actualSpan = taskMeta.createEl("span"); actualSpan.setText(`Actual: ${this.plugin.formatTimeHuman(task.actualMinutes)}`); if (task.actualMinutes > task.estimatedMinutes) { actualSpan.addClass("immerse-overtime-text"); } } if (task.scheduledDate) { const scheduleSpan = taskMeta.createEl("span", { cls: `immerse-schedule-badge ${isOverdue ? "overdue" : ""}` }); const dateStr = task.scheduledDate; const timeStr = task.scheduledTime || ""; if (isOverdue) { scheduleSpan.setText(`\u26A0\uFE0F OVERDUE: ${dateStr}${timeStr ? " " + timeStr : ""}`); } else { scheduleSpan.setText(`\u{1F4C5} ${dateStr}${timeStr ? " " + timeStr : ""}`); } if (task.reminderMinutes) { scheduleSpan.title = `Reminder set for ${task.reminderMinutes} min before`; } } const actions = taskEl.createEl("div", { cls: "immerse-task-actions" }); if (!task.completed) { const startBtn = actions.createEl("button", { cls: "immerse-task-btn", attr: { "aria-label": "Start Pomodoro" } }); startBtn.innerHTML = "\u25B6"; startBtn.addEventListener("click", (e) => { e.stopPropagation(); this.plugin.startPomodoro(task.id); }); const stopwatchBtn = actions.createEl("button", { cls: "immerse-task-btn", attr: { "aria-label": "Start Stopwatch" } }); stopwatchBtn.innerHTML = "\u23F1"; stopwatchBtn.addEventListener("click", (e) => { e.stopPropagation(); this.plugin.startTimer(task.id); }); } const editBtn = actions.createEl("button", { cls: "immerse-task-btn", attr: { "aria-label": "Edit" } }); editBtn.innerHTML = "\u270F\uFE0F"; editBtn.addEventListener("click", (e) => { e.stopPropagation(); new EditTaskModal(this.app, this.plugin, task).open(); }); const deleteBtn = actions.createEl("button", { cls: "immerse-task-btn immerse-delete-btn", attr: { "aria-label": "Delete" } }); deleteBtn.innerHTML = "\u{1F5D1}"; deleteBtn.addEventListener("click", (e) => { e.stopPropagation(); this.plugin.deleteTask(task.id); }); } }; // src/reportView.ts var import_obsidian3 = require("obsidian"); var VIEW_TYPE_REPORT = "immerse-report-view"; var ReportView = class extends import_obsidian3.ItemView { constructor(leaf, plugin) { super(leaf); this.selectedListIds = []; this.plugin = plugin; const today = new Date(); this.endDate = today.toISOString().split("T")[0]; const weekAgo = new Date(today); weekAgo.setDate(weekAgo.getDate() - 7); this.startDate = weekAgo.toISOString().split("T")[0]; } getViewType() { return VIEW_TYPE_REPORT; } getDisplayText() { return "\u{1F4CA} Reports"; } getIcon() { return "bar-chart-2"; } async onOpen() { const container = this.containerEl.children[1]; container.empty(); container.addClass("immerse-report-view"); this.renderContent(); } async onClose() { } renderContent() { const container = this.containerEl.children[1]; container.empty(); const header = container.createEl("div", { cls: "immerse-report-header" }); header.createEl("h2", { text: "\u{1F4CA} Reports", cls: "immerse-report-title" }); this.renderFilters(container); const generateBtn = container.createEl("button", { text: "\u{1F504} Generate Report", cls: "immerse-btn immerse-btn-primary immerse-report-generate-btn" }); generateBtn.addEventListener("click", () => this.renderReport(container)); this.renderReport(container); } renderFilters(container) { const filtersSection = container.createEl("div", { cls: "immerse-report-filters" }); const dateRow = filtersSection.createEl("div", { cls: "immerse-report-filter-row" }); new import_obsidian3.Setting(dateRow).setName("Start Date").addText((text) => { text.setValue(this.startDate).onChange((value) => this.startDate = value); text.inputEl.type = "date"; }); new import_obsidian3.Setting(dateRow).setName("End Date").addText((text) => { text.setValue(this.endDate).onChange((value) => this.endDate = value); text.inputEl.type = "date"; }); const quickFilters = filtersSection.createEl("div", { cls: "immerse-report-quick-filters" }); quickFilters.createEl("span", { text: "Quick select: ", cls: "immerse-filter-label" }); const filters = [ { label: "Today", days: 0 }, { label: "Last 7 days", days: 7 }, { label: "Last 30 days", days: 30 }, { label: "Last 90 days", days: 90 } ]; filters.forEach((filter) => { const btn = quickFilters.createEl("button", { text: filter.label, cls: "immerse-quick-filter-btn" }); btn.addEventListener("click", () => { const today = new Date(); this.endDate = today.toISOString().split("T")[0]; const startDate = new Date(today); startDate.setDate(startDate.getDate() - filter.days); this.startDate = startDate.toISOString().split("T")[0]; this.renderReport(container); }); }); } renderReport(container) { const oldReport = container.querySelector(".immerse-report-content"); if (oldReport) oldReport.remove(); const filters = { startDate: this.startDate, endDate: this.endDate, listIds: this.selectedListIds.length > 0 ? this.selectedListIds : void 0 }; const reportData = this.plugin.generateReport(filters); const reportContent = container.createEl("div", { cls: "immerse-report-content" }); if (reportData.totalTasks === 0) { reportContent.createEl("div", { text: "No data available for the selected period. Complete some tasks to see your stats!", cls: "immerse-no-data-message" }); return; } this.renderSummaryStats(reportContent, reportData); if (reportData.timeByList.length > 0) { this.renderTimeByList(reportContent, reportData); } this.renderInsights(reportContent, reportData); if (reportData.dailyBreakdown.length > 0) { this.renderDailyBreakdown(reportContent, reportData); } } renderSummaryStats(container, data) { const statsGrid = container.createEl("div", { cls: "immerse-stats-grid" }); const stats = [ { label: "TASKS DONE", value: data.totalTasks.toString(), icon: "\u2713" }, { label: "TASKS PER DAY", value: data.tasksPerDay.toFixed(1), icon: "\u{1F4C5}" }, { label: "HOURS PER DAY", value: data.hoursPerDay.toFixed(1), icon: "\u23F0" }, { label: "MINS PER TASK", value: data.minsPerTask.toString(), icon: "\u23F1\uFE0F" }, { label: "DAY STREAK", value: data.currentStreak.toString(), icon: "\u{1F525}" }, { label: "TOTAL HOURS", value: (data.totalMinutes / 60).toFixed(1), icon: "\u231A" } ]; stats.forEach((stat) => { const statCard = statsGrid.createEl("div", { cls: "immerse-stat-card" }); statCard.createEl("div", { text: stat.label, cls: "immerse-stat-label" }); const valueRow = statCard.createEl("div", { cls: "immerse-stat-value-row" }); valueRow.createEl("span", { text: stat.icon, cls: "immerse-stat-icon" }); valueRow.createEl("span", { text: stat.value, cls: "immerse-stat-value" }); }); } renderTimeByList(container, data) { const section = container.createEl("div", { cls: "immerse-report-section" }); section.createEl("h3", { text: "Time by List", cls: "immerse-report-section-title" }); const listContainer = section.createEl("div", { cls: "immerse-time-by-list" }); data.timeByList.forEach((item) => { const listItem = listContainer.createEl("div", { cls: "immerse-list-stat-item" }); const listInfo = listItem.createEl("div", { cls: "immerse-list-info" }); listInfo.createEl("span", { text: item.listIcon, cls: "immerse-list-icon" }); listInfo.createEl("span", { text: item.listName, cls: "immerse-list-name" }); const progressBar = listItem.createEl("div", { cls: "immerse-list-progress" }); const progress = progressBar.createEl("div", { cls: "immerse-list-progress-fill" }); progress.style.width = `${item.percentage}%`; progress.style.background = item.listColor; const stats = listItem.createEl("div", { cls: "immerse-list-stats" }); stats.createEl("span", { text: `${item.taskCount} tasks`, cls: "immerse-list-stat-text" }); stats.createEl("span", { text: `${this.plugin.formatTimeHuman(item.minutes)}`, cls: "immerse-list-stat-text" }); stats.createEl("span", { text: `${item.percentage.toFixed(1)}%`, cls: "immerse-list-stat-percentage" }); }); } renderInsights(container, data) { const section = container.createEl("div", { cls: "immerse-report-section" }); section.createEl("h3", { text: "Productivity Insights", cls: "immerse-report-section-title" }); const insightsGrid = section.createEl("div", { cls: "immerse-insights-grid" }); if (data.mostProductiveHour !== void 0) { const card = insightsGrid.createEl("div", { cls: "immerse-insight-card" }); card.createEl("span", { text: "\u{1F550}", cls: "immerse-insight-icon" }); card.createEl("span", { text: "MOST PRODUCTIVE HOUR", cls: "immerse-insight-label" }); card.createEl("span", { text: `${data.mostProductiveHour}:00 - ${data.mostProductiveHour + 1}:00`, cls: "immerse-insight-value" }); } if (data.mostProductiveDay) { const card = insightsGrid.createEl("div", { cls: "immerse-insight-card" }); card.createEl("span", { text: "\u{1F4C5}", cls: "immerse-insight-icon" }); card.createEl("span", { text: "MOST PRODUCTIVE DAY", cls: "immerse-insight-label" }); card.createEl("span", { text: data.mostProductiveDay, cls: "immerse-insight-value" }); } if (data.mostProductiveMonth) { const card = insightsGrid.createEl("div", { cls: "immerse-insight-card" }); card.createEl("span", { text: "\u{1F5D3}\uFE0F", cls: "immerse-insight-icon" }); card.createEl("span", { text: "MOST PRODUCTIVE MONTH", cls: "immerse-insight-label" }); card.createEl("span", { text: data.mostProductiveMonth, cls: "immerse-insight-value" }); } } renderPieChart(container, data) { const pieContainer = container.createEl("div", { cls: "immerse-daily-pie-container" }); const totalTasks = data.totalTasks; const totalMinutes = data.totalMinutes; const totalPomodoros = data.totalPomodoros; const taskMinutes = totalTasks * data.minsPerTask; const pomodoroMinutes = totalPomodoros * 25; const totalTime = taskMinutes + totalMinutes + pomodoroMinutes; if (totalTime === 0) return; const tasksPercent = taskMinutes / totalTime * 100; const hoursPercent = totalMinutes / totalTime * 100; const pomodorosPercent = pomodoroMinutes / totalTime * 100; const tasksDeg = tasksPercent / 100 * 360; const hoursDeg = tasksDeg + hoursPercent / 100 * 360; const pieChart = pieContainer.createEl("div", { cls: "immerse-daily-pie-chart" }); const gradient = `conic-gradient(from 0deg, #6366f1 0deg ${tasksDeg}deg, #22c55e ${tasksDeg}deg ${hoursDeg}deg, #f59e0b ${hoursDeg}deg 360deg)`; pieChart.style.background = gradient; const center = pieChart.createEl("div", { cls: "immerse-daily-pie-center" }); center.createEl("div", { text: data.totalTasks.toString(), cls: "immerse-daily-pie-center-value" }); center.createEl("div", { text: "TOTAL TASKS", cls: "immerse-daily-pie-center-label" }); const legend = pieContainer.createEl("div", { cls: "immerse-daily-pie-legend" }); const tasksItem = legend.createEl("div", { cls: "immerse-daily-pie-legend-item" }); const tasksColor = tasksItem.createEl("div", { cls: "immerse-daily-pie-legend-color" }); tasksColor.style.background = "#6366f1"; const tasksInfo = tasksItem.createEl("div", { cls: "immerse-daily-pie-legend-info" }); const tasksLabel = tasksInfo.createEl("div", { cls: "immerse-daily-pie-legend-label" }); tasksLabel.createEl("span", { text: "\u2713" }); tasksLabel.appendText("Tasks Completed"); tasksInfo.createEl("div", { text: totalTasks.toString(), cls: "immerse-daily-pie-legend-value" }); tasksInfo.createEl("div", { text: `${tasksPercent.toFixed(1)}%`, cls: "immerse-daily-pie-legend-percentage" }); const hoursItem = legend.createEl("div", { cls: "immerse-daily-pie-legend-item" }); const hoursColor = hoursItem.createEl("div", { cls: "immerse-daily-pie-legend-color" }); hoursColor.style.background = "#22c55e"; const hoursInfo = hoursItem.createEl("div", { cls: "immerse-daily-pie-legend-info" }); const hoursLabel = hoursInfo.createEl("div", { cls: "immerse-daily-pie-legend-label" }); hoursLabel.createEl("span", { text: "\u23F1\uFE0F" }); hoursLabel.appendText("Total Hours"); hoursInfo.createEl("div", { text: (totalMinutes / 60).toFixed(1), cls: "immerse-daily-pie-legend-value" }); hoursInfo.createEl("div", { text: `${hoursPercent.toFixed(1)}%`, cls: "immerse-daily-pie-legend-percentage" }); const pomodorosItem = legend.createEl("div", { cls: "immerse-daily-pie-legend-item" }); const pomodorosColor = pomodorosItem.createEl("div", { cls: "immerse-daily-pie-legend-color" }); pomodorosColor.style.background = "#f59e0b"; const pomodorosInfo = pomodorosItem.createEl("div", { cls: "immerse-daily-pie-legend-info" }); const pomodorosLabel = pomodorosInfo.createEl("div", { cls: "immerse-daily-pie-legend-label" }); pomodorosLabel.createEl("span", { text: "\u{1F345}" }); pomodorosLabel.appendText("Pomodoros"); pomodorosInfo.createEl("div", { text: totalPomodoros.toString(), cls: "immerse-daily-pie-legend-value" }); pomodorosInfo.createEl("div", { text: `${pomodorosPercent.toFixed(1)}%`, cls: "immerse-daily-pie-legend-percentage" }); } renderDailyBreakdown(container, data) { const section = container.createEl("div", { cls: "immerse-report-section" }); section.createEl("h3", { text: "Daily Breakdown", cls: "immerse-report-section-title" }); this.renderPieChart(section, data); const breakdownContainer = section.createEl("div", { cls: "immerse-daily-breakdown-container" }); const recentData = data.dailyBreakdown.slice(-10); const maxTasks = Math.max(...recentData.map((d) => d.tasks), 1); const maxHours = Math.max(...recentData.map((d) => d.hours), 1); const maxPomodoros = Math.max(...recentData.map((d) => d.pomodoros), 1); recentData.forEach((day) => { const row = breakdownContainer.createEl("div", { cls: "immerse-daily-row" }); const dateEl = row.createEl("div", { cls: "immerse-daily-date" }); const date = new Date(day.date + "T00:00:00"); const dayName = date.toLocaleDateString("en-US", { weekday: "short" }).toUpperCase(); const monthDay = date.toLocaleDateString("en-US", { month: "short", day: "numeric" }); dateEl.createEl("div", { text: dayName, cls: "immerse-daily-date-day" }); dateEl.createEl("div", { text: monthDay, cls: "immerse-daily-date-num" }); const barsContainer = row.createEl("div", { cls: "immerse-daily-bars" }); const tasksRow = barsContainer.createEl("div", { cls: "immerse-daily-bar-row" }); const tasksLabel = tasksRow.createEl("span", { cls: "immerse-daily-bar-label" }); tasksLabel.createEl("span", { text: "\u2713", cls: "immerse-daily-bar-icon" }); tasksLabel.appendText("Tasks"); const tasksTrack = tasksRow.createEl("div", { cls: "immerse-daily-bar-track" }); const tasksFill = tasksTrack.createEl("div", { cls: "immerse-daily-bar-fill tasks" }); tasksFill.style.width = `${day.tasks / maxTasks * 100}%`; tasksRow.createEl("span", { text: day.tasks.toString(), cls: "immerse-daily-bar-value" }); const hoursRow = barsContainer.createEl("div", { cls: "immerse-daily-bar-row" }); const hoursLabel = hoursRow.createEl("span", { cls: "immerse-daily-bar-label" }); hoursLabel.createEl("span", { text: "\u23F1\uFE0F", cls: "immerse-daily-bar-icon" }); hoursLabel.appendText("Hours"); const hoursTrack = hoursRow.createEl("div", { cls: "immerse-daily-bar-track" }); const hoursFill = hoursTrack.createEl("div", { cls: "immerse-daily-bar-fill hours" }); hoursFill.style.width = `${day.hours / maxHours * 100}%`; hoursRow.createEl("span", { text: day.hours.toFixed(1), cls: "immerse-daily-bar-value" }); const pomodorosRow = barsContainer.createEl("div", { cls: "immerse-daily-bar-row" }); const pomodorosLabel = pomodorosRow.createEl("span", { cls: "immerse-daily-bar-label" }); pomodorosLabel.createEl("span", { text: "\u{1F345}", cls: "immerse-daily-bar-icon" }); pomodorosLabel.appendText("Pomodoros"); const pomodorosTrack = pomodorosRow.createEl("div", { cls: "immerse-daily-bar-track" }); const pomodorosFill = pomodorosTrack.createEl("div", { cls: "immerse-daily-bar-fill pomodoros" }); pomodorosFill.style.width = `${day.pomodoros / maxPomodoros * 100}%`; pomodorosRow.createEl("span", { text: day.pomodoros.toString(), cls: "immerse-daily-bar-value" }); }); } }; // src/main.ts var ImmersePlugin = class extends import_obsidian4.Plugin { constructor() { super(...arguments); // Timer state this.timerInterval = null; this.currentTimerSeconds = 0; this.isTimerRunning = false; this.isBreakMode = false; this.activeTaskId = null; this.pomodoroCount = 0; // Timestamp-based tracking for reliable background timing this.timerStartTimestamp = 0; this.pausedTimeRemaining = 0; // Focus time tracking (in seconds for accuracy) this.focusSecondsToday = 0; this.secondsWorkedOnCurrentTask = 0; // Status bar element this.statusBarEl = null; // Reminder system this.reminderCheckInterval = null; this.notifiedReminders = /* @__PURE__ */ new Set(); } // Track which reminders have been shown async onload() { await this.loadAllData(); this.checkDailyReset(); document.addEventListener("visibilitychange", () => { if (!document.hidden && this.isTimerRunning) { this.syncTimerFromTimestamp(); } }); this.registerView( VIEW_TYPE_IMMERSE, (leaf) => new ImmerseView(leaf, this) ); this.registerView( VIEW_TYPE_REPORT, (leaf) => new ReportView(leaf, this) ); this.addRibbonIcon("zap", "Open Immerse", () => { this.activateView(); }); this.addCommand({ id: "open-immerse", name: "Open Immerse Panel", callback: () => this.activateView() }); this.addCommand({ id: "quick-add-task", name: "Quick Add Task", callback: () => new QuickAddTaskModal(this.app, this).open() }); this.addCommand({ id: "start-focus-mode", name: "Start Focus Mode on Next Task", callback: () => this.startFocusOnNextTask() }); this.addCommand({ id: "toggle-timer", name: "Toggle Timer (Play/Pause)", callback: () => this.toggleTimer() }); this.addCommand({ id: "complete-current-task", name: "Complete Current Task", callback: () => this.completeActiveTask() }); this.addCommand({ id: "view-reports", name: "View Reports", callback: () => this.activateReportView() }); this.addSettingTab(new ImmerseSettingTab(this.app, this)); this.createStatusBar(); if (this.settings.enableReminders) { this.startReminderSystem(); } } onunload() { this.stopTimer(); this.stopReminderSystem(); } async loadAllData() { const loaded = await this.loadData(); this.data = Object.assign({}, DEFAULT_DATA, (loaded == null ? void 0 : loaded.data) || {}); this.settings = Object.assign({}, DEFAULT_SETTINGS, (loaded == null ? void 0 : loaded.settings) || {}); if (!this.settings.lists || this.settings.lists.length === 0) { this.settings.lists = DEFAULT_SETTINGS.lists; } this.focusSecondsToday = (this.data.totalFocusMinutesToday || 0) * 60; } async saveAllData() { this.data.totalFocusMinutesToday = Math.floor(this.focusSecondsToday / 60); await this.saveData({ settings: this.settings, data: this.data }); } checkDailyReset() { const today = new Date().toDateString(); if (this.data.lastActiveDate !== today) { const yesterday = new Date(); yesterday.setDate(yesterday.getDate() - 1); if (this.data.lastActiveDate === yesterday.toDateString()) { this.data.streak++; } else if (this.data.lastActiveDate !== today) { this.data.streak = 0; } this.data.completedToday = 0; this.data.totalFocusMinutesToday = 0; this.focusSecondsToday = 0; this.data.lastActiveDate = today; this.saveAllData(); } } async activateView() { const { workspace } = this.app; let leaf = null; const leaves = workspace.getLeavesOfType(VIEW_TYPE_IMMERSE); if (leaves.length > 0) { leaf = leaves[0]; } else { leaf = workspace.getRightLeaf(false); if (leaf) { await leaf.setViewState({ type: VIEW_TYPE_IMMERSE, active: true }); } } if (leaf) { workspace.revealLeaf(leaf); } } async activateReportView() { const { workspace } = this.app; const existingLeaves = workspace.getLeavesOfType(VIEW_TYPE_REPORT); if (existingLeaves.length > 0) { workspace.revealLeaf(existingLeaves[0]); } else { const leaf = workspace.getLeaf("tab"); await leaf.setViewState({ type: VIEW_TYPE_REPORT, active: true }); workspace.revealLeaf(leaf); } } // ============ Task Management ============ createTask(text, estimatedMinutes = this.settings.defaultEstimateMinutes, list = "work") { return { id: this.generateId(), text, completed: false, estimatedMinutes, actualMinutes: 0, createdAt: Date.now(), list, notes: "", isActive: false }; } generateId() { return Date.now().toString(36) + Math.random().toString(36).substr(2); } addTask(task) { this.data.tasks.push(task); this.saveAllData(); this.refreshView(); } updateTask(taskId, updates) { const task = this.data.tasks.find((t) => t.id === taskId); if (task) { Object.assign(task, updates); this.saveAllData(); this.refreshView(); } } deleteTask(taskId) { this.data.tasks = this.data.tasks.filter((t) => t.id !== taskId); if (this.activeTaskId === taskId) { this.stopTimer(); this.activeTaskId = null; } this.saveAllData(); this.refreshView(); } completeTask(taskId) { const task = this.data.tasks.find((t) => t.id === taskId); if (task && !task.completed) { task.completed = true; task.completedAt = Date.now(); task.isActive = false; this.data.completedToday++; this.data.lastActiveDate = new Date().toDateString(); this.archiveCompletedTask(task); this.updateDailyStats(task); if (this.settings.enableCelebrations) { this.showCelebration(task); } if (this.settings.enableSounds) { this.playCompletionSound(); } if (this.settings.logToDaily) { this.logTaskToDailyNote(task); } if (this.activeTaskId === taskId) { this.stopTimer(); this.activeTaskId = null; } this.saveAllData(); this.refreshView(); } } completeActiveTask() { if (this.activeTaskId) { this.completeTask(this.activeTaskId); } else { new import_obsidian4.Notice("No active task to complete"); } } // ============ Data Archiving & Statistics ============ archiveCompletedTask(task) { const wasOverdue = task.scheduledDate && task.scheduledTime && new Date(`${task.scheduledDate}T${task.scheduledTime}`).getTime() < (task.completedAt || Date.now()); const record = { id: task.id, text: task.text, list: task.list, estimatedMinutes: task.estimatedMinutes, actualMinutes: task.actualMinutes, createdAt: task.createdAt, completedAt: task.completedAt || Date.now(), scheduledDate: task.scheduledDate, wasOverdue: wasOverdue || false }; this.data.completedTasksArchive.push(record); } updateDailyStats(task) { const today = new Date().toISOString().split("T")[0]; let todayStats = this.data.dailyStats.find((s) => s.date === today); if (!todayStats) { todayStats = { date: today, tasksCompleted: 0, totalMinutes: 0, pomodorosCompleted: 0, tasksByList: {}, minutesByList: {} }; this.data.dailyStats.push(todayStats); } todayStats.tasksCompleted++; todayStats.totalMinutes += task.actualMinutes; todayStats.pomodorosCompleted = this.data.pomodorosCompleted; todayStats.tasksByList[task.list] = (todayStats.tasksByList[task.list] || 0) + 1; todayStats.minutesByList[task.list] = (todayStats.minutesByList[task.list] || 0) + task.actualMinutes; if (this.data.dailyStats.length > 365) { this.data.dailyStats.sort((a, b) => b.date.localeCompare(a.date)); this.data.dailyStats = this.data.dailyStats.slice(0, 365); } } // ============ Report Generation ============ generateReport(filters) { const { startDate, endDate, listIds } = filters; const filteredStats = this.data.dailyStats.filter((stat) => { return stat.date >= startDate && stat.date <= endDate; }); const filteredTasks = this.data.completedTasksArchive.filter((task) => { const taskDate = new Date(task.completedAt).toISOString().split("T")[0]; const inDateRange = taskDate >= startDate && taskDate <= endDate; const inList = !listIds || listIds.includes(task.list); return inDateRange && inList; }); const totalTasks = filteredTasks.length; const totalMinutes = filteredTasks.reduce((sum, task) => sum + task.actualMinutes, 0); const totalPomodoros = filteredStats.reduce((sum, stat) => sum + stat.pomodorosCompleted, 0); const daysWithData = filteredStats.length || 1; const tasksPerDay = totalTasks / daysWithData; const hoursPerDay = totalMinutes / 60 / daysWithData; const minsPerTask = totalTasks > 0 ? totalMinutes / totalTasks : 0; const timeByListMap = {}; filteredTasks.forEach((task) => { if (!timeByListMap[task.list]) { timeByListMap[task.list] = { minutes: 0, taskCount: 0 }; } timeByListMap[task.list].minutes += task.actualMinutes; timeByListMap[task.list].taskCount++; }); const timeByList = this.settings.lists.map((list) => { const data = timeByListMap[list.id] || { minutes: 0, taskCount: 0 }; const percentage = totalMinutes > 0 ? data.minutes / totalMinutes * 100 : 0; return { listId: list.id, listName: list.name, listIcon: list.icon, listColor: list.color, minutes: data.minutes, taskCount: data.taskCount, percentage: Math.round(percentage * 10) / 10 // Round to 1 decimal }; }).filter((item) => item.minutes > 0); const dailyBreakdown = filteredStats.map((stat) => ({ date: stat.date, tasks: stat.tasksCompleted, hours: Math.round(stat.totalMinutes / 60 * 10) / 10, pomodoros: stat.pomodorosCompleted })); const mostProductiveHour = this.calculateMostProductiveHour(filteredTasks); const mostProductiveDay = this.calculateMostProductiveDay(filteredStats); const mostProductiveMonth = this.calculateMostProductiveMonth(filteredStats); return { totalTasks, totalMinutes, totalPomodoros, tasksPerDay: Math.round(tasksPerDay * 10) / 10, hoursPerDay: Math.round(hoursPerDay * 10) / 10, minsPerTask: Math.round(minsPerTask), currentStreak: this.data.streak, timeByList, dailyBreakdown, mostProductiveHour, mostProductiveDay, mostProductiveMonth }; } calculateMostProductiveHour(tasks) { if (tasks.length === 0) return void 0; const hourCounts = {}; tasks.forEach((task) => { const hour = new Date(task.completedAt).getHours(); hourCounts[hour] = (hourCounts[hour] || 0) + 1; }); let maxHour = 0; let maxCount = 0; for (const [hour, count] of Object.entries(hourCounts)) { if (count > maxCount) { maxCount = count; maxHour = parseInt(hour); } } return maxCount > 0 ? maxHour : void 0; } calculateMostProductiveDay(stats) { if (stats.length === 0) return void 0; const dayNames = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]; const dayCounts = {}; stats.forEach((stat) => { const dayOfWeek = new Date(stat.date).getDay(); const dayName = dayNames[dayOfWeek]; dayCounts[dayName] = (dayCounts[dayName] || 0) + stat.tasksCompleted; }); let maxDay = ""; let maxCount = 0; for (const [day, count] of Object.entries(dayCounts)) { if (count > maxCount) { maxCount = count; maxDay = day; } } return maxCount > 0 ? maxDay : void 0; } calculateMostProductiveMonth(stats) { if (stats.length === 0) return void 0; const monthNames = [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ]; const monthCounts = {}; stats.forEach((stat) => { const month = new Date(stat.date).getMonth(); const monthName = monthNames[month]; monthCounts[monthName] = (monthCounts[monthName] || 0) + stat.tasksCompleted; }); let maxMonth = ""; let maxCount = 0; for (const [month, count] of Object.entries(monthCounts)) { if (count > maxCount) { maxCount = count; maxMonth = month; } } return maxCount > 0 ? maxMonth : void 0; } // ============ Timer Management ============ // Sync timer based on timestamp when app returns from background syncTimerFromTimestamp() { if (!this.isTimerRunning) return; this.updateStatusBar(); this.updateTimerDisplay(); } startTimer(taskId) { const task = this.data.tasks.find((t) => t.id === taskId); if (!task) return; this.stopTimer(); this.activeTaskId = taskId; task.isActive = true; this.isBreakMode = false; this.currentTimerSeconds = 0; this.isTimerRunning = true; this.secondsWorkedOnCurrentTask = task.actualMinutes * 60; this.timerStartTimestamp = Date.now(); this.pausedTimeRemaining = 0; const initialSecondsWorked = this.secondsWorkedOnCurrentTask; let alertShown = false; this.refreshView(); this.updateStatusBar(); this.timerInterval = window.setInterval(() => { const now = Date.now(); const elapsedMs = now - this.timerStartTimestamp; const elapsedSeconds = Math.floor(elapsedMs / 1e3); this.currentTimerSeconds = elapsedSeconds; this.secondsWorkedOnCurrentTask = initialSecondsWorked + elapsedSeconds; task.actualMinutes = Math.floor(this.secondsWorkedOnCurrentTask / 60); const newFocusSeconds = Math.floor((this.data.totalFocusMinutesToday || 0) * 60) + elapsedSeconds; this.focusSecondsToday = newFocusSeconds; this.updateStatusBar(); this.updateTimerDisplay(); if (!alertShown && this.currentTimerSeconds >= task.estimatedMinutes * 60) { alertShown = true; if (this.settings.enableSounds) { this.playAlertSound(); } new import_obsidian4.Notice(`\u23F0 Time's up for: ${task.text}`); } }, 1e3); this.saveAllData(); } startPomodoro(taskId) { const task = this.data.tasks.find((t) => t.id === taskId); if (!task) return; this.stopTimer(); this.activeTaskId = taskId; task.isActive = true; this.isBreakMode = false; this.currentTimerSeconds = this.settings.pomodoroWorkMinutes * 60; this.isTimerRunning = true; this.secondsWorkedOnCurrentTask = Math.floor(task.actualMinutes * 60); this.timerStartTimestamp = Date.now(); this.pausedTimeRemaining = this.currentTimerSeconds; const initialSecondsWorked = this.secondsWorkedOnCurrentTask; this.refreshView(); this.updateStatusBar(); this.timerInterval = window.setInterval(() => { const now = Date.now(); const elapsedMs = now - this.timerStartTimestamp; const elapsedSeconds = Math.floor(elapsedMs / 1e3); this.currentTimerSeconds = Math.max(0, this.pausedTimeRemaining - elapsedSeconds); if (!this.isBreakMode) { this.secondsWorkedOnCurrentTask = initialSecondsWorked + elapsedSeconds; const actualMinutes = Math.floor(this.secondsWorkedOnCurrentTask / 60); if (task.actualMinutes !== actualMinutes) { task.actualMinutes = actualMinutes; } const newFocusSeconds = Math.floor((this.data.totalFocusMinutesToday || 0) * 60) + elapsedSeconds; this.focusSecondsToday = newFocusSeconds; } this.updateStatusBar(); this.updateTimerDisplay(); if (this.currentTimerSeconds <= 0) { this.handlePomodoroEnd(); } }, 1e3); } handlePomodoroEnd() { if (this.timerInterval) { window.clearInterval(this.timerInterval); this.timerInterval = null; } this.currentTimerSeconds = 0; this.isTimerRunning = false; this.updateStatusBar(); this.updateTimerDisplay(); if (!this.isBreakMode) { this.pomodoroCount++; this.data.pomodorosCompleted++; if (this.settings.enableSounds) { this.playAlertSound(); } new import_obsidian4.Notice("\u{1F345} Pomodoro complete! Time for a break."); if (this.settings.autoStartBreak) { this.startBreak(); } else { this.refreshView(); } } else { if (this.settings.enableSounds) { this.playAlertSound(); } new import_obsidian4.Notice("\u26A1 Break over! Ready to focus?"); this.refreshView(); } this.saveAllData(); } startBreak() { this.isBreakMode = true; this.isTimerRunning = true; const isLongBreak = this.pomodoroCount % this.settings.longBreakInterval === 0; this.currentTimerSeconds = (isLongBreak ? this.settings.longBreakMinutes : this.settings.pomodoroBreakMinutes) * 60; this.timerStartTimestamp = Date.now(); this.pausedTimeRemaining = this.currentTimerSeconds; new import_obsidian4.Notice(isLongBreak ? "\u2615 Long break time!" : "\u2615 Short break time!"); this.refreshView(); if (!this.timerInterval) { this.timerInterval = window.setInterval(() => { const now = Date.now(); const elapsedMs = now - this.timerStartTimestamp; const elapsedSeconds = Math.floor(elapsedMs / 1e3); this.currentTimerSeconds = Math.max(0, this.pausedTimeRemaining - elapsedSeconds); this.updateStatusBar(); this.updateTimerDisplay(); if (this.currentTimerSeconds <= 0) { this.handlePomodoroEnd(); } }, 1e3); } this.updateStatusBar(); } toggleTimer() { if (this.isTimerRunning && this.timerInterval) { window.clearInterval(this.timerInterval); this.timerInterval = null; this.isTimerRunning = false; this.pausedTimeRemaining = this.currentTimerSeconds; } else if (this.activeTaskId) { this.isTimerRunning = true; this.timerStartTimestamp = Date.now(); const task = this.data.tasks.find((t) => t.id === this.activeTaskId); const initialSecondsWorked = this.secondsWorkedOnCurrentTask; this.timerInterval = window.setInterval(() => { const now = Date.now(); const elapsedMs = now - this.timerStartTimestamp; const elapsedSeconds = Math.floor(elapsedMs / 1e3); this.currentTimerSeconds = Math.max(0, this.pausedTimeRemaining - elapsedSeconds); if (task && !this.isBreakMode) { this.secondsWorkedOnCurrentTask = initialSecondsWorked + elapsedSeconds; task.actualMinutes = Math.floor(this.secondsWorkedOnCurrentTask / 60); const newFocusSeconds = Math.floor((this.data.totalFocusMinutesToday || 0) * 60) + elapsedSeconds; this.focusSecondsToday = newFocusSeconds; } this.updateStatusBar(); this.updateTimerDisplay(); if (this.currentTimerSeconds <= 0) { this.handlePomodoroEnd(); } }, 1e3); } else { new import_obsidian4.Notice("No active task. Select a task first."); } this.updateStatusBar(); this.refreshView(); } stopTimer() { if (this.timerInterval) { window.clearInterval(this.timerInterval); this.timerInterval = null; } if (this.activeTaskId) { const task = this.data.tasks.find((t) => t.id === this.activeTaskId); if (task) { task.isActive = false; task.actualMinutes = 0; } } this.isTimerRunning = false; this.activeTaskId = null; this.secondsWorkedOnCurrentTask = 0; this.timerStartTimestamp = 0; this.pausedTimeRemaining = 0; this.updateStatusBar(); this.saveAllData(); this.refreshView(); } startFocusOnNextTask() { const pendingTasks = this.data.tasks.filter((t) => !t.completed); if (pendingTasks.length > 0) { this.startPomodoro(pendingTasks[0].id); } else { new import_obsidian4.Notice("No pending tasks. Add a task first!"); } } // ============ Status Bar Timer ============ createStatusBar() { this.statusBarEl = this.addStatusBarItem(); this.statusBarEl.addClass("immerse-status-bar"); this.updateStatusBar(); this.statusBarEl.addEventListener("click", () => { this.activateView(); }); } updateStatusBar() { if (!this.statusBarEl) return; if (this.activeTaskId) { const task = this.data.tasks.find((t) => t.id === this.activeTaskId); const taskName = this.isBreakMode ? "\u2615 Break" : (task == null ? void 0 : task.text.substring(0, 20)) || "Task"; const timeStr = this.formatTime(this.currentTimerSeconds); const icon = this.isTimerRunning ? "\u25B6" : "\u23F8"; this.statusBarEl.setText(`\u26A1 ${icon} ${timeStr} - ${taskName}${task && task.text.length > 20 ? "..." : ""}`); this.statusBarEl.addClass("immerse-status-active"); } else { this.statusBarEl.setText("\u26A1 Immerse"); this.statusBarEl.removeClass("immerse-status-active"); } } // ============ Reminder System ============ startReminderSystem() { this.reminderCheckInterval = window.setInterval(() => { this.checkReminders(); }, 3e4); this.checkReminders(); } stopReminderSystem() { if (this.reminderCheckInterval) { window.clearInterval(this.reminderCheckInterval); this.reminderCheckInterval = null; } } checkReminders() { if (!this.settings.enableReminders) return; const now = new Date(); const currentTime = now.getTime(); this.data.tasks.filter((task) => !task.completed && task.scheduledDate && task.scheduledTime).forEach((task) => { const reminderKey = `${task.id}-${task.scheduledDate}-${task.scheduledTime}`; if (this.notifiedReminders.has(reminderKey)) return; const scheduledDateTime = new Date(`${task.scheduledDate}T${task.scheduledTime}`); const scheduledTime = scheduledDateTime.getTime(); if (currentTime > scheduledTime) { this.showOverdueNotice(task); this.notifiedReminders.add(reminderKey); return; } if (task.reminderMinutes) { const reminderTime = scheduledTime - task.reminderMinutes * 60 * 1e3; if (currentTime >= reminderTime) { this.showReminder(task); this.notifiedReminders.add(reminderKey); } } }); } showReminder(task) { const timeStr = task.scheduledTime; new import_obsidian4.Notice(`\u{1F514} Reminder: "${task.text}" is scheduled for ${timeStr}`, 8e3); if (this.settings.enableSounds) { this.playAlertSound(); } } showOverdueNotice(task) { const dateStr = task.scheduledDate; const timeStr = task.scheduledTime; new import_obsidian4.Notice(`\u26A0\uFE0F Overdue: "${task.text}" was scheduled for ${dateStr} ${timeStr}`, 1e4); if (this.settings.enableSounds) { this.playAlertSound(); } } // ============ Sounds & Celebrations ============ showCelebration(task) { let messages = CELEBRATION_MESSAGES; let extraMessage = ""; if (task.actualMinutes < task.estimatedMinutes) { const saved = task.estimatedMinutes - task.actualMinutes; messages = EARLY_FINISH_MESSAGES; extraMessage = ` (${saved} min early!)`; } else if (task.actualMinutes > task.estimatedMinutes * 1.5) { messages = OVERTIME_MESSAGES; } const celebration = messages[Math.floor(Math.random() * messages.length)]; new import_obsidian4.Notice(`${celebration.emoji} ${celebration.message}${extraMessage}`); } playCompletionSound() { try { const audioContext = new (window.AudioContext || window.webkitAudioContext)(); const oscillator = audioContext.createOscillator(); const gainNode = audioContext.createGain(); oscillator.connect(gainNode); gainNode.connect(audioContext.destination); oscillator.frequency.setValueAtTime(800, audioContext.currentTime); oscillator.frequency.setValueAtTime(1e3, audioContext.currentTime + 0.1); oscillator.frequency.setValueAtTime(1200, audioContext.currentTime + 0.2); gainNode.gain.setValueAtTime(0.3, audioContext.currentTime); gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.3); oscillator.start(audioContext.currentTime); oscillator.stop(audioContext.currentTime + 0.3); } catch (e) { console.log("Audio not available"); } } playAlertSound() { try { const audioContext = new (window.AudioContext || window.webkitAudioContext)(); const oscillator = audioContext.createOscillator(); const gainNode = audioContext.createGain(); oscillator.connect(gainNode); gainNode.connect(audioContext.destination); oscillator.frequency.setValueAtTime(440, audioContext.currentTime); gainNode.gain.setValueAtTime(0.3, audioContext.currentTime); for (let i = 0; i < 3; i++) { oscillator.frequency.setValueAtTime(440, audioContext.currentTime + i * 0.3); oscillator.frequency.setValueAtTime(550, audioContext.currentTime + i * 0.3 + 0.15); } gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.9); oscillator.start(audioContext.currentTime); oscillator.stop(audioContext.currentTime + 0.9); } catch (e) { console.log("Audio not available"); } } // ============ Daily Note Logging ============ async logTaskToDailyNote(task) { try { const list = this.settings.lists.find((l) => l.id === task.list); const timeDiff = task.actualMinutes - task.estimatedMinutes; let timeComparison = ""; if (timeDiff < 0) { timeComparison = `${Math.abs(timeDiff)}min under estimate \u2728`; } else if (timeDiff > 0) { timeComparison = `${timeDiff}min over estimate`; } else { timeComparison = `exactly on target \u{1F3AF}`; } const completedTime = new Date(task.completedAt || Date.now()).toLocaleTimeString("en-US", { hour: "2-digit", minute: "2-digit", hour12: false }); const taskEntry = `- [x] ${task.text} | ${(list == null ? void 0 : list.icon) || "\u{1F4CB}"} ${(list == null ? void 0 : list.name) || "Task"} | \u23F1\uFE0F ${this.formatTimeHuman(task.actualMinutes)} / ${this.formatTimeHuman(task.estimatedMinutes)} (${timeComparison}) | \u2705 ${completedTime}`; await this.appendToDailyNote(taskEntry); } catch (e) { console.error("Failed to log task to daily note:", e); new import_obsidian4.Notice("Failed to log task to daily note. Make sure Daily Notes core plugin is enabled."); } } getDailyNoteSettings() { var _a, _b, _c; const dailyNotesPlugin = (_b = (_a = this.app.internalPlugins) == null ? void 0 : _a.plugins) == null ? void 0 : _b["daily-notes"]; if (!(dailyNotesPlugin == null ? void 0 : dailyNotesPlugin.enabled)) { return null; } const settings = (_c = dailyNotesPlugin.instance) == null ? void 0 : _c.options; return { folder: (settings == null ? void 0 : settings.folder) || "", format: (settings == null ? void 0 : settings.format) || "YYYY-MM-DD", template: (settings == null ? void 0 : settings.template) || "" }; } formatDailyNoteDate(format) { const now = new Date(); const year = now.getFullYear(); const month = now.getMonth() + 1; const day = now.getDate(); let result = format; result = result.replace(/YYYY/g, year.toString()); result = result.replace(/YY/g, year.toString().slice(-2)); result = result.replace(/MMMM/g, now.toLocaleDateString("en-US", { month: "long" })); result = result.replace(/MMM/g, now.toLocaleDateString("en-US", { month: "short" })); result = result.replace(/MM/g, month.toString().padStart(2, "0")); result = result.replace(/(? 0 ? `${hours}hr ${mins}min` : `${hours}hr`; } refreshView() { const leaves = this.app.workspace.getLeavesOfType(VIEW_TYPE_IMMERSE); leaves.forEach((leaf) => { if (leaf.view instanceof ImmerseView) { leaf.view.refresh(); } }); } // Light refresh - only updates timer display without rebuilding DOM updateTimerDisplay() { const leaves = this.app.workspace.getLeavesOfType(VIEW_TYPE_IMMERSE); leaves.forEach((leaf) => { if (leaf.view instanceof ImmerseView) { leaf.view.updateTimerDisplay(); } }); } getTasksByList(listId) { return this.data.tasks.filter((t) => t.list === listId); } getPendingTasks() { return this.data.tasks.filter((t) => !t.completed); } getTodaysTasks() { const today = new Date().toDateString(); return this.data.tasks.filter((t) => { if (t.scheduledDate === today) return true; if (!t.scheduledDate && !t.completed) return true; return false; }); } getStats() { const pending = this.getPendingTasks(); const totalEstimate = pending.reduce((sum, t) => sum + t.estimatedMinutes, 0); const completedTasks = this.data.tasks.filter((t) => t.completed); const avgAccuracy = completedTasks.length > 0 ? completedTasks.reduce((sum, t) => sum + t.estimatedMinutes / Math.max(t.actualMinutes, 1), 0) / completedTasks.length : 1; return { pendingCount: pending.length, completedToday: this.data.completedToday, totalEstimatedMinutes: totalEstimate, totalFocusMinutesToday: Math.floor(this.focusSecondsToday / 60), streak: this.data.streak, pomodorosCompleted: this.data.pomodorosCompleted, avgAccuracy: Math.round(avgAccuracy * 100) }; } }; var ImmerseSettingTab = class extends import_obsidian4.PluginSettingTab { constructor(app, plugin) { super(app, plugin); this.plugin = plugin; } display() { const { containerEl } = this; containerEl.empty(); containerEl.createEl("h1", { text: "\u26A1 Immerse Settings" }); containerEl.createEl("h2", { text: "\u{1F345} Pomodoro Timer" }); new import_obsidian4.Setting(containerEl).setName("Work Duration").setDesc("Length of each work session in minutes").addSlider((slider) => slider.setLimits(5, 60, 5).setValue(this.plugin.settings.pomodoroWorkMinutes).setDynamicTooltip().onChange(async (value) => { this.plugin.settings.pomodoroWorkMinutes = value; await this.plugin.saveAllData(); })); new import_obsidian4.Setting(containerEl).setName("Short Break Duration").setDesc("Length of short breaks in minutes").addSlider((slider) => slider.setLimits(1, 15, 1).setValue(this.plugin.settings.pomodoroBreakMinutes).setDynamicTooltip().onChange(async (value) => { this.plugin.settings.pomodoroBreakMinutes = value; await this.plugin.saveAllData(); })); new import_obsidian4.Setting(containerEl).setName("Long Break Duration").setDesc("Length of long breaks in minutes").addSlider((slider) => slider.setLimits(5, 30, 5).setValue(this.plugin.settings.longBreakMinutes).setDynamicTooltip().onChange(async (value) => { this.plugin.settings.longBreakMinutes = value; await this.plugin.saveAllData(); })); new import_obsidian4.Setting(containerEl).setName("Long Break Interval").setDesc("Number of pomodoros before a long break").addSlider((slider) => slider.setLimits(2, 6, 1).setValue(this.plugin.settings.longBreakInterval).setDynamicTooltip().onChange(async (value) => { this.plugin.settings.longBreakInterval = value; await this.plugin.saveAllData(); })); new import_obsidian4.Setting(containerEl).setName("Auto-start Breaks").setDesc("Automatically start break timer after work session").addToggle((toggle) => toggle.setValue(this.plugin.settings.autoStartBreak).onChange(async (value) => { this.plugin.settings.autoStartBreak = value; await this.plugin.saveAllData(); })); containerEl.createEl("h2", { text: "\u2699\uFE0F General" }); new import_obsidian4.Setting(containerEl).setName("Default Time Estimate").setDesc("Default estimated time for new tasks in minutes").addSlider((slider) => slider.setLimits(5, 120, 5).setValue(this.plugin.settings.defaultEstimateMinutes).setDynamicTooltip().onChange(async (value) => { this.plugin.settings.defaultEstimateMinutes = value; await this.plugin.saveAllData(); })); new import_obsidian4.Setting(containerEl).setName("Enable Sounds").setDesc("Play sounds for timer completion and task completion").addToggle((toggle) => toggle.setValue(this.plugin.settings.enableSounds).onChange(async (value) => { this.plugin.settings.enableSounds = value; await this.plugin.saveAllData(); })); new import_obsidian4.Setting(containerEl).setName("Enable Celebrations").setDesc("Show celebration messages when completing tasks").addToggle((toggle) => toggle.setValue(this.plugin.settings.enableCelebrations).onChange(async (value) => { this.plugin.settings.enableCelebrations = value; await this.plugin.saveAllData(); })); containerEl.createEl("h2", { text: "\u{1F4DD} Daily Note Integration" }); new import_obsidian4.Setting(containerEl).setName("Log completed tasks to daily note").setDesc("When you complete a task, add an entry to your daily note. Uses the core Daily Notes plugin settings.").addToggle((toggle) => toggle.setValue(this.plugin.settings.logToDaily).onChange(async (value) => { this.plugin.settings.logToDaily = value; await this.plugin.saveAllData(); })); const infoEl = containerEl.createEl("div", { cls: "setting-item-description" }); infoEl.style.marginTop = "-10px"; infoEl.style.marginBottom = "20px"; infoEl.innerHTML = ` This feature uses the Daily Notes core plugin. Configure your daily note folder, date format, and template in Settings \u2192 Core plugins \u2192 Daily notes. `; containerEl.createEl("h2", { text: "\u{1F4CB} Lists" }); this.plugin.settings.lists.forEach((list, index) => { new import_obsidian4.Setting(containerEl).setName(`${list.icon} ${list.name}`).addText((text) => text.setValue(list.name).setPlaceholder("List name").onChange(async (value) => { this.plugin.settings.lists[index].name = value; await this.plugin.saveAllData(); })).addText((text) => text.setValue(list.icon).setPlaceholder("Emoji").onChange(async (value) => { this.plugin.settings.lists[index].icon = value; await this.plugin.saveAllData(); })).addColorPicker((picker) => picker.setValue(list.color).onChange(async (value) => { this.plugin.settings.lists[index].color = value; await this.plugin.saveAllData(); })).addButton((btn) => btn.setIcon("trash").setTooltip("Delete list").onClick(async () => { this.plugin.settings.lists.splice(index, 1); await this.plugin.saveAllData(); this.display(); })); }); new import_obsidian4.Setting(containerEl).addButton((btn) => btn.setButtonText("+ Add List").onClick(async () => { this.plugin.settings.lists.push({ id: this.plugin.generateId(), name: "New List", color: "#6366f1", icon: "\u{1F4C1}" }); await this.plugin.saveAllData(); this.display(); })); containerEl.createEl("h2", { text: "\u{1F4D6} About" }); const aboutDiv = containerEl.createDiv({ cls: "immerse-about" }); aboutDiv.innerHTML = `

Immerse is heavily inspired by Blitzit, a fantastic productivity app that combines task management with focused time tracking.

This plugin brings similar functionality directly into Obsidian, allowing you to manage tasks, use the Pomodoro technique, and track your productivity without leaving your notes.

Source Code

`; } };