From b9213964d01494be3a3355927f0cd5f9b87adb8f Mon Sep 17 00:00:00 2001 From: Quorafind Date: Wed, 8 Oct 2025 01:30:40 +0800 Subject: [PATCH] feat(quick-capture): enhance minimal modal with editable target and resolved paths - Add FileSuggest to checkbox mode for editable target file selection - Display resolved file paths in file mode (process date templates) - Keep quick action buttons visible in both checkbox and file modes - Update minimal modal styling for improved UX --- .../MinimalQuickCaptureModalWithSwitch.ts | 110 +++++++++++++----- src/styles/quick-capture.css | 55 +++++++-- 2 files changed, 127 insertions(+), 38 deletions(-) diff --git a/src/components/features/quick-capture/modals/MinimalQuickCaptureModalWithSwitch.ts b/src/components/features/quick-capture/modals/MinimalQuickCaptureModalWithSwitch.ts index ff056483..2d236884 100644 --- a/src/components/features/quick-capture/modals/MinimalQuickCaptureModalWithSwitch.ts +++ b/src/components/features/quick-capture/modals/MinimalQuickCaptureModalWithSwitch.ts @@ -12,6 +12,8 @@ import { TaskMetadata, } from "./BaseQuickCaptureModal"; import { moment } from "obsidian"; +import { processDateTemplates } from "@/utils/file/file-operations"; +import { FileSuggest } from "@/components/ui/inputs/AutoComplete"; /** * Minimal Quick Capture Modal extending the base class @@ -26,10 +28,12 @@ export class MinimalQuickCaptureModal extends BaseQuickCaptureModal { // Suggest instances private minimalSuggest: MinimalQuickCaptureSuggest; private universalSuggest: UniversalEditorSuggest | null = null; + private fileSuggest: FileSuggest | null = null; // UI element references private targetIndicator: HTMLElement | null = null; private fileNameInput: HTMLInputElement | null = null; + private targetFileEl: HTMLDivElement | null = null; private editorContainer: HTMLElement | null = null; constructor(app: App, plugin: TaskProgressBarPlugin) { @@ -48,6 +52,7 @@ export class MinimalQuickCaptureModal extends BaseQuickCaptureModal { onOpen() { // Store modal instance reference for suggest system (this.modalEl as any).__minimalQuickCaptureModal = this; + this.modalEl.toggleClass('tg-minimal-capture-modal', true); // Set up the suggest system if (this.minimalSuggest) { @@ -132,35 +137,81 @@ export class MinimalQuickCaptureModal extends BaseQuickCaptureModal { this.targetIndicator.empty(); if (this.currentMode === "checkbox") { - // Show target file for checkbox mode + // Show editable target file for checkbox mode const label = this.targetIndicator.createSpan({ cls: "quick-capture-target-label", text: t("To: "), }); - const target = this.targetIndicator.createSpan({ - cls: "quick-capture-target-value", + // Create contenteditable element for target file path + this.targetFileEl = this.targetIndicator.createEl("div", { + cls: "quick-capture-target", + attr: { + contenteditable: "true", + spellcheck: "false", + }, text: this.tempTargetFilePath, }); + // Add FileSuggest for file selection + this.fileSuggest = new FileSuggest( + this.app, + this.targetFileEl, + this.plugin.settings.quickCapture, + (file) => { + this.targetFileEl!.textContent = file.path; + this.tempTargetFilePath = file.path; + this.taskMetadata.targetFile = file.path; + this.markdownEditor?.editor?.focus(); + } + ); + + // Update tempTargetFilePath when manually edited + this.targetFileEl.addEventListener("blur", () => { + if (this.targetFileEl) { + this.tempTargetFilePath = this.targetFileEl.textContent || ""; + this.taskMetadata.targetFile = this.tempTargetFilePath; + } + }); + // Show quick action buttons const buttonsContainer = this.contentContainer?.querySelector(".quick-capture-minimal-quick-actions"); if (buttonsContainer) { (buttonsContainer as HTMLElement).style.display = "flex"; } } else { - // Show file name input for file mode + // Show file name input for file mode with resolved path const label = this.targetIndicator.createSpan({ cls: "quick-capture-target-label", text: t("Save as: "), }); + // Get the template value and resolve it immediately + const templateValue = this.taskMetadata.customFileName || this.plugin.settings.quickCapture.defaultFileNameTemplate || "{{DATE:YYYY-MM-DD}} - "; + let resolvedPath = processDateTemplates(templateValue); + + // Add default folder if configured + const defaultFolder = this.plugin.settings.quickCapture.createFileMode?.defaultFolder?.trim(); + if ( + this.plugin.settings?.fileSource?.enabled && + defaultFolder && + !resolvedPath.includes("/") + ) { + resolvedPath = `${defaultFolder}/${resolvedPath}`; + } + + // Add .md extension if not present + if (!resolvedPath.endsWith(".md")) { + resolvedPath += ".md"; + } + + // Create input with resolved path (editable) this.fileNameInput = this.targetIndicator.createEl("input", { cls: "quick-capture-minimal-file-input", attr: { type: "text", placeholder: t("Enter file name..."), - value: this.taskMetadata.customFileName || this.plugin.settings.quickCapture.defaultFileNameTemplate || "{{DATE:YYYY-MM-DD}} - ", + value: resolvedPath, }, }); @@ -171,10 +222,10 @@ export class MinimalQuickCaptureModal extends BaseQuickCaptureModal { } }); - // Hide quick action buttons + // Keep quick action buttons visible in file mode const buttonsContainer = this.contentContainer?.querySelector(".quick-capture-minimal-quick-actions"); if (buttonsContainer) { - (buttonsContainer as HTMLElement).style.display = "none"; + (buttonsContainer as HTMLElement).style.display = "flex"; } } } @@ -189,7 +240,7 @@ export class MinimalQuickCaptureModal extends BaseQuickCaptureModal { this.dateButton = leftContainer.createEl("button", { cls: ["quick-action-button", "clickable-icon"], - attr: { "aria-label": t("Set date") }, + attr: {"aria-label": t("Set date")}, }); setIcon(this.dateButton, "calendar"); this.dateButton.addEventListener("click", () => this.showDatePicker()); @@ -197,7 +248,7 @@ export class MinimalQuickCaptureModal extends BaseQuickCaptureModal { this.priorityButton = leftContainer.createEl("button", { cls: ["quick-action-button", "clickable-icon"], - attr: { "aria-label": t("Set priority") }, + attr: {"aria-label": t("Set priority")}, }); setIcon(this.priorityButton, "zap"); this.priorityButton.addEventListener("click", () => @@ -210,7 +261,7 @@ export class MinimalQuickCaptureModal extends BaseQuickCaptureModal { this.locationButton = leftContainer.createEl("button", { cls: ["quick-action-button", "clickable-icon"], - attr: { "aria-label": t("Set location") }, + attr: {"aria-label": t("Set location")}, }); setIcon(this.locationButton, "folder"); this.locationButton.addEventListener("click", () => @@ -219,15 +270,16 @@ export class MinimalQuickCaptureModal extends BaseQuickCaptureModal { this.updateButtonState( this.locationButton, this.taskMetadata.location !== - (this.plugin.settings.quickCapture.targetType || "fixed") + (this.plugin.settings.quickCapture.targetType || "fixed") ); this.tagButton = leftContainer.createEl("button", { cls: ["quick-action-button", "clickable-icon"], - attr: { "aria-label": t("Add tags") }, + attr: {"aria-label": t("Add tags")}, }); setIcon(this.tagButton, "tag"); - this.tagButton.addEventListener("click", () => {}); + this.tagButton.addEventListener("click", () => { + }); this.updateButtonState( this.tagButton, !!(this.taskMetadata.tags && this.taskMetadata.tags.length > 0) @@ -318,13 +370,13 @@ export class MinimalQuickCaptureModal extends BaseQuickCaptureModal { public showDatePicker(cursor?: EditorPosition, coords?: any) { const quickDates = [ - { label: t("Tomorrow"), date: moment().add(1, "day").toDate() }, + {label: t("Tomorrow"), date: moment().add(1, "day").toDate()}, { label: t("Day after tomorrow"), date: moment().add(2, "day").toDate(), }, - { label: t("Next week"), date: moment().add(1, "week").toDate() }, - { label: t("Next month"), date: moment().add(1, "month").toDate() }, + {label: t("Next week"), date: moment().add(1, "week").toDate()}, + {label: t("Next month"), date: moment().add(1, "month").toDate()}, ]; const menu = new Menu(); @@ -373,11 +425,11 @@ export class MinimalQuickCaptureModal extends BaseQuickCaptureModal { public showPriorityMenu(cursor?: EditorPosition, coords?: any) { const priorities = [ - { level: 5, label: t("Highest"), icon: "🔺" }, - { level: 4, label: t("High"), icon: "⏫" }, - { level: 3, label: t("Medium"), icon: "🔼" }, - { level: 2, label: t("Low"), icon: "🔽" }, - { level: 1, label: t("Lowest"), icon: "⏬" }, + {level: 5, label: t("Highest"), icon: "🔺"}, + {level: 4, label: t("High"), icon: "⏫"}, + {level: 3, label: t("Medium"), icon: "🔼"}, + {level: 2, label: t("Low"), icon: "🔽"}, + {level: 1, label: t("Lowest"), icon: "⏬"}, ]; const menu = new Menu(); @@ -424,8 +476,8 @@ export class MinimalQuickCaptureModal extends BaseQuickCaptureModal { this.updateButtonState( this.locationButton!, this.taskMetadata.location !== - (this.plugin.settings.quickCapture.targetType || - "fixed") + (this.plugin.settings.quickCapture.targetType || + "fixed") ); // If called from suggest, replace the 📁 with file text @@ -444,8 +496,8 @@ export class MinimalQuickCaptureModal extends BaseQuickCaptureModal { this.updateButtonState( this.locationButton!, this.taskMetadata.location !== - (this.plugin.settings.quickCapture?.targetType || - "fixed") + (this.plugin.settings.quickCapture?.targetType || + "fixed") ); // If called from suggest, replace the 📁 with daily note text @@ -497,7 +549,7 @@ export class MinimalQuickCaptureModal extends BaseQuickCaptureModal { if (cm && cm.replaceRange) { cm.replaceRange( replacement, - { line: cursor.line, ch: cursor.ch - 1 }, + {line: cursor.line, ch: cursor.ch - 1}, cursor ); } @@ -718,6 +770,12 @@ export class MinimalQuickCaptureModal extends BaseQuickCaptureModal { this.universalSuggest = null; } + // Clean up file suggest + if (this.fileSuggest) { + this.fileSuggest.close(); + this.fileSuggest = null; + } + // Clean up suggest if (this.minimalSuggest) { this.minimalSuggest.setMinimalMode(false); diff --git a/src/styles/quick-capture.css b/src/styles/quick-capture.css index 542461be..453186f1 100644 --- a/src/styles/quick-capture.css +++ b/src/styles/quick-capture.css @@ -1,6 +1,5 @@ /* Quick Capture Enhanced Styles - with shadcn theme */ -/* shadcn-inspired theme colors integrated with Task Genius */ :root { /* Tab component colors */ --tg-tab-background: 0 0% 100%; @@ -8,6 +7,9 @@ --tg-tab-muted: 240 4.8% 95.9%; --tg-tab-muted-foreground: 240 3.8% 46.1%; --tg-tab-border: 240 5.9% 90%; + + /* Quick Capture Header background (light) */ + --tg-qc-header-bg: 0 0% 100%; } .theme-dark { @@ -16,16 +18,27 @@ --tg-tab-muted: 240 3.7% 15.9%; --tg-tab-muted-foreground: 240 5% 64.9%; --tg-tab-border: 240 3.7% 15.9%; + + /* Quick Capture Header background (dark) */ + --tg-qc-header-bg: 240 4% 10%; } -/* Quick Capture Header - shadcn style */ +/* Quick Capture Header - shadcn style, with custom background for header */ .quick-capture-header { display: flex; justify-content: space-between; align-items: center; padding: 12px 16px; border-bottom: 1px solid hsl(var(--tg-tab-border)); - background: hsl(var(--tg-tab-background)); + background: hsl(var(--tg-qc-header-bg)); +} + +.tg-minimal-capture-modal .quick-capture-header { + padding: 4px 8px; +} + +.tg-minimal-capture-modal .model-header { + margin-bottom: 0; } /* Quick Capture Tabs - shadcn style */ @@ -54,6 +67,14 @@ position: relative; } +.tg-minimal-capture-modal .quick-capture-tab-text { + display: none; +} + +.tg-minimal-capture-modal .quick-capture-tab-icon { + margin-right: 0; +} + .quick-capture-tab:hover:not(.active) { color: hsl(var(--tg-tab-foreground)); } @@ -98,9 +119,9 @@ } .quick-capture-clear:hover { - background: hsl(var(--tg-tab-muted)); - color: hsl(var(--tg-tab-foreground)); - border-color: hsl(var(--tg-tab-muted-foreground)); + /*background: hsl(var(--tg-tab-muted));*/ + /*color: hsl(var(--tg-tab-foreground));*/ + /*border-color: hsl(var(--tg-tab-muted-foreground));*/ } /* Quick Capture Content */ @@ -207,15 +228,25 @@ .quick-capture-modal.quick-capture-checkbox { width: 800px; max-width: 90vw; - height: 600px; + height: 700px; max-height: 80vh; + min-height: fit-content; } .quick-capture-modal.quick-capture-file { - width: 700px; + width: 800px; max-width: 90vw; - height: 500px; - max-height: 70vh; + height: 750px; + max-height: 80vh; + min-height: fit-content; +} + +.quick-capture-modal.tg-minimal-capture-modal { + width: 600px; + max-width: 90vw; + height: 300px; + max-height: 80vh; + min-height: fit-content; } /* Quick Capture Panel */ @@ -268,8 +299,6 @@ /* Minimal Quick Capture Target Display */ .quick-capture-minimal-target-container { - padding: 8px 12px; - background: hsl(var(--tg-tab-muted)); border-radius: 6px; margin-bottom: 12px; } @@ -491,10 +520,12 @@ font-size: var(--font-ui-medium); font-weight: bold; } + .quick-capture-modal-editor { min-height: 150px; margin-bottom: 20px; } + .quick-capture-modal-buttons { display: flex; justify-content: flex-end;