style: format code with prettier

Applied consistent code formatting across multiple components including
trailing commas, quote style normalization, and proper indentation.
No functional changes.

Files updated:
- FluentComponentManager: Consistent trailing commas
- KanbanComponent: Code formatting improvements
- MarkdownRenderer: Quote and comma consistency
- StatusCycler: Formatting cleanup
- FluentTaskView: Trailing comma fixes
- Calendar/Task components: Minor formatting
- CSS files: Formatting improvements
This commit is contained in:
Quorafind 2025-10-29 15:54:22 +08:00
parent 77f58747fd
commit bc3a1d31d4
10 changed files with 396 additions and 382 deletions

View file

@ -93,7 +93,7 @@ export class FluentComponentManager extends Component {
onTaskContextMenu: (event: MouseEvent, task: Task) => void;
onKanbanTaskStatusUpdate: (
taskId: string,
newStatusMark: string
newStatusMark: string,
) => void;
};
@ -107,15 +107,15 @@ export class FluentComponentManager extends Component {
onTaskCompleted: (task: Task) => void;
onTaskUpdate: (
originalTask: Task,
updatedTask: Task
updatedTask: Task,
) => Promise<void>;
onTaskContextMenu: (event: MouseEvent, task: Task) => void;
onKanbanTaskStatusUpdate: (
taskId: string,
newStatusMark: string
newStatusMark: string,
) => void;
},
private selectionManager?: TaskSelectionManager
private selectionManager?: TaskSelectionManager,
) {
super();
this.viewHandlers = viewHandlers;
@ -145,7 +145,7 @@ export class FluentComponentManager extends Component {
this.app,
this.plugin,
this.contentArea,
viewHandlers
viewHandlers,
);
this.parentView.addChild(this.viewComponentManager);
@ -166,7 +166,7 @@ export class FluentComponentManager extends Component {
if (task) this.viewHandlers.onTaskContextMenu(event, task);
},
selectionManager: this.selectionManager,
}
},
);
this.parentView.addChild(this.contentComponent);
this.contentComponent.load();
@ -187,14 +187,14 @@ export class FluentComponentManager extends Component {
if (originalTask && updatedTask) {
await this.viewHandlers.onTaskUpdate(
originalTask,
updatedTask
updatedTask,
);
}
},
onTaskContextMenu: (event, task) => {
if (task) this.viewHandlers.onTaskContextMenu(event, task);
},
}
},
);
this.parentView.addChild(this.forecastComponent);
this.forecastComponent.load();
@ -215,14 +215,14 @@ export class FluentComponentManager extends Component {
if (originalTask && updatedTask) {
await this.viewHandlers.onTaskUpdate(
originalTask,
updatedTask
updatedTask,
);
}
},
onTaskContextMenu: (event, task) => {
if (task) this.viewHandlers.onTaskContextMenu(event, task);
},
}
},
);
this.parentView.addChild(this.tagsComponent);
this.tagsComponent.load();
@ -243,14 +243,14 @@ export class FluentComponentManager extends Component {
if (originalTask && updatedTask) {
await this.viewHandlers.onTaskUpdate(
originalTask,
updatedTask
updatedTask,
);
}
},
onTaskContextMenu: (event, task) => {
if (task) this.viewHandlers.onTaskContextMenu(event, task);
},
}
},
);
this.parentView.addChild(this.projectsComponent);
this.projectsComponent.load();
@ -271,14 +271,14 @@ export class FluentComponentManager extends Component {
if (originalTask && updatedTask) {
await this.viewHandlers.onTaskUpdate(
originalTask,
updatedTask
updatedTask,
);
}
},
onTaskContextMenu: (event, task) => {
if (task) this.viewHandlers.onTaskContextMenu(event, task);
},
}
},
);
this.parentView.addChild(this.reviewComponent);
this.reviewComponent.load();
@ -304,7 +304,7 @@ export class FluentComponentManager extends Component {
onEventContextMenu: (ev: MouseEvent, event: Task) => {
if (event) this.viewHandlers.onTaskContextMenu(ev, event);
},
}
},
);
this.parentView.addChild(this.calendarComponent);
@ -318,7 +318,7 @@ export class FluentComponentManager extends Component {
onTaskStatusUpdate: async (taskId, newStatusMark) => {
this.viewHandlers.onKanbanTaskStatusUpdate(
taskId,
newStatusMark
newStatusMark,
);
},
onTaskSelected: (task) => {
@ -330,7 +330,7 @@ export class FluentComponentManager extends Component {
onTaskContextMenu: (event, task) => {
if (task) this.viewHandlers.onTaskContextMenu(event, task);
},
}
},
);
this.parentView.addChild(this.kanbanComponent);
@ -345,7 +345,7 @@ export class FluentComponentManager extends Component {
this.viewHandlers.onTaskCompleted(task),
onTaskContextMenu: (event: MouseEvent, task: Task) =>
this.viewHandlers.onTaskContextMenu(event, task),
}
},
);
this.parentView.addChild(this.ganttComponent);
this.ganttComponent.load();
@ -376,7 +376,7 @@ export class FluentComponentManager extends Component {
// Smart hiding - only hide currently visible component (unless initial hide)
if (!isInitialHide && this.currentVisibleComponent) {
console.log(
"[FluentComponent] Smart hide - only hiding current visible component"
"[FluentComponent] Smart hide - only hiding current visible component",
);
this.currentVisibleComponent.containerEl?.hide();
this.currentVisibleComponent = null;
@ -384,7 +384,7 @@ export class FluentComponentManager extends Component {
// Hide all components
console.log(
"[FluentComponent] Hiding all components",
isInitialHide ? "(initial hide)" : ""
isInitialHide ? "(initial hide)" : "",
);
this.contentComponent?.containerEl.hide();
this.forecastComponent?.containerEl.hide();
@ -417,20 +417,20 @@ export class FluentComponentManager extends Component {
filteredTasks: Task[],
currentFilterState: RootFilterState | null,
viewMode: ViewMode,
project?: string | null
project?: string | null,
): void {
console.log(
"[FluentComponent] switchView called with:",
viewId,
"viewMode:",
viewMode
viewMode,
);
// Remove transient overlays (loading/error/empty) before showing components
if (this.contentArea) {
this.contentArea
.querySelectorAll(
".tg-fluent-loading, .tg-fluent-error-state, .tg-fluent-empty-state"
".tg-fluent-loading, .tg-fluent-error-state, .tg-fluent-empty-state",
)
.forEach((el) => el.remove());
}
@ -457,7 +457,7 @@ export class FluentComponentManager extends Component {
viewId,
tasks,
filteredTasks,
viewMode
viewMode,
);
return;
}
@ -478,7 +478,7 @@ export class FluentComponentManager extends Component {
this.app,
this.plugin,
twoColumnConfig,
viewId
viewId,
);
this.parentView.addChild(twoColumnComponent);
@ -496,7 +496,7 @@ export class FluentComponentManager extends Component {
const twoColumnComponent = this.twoColumnViewComponents.get(viewId);
if (!twoColumnComponent) {
console.warn(
`[FluentComponent] Missing two column component for view ${viewId}`
`[FluentComponent] Missing two column component for view ${viewId}`,
);
return;
}
@ -508,7 +508,7 @@ export class FluentComponentManager extends Component {
// Check if it's a special view managed by ViewComponentManager
if (this.viewComponentManager.isSpecialView(viewId)) {
targetComponent = this.viewComponentManager.showComponent(
viewId
viewId,
) as ManagedViewComponent | null;
} else if (
specificViewType === "forecast" ||
@ -530,13 +530,13 @@ export class FluentComponentManager extends Component {
// 2. Without project selection (from view navigation) → use ProjectsComponent for overview
if (project) {
console.log(
"[FluentComponent] Projects view with selected project - using ContentComponent"
"[FluentComponent] Projects view with selected project - using ContentComponent",
);
targetComponent = this.contentComponent;
modeForComponent = viewId;
} else {
console.log(
"[FluentComponent] Projects view without selection - using ProjectsComponent"
"[FluentComponent] Projects view without selection - using ProjectsComponent",
);
targetComponent = this.projectsComponent;
modeForComponent = viewId;
@ -569,13 +569,13 @@ export class FluentComponentManager extends Component {
console.log(
"[FluentComponent] Target component determined:",
targetComponent?.constructor?.name
targetComponent?.constructor?.name,
);
if (targetComponent) {
console.log(
`[FluentComponent] Activating component for view ${viewId}:`,
targetComponent.constructor.name
targetComponent.constructor.name,
);
targetComponent.containerEl.show();
this.currentVisibleComponent = targetComponent;
@ -583,7 +583,7 @@ export class FluentComponentManager extends Component {
// Set view mode first for ContentComponent
if (typeof targetComponent.setViewMode === "function") {
console.log(
`[FluentComponent] Setting view mode for ${viewId} to ${modeForComponent}`
`[FluentComponent] Setting view mode for ${viewId} to ${modeForComponent}`,
);
targetComponent.setViewMode(modeForComponent as any, project);
}
@ -595,7 +595,7 @@ export class FluentComponentManager extends Component {
if (viewId === "review" || viewId === "tags") {
console.log(
`[FluentComponent] Calling setTasks for ${viewId} with ALL tasks:`,
tasks.length
tasks.length,
);
targetComponent.setTasks(tasks);
} else if (viewId === "projects" && !project) {
@ -603,7 +603,7 @@ export class FluentComponentManager extends Component {
// and FILTERED tasks to apply filter to project task lists
// This ensures: left sidebar shows all projects, right panel shows filtered tasks
console.log(
`[FluentComponent] Calling setTasks for projects with ALL tasks (${tasks.length}) and FILTERED tasks (${filteredTasks.length})`
`[FluentComponent] Calling setTasks for projects with ALL tasks (${tasks.length}) and FILTERED tasks (${filteredTasks.length})`,
);
targetComponent.setTasks(tasks, filteredTasks);
} else {
@ -612,14 +612,14 @@ export class FluentComponentManager extends Component {
// Forecast view: remove badge-only items
if (viewId === "forecast") {
filteredTasksLocal = filteredTasksLocal.filter(
(task) => !(task as any).badge
(task) => !(task as any).badge,
);
}
console.log(
"[FluentComponent] Calling setTasks with filtered:",
filteredTasksLocal.length,
"all:",
tasks.length
tasks.length,
);
targetComponent.setTasks(filteredTasksLocal, tasks);
}
@ -641,8 +641,8 @@ export class FluentComponentManager extends Component {
tasks,
viewId as any,
this.plugin,
filterOptions
)
filterOptions,
),
);
}
@ -655,7 +655,7 @@ export class FluentComponentManager extends Component {
}
} else {
console.warn(
`[FluentComponent] No target component found for viewId: ${viewId}`
`[FluentComponent] No target component found for viewId: ${viewId}`,
);
}
}
@ -667,11 +667,11 @@ export class FluentComponentManager extends Component {
viewId: string,
tasks: Task[],
filteredTasks: Task[],
viewMode: ViewMode
viewMode: ViewMode,
): void {
console.log(
"[FluentComponent] renderContentWithViewMode called, viewMode:",
viewMode
viewMode,
);
// Hide current component
@ -690,7 +690,7 @@ export class FluentComponentManager extends Component {
console.log(
"[FluentComponent] Setting tasks to ContentComponent, filtered:",
filteredTasks.length
filteredTasks.length,
);
this.contentComponent.setTasks(filteredTasks, tasks);
this.currentVisibleComponent = this.contentComponent;
@ -705,7 +705,7 @@ export class FluentComponentManager extends Component {
console.log(
"[FluentComponent] Setting",
filteredTasks.length,
"tasks to kanban"
"tasks to kanban",
);
this.kanbanComponent.setTasks(filteredTasks);
this.currentVisibleComponent = this.kanbanComponent;
@ -714,11 +714,11 @@ export class FluentComponentManager extends Component {
case "calendar":
// Use CalendarComponent
console.log(
"[FluentComponent] Calendar mode in renderContentWithViewMode"
"[FluentComponent] Calendar mode in renderContentWithViewMode",
);
if (!this.calendarComponent) {
console.log(
"[FluentComponent] No calendar component available!"
"[FluentComponent] No calendar component available!",
);
return;
}
@ -729,7 +729,7 @@ export class FluentComponentManager extends Component {
console.log(
"[FluentComponent] Setting",
filteredTasks.length,
"tasks to calendar"
"tasks to calendar",
);
this.calendarComponent.setTasks(filteredTasks);
this.currentVisibleComponent = this.calendarComponent;
@ -745,7 +745,7 @@ export class FluentComponentManager extends Component {
viewId: string,
tasks: Task[],
filteredTasks: Task[],
viewMode: ViewMode
viewMode: ViewMode,
): void {
// Content-based views (list/tree/kanban/calendar)
if (this.isContentBasedView(viewId)) {
@ -762,7 +762,7 @@ export class FluentComponentManager extends Component {
this.contentComponent?.setTasks?.(
filteredTasks,
tasks,
true
true,
);
break;
}
@ -891,7 +891,7 @@ export class FluentComponentManager extends Component {
emptyEl.createDiv({
cls: "tg-fluent-empty-description",
text: t(
"Create your first task to get started with Task Genius"
"Create your first task to get started with Task Genius",
),
});
@ -925,12 +925,12 @@ export class FluentComponentManager extends Component {
*/
getAvailableModesForView(
viewId: string,
selectedProject?: string | null
selectedProject?: string | null,
): ViewMode[] {
// Check for special two-column views
const viewConfig = getViewSettingOrDefault(
this.plugin,
viewId as ViewMode
viewId as ViewMode,
);
if (viewConfig?.specificConfig?.viewType === "twocolumn") {
return [];

View file

@ -27,12 +27,6 @@ import {
isProjectReadonly,
} from "@/utils/task/task-operations";
// CSS classes for drop indicators
const DROP_INDICATOR_BEFORE_CLASS = "tg-kanban-card--drop-indicator-before";
const DROP_INDICATOR_AFTER_CLASS = "tg-kanban-card--drop-indicator-after";
const DROP_INDICATOR_EMPTY_CLASS =
"tg-kanban-column-content--drop-indicator-empty";
export interface KanbanSortOption {
field:
| "priority"
@ -60,7 +54,7 @@ export class KanbanComponent extends Component {
private params: {
onTaskStatusUpdate?: (
taskId: string,
newStatusMark: string
newStatusMark: string,
) => Promise<void>;
onTaskSelected?: (task: Task) => void;
onTaskCompleted?: (task: Task) => void;
@ -85,13 +79,13 @@ export class KanbanComponent extends Component {
params: {
onTaskStatusUpdate?: (
taskId: string,
newStatusMark: string
newStatusMark: string,
) => Promise<void>;
onTaskSelected?: (task: Task) => void;
onTaskCompleted?: (task: Task) => void;
onTaskContextMenu?: (ev: MouseEvent, task: Task) => void;
} = {},
viewId = "kanban" // 新增视图ID参数
viewId = "kanban", // 新增视图ID参数
) {
super();
this.app = app;
@ -105,16 +99,18 @@ export class KanbanComponent extends Component {
/**
* Set configuration override from Bases view config
*/
public setConfigOverride(config: Partial<KanbanSpecificConfig> | null): void {
public setConfigOverride(
config: Partial<KanbanSpecificConfig> | null,
): void {
const isChanged = this.hasConfigOverrideChanged(config);
this.configOverride = config;
console.log('[Kanban] setConfigOverride received', config);
console.log("[Kanban] setConfigOverride received", config);
if (isChanged) {
// Refresh derived state from effective config (sort/hide/column order)
this.loadKanbanConfig();
const eff = this.getEffectiveKanbanConfig();
console.log('[Kanban] effective config after override', eff);
console.log("[Kanban] effective config after override", eff);
if (this.columnContainerEl) {
// Rebuild columns with the new configuration so changes like groupBy
// take effect immediately without requiring a data refresh.
@ -125,9 +121,11 @@ export class KanbanComponent extends Component {
private getEffectiveKanbanConfig(): KanbanSpecificConfig | undefined {
const pluginConfig = this.plugin.settings.viewConfiguration.find(
(v) => v.id === this.currentViewId
(v) => v.id === this.currentViewId,
)?.specificConfig as KanbanSpecificConfig;
return this.configOverride ? {...(pluginConfig ?? {}), ...this.configOverride} : pluginConfig;
return this.configOverride
? { ...(pluginConfig ?? {}), ...this.configOverride }
: pluginConfig;
}
override onload() {
@ -173,7 +171,7 @@ export class KanbanComponent extends Component {
}
private hasConfigOverrideChanged(
nextConfig: Partial<KanbanSpecificConfig> | null
nextConfig: Partial<KanbanSpecificConfig> | null,
): boolean {
if (!this.configOverride && !nextConfig) {
return false;
@ -185,7 +183,8 @@ export class KanbanComponent extends Component {
try {
return (
JSON.stringify(this.configOverride) !== JSON.stringify(nextConfig)
JSON.stringify(this.configOverride) !==
JSON.stringify(nextConfig)
);
} catch (error) {
console.warn("Failed to compare kanban config overrides:", error);
@ -211,7 +210,7 @@ export class KanbanComponent extends Component {
},
(el) => {
setIcon(el, "arrow-up-down");
}
},
);
this.registerDomEvent(sortButton, "click", (event) => {
@ -265,7 +264,7 @@ export class KanbanComponent extends Component {
item.setTitle(option.label)
.setChecked(
option.field === this.sortOption.field &&
option.order === this.sortOption.order
option.order === this.sortOption.order,
)
.onClick(() => {
this.sortOption = option;
@ -296,7 +295,7 @@ export class KanbanComponent extends Component {
this.applyFiltersAndRender(); // Re-render when filters change
},
},
this.plugin // Pass plugin instance
this.plugin, // Pass plugin instance
);
this.addChild(this.filterComponent); // Register as child component
@ -312,7 +311,7 @@ export class KanbanComponent extends Component {
this.filterComponent.updateFilterOptions(this.allTasks);
} else {
console.warn(
"Filter component not initialized when setting tasks."
"Filter component not initialized when setting tasks.",
);
// Options will be built when renderFilterControls is called if it hasn't been yet.
// If renderFilterControls already ran, this might indicate an issue.
@ -375,7 +374,7 @@ export class KanbanComponent extends Component {
return task.filePath === filter.value;
default:
console.warn(
`Unknown filter category in Kanban: ${filter.category}`
`Unknown filter category in Kanban: ${filter.category}`,
);
return true;
}
@ -417,7 +416,7 @@ export class KanbanComponent extends Component {
// Allow multiple symbols to represent the same logical status (e.g., In Progress: "/" and ">")
private getAllowedMarksForStatusName(
statusName: string
statusName: string,
): Set<string> | null {
if (!statusName) return null;
const s = statusName.trim().toLowerCase();
@ -444,7 +443,7 @@ export class KanbanComponent extends Component {
raw
.split("|")
.map((ch: string) => ch.trim())
.filter((ch: string) => ch.length === 1)
.filter((ch: string) => ch.length === 1),
);
return set.size > 0 ? set : null;
}
@ -452,7 +451,7 @@ export class KanbanComponent extends Component {
// Handle filter application from clickable metadata
private handleFilterApply = (
filterType: string,
value: string | number | string[]
value: string | number | string[],
) => {
// Convert value to string for consistent handling
let stringValue = Array.isArray(value) ? value[0] : value.toString();
@ -474,7 +473,7 @@ export class KanbanComponent extends Component {
// Check if filter already exists
const existingFilterIndex = this.activeFilters.findIndex(
(f) => f.category === filterType && f.value === stringValue
(f) => f.category === filterType && f.value === stringValue,
);
if (existingFilterIndex === -1) {
@ -491,7 +490,7 @@ export class KanbanComponent extends Component {
this.activeFilters.map((f) => ({
category: f.category,
value: f.value,
}))
})),
);
}
@ -534,7 +533,7 @@ export class KanbanComponent extends Component {
// Resolve effective config (Bases override wins over plugin settings)
const kanbanConfig = this.getEffectiveKanbanConfig();
console.log('[Kanban] renderColumns effective config', kanbanConfig);
console.log("[Kanban] renderColumns effective config", kanbanConfig);
const groupBy = kanbanConfig?.groupBy || "status";
@ -588,7 +587,7 @@ export class KanbanComponent extends Component {
statusNames = [...spaceStatus, ...otherStatuses, ...xStatus];
// Apply saved column order to status names
const statusColumns = statusNames.map((name) => ({title: name}));
const statusColumns = statusNames.map((name) => ({ title: name }));
const orderedStatusColumns = this.applyColumnOrder(statusColumns);
const orderedStatusNames = orderedStatusColumns.map((col) => col.title);
@ -605,10 +604,10 @@ export class KanbanComponent extends Component {
...this.params,
onTaskStatusUpdate: (
taskId: string,
newStatusMark: string
newStatusMark: string,
) => this.handleStatusUpdate(taskId, newStatusMark),
onFilterApply: this.handleFilterApply,
}
},
);
this.addChild(column);
this.columns.push(column);
@ -617,7 +616,7 @@ export class KanbanComponent extends Component {
private renderCustomColumns(
groupBy: string,
customColumns?: KanbanColumnConfig[]
customColumns?: KanbanColumnConfig[],
) {
let columnConfigs: { title: string; value: any; id: string }[] = [];
@ -641,7 +640,7 @@ export class KanbanComponent extends Component {
orderedColumnConfigs.forEach((config) => {
const tasksForColumn = this.getTasksForProperty(
groupBy,
config.value
config.value,
);
const column = new KanbanColumnComponent(
@ -657,10 +656,10 @@ export class KanbanComponent extends Component {
taskId,
groupBy,
config.value,
newValue
newValue,
),
onFilterApply: this.handleFilterApply,
}
},
);
this.addChild(column);
this.columns.push(column);
@ -668,17 +667,17 @@ export class KanbanComponent extends Component {
}
private generateDefaultColumns(
groupBy: string
groupBy: string,
): { title: string; value: any; id: string }[] {
switch (groupBy) {
case "priority":
return [
{title: "🔺 Highest", value: 5, id: "priority-5"},
{title: "⏫ High", value: 4, id: "priority-4"},
{title: "🔼 Medium", value: 3, id: "priority-3"},
{title: "🔽 Low", value: 2, id: "priority-2"},
{title: "⏬ Lowest", value: 1, id: "priority-1"},
{title: "No Priority", value: null, id: "priority-none"},
{ title: "🔺 Highest", value: 5, id: "priority-5" },
{ title: "⏫ High", value: 4, id: "priority-4" },
{ title: "🔼 Medium", value: 3, id: "priority-3" },
{ title: "🔽 Low", value: 2, id: "priority-2" },
{ title: "⏬ Lowest", value: 1, id: "priority-1" },
{ title: "No Priority", value: null, id: "priority-none" },
];
case "tags":
// Get unique tags from all tasks
@ -719,7 +718,7 @@ export class KanbanComponent extends Component {
title: project,
value: project,
id: `project-${project}`,
})
}),
);
projectColumns.push({
title: "No Project",
@ -741,7 +740,7 @@ export class KanbanComponent extends Component {
title: `@${context}`,
value: context,
id: `context-${context}`,
})
}),
);
contextColumns.push({
title: "No Context",
@ -758,7 +757,7 @@ export class KanbanComponent extends Component {
value: "overdue",
id: `${groupBy}-overdue`,
},
{title: "Today", value: "today", id: `${groupBy}-today`},
{ title: "Today", value: "today", id: `${groupBy}-today` },
{
title: "Tomorrow",
value: "tomorrow",
@ -774,8 +773,8 @@ export class KanbanComponent extends Component {
value: "nextWeek",
id: `${groupBy}-nextWeek`,
},
{title: "Later", value: "later", id: `${groupBy}-later`},
{title: "No Date", value: null, id: `${groupBy}-none`},
{ title: "Later", value: "later", id: `${groupBy}-later` },
{ title: "No Date", value: null, id: `${groupBy}-none` },
];
case "filePath":
// Get unique file paths from all tasks
@ -791,7 +790,7 @@ export class KanbanComponent extends Component {
id: `path-${path.replace(/[^a-zA-Z0-9]/g, "-")}`,
}));
default:
return [{title: "All Tasks", value: null, id: "all"}];
return [{ title: "All Tasks", value: null, id: "all" }];
}
}
@ -826,9 +825,9 @@ export class KanbanComponent extends Component {
private compareTasks(
a: Task,
b: Task,
sortOption: KanbanSortOption
sortOption: KanbanSortOption,
): number {
const {field, order} = sortOption;
const { field, order } = sortOption;
let comparison = 0;
// Ensure both tasks have metadata property
@ -918,8 +917,8 @@ export class KanbanComponent extends Component {
dropTargetColumnContent.closest(".tg-kanban-column");
const targetColumnTitle = targetColumnEl
? (targetColumnEl as HTMLElement).querySelector(
".tg-kanban-column-title"
)?.textContent
".tg-kanban-column-title",
)?.textContent
: null;
// Get source column information
@ -927,66 +926,67 @@ export class KanbanComponent extends Component {
sourceColumnContent.closest(".tg-kanban-column");
const sourceColumnTitle = sourceColumnEl
? (sourceColumnEl as HTMLElement).querySelector(
".tg-kanban-column-title"
)?.textContent
".tg-kanban-column-title",
)?.textContent
: null;
if (targetColumnTitle && sourceColumnTitle) {
const kanbanConfig =
this.plugin.settings.viewConfiguration.find(
(v) => v.id === this.currentViewId
(v) => v.id === this.currentViewId,
)?.specificConfig as KanbanSpecificConfig;
const groupByPlugin = kanbanConfig?.groupBy || "status";
const groupBy = (this.getEffectiveKanbanConfig()?.groupBy) || groupByPlugin;
const groupBy =
this.getEffectiveKanbanConfig()?.groupBy || groupByPlugin;
if (groupBy === "status") {
// Handle status-based grouping (original logic)
const targetStatusMark = this.resolveStatusMark(
(targetColumnTitle || "").trim()
(targetColumnTitle || "").trim(),
);
if (targetStatusMark !== undefined) {
console.log(
`Kanban requesting status update for task ${taskId} to status ${targetColumnTitle} (mark: ${targetStatusMark})`
`Kanban requesting status update for task ${taskId} to status ${targetColumnTitle} (mark: ${targetStatusMark})`,
);
await this.handleStatusUpdate(taskId, targetStatusMark);
} else {
console.warn(
`Could not find status mark for status name: ${targetColumnTitle}`
`Could not find status mark for status name: ${targetColumnTitle}`,
);
}
} else {
const effectiveCustomColumns = (this.getEffectiveKanbanConfig()?.customColumns) || kanbanConfig?.customColumns;
const effectiveCustomColumns =
this.getEffectiveKanbanConfig()?.customColumns ||
kanbanConfig?.customColumns;
// Handle property-based grouping
const targetValue = this.getColumnValueFromTitle(
targetColumnTitle,
groupBy,
effectiveCustomColumns
effectiveCustomColumns,
);
const sourceValue = this.getColumnValueFromTitle(
sourceColumnTitle,
groupBy,
effectiveCustomColumns
effectiveCustomColumns,
);
console.log(
`Kanban requesting ${groupBy} update for task ${taskId} from ${sourceValue} to value: ${targetValue}`
`Kanban requesting ${groupBy} update for task ${taskId} from ${sourceValue} to value: ${targetValue}`,
);
await this.handlePropertyUpdate(
taskId,
groupBy,
sourceValue,
targetValue
targetValue,
);
}
// After update, select the moved task so the status panel (details) reflects changes
const movedTask =
this.allTasks.find((t) => t.id === taskId) ||
this.tasks.find((t) => t.id === taskId);
if (movedTask && this.params?.onTaskSelected) {
this.params.onTaskSelected(movedTask);
}
// Don't auto-select task after drag to avoid unwanted detail panel opening
// Users can manually click the card if they want to see details
//
// Previously: Auto-selected moved task so status panel reflects changes
// Issue: This opens detail panel after every drag operation, disrupting workflow
}
}
}
@ -1001,10 +1001,10 @@ export class KanbanComponent extends Component {
order: kanbanConfig.defaultSortOrder || "desc",
label: this.getSortOptionLabel(
kanbanConfig.defaultSortField || "priority",
kanbanConfig.defaultSortOrder || "desc"
kanbanConfig.defaultSortOrder || "desc",
),
};
console.log('[Kanban] loadKanbanConfig applied', {
console.log("[Kanban] loadKanbanConfig applied", {
hideEmptyColumns: this.hideEmptyColumns,
defaultSortField: this.sortOption.field,
defaultSortOrder: this.sortOption.order,
@ -1044,7 +1044,7 @@ export class KanbanComponent extends Component {
if (typeof exact === "string") return exact;
// Try case-insensitive match
for (const [name, mark] of Object.entries(
this.plugin.settings.taskStatusMarks
this.plugin.settings.taskStatusMarks,
)) {
if (name.toLowerCase() === trimmed.toLowerCase()) {
return mark as string;
@ -1059,14 +1059,23 @@ export class KanbanComponent extends Component {
private async handleStatusUpdate(
taskId: string,
newStatusMark: string
newStatusMark: string,
): Promise<void> {
console.log(`[Kanban] handleStatusUpdate called: taskId=${taskId}, mark=${newStatusMark}`);
console.log('[Kanban] onTaskStatusUpdate callback exists:', !!this.params.onTaskStatusUpdate);
if (this.params.onTaskStatusUpdate) {
try {
console.log('[Kanban] Calling onTaskStatusUpdate callback...');
await this.params.onTaskStatusUpdate(taskId, newStatusMark);
console.log('[Kanban] onTaskStatusUpdate callback completed successfully');
} catch (error) {
console.error("Failed to update task status:", error);
console.error("[Kanban] Failed to update task status:", error);
console.error("[Kanban] Error details:", error.stack);
}
} else {
console.error('[Kanban] CRITICAL: onTaskStatusUpdate callback is not defined!');
console.error('[Kanban] this.params:', this.params);
}
}
@ -1074,7 +1083,7 @@ export class KanbanComponent extends Component {
taskId: string,
groupBy: string,
oldValue: any,
newValue: string
newValue: string,
): Promise<void> {
// This method will handle updating task properties when dragged between columns
if (groupBy === "status") {
@ -1086,7 +1095,7 @@ export class KanbanComponent extends Component {
const taskToUpdate = this.allTasks.find((task) => task.id === taskId);
if (!taskToUpdate) {
console.warn(
`Task with ID ${taskId} not found for property update`
`Task with ID ${taskId} not found for property update`,
);
return;
}
@ -1094,7 +1103,7 @@ export class KanbanComponent extends Component {
taskToUpdate.metadata = taskToUpdate.metadata || {};
// Create updated task object
const updatedTask = {...taskToUpdate};
const updatedTask = { ...taskToUpdate };
// Update the specific property based on groupBy type
switch (groupBy) {
@ -1130,7 +1139,7 @@ export class KanbanComponent extends Component {
];
currentTags = currentTags.filter(
(tag) => !oldTagVariants.includes(tag)
(tag) => !oldTagVariants.includes(tag),
);
console.log("Tags after removing old:", currentTags);
}
@ -1146,7 +1155,7 @@ export class KanbanComponent extends Component {
];
const hasNewTag = currentTags.some((tag) =>
newTagVariants.includes(tag)
newTagVariants.includes(tag),
);
if (!hasNewTag) {
// Add the tag in the same format as existing tags, or without # if no existing tags
@ -1194,7 +1203,7 @@ export class KanbanComponent extends Component {
break;
default:
console.warn(
`Unsupported property type for update: ${groupBy}`
`Unsupported property type for update: ${groupBy}`,
);
return;
}
@ -1205,7 +1214,7 @@ export class KanbanComponent extends Component {
`Updating task ${taskId} ${groupBy} from:`,
oldValue,
"to:",
newValue
newValue,
);
if (this.plugin.writeAPI) {
const result = await this.plugin.writeAPI.updateTask({
@ -1215,7 +1224,7 @@ export class KanbanComponent extends Component {
if (!result.success) {
console.error(
`Failed to update task ${taskId} property ${groupBy}:`,
result.error
result.error,
);
}
} else {
@ -1224,7 +1233,7 @@ export class KanbanComponent extends Component {
} catch (error) {
console.error(
`Failed to update task ${taskId} property ${groupBy}:`,
error
error,
);
}
}
@ -1246,7 +1255,7 @@ export class KanbanComponent extends Component {
return (
metadata.tags &&
metadata.tags.some(
(tag) => typeof tag === "string" && tag === value
(tag) => typeof tag === "string" && tag === value,
)
);
case "project":
@ -1281,18 +1290,18 @@ export class KanbanComponent extends Component {
private matchesDateCategory(
task: Task,
dateField: string,
category: string
category: string,
): boolean {
const now = new Date();
const today = new Date(
now.getFullYear(),
now.getMonth(),
now.getDate()
now.getDate(),
);
const tomorrow = new Date(today.getTime() + 24 * 60 * 60 * 1000);
const weekFromNow = new Date(today.getTime() + 7 * 24 * 60 * 60 * 1000);
const twoWeeksFromNow = new Date(
today.getTime() + 14 * 24 * 60 * 60 * 1000
today.getTime() + 14 * 24 * 60 * 60 * 1000,
);
const metadata = task.metadata || {};
@ -1324,7 +1333,7 @@ export class KanbanComponent extends Component {
return (
taskDateObj >= tomorrow &&
taskDateObj <
new Date(tomorrow.getTime() + 24 * 60 * 60 * 1000)
new Date(tomorrow.getTime() + 24 * 60 * 60 * 1000)
);
case "thisWeek":
return taskDateObj >= tomorrow && taskDateObj < weekFromNow;
@ -1346,7 +1355,7 @@ export class KanbanComponent extends Component {
private getColumnValueFromTitle(
title: string,
groupBy: string,
customColumns?: KanbanColumnConfig[]
customColumns?: KanbanColumnConfig[],
): any {
console.log("customColumns", customColumns);
if (customColumns && customColumns.length > 0) {
@ -1393,7 +1402,7 @@ export class KanbanComponent extends Component {
}
private convertDateCategoryToTimestamp(
category: string
category: string,
): number | undefined {
if (category === null || category === "" || category === "none") {
return undefined;
@ -1403,7 +1412,7 @@ export class KanbanComponent extends Component {
const today = new Date(
now.getFullYear(),
now.getMonth(),
now.getDate()
now.getDate(),
);
switch (category) {
@ -1415,19 +1424,19 @@ export class KanbanComponent extends Component {
return today.getTime();
case "tomorrow":
return new Date(
today.getTime() + 24 * 60 * 60 * 1000
today.getTime() + 24 * 60 * 60 * 1000,
).getTime();
case "thisWeek":
// Set to end of this week (Sunday)
const daysUntilSunday = 7 - today.getDay();
return new Date(
today.getTime() + daysUntilSunday * 24 * 60 * 60 * 1000
today.getTime() + daysUntilSunday * 24 * 60 * 60 * 1000,
).getTime();
case "nextWeek":
// Set to end of next week
const daysUntilNextSunday = 14 - today.getDay();
return new Date(
today.getTime() + daysUntilNextSunday * 24 * 60 * 60 * 1000
today.getTime() + daysUntilNextSunday * 24 * 60 * 60 * 1000,
).getTime();
case "later":
// Set to one month from now
@ -1448,7 +1457,7 @@ export class KanbanComponent extends Component {
// We need to check against the current column configuration
const kanbanConfig =
this.plugin.settings.viewConfiguration.find(
(v) => v.id === this.currentViewId
(v) => v.id === this.currentViewId,
)?.specificConfig as KanbanSpecificConfig;
if (
@ -1469,7 +1478,7 @@ export class KanbanComponent extends Component {
metadata.tags.some(
(tag) =>
typeof tag === "string" &&
tag === column.value
tag === column.value,
)
) {
return column.value;
@ -1483,7 +1492,7 @@ export class KanbanComponent extends Component {
}
// Return the first string tag (for simplicity, as we need to determine which column it came from)
const firstStringTag = metadata.tags.find(
(tag) => typeof tag === "string"
(tag) => typeof tag === "string",
);
return firstStringTag || "";
}
@ -1516,12 +1525,12 @@ export class KanbanComponent extends Component {
const today = new Date(
now.getFullYear(),
now.getMonth(),
now.getDate()
now.getDate(),
);
const tomorrow = new Date(today.getTime() + 24 * 60 * 60 * 1000);
const weekFromNow = new Date(today.getTime() + 7 * 24 * 60 * 60 * 1000);
const twoWeeksFromNow = new Date(
today.getTime() + 14 * 24 * 60 * 60 * 1000
today.getTime() + 14 * 24 * 60 * 60 * 1000,
);
const taskDate = new Date(timestamp);
@ -1563,7 +1572,7 @@ export class KanbanComponent extends Component {
} catch (error) {
console.warn(
"Failed to load column order from localStorage:",
error
error,
);
this.columnOrder = [];
}
@ -1580,7 +1589,7 @@ export class KanbanComponent extends Component {
}
private applyColumnOrder<T extends { title: string; id?: string }>(
columns: T[]
columns: T[],
): T[] {
try {
if (this.columnOrder.length === 0) {
@ -1589,7 +1598,7 @@ export class KanbanComponent extends Component {
if (!Array.isArray(columns)) {
console.warn(
"Invalid columns array provided to applyColumnOrder"
"Invalid columns array provided to applyColumnOrder",
);
return [];
}
@ -1603,11 +1612,11 @@ export class KanbanComponent extends Component {
const columnIndex = remainingColumns.findIndex(
(col) =>
(col.id && col.id === orderedId) ||
col.title === orderedId
col.title === orderedId,
);
if (columnIndex !== -1) {
orderedColumns.push(
remainingColumns.splice(columnIndex, 1)[0]
remainingColumns.splice(columnIndex, 1)[0],
);
}
}
@ -1667,12 +1676,12 @@ export class KanbanComponent extends Component {
columnElements.forEach((columnEl) => {
const columnTitle = (columnEl as HTMLElement).querySelector(
".tg-kanban-column-title"
".tg-kanban-column-title",
)?.textContent;
if (columnTitle) {
// Use the data-status-name attribute if available, otherwise use title
const statusName = (columnEl as HTMLElement).getAttribute(
"data-status-name"
"data-status-name",
);
const columnId = statusName || columnTitle;

View file

@ -2,7 +2,6 @@ import { App, Component } from "obsidian";
import { Task } from "@/types/task";
import { TaskListItemComponent } from "./listItem";
import { TaskTreeItemComponent } from "./treeItem";
import { tasksToTree } from "@/utils/ui/tree-view-utils";
import { t } from "@/translations/helper";
import TaskProgressBarPlugin from "@/index";
@ -22,7 +21,7 @@ export class TaskListRendererComponent extends Component {
private containerEl: HTMLElement, // The HTML element to render tasks into
private plugin: TaskProgressBarPlugin,
private app: App,
private context: string // Context identifier (e.g., "projects", "review")
private context: string, // Context identifier (e.g., "projects", "review")
) {
super();
// Add this renderer as a child of the parent component
@ -43,7 +42,7 @@ export class TaskListRendererComponent extends Component {
isTreeView: boolean,
allTasksMap: Map<string, Task>, // Make it optional but required for tree view
emptyMessage: string = t("No tasks found."),
append: boolean = false
append: boolean = false,
) {
if (!append) {
this.cleanupComponents();
@ -62,7 +61,7 @@ export class TaskListRendererComponent extends Component {
// Fallback: if tree view is requested but no map provided, build it from section tasks
// This might lead to incomplete trees if parents are outside the section.
console.warn(
"TaskListRendererComponent: allTasksMap not provided for tree view. Tree may be incomplete."
"TaskListRendererComponent: allTasksMap not provided for tree view. Tree may be incomplete.",
);
this.allTasksMap = new Map(tasks.map((task) => [task.id, task]));
}
@ -70,10 +69,10 @@ export class TaskListRendererComponent extends Component {
if (isTreeView) {
if (!this.allTasksMap || this.allTasksMap.size === 0) {
console.error(
"TaskListRendererComponent: Cannot render tree view without allTasksMap."
"TaskListRendererComponent: Cannot render tree view without allTasksMap.",
);
this.renderEmptyState(
"Error: Task data unavailable for tree view."
"Error: Task data unavailable for tree view.",
); // Show error
return;
}
@ -90,7 +89,7 @@ export class TaskListRendererComponent extends Component {
task,
this.context,
this.app,
this.plugin
this.plugin,
);
// Set up event handlers
@ -109,13 +108,13 @@ export class TaskListRendererComponent extends Component {
"TaskListRendererComponent onTaskUpdate",
this.onTaskUpdate,
originalTask.content,
updatedTask.content
updatedTask.content,
);
if (this.onTaskUpdate) {
console.log(
"TaskListRendererComponent onTaskUpdate",
originalTask.content,
updatedTask.content
updatedTask.content,
);
await this.onTaskUpdate(originalTask, updatedTask);
}
@ -141,7 +140,7 @@ export class TaskListRendererComponent extends Component {
private renderTreeView(
sectionTasks: Task[],
allTasksMap: Map<string, Task>
allTasksMap: Map<string, Task>,
) {
const fragment = document.createDocumentFragment();
const sectionTaskIds = new Set(sectionTasks.map((t) => t.id)); // IDs of tasks belonging to this section
@ -151,7 +150,7 @@ export class TaskListRendererComponent extends Component {
const markSubtreeAsProcessed = (
rootTask: Task,
sectionTaskIds: Set<string>,
processedTaskIds: Set<string>
processedTaskIds: Set<string>,
) => {
if (sectionTaskIds.has(rootTask.id)) {
processedTaskIds.add(rootTask.id);
@ -164,7 +163,7 @@ export class TaskListRendererComponent extends Component {
markSubtreeAsProcessed(
childTask,
sectionTaskIds,
processedTaskIds
processedTaskIds,
);
}
});
@ -197,11 +196,11 @@ export class TaskListRendererComponent extends Component {
!sectionTaskIds.has(currentTask.metadata.parent)
) {
const parentTask = allTasksMap.get(
currentTask.metadata.parent
currentTask.metadata.parent,
);
if (!parentTask) {
console.warn(
`Parent task ${currentTask.metadata.parent} not found in allTasksMap.`
`Parent task ${currentTask.metadata.parent} not found in allTasksMap.`,
);
break;
}
@ -219,7 +218,7 @@ export class TaskListRendererComponent extends Component {
markSubtreeAsProcessed(
actualRoot,
sectionTaskIds,
processedTaskIds
processedTaskIds,
);
}
}
@ -238,7 +237,7 @@ export class TaskListRendererComponent extends Component {
directChildren.push(childTask);
} else {
console.warn(
`Child task ${childId} (parent: ${rootTask.id}) not found in allTasksMap.`
`Child task ${childId} (parent: ${rootTask.id}) not found in allTasksMap.`,
);
}
});
@ -253,7 +252,7 @@ export class TaskListRendererComponent extends Component {
0, // Root level is 0
directChildren, // Pass the actual children from the full map
allTasksMap, // Pass the full map for recursive building
this.plugin
this.plugin,
);
// Set up event handlers
@ -302,7 +301,7 @@ export class TaskListRendererComponent extends Component {
// Try updating in list view components
const listItemComponent = this.taskComponents.find(
(c) => c.getTask().id === updatedTask.id
(c) => c.getTask().id === updatedTask.id,
);
if (listItemComponent) {
listItemComponent.updateTask(updatedTask);

View file

@ -177,6 +177,8 @@ export class ContentComponent extends Component {
if (viewToggleBtn) {
setIcon(viewToggleBtn, this.isTreeView ? "git-branch" : "list");
}
console.log("toggle view mode");
// Save the new view mode state
saveViewMode(this.app, this.currentViewId, this.isTreeView);
this.refreshTaskList(); // Refresh list completely on view mode change
@ -258,11 +260,6 @@ export class ContentComponent extends Component {
const viewConfig = getViewSettingOrDefault(this.plugin, viewId);
let title = t(viewConfig.name);
// Special handling for project view title (if needed, maybe handled by component itself)
// if (viewId === "projects" && this.selectedProjectForView) {
// const projectName = this.selectedProjectForView.split("/").pop();
// title = projectName || t("Project");
// }
this.titleEl.setText(title);
// Re-initialize view mode for the new view
@ -374,6 +371,7 @@ export class ContentComponent extends Component {
}
private refreshTaskList() {
console.log("refreshing");
// Defer rendering if container is not visible yet (e.g., view hidden during init)
if (!this.isContainerVisible()) {
this.pendingForceRefresh = true;

View file

@ -110,7 +110,7 @@ export function clearAllMarks(markdown: string): string {
const escapedSymbol = symbol.replace(/[.*+?^${}()|[\\\]]/g, "\\$&");
const regex = new RegExp(
`${escapedSymbol}\\uFE0F? *\\d{4}-\\d{2}-\\d{2}`, // Use escaped symbol
"gu"
"gu",
);
cleanedMarkdown = cleanedMarkdown.replace(regex, "");
});
@ -119,7 +119,7 @@ export function clearAllMarks(markdown: string): string {
// First remove priority emojis anywhere in the text (with optional variation selector)
cleanedMarkdown = cleanedMarkdown.replace(
/(?:🔺||🔼|🔽|?|\[#[A-E]\])/gu,
""
"",
);
// Remove standalone exclamation marks (priority indicators)
@ -138,7 +138,7 @@ export function clearAllMarks(markdown: string): string {
const escapedRecurrenceSymbol =
DEFAULT_SYMBOLS.recurrenceSymbol.replace(
/[.*+?^${}()|[\\\]]/g,
"\\$&"
"\\$&",
);
// Create a string of escaped date/completion symbols for the lookahead
const escapedOtherSymbols = symbolsToRemove
@ -152,9 +152,9 @@ export function clearAllMarks(markdown: string): string {
const recurrenceRegex = new RegExp(
`${escapedRecurrenceSymbol}\\uFE0F? *.*?` +
// Lookahead for: space followed by (any date/completion/recurrence symbol OR non-date symbols OR @ OR #) OR end of string
`(?=\s(?:[${escapedOtherSymbols}${escapedNonDateSymbols}${escapedRecurrenceSymbol}]|@|#)|$)`,
"gu"
// Lookahead for: space followed by (any date/completion/recurrence symbol OR non-date symbols OR @ OR #) OR end of string
`(?=\s(?:[${escapedOtherSymbols}${escapedNonDateSymbols}${escapedRecurrenceSymbol}]|@|#)|$)`,
"gu",
);
cleanedMarkdown = cleanedMarkdown.replace(recurrenceRegex, "");
}
@ -163,7 +163,7 @@ export function clearAllMarks(markdown: string): string {
cleanedMarkdown = cleanedMarkdown.replace(
/\[(?:due|📅|completion|✅|created||start|🛫|scheduled|⏳|cancelled|❌|id|🆔|dependsOn|⛔|onCompletion|🏁|priority|repeat|recurrence|🔁|project|context)::\s*[^\]]+\]/gi,
// Corrected the emoji in the previous attempt
""
"",
);
// --- General Cleaning ---
@ -202,7 +202,7 @@ export function clearAllMarks(markdown: string): string {
const overlaps = preservedSegments.some(
(ps) =>
Math.max(ps.index, currentStart) <
Math.min(ps.index + ps.length, currentEnd)
Math.min(ps.index + ps.length, currentEnd),
);
if (!overlaps) {
preservedSegments.push({
@ -222,7 +222,7 @@ export function clearAllMarks(markdown: string): string {
const overlaps = preservedSegments.some(
(ps) =>
Math.max(ps.index, currentStart) <
Math.min(ps.index + ps.length, currentEnd)
Math.min(ps.index + ps.length, currentEnd),
);
if (!overlaps) {
preservedSegments.push({
@ -268,7 +268,7 @@ export function clearAllMarks(markdown: string): string {
// Remove any remaining simple tags but preserve special tags like #123-123-123
// Also ignore escaped \# (do not treat as tag)
tempMarkdown = (function removeSimpleTagsIgnoringEscapes(
input: string
input: string,
): string {
let out = "";
let i = 0;
@ -326,7 +326,7 @@ export function clearAllMarks(markdown: string): string {
// Task marker and final cleaning (applied to the string with links/code restored)
tempMarkdown = tempMarkdown.replace(
/^([\s>]*)?(-|\d+\.|\*|\+)\s\[([^\[\]]{1})\]\s*/,
""
"",
);
tempMarkdown = tempMarkdown.replace(/^# /, "");
tempMarkdown = tempMarkdown.replace(/\s+/g, " ").trim();
@ -343,15 +343,15 @@ export class MarkdownRendererComponent extends Component {
private container: HTMLElement;
private sourcePath: string;
private currentFile: TFile | null = null;
private renderQueue: Array<{ markdown: string; blockId?: string }> = [];
private isRendering: boolean = false;
private renderQueue: Array<{ markdown: string; contentId?: string }> = [];
private isRendering = false;
private blockElements: Map<string, HTMLElement> = new Map();
constructor(
private app: App,
container: HTMLElement,
sourcePath: string = "",
private hideMarks: boolean = true
sourcePath = "",
private hideMarks: boolean = true,
) {
super();
this.container = container;
@ -381,7 +381,7 @@ export class MarkdownRendererComponent extends Component {
*/
public async render(
markdown: string,
clearContainer: boolean = true
clearContainer = true,
): Promise<void> {
if (clearContainer) {
this.clear();
@ -392,15 +392,15 @@ export class MarkdownRendererComponent extends Component {
// Create block elements for each content block
for (let i = 0; i < blocks.length; i++) {
const blockId = `block-${Date.now()}-${i}`;
const contentId = `content-${Date.now()}-${i}`;
const blockEl = this.container.createEl("div", {
cls: ["markdown-block", "markdown-renderer"],
});
blockEl.dataset.blockId = blockId;
this.blockElements.set(blockId, blockEl);
blockEl.dataset.contentId = contentId;
this.blockElements.set(contentId, blockEl);
// Queue this block for rendering
this.queueRender(blocks[i], blockId);
this.queueRender(blocks[i], contentId);
}
// Start processing the queue
@ -425,8 +425,8 @@ export class MarkdownRendererComponent extends Component {
/**
* Queue a markdown block for rendering
*/
private queueRender(markdown: string, blockId?: string): void {
this.renderQueue.push({markdown, blockId});
private queueRender(markdown: string, contentId?: string): void {
this.renderQueue.push({ markdown, contentId });
this.processRenderQueue();
}
@ -445,11 +445,11 @@ export class MarkdownRendererComponent extends Component {
const item = this.renderQueue.shift();
if (!item) continue;
const {markdown, blockId} = item;
const { markdown, contentId } = item;
if (blockId) {
if (contentId) {
// Render to a specific block
const blockEl = this.blockElements.get(blockId);
const blockEl = this.blockElements.get(contentId);
if (blockEl) {
blockEl.empty();
await ObsidianMarkdownRenderer.render(
@ -457,7 +457,7 @@ export class MarkdownRendererComponent extends Component {
markdown,
blockEl,
this.sourcePath,
this
this,
);
}
} else {
@ -467,7 +467,7 @@ export class MarkdownRendererComponent extends Component {
markdown,
this.container,
this.sourcePath,
this
this,
);
}
@ -481,12 +481,12 @@ export class MarkdownRendererComponent extends Component {
/**
* Update a specific block with new content
* @param blockId The ID of the block to update
* @param contentId The ID of the block to update
* @param markdown The new markdown content
*/
public updateBlock(blockId: string, markdown: string): void {
if (this.blockElements.has(blockId)) {
this.queueRender(markdown, blockId);
public updateBlock(contentId: string, markdown: string): void {
if (this.blockElements.has(contentId)) {
this.queueRender(markdown, contentId);
}
}
@ -507,26 +507,26 @@ export class MarkdownRendererComponent extends Component {
* @returns The ID of the new block
*/
public addBlock(markdown: string): string {
const blockId = `block-${Date.now()}-${this.blockElements.size}`;
const contentId = `content-${Date.now()}-${this.blockElements.size}`;
const blockEl = this.container.createEl("div", {
cls: "markdown-block",
});
blockEl.dataset.blockId = blockId;
this.blockElements.set(blockId, blockEl);
blockEl.dataset.contentId = contentId;
this.blockElements.set(contentId, blockEl);
this.queueRender(markdown, blockId);
return blockId;
this.queueRender(markdown, contentId);
return contentId;
}
/**
* Remove a specific block
* @param blockId The ID of the block to remove
* @param contentId The ID of the block to remove
*/
public removeBlock(blockId: string): void {
const blockEl = this.blockElements.get(blockId);
public removeBlock(contentId: string): void {
const blockEl = this.blockElements.get(contentId);
if (blockEl) {
blockEl.remove();
this.blockElements.delete(blockId);
this.blockElements.delete(contentId);
}
}

View file

@ -19,7 +19,7 @@ import { parseTaskLine } from "@/utils/task/task-operations";
*/
export function cycleCompleteStatusExtension(
app: App,
plugin: TaskProgressBarPlugin
plugin: TaskProgressBarPlugin,
) {
return EditorState.transactionFilter.of((tr) => {
return handleCycleCompleteStatusTransaction(tr, app, plugin);
@ -59,7 +59,7 @@ function isValidTaskMarkerReplacement(
originalText: string,
pos: number,
newLineText: string,
plugin: TaskProgressBarPlugin
plugin: TaskProgressBarPlugin,
): boolean {
// Only single character replacements are considered valid task marker operations
if (toA - fromA !== 1 || insertedText.length !== 1) {
@ -102,7 +102,7 @@ function isValidTaskMarkerReplacement(
// Log successful validation for debugging
console.log(
`Valid task marker replacement detected. No user selection or selection doesn't cover replacement range. Original: '${originalText}' -> New: '${insertedText}' at position ${fromA}-${toA}`
`Valid task marker replacement detected. No user selection or selection doesn't cover replacement range. Original: '${originalText}' -> New: '${insertedText}' at position ${fromA}-${toA}`,
);
return true;
@ -118,7 +118,7 @@ function isValidTaskMarkerReplacement(
export function findTaskStatusChanges(
tr: Transaction,
tasksPluginLoaded: boolean,
plugin?: TaskProgressBarPlugin
plugin?: TaskProgressBarPlugin,
): {
position: number;
currentMark: string;
@ -176,12 +176,12 @@ export function findTaskStatusChanges(
(change.text === "" &&
(tr.startState.doc.sliceString(
change.fromA,
change.toA
change.toA,
) === "\t" ||
tr.startState.doc.sliceString(
change.fromA,
change.toA
) === " "))
change.toA,
) === " ")),
);
if (allIndentChanges) {
@ -203,14 +203,14 @@ export function findTaskStatusChanges(
toA: number,
fromB: number,
toB: number,
inserted: Text
inserted: Text,
) => {
// Check for deletion operation (inserted text is empty)
if (inserted.toString() === "" && toA > fromA) {
// Get the deleted content
const deletedContent = tr.startState.doc.sliceString(
fromA,
toA
toA,
);
// Check if the deleted content is a dash character
if (deletedContent === "-") {
@ -218,14 +218,14 @@ export function findTaskStatusChanges(
const line = tr.startState.doc.lineAt(fromA);
const textBeforeDash = line.text.substring(
0,
fromA - line.from
fromA - line.from,
);
if (textBeforeDash.trim() === "") {
isDeletingTaskMarker = true;
}
}
}
}
},
);
if (isDeletingTaskMarker) {
@ -239,7 +239,7 @@ export function findTaskStatusChanges(
toA: number,
fromB: number,
toB: number,
inserted: Text
inserted: Text,
) => {
// Get the inserted text
const insertedText = inserted.toString();
@ -247,7 +247,7 @@ export function findTaskStatusChanges(
// Check if this is a new task creation with a newline
if (insertedText.includes("\n")) {
console.log(
"New task creation detected with newline, skipping"
"New task creation detected with newline, skipping",
);
return;
}
@ -334,8 +334,8 @@ export function findTaskStatusChanges(
/[a-zA-Z]/.test(insertedText) &&
(plugin
? !Object.values(
getTaskStatusConfig(plugin).marks
).includes(insertedText)
getTaskStatusConfig(plugin).marks,
).includes(insertedText)
: true)
)
) {
@ -343,7 +343,7 @@ export function findTaskStatusChanges(
if (fromA !== toA) {
const originalText = tr.startState.doc.sliceString(
fromA,
toA
toA,
);
// Only perform validation if plugin is provided
@ -357,23 +357,23 @@ export function findTaskStatusChanges(
originalText,
pos,
newLineText,
plugin
plugin,
);
if (!isValidReplacement) {
console.log(
`Detected invalid task marker replacement (fromA=${fromA}, toA=${toA}). User manually input '${insertedText}' (original: '${originalText}'), skipping automatic cycling.`
`Detected invalid task marker replacement (fromA=${fromA}, toA=${toA}). User manually input '${insertedText}' (original: '${originalText}'), skipping automatic cycling.`,
);
return; // Skip this change, don't add to taskChanges
}
console.log(
`Detected valid task marker replacement (fromA=${fromA}, toA=${toA}). Original: '${originalText}' -> New: '${insertedText}', proceeding with automatic cycling.`
`Detected valid task marker replacement (fromA=${fromA}, toA=${toA}). Original: '${originalText}' -> New: '${insertedText}', proceeding with automatic cycling.`,
);
} else {
// Fallback to original logic for backwards compatibility
console.log(
`Detected replacement operation (fromA=${fromA}, toA=${toA}). User manually input '${insertedText}', skipping automatic cycling.`
`Detected replacement operation (fromA=${fromA}, toA=${toA}). User manually input '${insertedText}', skipping automatic cycling.`,
);
return; // Skip this change, don't add to taskChanges
}
@ -434,12 +434,12 @@ export function findTaskStatusChanges(
originalFromB: fromB,
originalToB: toB,
originalInsertedText: insertedText,
}
}
: null,
});
}
}
}
},
);
return taskChanges;
@ -455,7 +455,7 @@ export function findTaskStatusChanges(
export function handleCycleCompleteStatusTransaction(
tr: Transaction,
app: App,
plugin: TaskProgressBarPlugin
plugin: TaskProgressBarPlugin,
): TransactionSpec {
// Only process transactions that change the document and are user input events
if (!tr.docChanged) {
@ -477,8 +477,6 @@ export function handleCycleCompleteStatusTransaction(
return tr;
}
console.log(tr.changes, "changes");
// Check for markdown link insertion (cmd+k)
if (tr.isUserEvent("input.autocomplete")) {
// Look for typical markdown link pattern [text]() in the changes
@ -509,7 +507,7 @@ export function handleCycleCompleteStatusTransaction(
toA: number,
fromB: number,
toB: number,
inserted: Text
inserted: Text,
) => {
// Check if this removes a dash character and somehow modifies a task marker elsewhere
const insertedText = inserted.toString();
@ -522,13 +520,13 @@ export function handleCycleCompleteStatusTransaction(
tr.newDoc
.sliceString(
Math.max(0, fromB - 5),
Math.min(fromB + 5, tr.newDoc.length)
Math.min(fromB + 5, tr.newDoc.length),
)
.includes("[")
) {
hasInvalidTaskChange = true;
}
}
},
);
if (hasInvalidTaskChange) {
@ -539,7 +537,7 @@ export function handleCycleCompleteStatusTransaction(
const taskStatusChanges = findTaskStatusChanges(
tr,
!!getTasksAPI(plugin),
plugin
plugin,
);
if (taskStatusChanges.length === 0) {
return tr;
@ -548,7 +546,7 @@ export function handleCycleCompleteStatusTransaction(
// Get the task cycle and marks from plugin settings
const { cycle, marks, excludeMarksFromCycle } = getTaskStatusConfig(plugin);
const remainingCycle = cycle.filter(
(state) => !excludeMarksFromCycle.includes(state)
(state) => !excludeMarksFromCycle.includes(state),
);
// If no cycle is defined, don't do anything
@ -579,7 +577,7 @@ export function handleCycleCompleteStatusTransaction(
// Check for deletions and task changes in the same transaction
const hasDeletion = changes.some(
(change) => change.text === "" && change.toA > change.fromA
(change) => change.text === "" && change.toA > change.fromA,
);
const hasTaskMarkerChange = changes.some((change) => {
// Check if this change affects a task marker position [x]
@ -709,11 +707,16 @@ export function handleCycleCompleteStatusTransaction(
const nextStatus = remainingCycle[nextStatusIndex];
const nextMark = marks[nextStatus] || " ";
// Log the cycle calculation for debugging
console.log(
`[Status Cycler] Calculated cycle: '${currentMark}' (${remainingCycle[currentStatusIndex]}) → '${nextMark}' (${nextStatus})`,
);
// Check if the current mark is the same as what would be the next mark in the cycle
// If they are the same, we don't need to process this further
if (currentMark === nextMark) {
console.log(
`Current mark '${currentMark}' is already the next mark in the cycle. Skipping processing.`
`Current mark '${currentMark}' is already the next mark in the cycle. Skipping processing.`,
);
continue;
}
@ -732,7 +735,7 @@ export function handleCycleCompleteStatusTransaction(
// If user's input already matches the next mark, don't cycle
if (userInputMark === nextMark) {
console.log(
`User input '${userInputMark}' already matches the next mark '${nextMark}' in the cycle. Skipping processing.`
`User input '${userInputMark}' already matches the next mark '${nextMark}' in the cycle. Skipping processing.`,
);
continue;
}
@ -741,7 +744,7 @@ export function handleCycleCompleteStatusTransaction(
const posLine = tr.newDoc.lineAt(position);
const newLineText = posLine.text;
const originalPosLine = tr.startState.doc.lineAt(
Math.min(position, tr.startState.doc.length)
Math.min(position, tr.startState.doc.length),
);
const originalLineText = originalPosLine.text;
@ -782,7 +785,7 @@ export function handleCycleCompleteStatusTransaction(
// This prevents unexpected data loss when creating a task
if (isNewEmptyTask || isManualTaskCreation) {
console.log(
`New empty task detected with mark ' ', leaving as is regardless of alwaysCycleNewTasks setting`
`New empty task detected with mark ' ', leaving as is regardless of alwaysCycleNewTasks setting`,
);
continue;
}
@ -801,7 +804,7 @@ export function handleCycleCompleteStatusTransaction(
// Check if the mark position is within the current line and valid
if (markPosition < lineAtMark.from || markPosition >= lineEnd) {
console.log(
`Mark position ${markPosition} is beyond the current line range ${lineAtMark.from}-${lineEnd}, skipping processing`
`Mark position ${markPosition} is beyond the current line range ${lineAtMark.from}-${lineEnd}, skipping processing`,
);
continue;
}
@ -810,7 +813,7 @@ export function handleCycleCompleteStatusTransaction(
const validTo = Math.min(markPosition + 1, lineEnd);
if (validTo <= markPosition) {
console.log(
`Invalid modification range ${markPosition}-${validTo}, skipping processing`
`Invalid modification range ${markPosition}-${validTo}, skipping processing`,
);
continue;
}
@ -819,45 +822,17 @@ export function handleCycleCompleteStatusTransaction(
completingTask = true;
}
// If nextMark is 'x', 'X', or space and we have Tasks plugin info, use the original insertion
if (
(nextMark === "x" || nextMark === "X" || nextMark === " ") &&
tasksInfo !== null
) {
// Verify if the Tasks plugin's modification range is within the same line
const origLineAtFromA = tr.startState.doc.lineAt(
tasksInfo.originalFromA
);
const origLineAtToA = tr.startState.doc.lineAt(
Math.min(tasksInfo.originalToA, tr.startState.doc.length)
);
// Log the status cycle for debugging
console.log(
`[Status Cycler] Cycling at position ${markPosition}: '${currentMark}' → '${nextMark}'`,
);
if (origLineAtFromA.number !== origLineAtToA.number) {
console.log(
`Tasks plugin modification range spans multiple lines ${origLineAtFromA.number}-${origLineAtToA.number}, using safe modification range`
);
// Use the safe modification range
newChanges.push({
from: markPosition,
to: validTo,
insert: nextMark,
});
} else {
// Use the original insertion from Tasks plugin
newChanges.push({
from: tasksInfo.originalFromA,
to: tasksInfo.originalToA,
insert: tasksInfo.originalInsertedText,
});
}
} else {
// Add a change to replace the current mark with the next one
newChanges.push({
from: markPosition,
to: validTo,
insert: nextMark,
});
}
// Always use the calculated nextMark for consistent cycling behavior
newChanges.push({
from: markPosition,
to: validTo,
insert: nextMark,
});
}
// If we found any changes to make, create a new transaction
@ -870,7 +845,7 @@ export function handleCycleCompleteStatusTransaction(
line.text,
line.number,
plugin.settings.preferMetadataFormat,
plugin // Pass plugin for configurable prefix support
plugin, // Pass plugin for configurable prefix support
);
// if (completingTask && task) {
// app.workspace.trigger("task-genius:task-completed", task);

View file

@ -140,7 +140,7 @@ export class FluentTaskView extends ItemView {
updateWorkspaceLeafWidth = debounce(() => {
this.app.workspace.trigger(
"task-genius:leaf-width-updated",
this.leaf.width
this.leaf.width,
);
}, 200);
@ -190,7 +190,7 @@ export class FluentTaskView extends ItemView {
} catch (e) {
console.warn(
"[TG] Failed to sync selectedProject from filter state",
e
e,
);
}
@ -202,7 +202,7 @@ export class FluentTaskView extends ItemView {
this.filteredTasks = this.dataManager.applyFilters(this.tasks);
this.updateView();
},
150
150,
);
onResize(): void {
@ -213,7 +213,7 @@ export class FluentTaskView extends ItemView {
const availableModes =
this.componentManager?.getAvailableModesForView(
viewId,
this.viewState.selectedProject
this.viewState.selectedProject,
) ?? [];
const storedMode = this.viewState.viewModeByViewId?.[viewId];
@ -243,7 +243,7 @@ export class FluentTaskView extends ItemView {
const prefersTree = getInitialViewMode(
this.app,
this.plugin,
viewId
viewId,
);
if (prefersTree && availableModes.includes("tree")) {
return "tree";
@ -263,7 +263,7 @@ export class FluentTaskView extends ItemView {
const availableModes = this.componentManager?.getAvailableModesForView(
viewId,
this.viewState.selectedProject
this.viewState.selectedProject,
);
if (
availableModes &&
@ -289,14 +289,14 @@ export class FluentTaskView extends ItemView {
// ====================
if (this.DEBUG_MODE) {
console.log(
"[TG] Initializing UI, managers, structure, and events..."
"[TG] Initializing UI, managers, structure, and events...",
);
}
this.contentEl.empty();
this.contentEl.toggleClass(
["task-genius-fluent-view", "task-genius-view"],
true
true,
);
// Create root container (use exact same class as original)
@ -320,7 +320,7 @@ export class FluentTaskView extends ItemView {
if (this.DEBUG_MODE) {
console.log(
"[TG] ✅ UI, managers, structure, and events initialized"
"[TG] ✅ UI, managers, structure, and events initialized",
);
}
@ -338,7 +338,7 @@ export class FluentTaskView extends ItemView {
this.viewState.currentWorkspace = savedWorkspaceId;
if (this.DEBUG_MODE) {
console.log(
`[TG] Restored workspace ID: ${savedWorkspaceId}`
`[TG] Restored workspace ID: ${savedWorkspaceId}`,
);
}
}
@ -355,7 +355,7 @@ export class FluentTaskView extends ItemView {
this.liveFilterState = state;
this.app.saveLocalStorage(
"task-genius-view-filter",
state || null
state || null,
);
},
setCurrentFilterState: (state) => {
@ -373,14 +373,14 @@ export class FluentTaskView extends ItemView {
const availableModes =
this.componentManager?.getAvailableModesForView(
this.currentViewId,
selectedProject
selectedProject,
) ?? [];
if (
availableModes.length > 0 &&
!availableModes.includes(viewMode)
) {
return this.ensureViewModeForView(
this.currentViewId
this.currentViewId,
);
}
return viewMode;
@ -388,7 +388,7 @@ export class FluentTaskView extends ItemView {
this.viewState.viewMode = normalizedMode;
this.recordViewModeForView(
this.currentViewId,
normalizedMode
normalizedMode,
);
this.topNavigation?.setViewMode(normalizedMode);
if (clearSearch) {
@ -408,7 +408,7 @@ export class FluentTaskView extends ItemView {
await this.dataManager.loadTasks(false); // Will trigger onTasksLoaded callback
await this.dataManager.registerDataflowListeners();
console.log(
`[TG] ✅ Loaded ${this.tasks.length} tasks, ${this.filteredTasks.length} after filters`
`[TG] ✅ Loaded ${this.tasks.length} tasks, ${this.filteredTasks.length} after filters`,
);
// ====================
@ -433,7 +433,7 @@ export class FluentTaskView extends ItemView {
// ====================
if (this.DEBUG_MODE) {
console.log(
`[TG] Finalizing (isInitializing was ${this.isInitializing})`
`[TG] Finalizing (isInitializing was ${this.isInitializing})`,
);
}
@ -463,7 +463,7 @@ export class FluentTaskView extends ItemView {
searchQuery: this.viewState.searchQuery || "",
filterInputValue: this.viewState.filterInputValue || "",
}),
() => this.isInitializing
() => this.isInitializing,
);
this.dataManager.setCallbacks({
onTasksLoaded: (tasks, error) => {
@ -479,7 +479,7 @@ export class FluentTaskView extends ItemView {
this.isLoading = false;
// Apply filters immediately after loading
this.filteredTasks = this.dataManager.applyFilters(
this.tasks
this.tasks,
);
this.updateView();
}
@ -504,7 +504,7 @@ export class FluentTaskView extends ItemView {
this.app,
this.plugin,
() => this.workspaceId,
() => this.useSideLeaves()
() => this.useSideLeaves(),
);
this.actionHandlers.setCallbacks({
onTaskSelectionChanged: (task) => {
@ -520,7 +520,7 @@ export class FluentTaskView extends ItemView {
this.tasks[index] = updatedTask;
// Re-apply filters
this.filteredTasks = this.dataManager.applyFilters(
this.tasks
this.tasks,
);
this.updateView();
}
@ -551,7 +551,7 @@ export class FluentTaskView extends ItemView {
onNavigateToView: (viewId) => {
this.recordViewModeForView(
this.currentViewId,
this.viewState.viewMode
this.viewState.viewMode,
);
this.currentViewId = viewId;
@ -559,7 +559,7 @@ export class FluentTaskView extends ItemView {
// This enables the full project overview mode
if (viewId === "projects") {
console.log(
"[TG] Navigating to projects overview - clearing project selection"
"[TG] Navigating to projects overview - clearing project selection",
);
this.viewState.selectedProject = undefined;
@ -573,25 +573,25 @@ export class FluentTaskView extends ItemView {
.map((g: any) => ({
...g,
filters: (g.filters || []).filter(
(f: any) => f.property !== "project"
(f: any) => f.property !== "project",
),
}))
.filter(
(g: any) =>
g.filters && g.filters.length > 0
g.filters && g.filters.length > 0,
);
this.liveFilterState = nextState as any;
this.currentFilterState = nextState as any;
this.app.saveLocalStorage(
"task-genius-view-filter",
nextState
nextState,
);
// Broadcast filter change
this.app.workspace.trigger(
"task-genius:filter-changed",
nextState
nextState,
);
}
} catch (e) {
@ -607,6 +607,7 @@ export class FluentTaskView extends ItemView {
this.recordViewModeForView(viewId, nextMode);
this.topNavigation?.setViewMode(nextMode);
this.updateView();
this.workspaceStateManager.saveFilterStateToWorkspace();
},
onSearchQueryChanged: (query) => {
this.viewState.searchQuery = query;
@ -622,17 +623,17 @@ export class FluentTaskView extends ItemView {
// Switch to projects view
this.recordViewModeForView(
this.currentViewId,
this.viewState.viewMode
this.viewState.viewMode,
);
this.currentViewId = "projects";
this.filteredTasks = this.dataManager.applyFilters(this.tasks);
const projectsViewMode = this.ensureViewModeForView(
this.currentViewId
this.currentViewId,
);
this.viewState.viewMode = projectsViewMode;
this.recordViewModeForView(
this.currentViewId,
projectsViewMode
projectsViewMode,
);
this.topNavigation?.setViewMode(projectsViewMode);
@ -649,7 +650,7 @@ export class FluentTaskView extends ItemView {
.map((g: any) => ({
...g,
filters: (g.filters || []).filter(
(f: any) => f.property !== "project"
(f: any) => f.property !== "project",
),
}))
.filter((g: any) => g.filters && g.filters.length > 0); // Remove empty groups
@ -675,26 +676,27 @@ export class FluentTaskView extends ItemView {
this.currentFilterState = nextState as any;
this.app.saveLocalStorage(
"task-genius-view-filter",
nextState
nextState,
);
// Broadcast so any open filter UI reacts and header button shows reset
// The filter-changed event listener will handle applyFilters and updateView
this.app.workspace.trigger(
"task-genius:filter-changed",
nextState
nextState,
);
} catch (e) {
console.warn(
"[TG] Failed to project-sync filter UI state",
e
e,
);
// If filter sync fails, still update the view
this.filteredTasks = this.dataManager.applyFilters(
this.tasks
this.tasks,
);
this.updateView();
}
this.workspaceStateManager.saveFilterStateToWorkspace();
},
onViewModeChanged: (mode) => {
this.viewState.viewMode = mode;
@ -729,7 +731,7 @@ export class FluentTaskView extends ItemView {
viewMode: this.viewState.viewMode,
}),
() => this.currentFilterState,
() => this.liveFilterState
() => this.liveFilterState,
);
this.addChild(this.workspaceStateManager);
@ -737,7 +739,7 @@ export class FluentTaskView extends ItemView {
this.selectionManager = new TaskSelectionManager(
this.app,
this.plugin,
this
this,
);
this.addChild(this.selectionManager);
@ -805,7 +807,7 @@ export class FluentTaskView extends ItemView {
this.rootContainerEl,
this.headerEl, // Obsidian's view header
this.titleEl, // Obsidian's view title element
() => this.filteredTasks.length
() => this.filteredTasks.length,
);
this.layoutManager.setOnSidebarNavigate((viewId) => {
this.actionHandlers.handleNavigate(viewId);
@ -823,7 +825,7 @@ export class FluentTaskView extends ItemView {
onTaskUpdate: async (originalTask, updatedTask) => {
await this.actionHandlers.handleTaskUpdate(
originalTask,
updatedTask
updatedTask,
);
},
});
@ -843,7 +845,7 @@ export class FluentTaskView extends ItemView {
} else {
sidebarEl.hide();
console.log(
"[TG] Using workspace side leaves: skip in-view sidebar"
"[TG] Using workspace side leaves: skip in-view sidebar",
);
}
@ -861,7 +863,7 @@ export class FluentTaskView extends ItemView {
// Sort click callback (future extension point)
},
() => this.actionHandlers.handleSettingsClick(),
[] // Start with empty modes, will be set by updateView
[], // Start with empty modes, will be set by updateView
);
this.addChild(this.topNavigation);
@ -882,23 +884,31 @@ export class FluentTaskView extends ItemView {
onTaskUpdate: async (originalTask, updatedTask) => {
await this.actionHandlers.handleTaskUpdate(
originalTask,
updatedTask
updatedTask,
);
},
onTaskContextMenu: (event, task) => {
this.actionHandlers.handleTaskContextMenu(event, task);
},
onKanbanTaskStatusUpdate: (taskId, newStatusMark) => {
console.log(`[FluentTaskView] Kanban status update callback received: taskId=${taskId}, mark=${newStatusMark}`);
console.log(`[FluentTaskView] Current task list size: ${this.tasks.length}`);
const task = this.tasks.find((t) => t.id === taskId);
if (task) {
console.log('[FluentTaskView] Task found, delegating to action handler');
this.actionHandlers.handleKanbanTaskStatusUpdate(
task,
newStatusMark
newStatusMark,
);
} else {
console.error(`[FluentTaskView] CRITICAL: Task ${taskId} not found in local task list`);
console.error('[FluentTaskView] Available task IDs:', this.tasks.slice(0, 5).map(t => t.id));
console.error('[FluentTaskView] This indicates a synchronization issue between kanban and main view');
}
},
},
this.selectionManager
this.selectionManager,
);
this.addChild(this.componentManager);
this.componentManager.initializeViewComponents();
@ -947,7 +957,7 @@ export class FluentTaskView extends ItemView {
this.workspaceStateManager.captureFilterStateSnapshot();
if (snapshot) {
await this.workspaceStateManager.saveFilterStateImmediately(
snapshot
snapshot,
);
}
@ -969,7 +979,7 @@ export class FluentTaskView extends ItemView {
this.liveFilterState = state;
this.app.saveLocalStorage(
"task-genius-view-filter",
state || null
state || null,
);
},
setCurrentFilterState: (state) => {
@ -988,14 +998,14 @@ export class FluentTaskView extends ItemView {
const availableModes =
this.componentManager?.getAvailableModesForView(
this.currentViewId,
selectedProject
selectedProject,
) ?? [];
if (
availableModes.length > 0 &&
!availableModes.includes(viewMode)
) {
return this.ensureViewModeForView(
this.currentViewId
this.currentViewId,
);
}
return viewMode;
@ -1003,7 +1013,7 @@ export class FluentTaskView extends ItemView {
this.viewState.viewMode = normalizedMode;
this.recordViewModeForView(
this.currentViewId,
normalizedMode
normalizedMode,
);
this.topNavigation?.setViewMode(normalizedMode);
if (clearSearch) {
@ -1019,7 +1029,7 @@ export class FluentTaskView extends ItemView {
// Reload tasks
await this.dataManager.loadTasks();
}
})
}),
);
// Workspace overrides saved event
@ -1029,7 +1039,7 @@ export class FluentTaskView extends ItemView {
await this.workspaceStateManager.applyWorkspaceSettings();
await this.dataManager.loadTasks();
}
})
}),
);
// Settings changed event (skip for filter state changes)
@ -1037,7 +1047,7 @@ export class FluentTaskView extends ItemView {
on(this.app, Events.SETTINGS_CHANGED, async () => {
// Reload on settings change (unless caused by filter state save)
await this.dataManager.loadTasks();
})
}),
);
}
@ -1047,8 +1057,8 @@ export class FluentTaskView extends ItemView {
"task-genius:filter-changed",
(filterState: RootFilterState, leafId?: string) => {
this.handleFilterChanged(filterState, leafId);
}
)
},
),
);
// Sidebar selection changed (when using side leaves)
@ -1062,9 +1072,10 @@ export class FluentTaskView extends ItemView {
) {
this.currentViewId = payload.selectionId;
this.filteredTasks = this.dataManager.applyFilters(
this.tasks
this.tasks,
);
this.updateView();
this.workspaceStateManager.saveFilterStateToWorkspace();
}
if (
payload.selectionType === "project" &&
@ -1072,11 +1083,11 @@ export class FluentTaskView extends ItemView {
) {
// Use the full project selection logic via actionHandlers
this.actionHandlers.handleProjectSelect(
payload.selectionId || ""
payload.selectionId || "",
);
}
}
})
}),
);
}
@ -1098,31 +1109,20 @@ export class FluentTaskView extends ItemView {
* Update view with current state
*/
private updateView() {
// Enhanced logging with all critical state
console.log(
`[TG] updateView called: ` +
`isInitializing=${this.isInitializing}, ` +
`viewId=${this.currentViewId}, ` +
`tasks=${this.tasks.length}, ` +
`filtered=${this.filteredTasks.length}, ` +
`isLoading=${this.isLoading}, ` +
`hasError=${!!this.loadError}`
);
if (this.isInitializing) {
console.log("[TG] ⏭️ Skip update during initialization");
console.log("[TG] Skip update during initialization");
return;
}
console.log(
`[TG] ▶️ Proceeding with view update for ${this.currentViewId}`
`[TG] Proceeding with view update for ${this.currentViewId}`,
);
// Update top navigation available modes based on current view
const availableModes =
this.componentManager?.getAvailableModesForView(
this.currentViewId,
this.viewState.selectedProject
this.viewState.selectedProject,
) ?? [];
this.topNavigation?.updateAvailableModes(availableModes);
@ -1178,7 +1178,7 @@ export class FluentTaskView extends ItemView {
this.filteredTasks,
this.currentFilterState,
this.viewState.viewMode,
this.viewState.selectedProject
this.viewState.selectedProject,
);
}

View file

@ -179,6 +179,7 @@
.full-calendar-container .calendar-event {
/* Example style for an event */
background-color: var(--interactive-accent);
--bold-color: var(--text-on-accent);
color: var(--text-on-accent);
border-radius: var(--radius-s);
padding: 2px 4px;
@ -300,7 +301,9 @@
/* margin-bottom: var(--size-4-2); */
width: 100%; /* Ensure it takes full width */
border-radius: var(--radius-s);
transition: transform 0.15s ease, box-shadow 0.15s ease;
transition:
transform 0.15s ease,
box-shadow 0.15s ease;
}
.full-calendar-container .calendar-event-timed:hover {
@ -445,24 +448,49 @@
}
/* Adjust grid layouts when weekends are hidden */
.full-calendar-container .calendar-view-container.hide-weekends .calendar-weekday-header {
grid-template-columns: repeat(5, 1fr) !important; /* 5 columns instead of 7 when weekends hidden */
.full-calendar-container
.calendar-view-container.hide-weekends
.calendar-weekday-header {
grid-template-columns: repeat(
5,
1fr
) !important; /* 5 columns instead of 7 when weekends hidden */
}
.full-calendar-container .calendar-view-container.hide-weekends .calendar-month-grid {
grid-template-columns: repeat(5, 1fr) !important; /* 5 columns instead of 7 when weekends hidden */
.full-calendar-container
.calendar-view-container.hide-weekends
.calendar-month-grid {
grid-template-columns: repeat(
5,
1fr
) !important; /* 5 columns instead of 7 when weekends hidden */
}
.full-calendar-container .calendar-view-container.hide-weekends .calendar-week-header {
grid-template-columns: repeat(5, 1fr) !important; /* 5 columns instead of 7 when weekends hidden */
.full-calendar-container
.calendar-view-container.hide-weekends
.calendar-week-header {
grid-template-columns: repeat(
5,
1fr
) !important; /* 5 columns instead of 7 when weekends hidden */
}
.full-calendar-container .calendar-view-container.hide-weekends .calendar-week-grid {
grid-template-columns: repeat(5, 1fr) !important; /* 5 columns instead of 7 when weekends hidden */
.full-calendar-container
.calendar-view-container.hide-weekends
.calendar-week-grid {
grid-template-columns: repeat(
5,
1fr
) !important; /* 5 columns instead of 7 when weekends hidden */
}
.full-calendar-container .calendar-view-container.hide-weekends .mini-month-grid {
grid-template-columns: repeat(5, 1fr) !important; /* 5 columns instead of 7 when weekends hidden */
.full-calendar-container
.calendar-view-container.hide-weekends
.mini-month-grid {
grid-template-columns: repeat(
5,
1fr
) !important; /* 5 columns instead of 7 when weekends hidden */
}
/* Note: Weekend elements are not created when hideWeekends is enabled,
@ -637,7 +665,9 @@
/* Make calendar events draggable */
.calendar-events-container .calendar-event {
cursor: grab;
transition: transform 0.2s ease, box-shadow 0.2s ease;
transition:
transform 0.2s ease,
box-shadow 0.2s ease;
}
.calendar-events-container .calendar-event:hover {

View file

@ -530,7 +530,9 @@ export function filterTasks(
// --- 基本筛选:隐藏已完成和空白任务 ---
// 注意:这些是基础过滤条件,始终应用
if (viewConfig.hideCompletedAndAbandonedTasks) {
filtered = filtered.filter((task) => !task.completed);
filtered = filtered.filter((task) =>
isNotCompleted(plugin, task, viewId),
);
}
if (viewConfig.filterBlanks) {

File diff suppressed because one or more lines are too long