refactor: consolidate property settings into unified Task Properties tab

- Move property key, default value, and NLP trigger settings into unified
  property cards in the Task Properties tab
- Remove Defaults tab entirely (body template moved to Features tab)
- Move title/filename settings from Appearance tab into Title property card
- Move project autosuggest settings from Appearance tab into Projects card
- Add collapsible sections for status/priority values, autosuggest filters,
  and display customization
- Add onCollapseChange callback to CardComponent for preserving collapse state
- Add "Feature Properties" section for pomodoro and calendar sync properties
- Improve styling for default project chips and remove button shadows
- Add right-aligned inputs in card components
This commit is contained in:
callumalpass 2025-12-02 09:29:19 +11:00
parent 3d6192da59
commit d9c4b2f196
8 changed files with 1993 additions and 1526 deletions

View file

@ -823,6 +823,145 @@ export const en: TranslationTree = {
},
},
taskProperties: {
// Section headers for property card layout
sections: {
coreProperties: "Core Properties",
corePropertiesDesc: "Status and priority are the core properties that define a task's state and importance.",
dateProperties: "Date Properties",
datePropertiesDesc: "Configure when tasks are due and scheduled.",
organizationProperties: "Organization Properties",
organizationPropertiesDesc: "Organize tasks with contexts, projects, and tags.",
taskDetails: "Task Details",
taskDetailsDesc: "Additional details like time estimates, recurrence, and reminders.",
metadataProperties: "Metadata Properties",
metadataPropertiesDesc: "System-managed properties for tracking task history.",
featureProperties: "Feature Properties",
featurePropertiesDesc: "Properties used by specific TaskNotes features like Pomodoro timer and calendar sync.",
},
// Property card common fields
propertyCard: {
propertyKey: "Property key:",
default: "Default:",
nlpTrigger: "NLP trigger:",
triggerChar: "Trigger character:",
triggerEmpty: "Trigger cannot be empty",
triggerTooLong: "Trigger is too long (max 10 characters)",
},
// Individual property names and descriptions
properties: {
status: {
name: "Status",
description: "Task status indicating completion state",
},
priority: {
name: "Priority",
description: "Task priority level",
},
due: {
name: "Due Date",
description: "When the task must be completed",
},
scheduled: {
name: "Scheduled Date",
description: "When to work on the task",
},
contexts: {
name: "Contexts",
description: "Where or how the task can be done (e.g., @home, @work)",
},
projects: {
name: "Projects",
description: "Projects this task belongs to",
},
tags: {
name: "Tags",
description: "Obsidian tags for categorization",
},
timeEstimate: {
name: "Time Estimate",
description: "Estimated time to complete the task",
},
recurrence: {
name: "Recurrence",
description: "Pattern for repeating tasks",
},
reminders: {
name: "Reminders",
description: "Notifications before task deadlines",
},
title: {
name: "Title",
description: "The task title/name",
},
dateCreated: {
name: "Date Created",
description: "When the task was created",
},
dateModified: {
name: "Date Modified",
description: "When the task was last modified",
},
completedDate: {
name: "Completed Date",
description: "When the task was completed",
},
archiveTag: {
name: "Archive Tag",
description: "Tag used to mark archived tasks",
},
timeEntries: {
name: "Time Entries",
description: "Time tracking entries for the task",
},
completeInstances: {
name: "Complete Instances",
description: "Completion history for recurring tasks",
},
blockedBy: {
name: "Blocked By",
description: "Tasks that must be completed first",
},
pomodoros: {
name: "Pomodoros",
description: "Pomodoro session count for the task",
},
icsEventId: {
name: "ICS Event ID",
description: "Calendar event identifier",
},
icsEventTag: {
name: "ICS Event Tag",
description: "Tag for calendar event linking",
},
},
// Card-specific labels
statusCard: {
valuesHeader: "Status Values",
},
priorityCard: {
valuesHeader: "Priority Values",
},
projectsCard: {
defaultProjects: "Default projects:",
useParentNote: "Use parent note as project:",
noDefaultProjects: "No default projects selected",
autosuggestFilters: "Autosuggest Filters",
customizeDisplay: "Customize Display",
filtersOn: "Filters On",
},
titleCard: {
storeTitleInFilename: "Store title in filename:",
storedInFilename: "Stored in filename",
filenameUpdatesWithTitle: "Filename will automatically update when the task title changes.",
filenameFormat: "Filename format:",
customTemplate: "Custom template:",
},
tagsCard: {
nativeObsidianTags: "Uses native Obsidian tags",
},
remindersCard: {
defaultReminders: "Default Reminders",
},
taskStatuses: {
header: "Task Statuses",
description:

View file

@ -4,7 +4,6 @@ import { debounce } from "./components/settingHelpers";
import { renderGeneralTab } from "./tabs/generalTab";
import { renderTaskPropertiesTab } from "./tabs/taskPropertiesTab";
import { renderModalFieldsTab } from "./tabs/modalFieldsTab";
import { renderDefaultsTab } from "./tabs/defaultsTab";
import { renderAppearanceTab } from "./tabs/appearanceTab";
import { renderFeaturesTab } from "./tabs/featuresTab";
import { renderIntegrationsTab } from "./tabs/integrationsTab";
@ -47,7 +46,7 @@ export class TaskNotesSettingTab extends PluginSettingTab {
// Create tab navigation
const tabNav = containerEl.createDiv("settings-tab-nav settings-view__tab-nav");
// Define the new 7-tab structure
// Define the 6-tab structure (defaults merged into task-properties)
const allTabs: TabConfig[] = [
{
id: "general",
@ -64,11 +63,6 @@ export class TaskNotesSettingTab extends PluginSettingTab {
nameKey: "settings.tabs.modalFields",
renderFn: renderModalFieldsTab,
},
{
id: "defaults",
nameKey: "settings.tabs.defaults",
renderFn: renderDefaultsTab,
},
{
id: "appearance",
nameKey: "settings.tabs.appearance",
@ -221,11 +215,6 @@ export class TaskNotesSettingTab extends PluginSettingTab {
nameKey: "settings.tabs.modalFields",
renderFn: renderModalFieldsTab,
},
{
id: "defaults",
nameKey: "settings.tabs.defaults",
renderFn: renderDefaultsTab,
},
{
id: "appearance",
nameKey: "settings.tabs.appearance",

View file

@ -189,6 +189,8 @@ export interface CardConfig {
collapsible?: boolean;
/** Whether the card should start collapsed (only applies if collapsible is true) */
defaultCollapsed?: boolean;
/** Callback when collapse state changes */
onCollapseChange?: (collapsed: boolean) => void;
/** Color indicator on the left side of the card */
colorIndicator?: {
color: string;
@ -364,10 +366,12 @@ export function createCard(container: HTMLElement, config: CardConfig): HTMLElem
// Expand
card.removeClass("tasknotes-settings__card--collapsed");
header.title = "Collapse card";
config.onCollapseChange?.(false);
} else {
// Collapse
card.addClass("tasknotes-settings__card--collapsed");
header.title = "Expand card";
config.onCollapseChange?.(true);
}
};

View file

@ -59,84 +59,7 @@ export function renderAppearanceTab(
const currentLabels = getPropertyLabels(plugin, currentProperties);
createHelpText(container, `Currently showing: ${currentLabels.join(", ")}`);
// Task Filenames Section
createSectionHeader(container, translate("settings.appearance.taskFilenames.header"));
createHelpText(container, translate("settings.appearance.taskFilenames.description"));
createToggleSetting(container, {
name: translate("settings.appearance.taskFilenames.storeTitleInFilename.name"),
desc: translate("settings.appearance.taskFilenames.storeTitleInFilename.description"),
getValue: () => plugin.settings.storeTitleInFilename,
setValue: async (value: boolean) => {
plugin.settings.storeTitleInFilename = value;
save();
// Re-render to show/hide other options
renderAppearanceTab(container, plugin, save);
},
});
if (!plugin.settings.storeTitleInFilename) {
createDropdownSetting(container, {
name: translate("settings.appearance.taskFilenames.filenameFormat.name"),
desc: translate("settings.appearance.taskFilenames.filenameFormat.description"),
options: [
{
value: "title",
label: translate(
"settings.appearance.taskFilenames.filenameFormat.options.title"
),
},
{
value: "zettel",
label: translate(
"settings.appearance.taskFilenames.filenameFormat.options.zettel"
),
},
{
value: "timestamp",
label: translate(
"settings.appearance.taskFilenames.filenameFormat.options.timestamp"
),
},
{
value: "custom",
label: translate(
"settings.appearance.taskFilenames.filenameFormat.options.custom"
),
},
],
getValue: () => plugin.settings.taskFilenameFormat,
setValue: async (value: string) => {
plugin.settings.taskFilenameFormat = value as any;
save();
// Re-render to update visibility
renderAppearanceTab(container, plugin, save);
},
ariaLabel: "Task filename generation format",
});
if (plugin.settings.taskFilenameFormat === "custom") {
createTextSetting(container, {
name: translate("settings.appearance.taskFilenames.customTemplate.name"),
desc: translate("settings.appearance.taskFilenames.customTemplate.description"),
placeholder: translate(
"settings.appearance.taskFilenames.customTemplate.placeholder"
),
getValue: () => plugin.settings.customFilenameTemplate,
setValue: async (value: string) => {
plugin.settings.customFilenameTemplate = value;
save();
},
ariaLabel: "Custom filename template with variables",
});
createHelpText(
container,
translate("settings.appearance.taskFilenames.customTemplate.helpText")
);
}
}
// Note: Task Filenames settings have been moved to the Title property card in Task Properties tab
// Display Formatting Section
createSectionHeader(container, translate("settings.appearance.displayFormatting.header"));
@ -710,250 +633,4 @@ export function renderAppearanceTab(
save();
},
});
// Project Autosuggest Section
createSectionHeader(container, translate("settings.appearance.projectAutosuggest.header"));
createHelpText(container, translate("settings.appearance.projectAutosuggest.description"));
// Tag filtering
createTextSetting(container, {
name: translate("settings.appearance.projectAutosuggest.requiredTags.name"),
desc: translate("settings.appearance.projectAutosuggest.requiredTags.description"),
placeholder: translate("settings.appearance.projectAutosuggest.requiredTags.placeholder"),
getValue: () => plugin.settings.projectAutosuggest?.requiredTags?.join(", ") ?? "",
setValue: async (value: string) => {
if (!plugin.settings.projectAutosuggest) {
plugin.settings.projectAutosuggest = {
enableFuzzy: false,
rows: [],
showAdvanced: false,
requiredTags: [],
includeFolders: [],
propertyKey: "",
propertyValue: "",
};
}
plugin.settings.projectAutosuggest.requiredTags = value
.split(",")
.map((tag) => tag.trim())
.filter((tag) => tag.length > 0);
save();
},
ariaLabel: "Required tags for project suggestions",
});
// Folder filtering
createTextSetting(container, {
name: translate("settings.appearance.projectAutosuggest.includeFolders.name"),
desc: translate("settings.appearance.projectAutosuggest.includeFolders.description"),
placeholder: translate("settings.appearance.projectAutosuggest.includeFolders.placeholder"),
getValue: () => plugin.settings.projectAutosuggest?.includeFolders?.join(", ") ?? "",
setValue: async (value: string) => {
if (!plugin.settings.projectAutosuggest) {
plugin.settings.projectAutosuggest = {
enableFuzzy: false,
rows: [],
showAdvanced: false,
requiredTags: [],
includeFolders: [],
propertyKey: "",
propertyValue: "",
};
}
plugin.settings.projectAutosuggest.includeFolders = value
.split(",")
.map((folder) => folder.trim())
.filter((folder) => folder.length > 0);
save();
},
ariaLabel: "Include folders for project suggestions",
});
// Property filtering
createTextSetting(container, {
name: translate("settings.appearance.projectAutosuggest.requiredPropertyKey.name"),
desc: translate("settings.appearance.projectAutosuggest.requiredPropertyKey.description"),
placeholder: translate(
"settings.appearance.projectAutosuggest.requiredPropertyKey.placeholder"
),
getValue: () => plugin.settings.projectAutosuggest?.propertyKey ?? "",
setValue: async (value: string) => {
if (!plugin.settings.projectAutosuggest) {
plugin.settings.projectAutosuggest = {
enableFuzzy: false,
rows: [],
showAdvanced: false,
requiredTags: [],
includeFolders: [],
propertyKey: "",
propertyValue: "",
};
}
plugin.settings.projectAutosuggest.propertyKey = value.trim();
save();
},
ariaLabel: "Required frontmatter property key for project suggestions",
debounceMs: 500, // Prevent rapid save calls while typing
});
createTextSetting(container, {
name: translate("settings.appearance.projectAutosuggest.requiredPropertyValue.name"),
desc: translate("settings.appearance.projectAutosuggest.requiredPropertyValue.description"),
placeholder: translate(
"settings.appearance.projectAutosuggest.requiredPropertyValue.placeholder"
),
getValue: () => plugin.settings.projectAutosuggest?.propertyValue ?? "",
setValue: async (value: string) => {
if (!plugin.settings.projectAutosuggest) {
plugin.settings.projectAutosuggest = {
enableFuzzy: false,
rows: [],
showAdvanced: false,
requiredTags: [],
includeFolders: [],
propertyKey: "",
propertyValue: "",
};
}
plugin.settings.projectAutosuggest.propertyValue = value.trim();
save();
},
ariaLabel: "Required frontmatter property value for project suggestions",
debounceMs: 500, // Prevent rapid save calls while typing
});
createToggleSetting(container, {
name: translate("settings.appearance.projectAutosuggest.customizeDisplay.name"),
desc: translate("settings.appearance.projectAutosuggest.customizeDisplay.description"),
getValue: () => plugin.settings.projectAutosuggest?.showAdvanced ?? false,
setValue: async (value: boolean) => {
if (!plugin.settings.projectAutosuggest) {
plugin.settings.projectAutosuggest = {
enableFuzzy: false,
rows: [],
showAdvanced: false,
requiredTags: [],
includeFolders: [],
propertyKey: "",
propertyValue: "",
};
}
plugin.settings.projectAutosuggest.showAdvanced = value;
save();
// Refresh the settings display
renderAppearanceTab(container, plugin, save);
},
});
// Only show advanced settings if enabled
if (plugin.settings.projectAutosuggest?.showAdvanced) {
createToggleSetting(container, {
name: translate("settings.appearance.projectAutosuggest.enableFuzzyMatching.name"),
desc: translate(
"settings.appearance.projectAutosuggest.enableFuzzyMatching.description"
),
getValue: () => plugin.settings.projectAutosuggest?.enableFuzzy ?? false,
setValue: async (value: boolean) => {
if (!plugin.settings.projectAutosuggest) {
plugin.settings.projectAutosuggest = {
enableFuzzy: false,
rows: [],
showAdvanced: false,
requiredTags: [],
includeFolders: [],
propertyKey: "",
propertyValue: "",
};
}
plugin.settings.projectAutosuggest.enableFuzzy = value;
save();
},
});
// Display rows configuration
createHelpText(
container,
translate("settings.appearance.projectAutosuggest.displayRowsHelp")
);
const getRows = (): string[] =>
(plugin.settings.projectAutosuggest?.rows ?? []).slice(0, 3);
const setRow = async (idx: number, value: string) => {
if (!plugin.settings.projectAutosuggest) {
plugin.settings.projectAutosuggest = {
enableFuzzy: false,
rows: [],
showAdvanced: false,
requiredTags: [],
includeFolders: [],
propertyKey: "",
propertyValue: "",
};
}
const current = plugin.settings.projectAutosuggest.rows ?? [];
const next = [...current];
next[idx] = value;
plugin.settings.projectAutosuggest.rows = next.slice(0, 3);
save();
};
createTextSetting(container, {
name: translate("settings.appearance.projectAutosuggest.displayRows.row1.name"),
desc: translate("settings.appearance.projectAutosuggest.displayRows.row1.description"),
placeholder: translate(
"settings.appearance.projectAutosuggest.displayRows.row1.placeholder"
),
getValue: () => getRows()[0] || "",
setValue: async (value: string) => setRow(0, value),
ariaLabel: "Project autosuggest display row 1",
});
createTextSetting(container, {
name: translate("settings.appearance.projectAutosuggest.displayRows.row2.name"),
desc: translate("settings.appearance.projectAutosuggest.displayRows.row2.description"),
placeholder: translate(
"settings.appearance.projectAutosuggest.displayRows.row2.placeholder"
),
getValue: () => getRows()[1] || "",
setValue: async (value: string) => setRow(1, value),
ariaLabel: "Project autosuggest display row 2",
});
createTextSetting(container, {
name: translate("settings.appearance.projectAutosuggest.displayRows.row3.name"),
desc: translate("settings.appearance.projectAutosuggest.displayRows.row3.description"),
placeholder: translate(
"settings.appearance.projectAutosuggest.displayRows.row3.placeholder"
),
getValue: () => getRows()[2] || "",
setValue: async (value: string) => setRow(2, value),
ariaLabel: "Project autosuggest display row 3",
});
// Concise help section
const helpContainer = container.createDiv("tasknotes-settings__help-section");
helpContainer.createEl("h4", {
text: translate("settings.appearance.projectAutosuggest.quickReference.header"),
});
const helpList = helpContainer.createEl("ul");
helpList.createEl("li", {
text: translate("settings.appearance.projectAutosuggest.quickReference.properties"),
});
helpList.createEl("li", {
text: translate("settings.appearance.projectAutosuggest.quickReference.labels"),
});
helpList.createEl("li", {
text: translate("settings.appearance.projectAutosuggest.quickReference.searchable"),
});
helpList.createEl("li", {
text: translate("settings.appearance.projectAutosuggest.quickReference.staticText"),
});
helpContainer.createEl("p", {
text: translate(
"settings.appearance.projectAutosuggest.quickReference.alwaysSearchable"
),
cls: "settings-help-note",
});
}
}

View file

@ -1,674 +0,0 @@
import { TAbstractFile, Setting } from "obsidian";
import TaskNotesPlugin from "../../main";
import { DefaultReminder } from "../../types/settings";
import {
createSectionHeader,
createTextSetting,
createToggleSetting,
createDropdownSetting,
createNumberSetting,
createHelpText,
} from "../components/settingHelpers";
import {
createCard,
createDeleteHeaderButton,
createCardInput,
createCardSelect,
createCardNumberInput,
showCardEmptyState,
CardRow,
} from "../components/CardComponent";
import { ProjectSelectModal } from "../../modals/ProjectSelectModal";
import { splitListPreservingLinksAndQuotes } from "../../utils/stringSplit";
import type { TranslationKey } from "../../i18n";
// interface ReminderItem extends ListEditorItem, DefaultReminder {}
/**
* Renders the Defaults & Templates tab - settings for speeding up new task creation
*/
export function renderDefaultsTab(
container: HTMLElement,
plugin: TaskNotesPlugin,
save: () => void
): void {
container.empty();
const translate = (key: TranslationKey, params?: Record<string, string | number>) =>
plugin.i18n.translate(key, params);
// Basic Defaults Section
createSectionHeader(container, translate("settings.defaults.header.basicDefaults"));
createHelpText(container, translate("settings.defaults.description.basicDefaults"));
createDropdownSetting(container, {
name: translate("settings.defaults.basicDefaults.defaultStatus.name"),
desc: translate("settings.defaults.basicDefaults.defaultStatus.description"),
options: plugin.settings.customStatuses.map((status) => ({
value: status.value,
label: status.label || status.value,
})),
getValue: () => plugin.settings.defaultTaskStatus,
setValue: async (value: string) => {
plugin.settings.defaultTaskStatus = value;
save();
},
});
createDropdownSetting(container, {
name: translate("settings.defaults.basicDefaults.defaultPriority.name"),
desc: translate("settings.defaults.basicDefaults.defaultPriority.description"),
options: [
{ value: "", label: translate("settings.defaults.options.noDefault") },
...plugin.settings.customPriorities.map((priority) => ({
value: priority.value,
label: priority.label || priority.value,
})),
],
getValue: () => plugin.settings.defaultTaskPriority,
setValue: async (value: string) => {
plugin.settings.defaultTaskPriority = value;
save();
},
});
createTextSetting(container, {
name: translate("settings.defaults.basicDefaults.defaultContexts.name"),
desc: translate("settings.defaults.basicDefaults.defaultContexts.description"),
placeholder: translate("settings.defaults.basicDefaults.defaultContexts.placeholder"),
getValue: () => plugin.settings.taskCreationDefaults.defaultContexts,
setValue: async (value: string) => {
plugin.settings.taskCreationDefaults.defaultContexts = value;
save();
},
});
createTextSetting(container, {
name: translate("settings.defaults.basicDefaults.defaultTags.name"),
desc: translate("settings.defaults.basicDefaults.defaultTags.description"),
placeholder: translate("settings.defaults.basicDefaults.defaultTags.placeholder"),
getValue: () => plugin.settings.taskCreationDefaults.defaultTags,
setValue: async (value: string) => {
plugin.settings.taskCreationDefaults.defaultTags = value;
save();
},
});
// Default projects with file picker
const selectedDefaultProjectFiles: TAbstractFile[] = [];
const defaultProjectsContainer = container.createDiv("default-projects-container");
const defaultProjectsSettingDiv = defaultProjectsContainer.createDiv();
new Setting(defaultProjectsSettingDiv)
.setName(translate("settings.defaults.basicDefaults.defaultProjects.name"))
.setDesc(translate("settings.defaults.basicDefaults.defaultProjects.description"))
.addButton((button) => {
button
.setButtonText(
translate("settings.defaults.basicDefaults.defaultProjects.selectButton")
)
.setTooltip(
translate("settings.defaults.basicDefaults.defaultProjects.selectTooltip")
)
.onClick(() => {
const modal = new ProjectSelectModal(
plugin.app,
plugin,
(file: TAbstractFile) => {
// Add the selected file if not already in the list
if (!selectedDefaultProjectFiles.includes(file)) {
selectedDefaultProjectFiles.push(file);
const projectLinks = selectedDefaultProjectFiles
.map((f) => `[[${f.path.replace(/\.md$/, "")}]]`)
.join(", ");
plugin.settings.taskCreationDefaults.defaultProjects = projectLinks;
save();
renderDefaultProjectsList(
defaultProjectsContainer,
plugin,
save,
selectedDefaultProjectFiles,
translate
);
}
}
);
modal.open();
});
button.buttonEl.addClass("tn-btn");
button.buttonEl.addClass("tn-btn--ghost");
});
// Initialize selected projects from settings
if (plugin.settings.taskCreationDefaults.defaultProjects) {
const projectPaths = splitListPreservingLinksAndQuotes(
plugin.settings.taskCreationDefaults.defaultProjects
)
.map((link) => link.replace(/\[\[|\]\]/g, "").trim())
.filter((path) => path);
projectPaths.forEach((path) => {
const file =
plugin.app.vault.getAbstractFileByPath(path + ".md") ||
plugin.app.vault.getAbstractFileByPath(path);
if (file) {
selectedDefaultProjectFiles.push(file);
}
});
}
renderDefaultProjectsList(
defaultProjectsContainer,
plugin,
save,
selectedDefaultProjectFiles,
translate
);
createToggleSetting(container, {
name: translate("settings.defaults.basicDefaults.useParentNoteAsProject.name"),
desc: translate("settings.defaults.basicDefaults.useParentNoteAsProject.description"),
getValue: () => plugin.settings.taskCreationDefaults.useParentNoteAsProject,
setValue: async (value: boolean) => {
plugin.settings.taskCreationDefaults.useParentNoteAsProject = value;
save();
},
});
createNumberSetting(container, {
name: translate("settings.defaults.basicDefaults.defaultTimeEstimate.name"),
desc: translate("settings.defaults.basicDefaults.defaultTimeEstimate.description"),
placeholder: translate("settings.defaults.basicDefaults.defaultTimeEstimate.placeholder"),
min: 0,
getValue: () => plugin.settings.taskCreationDefaults.defaultTimeEstimate,
setValue: async (value: number) => {
plugin.settings.taskCreationDefaults.defaultTimeEstimate = value;
save();
},
});
createDropdownSetting(container, {
name: translate("settings.defaults.basicDefaults.defaultRecurrence.name"),
desc: translate("settings.defaults.basicDefaults.defaultRecurrence.description"),
options: [
{ value: "none", label: translate("settings.defaults.options.none") },
{ value: "daily", label: translate("settings.defaults.options.daily") },
{ value: "weekly", label: translate("settings.defaults.options.weekly") },
{ value: "monthly", label: translate("settings.defaults.options.monthly") },
{ value: "yearly", label: translate("settings.defaults.options.yearly") },
],
getValue: () => plugin.settings.taskCreationDefaults.defaultRecurrence,
setValue: async (value: string) => {
plugin.settings.taskCreationDefaults.defaultRecurrence = value as any;
save();
},
});
// Date Defaults Section
createSectionHeader(container, translate("settings.defaults.header.dateDefaults"));
createHelpText(container, translate("settings.defaults.description.dateDefaults"));
createDropdownSetting(container, {
name: translate("settings.defaults.dateDefaults.defaultDueDate.name"),
desc: translate("settings.defaults.dateDefaults.defaultDueDate.description"),
options: [
{ value: "none", label: translate("settings.defaults.options.none") },
{ value: "today", label: translate("settings.defaults.options.today") },
{ value: "tomorrow", label: translate("settings.defaults.options.tomorrow") },
{ value: "next-week", label: translate("settings.defaults.options.nextWeek") },
],
getValue: () => plugin.settings.taskCreationDefaults.defaultDueDate,
setValue: async (value: string) => {
plugin.settings.taskCreationDefaults.defaultDueDate = value as any;
save();
},
});
createDropdownSetting(container, {
name: translate("settings.defaults.dateDefaults.defaultScheduledDate.name"),
desc: translate("settings.defaults.dateDefaults.defaultScheduledDate.description"),
options: [
{ value: "none", label: translate("settings.defaults.options.none") },
{ value: "today", label: translate("settings.defaults.options.today") },
{ value: "tomorrow", label: translate("settings.defaults.options.tomorrow") },
{ value: "next-week", label: translate("settings.defaults.options.nextWeek") },
],
getValue: () => plugin.settings.taskCreationDefaults.defaultScheduledDate,
setValue: async (value: string) => {
plugin.settings.taskCreationDefaults.defaultScheduledDate = value as any;
save();
},
});
// Reminder Defaults Section
createSectionHeader(container, translate("settings.defaults.header.defaultReminders"));
createHelpText(container, translate("settings.defaults.description.defaultReminders"));
// Reminder list - using card layout
const remindersContainer = container.createDiv("tasknotes-reminders-container");
renderRemindersList(remindersContainer, plugin, save, translate);
// Add reminder button
new Setting(container)
.setName(translate("settings.defaults.reminders.addReminder.name"))
.setDesc(translate("settings.defaults.reminders.addReminder.description"))
.addButton((button) =>
button
.setButtonText(translate("settings.defaults.reminders.addReminder.buttonText"))
.onClick(async () => {
const newId = `reminder_${Date.now()}`;
const newReminder = {
id: newId,
type: "relative" as const,
relatedTo: "due" as const,
offset: 1,
unit: "hours" as const,
direction: "before" as const,
description: "Reminder",
};
plugin.settings.taskCreationDefaults.defaultReminders =
plugin.settings.taskCreationDefaults.defaultReminders || [];
plugin.settings.taskCreationDefaults.defaultReminders.push(newReminder);
save();
renderRemindersList(remindersContainer, plugin, save, translate);
})
);
// Template Settings Section
createSectionHeader(container, translate("settings.defaults.header.bodyTemplate"));
createHelpText(container, translate("settings.defaults.description.bodyTemplate"));
createToggleSetting(container, {
name: translate("settings.defaults.bodyTemplate.useBodyTemplate.name"),
desc: translate("settings.defaults.bodyTemplate.useBodyTemplate.description"),
getValue: () => plugin.settings.taskCreationDefaults.useBodyTemplate,
setValue: async (value: boolean) => {
plugin.settings.taskCreationDefaults.useBodyTemplate = value;
save();
// Re-render to show/hide template path
renderDefaultsTab(container, plugin, save);
},
});
if (plugin.settings.taskCreationDefaults.useBodyTemplate) {
createTextSetting(container, {
name: translate("settings.defaults.bodyTemplate.bodyTemplateFile.name"),
desc: translate("settings.defaults.bodyTemplate.bodyTemplateFile.description"),
placeholder: translate("settings.defaults.bodyTemplate.bodyTemplateFile.placeholder"),
getValue: () => plugin.settings.taskCreationDefaults.bodyTemplate,
setValue: async (value: string) => {
plugin.settings.taskCreationDefaults.bodyTemplate = value;
save();
},
ariaLabel: translate("settings.defaults.bodyTemplate.bodyTemplateFile.ariaLabel"),
});
}
// Template Variables Help
if (plugin.settings.taskCreationDefaults.useBodyTemplate) {
const helpContainer = container.createDiv("tasknotes-settings__help-section");
helpContainer.createEl("h4", {
text: translate("settings.defaults.bodyTemplate.variablesHeader"),
});
const helpList = helpContainer.createEl("ul");
helpList.createEl("li", {
text: translate("settings.defaults.bodyTemplate.variables.title"),
});
helpList.createEl("li", {
text: translate("settings.defaults.bodyTemplate.variables.details"),
});
helpList.createEl("li", {
text: translate("settings.defaults.bodyTemplate.variables.date"),
});
helpList.createEl("li", {
text: translate("settings.defaults.bodyTemplate.variables.time"),
});
helpList.createEl("li", {
text: translate("settings.defaults.bodyTemplate.variables.priority"),
});
helpList.createEl("li", {
text: translate("settings.defaults.bodyTemplate.variables.status"),
});
helpList.createEl("li", {
text: translate("settings.defaults.bodyTemplate.variables.contexts"),
});
helpList.createEl("li", {
text: translate("settings.defaults.bodyTemplate.variables.tags"),
});
helpList.createEl("li", {
text: translate("settings.defaults.bodyTemplate.variables.projects"),
});
}
// Instant Conversion Section
createSectionHeader(container, translate("settings.defaults.header.instantTaskConversion"));
createHelpText(container, translate("settings.defaults.description.instantTaskConversion"));
createToggleSetting(container, {
name: translate("settings.defaults.instantConversion.useDefaultsOnInstantConvert.name"),
desc: translate(
"settings.defaults.instantConversion.useDefaultsOnInstantConvert.description"
),
getValue: () => plugin.settings.useDefaultsOnInstantConvert,
setValue: async (value: boolean) => {
plugin.settings.useDefaultsOnInstantConvert = value;
save();
},
});
}
function renderDefaultProjectsList(
container: HTMLElement,
plugin: TaskNotesPlugin,
save: () => void,
selectedFiles: TAbstractFile[],
translate: (key: TranslationKey, params?: Record<string, string | number>) => string
): void {
// Remove existing projects list
const existingList = container.querySelector(".default-projects-list");
if (existingList) {
existingList.remove();
}
if (selectedFiles.length === 0) return;
const projectsList = container.createDiv("default-projects-list");
selectedFiles.forEach((file) => {
createCard(projectsList, {
id: file.path,
collapsible: true,
defaultCollapsed: true,
header: {
primaryText: file.name.replace(/\.md$/, ""),
actions: [
createDeleteHeaderButton(
() => {
const index = selectedFiles.indexOf(file);
if (index > -1) {
selectedFiles.splice(index, 1);
const projectLinks = selectedFiles
.map((f) => `[[${f.path.replace(/\.md$/, "")}]]`)
.join(", ");
plugin.settings.taskCreationDefaults.defaultProjects = projectLinks;
save();
renderDefaultProjectsList(
container,
plugin,
save,
selectedFiles,
translate
);
}
},
translate("settings.defaults.basicDefaults.defaultProjects.removeTooltip", {
name: file.name,
})
),
],
},
});
});
}
function renderRelativeReminderConfig(
reminder: DefaultReminder,
updateItem: (updates: Partial<DefaultReminder>) => void,
translate: (key: TranslationKey, params?: Record<string, string | number>) => string
): CardRow[] {
const offsetInput = createCardNumberInput(0, undefined, 1, reminder.offset);
offsetInput.addEventListener("input", () => {
const offset = parseInt(offsetInput.value);
if (!isNaN(offset) && offset >= 0) {
updateItem({ offset });
}
});
const unitSelect = createCardSelect(
[
{ value: "minutes", label: translate("settings.defaults.reminders.units.minutes") },
{ value: "hours", label: translate("settings.defaults.reminders.units.hours") },
{ value: "days", label: translate("settings.defaults.reminders.units.days") },
],
reminder.unit
);
unitSelect.addEventListener("change", () => {
updateItem({ unit: unitSelect.value as any });
});
const directionSelect = createCardSelect(
[
{ value: "before", label: translate("settings.defaults.reminders.directions.before") },
{ value: "after", label: translate("settings.defaults.reminders.directions.after") },
],
reminder.direction
);
directionSelect.addEventListener("change", () => {
updateItem({ direction: directionSelect.value as any });
});
const relatedToSelect = createCardSelect(
[
{ value: "due", label: translate("settings.defaults.reminders.relatedTo.due") },
{
value: "scheduled",
label: translate("settings.defaults.reminders.relatedTo.scheduled"),
},
],
reminder.relatedTo
);
relatedToSelect.addEventListener("change", () => {
updateItem({ relatedTo: relatedToSelect.value as any });
});
return [
{ label: translate("settings.defaults.reminders.fields.offset"), input: offsetInput },
{ label: translate("settings.defaults.reminders.fields.unit"), input: unitSelect },
{
label: translate("settings.defaults.reminders.fields.direction"),
input: directionSelect,
},
{
label: translate("settings.defaults.reminders.fields.relatedTo"),
input: relatedToSelect,
},
];
}
function renderAbsoluteReminderConfig(
reminder: DefaultReminder,
updateItem: (updates: Partial<DefaultReminder>) => void,
translate: (key: TranslationKey, params?: Record<string, string | number>) => string
): CardRow[] {
const dateInput = createCardInput(
"date",
reminder.absoluteDate || new Date().toISOString().split("T")[0]
);
dateInput.addEventListener("input", () => {
updateItem({ absoluteDate: dateInput.value });
});
const timeInput = createCardInput("time", reminder.absoluteTime || "09:00");
timeInput.addEventListener("input", () => {
updateItem({ absoluteTime: timeInput.value });
});
return [
{ label: translate("settings.defaults.reminders.fields.date"), input: dateInput },
{ label: translate("settings.defaults.reminders.fields.time"), input: timeInput },
];
}
function renderRemindersList(
container: HTMLElement,
plugin: TaskNotesPlugin,
save: () => void,
translate: (key: TranslationKey, params?: Record<string, string | number>) => string
): void {
container.empty();
if (
!plugin.settings.taskCreationDefaults.defaultReminders ||
plugin.settings.taskCreationDefaults.defaultReminders.length === 0
) {
showCardEmptyState(
container,
translate("settings.defaults.reminders.emptyState"),
translate("settings.defaults.reminders.emptyStateButton"),
() => {
// Trigger the add reminder button
const addReminderButton = document.querySelector(
'[data-setting-name="Add default reminder"] button'
);
if (addReminderButton) {
(addReminderButton as HTMLElement).click();
}
}
);
return;
}
plugin.settings.taskCreationDefaults.defaultReminders.forEach((reminder, index) => {
const timingText = formatReminderTiming(reminder, translate);
const descInput = createCardInput(
"text",
translate("settings.defaults.reminders.reminderDescription"),
reminder.description
);
const typeSelect = createCardSelect(
[
{
value: "relative",
label: translate("settings.defaults.reminders.types.relative"),
},
{
value: "absolute",
label: translate("settings.defaults.reminders.types.absolute"),
},
],
reminder.type
);
const updateCallback = (updates: Partial<DefaultReminder>) => {
Object.assign(reminder, updates);
save();
const card = container.querySelector(`[data-card-id="${reminder.id}"]`);
if (card) {
const secondaryText = card.querySelector(
".tasknotes-settings__card-secondary-text"
);
if (secondaryText) {
secondaryText.textContent = formatReminderTiming(reminder, translate);
}
}
};
const configRows =
reminder.type === "relative"
? renderRelativeReminderConfig(reminder, updateCallback, translate)
: renderAbsoluteReminderConfig(reminder, updateCallback, translate);
const card = createCard(container, {
id: reminder.id,
collapsible: true,
defaultCollapsed: true,
header: {
primaryText:
reminder.description ||
translate("settings.defaults.reminders.unnamedReminder"),
secondaryText: timingText,
actions: [
createDeleteHeaderButton(() => {
plugin.settings.taskCreationDefaults.defaultReminders.splice(index, 1);
save();
renderRemindersList(container, plugin, save, translate);
}, translate("settings.defaults.reminders.deleteTooltip")),
],
},
content: {
sections: [
{
rows: [
{
label: translate("settings.defaults.reminders.fields.description"),
input: descInput,
},
{
label: translate("settings.defaults.reminders.fields.type"),
input: typeSelect,
},
],
},
{
rows: configRows,
},
],
},
});
descInput.addEventListener("input", () => {
reminder.description = descInput.value;
save();
const primaryText = card.querySelector(".tasknotes-settings__card-primary-text");
if (primaryText) {
primaryText.textContent =
reminder.description ||
translate("settings.defaults.reminders.unnamedReminder");
}
});
typeSelect.addEventListener("change", () => {
reminder.type = typeSelect.value as any;
save();
// Re-render only the config rows
const newConfigRows =
reminder.type === "relative"
? renderRelativeReminderConfig(reminder, updateCallback, translate)
: renderAbsoluteReminderConfig(reminder, updateCallback, translate);
const content = card.querySelector(".tasknotes-settings__card-content");
if (content && content.children[1]) {
const newSection = document.createElement("div");
newConfigRows.forEach((row) => {
const configRow = newSection.createDiv("tasknotes-settings__card-config-row");
if (row.fullWidth) {
configRow.style.flexDirection = "column";
configRow.style.alignItems = "flex-start";
configRow.style.gap = "0.5rem";
}
const label = configRow.createSpan("tasknotes-settings__card-config-label");
label.textContent = row.label;
configRow.appendChild(row.input);
});
content.replaceChild(newSection, content.children[1]);
}
});
});
}
function formatReminderTiming(
reminder: DefaultReminder,
translate: (key: TranslationKey, params?: Record<string, string | number>) => string
): string {
if (reminder.type === "relative") {
const direction =
reminder.direction === "before"
? translate("settings.defaults.reminders.directions.before")
: translate("settings.defaults.reminders.directions.after");
const unit = translate(
`settings.defaults.reminders.units.${reminder.unit || "hours"}` as TranslationKey
);
const offset = reminder.offset ?? 1;
const relatedTo =
reminder.relatedTo === "due"
? translate("settings.defaults.reminders.relatedTo.due")
: translate("settings.defaults.reminders.relatedTo.scheduled");
return `${offset} ${unit} ${direction} ${relatedTo}`;
} else {
const date = reminder.absoluteDate || translate("settings.defaults.reminders.fields.date");
const time = reminder.absoluteTime || translate("settings.defaults.reminders.fields.time");
return `${date} at ${time}`;
}
}

View file

@ -7,7 +7,6 @@ import {
createDropdownSetting,
createNumberSetting,
createHelpText,
// createButtonSetting
} from "../components/settingHelpers";
import { showStorageLocationConfirmationModal } from "../../modals/StorageLocationConfirmationModal";
import { getAvailableLanguages } from "../../locales";
@ -146,204 +145,85 @@ export function renderFeaturesTab(
},
});
// Deprecated: Old status trigger setting (kept for backward compatibility during migration)
// New trigger configuration UI below replaces this
// NLP Triggers Configuration
createSectionHeader(container, "NLP Triggers");
createHelpText(
container,
"Configure trigger characters or strings for each property type. When you type a trigger followed by text, autocomplete suggestions will appear."
);
// Helper function to render a trigger setting
const renderTriggerSetting = (
propertyId: string,
displayName: string,
description: string,
defaultTrigger: string
) => {
const triggerConfig = plugin.settings.nlpTriggers.triggers.find(
(t) => t.propertyId === propertyId
);
const currentTrigger = triggerConfig?.trigger || defaultTrigger;
const isEnabled = triggerConfig?.enabled ?? true;
const setting = new Setting(container)
.setName(displayName)
.setDesc(description)
.addToggle((toggle) => {
toggle.setValue(isEnabled).onChange(async (enabled) => {
// Update or create trigger config
const index = plugin.settings.nlpTriggers.triggers.findIndex(
(t) => t.propertyId === propertyId
);
if (index !== -1) {
plugin.settings.nlpTriggers.triggers[index].enabled = enabled;
} else {
plugin.settings.nlpTriggers.triggers.push({
propertyId,
trigger: defaultTrigger,
enabled,
});
}
save();
// Re-render to show/hide trigger input
renderFeaturesTab(container, plugin, save);
});
});
if (isEnabled) {
setting.addText((text) => {
text.setValue(currentTrigger)
.setPlaceholder(defaultTrigger)
.onChange(async (value) => {
// Validate trigger (allow trailing/leading spaces for triggers like "def: ")
if (value.trim().length === 0) {
new Notice("Trigger cannot be empty");
return;
}
if (value.length > 10) {
new Notice("Trigger is too long (max 10 characters)");
return;
}
// Update trigger (use raw value, don't trim)
const index = plugin.settings.nlpTriggers.triggers.findIndex(
(t) => t.propertyId === propertyId
);
if (index !== -1) {
plugin.settings.nlpTriggers.triggers[index].trigger = value;
} else {
plugin.settings.nlpTriggers.triggers.push({
propertyId,
trigger: value,
enabled: true,
});
}
save();
});
text.inputEl.style.width = "100px";
});
// Add special note for tags trigger
if (propertyId === "tags" && currentTrigger !== "#") {
setting.descEl.createDiv({
text: "⚠️ Using custom tag trigger - Obsidian's native tag suggester will be disabled.",
cls: "setting-item-description",
});
}
}
};
// Built-in triggers
renderTriggerSetting(
"tags",
"Tags Trigger",
"Trigger for #tags. When set to '#', uses Obsidian's native tag suggester.",
"#"
);
renderTriggerSetting(
"contexts",
"Contexts Trigger",
"Trigger for @contexts. Type this character followed by a context name.",
"@"
);
renderTriggerSetting(
"projects",
"Projects Trigger",
"Trigger for +projects. Supports wikilinks for multi-word projects.",
"+"
);
renderTriggerSetting(
"status",
"Status Trigger",
"Trigger for status suggestions. Type this to see available statuses.",
"*"
);
renderTriggerSetting(
"priority",
"Priority Trigger",
"Trigger for priority suggestions. Disabled by default (priority uses keyword matching).",
"!"
);
// User-defined field triggers
if (plugin.settings.userFields && plugin.settings.userFields.length > 0) {
createHelpText(
container,
"User-Defined Fields: Configure triggers for your custom fields below."
);
for (const userField of plugin.settings.userFields) {
const triggerConfig = plugin.settings.nlpTriggers.triggers.find(
(t) => t.propertyId === userField.id
);
const isEnabled = triggerConfig?.enabled ?? false;
const currentTrigger = triggerConfig?.trigger || `${userField.id}:`;
const setting = new Setting(container)
.setName(`${userField.displayName} Trigger`)
.setDesc(`Trigger for custom field "${userField.displayName}" (${userField.type})`)
.addToggle((toggle) => {
toggle.setValue(isEnabled).onChange(async (enabled) => {
const index = plugin.settings.nlpTriggers.triggers.findIndex(
(t) => t.propertyId === userField.id
);
if (index !== -1) {
plugin.settings.nlpTriggers.triggers[index].enabled = enabled;
} else {
plugin.settings.nlpTriggers.triggers.push({
propertyId: userField.id,
trigger: currentTrigger,
enabled,
});
}
save();
renderFeaturesTab(container, plugin, save);
});
});
if (isEnabled) {
setting.addText((text) => {
text.setValue(currentTrigger)
.setPlaceholder(`${userField.id}:`)
.onChange(async (value) => {
// Validate trigger (allow trailing/leading spaces)
if (value.trim().length === 0) {
new Notice("Trigger cannot be empty");
return;
}
if (value.length > 10) {
new Notice("Trigger is too long (max 10 characters)");
return;
}
const index = plugin.settings.nlpTriggers.triggers.findIndex(
(t) => t.propertyId === userField.id
);
if (index !== -1) {
plugin.settings.nlpTriggers.triggers[index].trigger = value;
} else {
plugin.settings.nlpTriggers.triggers.push({
propertyId: userField.id,
trigger: value,
enabled: true,
});
}
save();
});
text.inputEl.style.width = "100px";
});
}
}
}
// Note: NLP triggers are now configured per-property in the Task Properties tab
}
// Task Creation Section (Body Templates)
createSectionHeader(container, translate("settings.defaults.header.bodyTemplate"));
createHelpText(container, translate("settings.defaults.description.bodyTemplate"));
createToggleSetting(container, {
name: translate("settings.defaults.bodyTemplate.useBodyTemplate.name"),
desc: translate("settings.defaults.bodyTemplate.useBodyTemplate.description"),
getValue: () => plugin.settings.taskCreationDefaults.useBodyTemplate,
setValue: async (value: boolean) => {
plugin.settings.taskCreationDefaults.useBodyTemplate = value;
save();
// Re-render to show/hide template path
renderFeaturesTab(container, plugin, save);
},
});
if (plugin.settings.taskCreationDefaults.useBodyTemplate) {
createTextSetting(container, {
name: translate("settings.defaults.bodyTemplate.bodyTemplateFile.name"),
desc: translate("settings.defaults.bodyTemplate.bodyTemplateFile.description"),
placeholder: translate("settings.defaults.bodyTemplate.bodyTemplateFile.placeholder"),
getValue: () => plugin.settings.taskCreationDefaults.bodyTemplate,
setValue: async (value: string) => {
plugin.settings.taskCreationDefaults.bodyTemplate = value;
save();
},
});
// Template Variables Help
const helpContainer = container.createDiv("tasknotes-settings__help-section");
helpContainer.createEl("h4", {
text: translate("settings.defaults.bodyTemplate.variablesHeader"),
});
const helpList = helpContainer.createEl("ul");
helpList.createEl("li", {
text: translate("settings.defaults.bodyTemplate.variables.title"),
});
helpList.createEl("li", {
text: translate("settings.defaults.bodyTemplate.variables.details"),
});
helpList.createEl("li", {
text: translate("settings.defaults.bodyTemplate.variables.date"),
});
helpList.createEl("li", {
text: translate("settings.defaults.bodyTemplate.variables.time"),
});
helpList.createEl("li", {
text: translate("settings.defaults.bodyTemplate.variables.priority"),
});
helpList.createEl("li", {
text: translate("settings.defaults.bodyTemplate.variables.status"),
});
helpList.createEl("li", {
text: translate("settings.defaults.bodyTemplate.variables.contexts"),
});
helpList.createEl("li", {
text: translate("settings.defaults.bodyTemplate.variables.tags"),
});
helpList.createEl("li", {
text: translate("settings.defaults.bodyTemplate.variables.projects"),
});
}
// Instant Conversion Defaults
createToggleSetting(container, {
name: translate("settings.defaults.instantConversion.useDefaultsOnInstantConvert.name"),
desc: translate(
"settings.defaults.instantConversion.useDefaultsOnInstantConvert.description"
),
getValue: () => plugin.settings.useDefaultsOnInstantConvert,
setValue: async (value: boolean) => {
plugin.settings.useDefaultsOnInstantConvert = value;
save();
},
});
// Pomodoro Timer Section
createSectionHeader(container, translate("settings.features.pomodoro.header"));
createHelpText(container, translate("settings.features.pomodoro.description"));

File diff suppressed because it is too large Load diff

View file

@ -69,7 +69,7 @@
border: 1px solid var(--background-modifier-border);
border-radius: 6px;
margin-bottom: 12px;
overflow: hidden;
overflow: visible;
}
.tasknotes-settings__card:hover {
@ -140,6 +140,7 @@
.tasknotes-settings__card-config-row {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 12px;
}
@ -178,7 +179,8 @@ select.tasknotes-settings__card-input {
.tasknotes-settings__card-input[type="text"],
.tasknotes-settings__card-input[type="url"],
.tasknotes-settings__card-input[type="email"] {
flex: 1;
flex: 0 1 auto;
min-width: 150px;
}
.tasknotes-settings__card-input[type="checkbox"] {
@ -262,7 +264,7 @@ select.tasknotes-settings__card-input {
.tasknotes-settings__card-header-btn {
width: 20px;
height: 20px;
border: none;
border: none !important;
background: transparent;
color: var(--text-muted);
cursor: var(--cursor);
@ -270,6 +272,15 @@ select.tasknotes-settings__card-input {
display: flex;
align-items: center;
justify-content: center;
box-shadow: none !important;
outline: none !important;
padding: 0;
}
.tasknotes-settings__card-header-btn:focus {
box-shadow: none !important;
outline: none !important;
border: none !important;
}
.tasknotes-settings__card-header-btn:hover {
@ -312,6 +323,65 @@ select.tasknotes-settings__card-input {
white-space: nowrap;
}
/* Selected Project Items (chips) */
.default-projects-list-container {
display: flex;
flex-wrap: wrap;
gap: 6px;
margin-top: 8px;
}
.tasknotes-settings__project-item {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 4px 8px 4px 12px;
background: var(--background-secondary);
border: 1px solid var(--background-modifier-border);
border-radius: 16px;
font-size: 12px;
color: var(--text-normal);
transition: all 0.15s ease;
}
.tasknotes-settings__project-item:hover {
background: var(--background-modifier-hover);
border-color: var(--background-modifier-border-hover);
}
.tasknotes-settings__project-item span {
max-width: 200px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.tasknotes-settings__project-item button {
display: flex;
align-items: center;
justify-content: center;
width: 18px;
height: 18px;
padding: 0;
border: none;
border-radius: 50%;
background: transparent;
color: var(--text-muted);
cursor: pointer;
transition: all 0.15s ease;
flex-shrink: 0;
}
.tasknotes-settings__project-item button:hover {
background: var(--background-modifier-error);
color: var(--text-on-accent);
}
.tasknotes-settings__project-item button svg {
width: 12px;
height: 12px;
}
/* Drag Handle */
.tasknotes-settings__card-drag-handle {
position: absolute;
@ -935,10 +1005,9 @@ select.tasknotes-settings__card-input {
}
.tasknotes-settings__collapsible-section-content {
max-height: 500px;
max-height: none;
opacity: 1;
overflow: visible;
transition: max-height 0.3s ease, opacity 0.2s ease, padding 0.2s ease;
}
/* Collapsed state */