feat(quick-capture): add electron-based quick capture window

Implement a new electron-based quick capture window for rapid task entry from system tray. Features include:
- Standalone capture window with optimized UI for fast task entry
- Smart task parsing with natural language support (tomorrow, next week, etc.)
- Project/context tag autocompletion from vault
- Keyboard shortcuts for priority setting and quick actions
- System tray menu integration with keyboard accelerator
- Automatic window management and proper cleanup on close

The quick capture window provides a distraction-free interface for capturing tasks
without needing to switch context to the main Obsidian window.
This commit is contained in:
Quorafind 2025-09-03 16:41:07 +08:00
parent 4a5ad5d96a
commit ae80f14fde
3 changed files with 1345 additions and 0 deletions

View file

@ -4,6 +4,7 @@ import type { Task } from "../types/task";
import { TrayMenuBuilder } from "./tray-menu";
import { getTaskGeniusIcon } from "../icon";
import { t } from "@/translations/helper";
import { ElectronQuickCapture } from "./electron-quick-capture";
/** Desktop integration manager for system tray, notifications, and desktop features */
export class DesktopIntegrationManager extends Component {
@ -16,6 +17,7 @@ export class DesktopIntegrationManager extends Component {
private nativeThemeHandler?: () => void;
private beforeUnloadHandler?: () => void;
private trayClickHandler?: () => void;
private electronQuickCapture?: ElectronQuickCapture;
constructor(private plugin: TaskProgressBarPlugin) {
super();
@ -25,6 +27,9 @@ export class DesktopIntegrationManager extends Component {
// Initialize on load
if (!Platform.isDesktopApp) return;
// Initialize quick capture manager
this.electronQuickCapture = new ElectronQuickCapture(this.plugin);
// Minimal-change safeguard for hard reloads (window.location)
try {
this.beforeUnloadHandler = () => {
@ -151,6 +156,12 @@ export class DesktopIntegrationManager extends Component {
}
this.electronTray = null;
this.trayOwnerToken = undefined;
// Clean up quick capture
if (this.electronQuickCapture) {
this.electronQuickCapture.destroy();
this.electronQuickCapture = undefined;
}
}
// Called when settings change
@ -631,6 +642,7 @@ export class DesktopIntegrationManager extends Component {
pickCustomDate: (task: Task) =>
this.openDatePickerForTask(task),
sendDaily: () => this.sendDailySummary(),
quickCapture: () => this.openQuickCaptureWindow(),
});
} catch (e) {
console.warn("Failed to update tray:", e);
@ -705,6 +717,14 @@ export class DesktopIntegrationManager extends Component {
});
});
menu.addSeparator();
menu.addItem((i: any) => {
i.setTitle("Quick Capture...")
.setIcon("plus-circle")
.onClick(() => {
this.openQuickCaptureWindow();
});
});
menu.addSeparator();
// Show top 7 tasks within horizon
const topTasks = pending
@ -1066,6 +1086,20 @@ export class DesktopIntegrationManager extends Component {
});
}
private async openQuickCaptureWindow(): Promise<void> {
if (!this.electronQuickCapture) {
new Notice(t("Quick capture not available"));
return;
}
try {
await this.electronQuickCapture.openCaptureWindow();
} catch (error) {
console.error("Failed to open quick capture:", error);
new Notice(t("Failed to open quick capture window"));
}
}
private focusObsidianWindow(): void {
try {
const electron = this.getElectron();

File diff suppressed because it is too large Load diff

View file

@ -67,6 +67,7 @@ export class TrayMenuBuilder {
setPriority: (task: Task, level: number) => Promise<void>;
pickCustomDate: (task: Task) => Promise<void>;
sendDaily: () => Promise<void>;
quickCapture: () => Promise<void>;
}
): Promise<void> {
const electron = this.getElectron();
@ -87,6 +88,12 @@ export class TrayMenuBuilder {
click: () => actions.openTaskView(),
});
template.push({ type: "separator" });
template.push({
label: "Quick Capture...",
accelerator: "CmdOrCtrl+Shift+Q",
click: () => actions.quickCapture(),
});
template.push({ type: "separator" });
for (const t of tasks) {
const taskLabel =