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
This commit is contained in:
Quorafind 2025-10-08 01:30:40 +08:00
parent aaa4ff43eb
commit b9213964d0
2 changed files with 127 additions and 38 deletions

View file

@ -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);

View file

@ -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;