fix(workspace): resolve filter state persistence and restoration issues

- Fix workspace filter state not persisting correctly across switches
- Add detailed logging for workspace operations debugging
- Store fluentFilterState separately per workspace to prevent cross-contamination
- Handle default workspace fluentFilterState as workspace-scoped setting
- Save filter state when search query or project selection changes
- Clear cache properly when switching workspaces
- Add console logging to trace filter save/restore operations
This commit is contained in:
Quorafind 2025-09-22 21:10:07 +08:00
parent 2b7179ed9e
commit f617992d80
3 changed files with 358 additions and 93 deletions

View file

@ -18,7 +18,10 @@ import "./styles/v2-content-header.css";
import "@/styles/v2-project-popover.css";
import { TopNavigation, ViewMode } from "./components/V2TopNavigation";
import { V2ViewState } from "./types";
import { onWorkspaceSwitched, onWorkspaceOverridesSaved } from "./events/ui-event";
import {
onWorkspaceSwitched,
onWorkspaceOverridesSaved,
} from "./events/ui-event";
import { Events, on } from "../../dataflow/events/Events";
import { TaskListItemComponent } from "../../components/features/task/view/listItem";
import { TaskTreeItemComponent } from "../../components/features/task/view/treeItem";
@ -144,7 +147,8 @@ export class TaskViewV2 extends ItemView {
this.tasks = this.plugin.preloadedTasks || [];
// Initialize workspace ID
this.workspaceId = plugin.workspaceManager?.getActiveWorkspace().id || "";
this.workspaceId =
plugin.workspaceManager?.getActiveWorkspace().id || "";
this.viewState.currentWorkspace = this.workspaceId;
}
@ -170,6 +174,9 @@ export class TaskViewV2 extends ItemView {
this.registerEvent(
onWorkspaceSwitched(this.app, async (payload) => {
if (payload.workspaceId !== this.workspaceId) {
// Save current workspace state before switching
this.saveWorkspaceLayout();
// Switch to the new workspace and restore its state
this.workspaceId = payload.workspaceId;
this.viewState.currentWorkspace = payload.workspaceId;
await this.applyWorkspaceSettings();
@ -726,6 +733,10 @@ export class TaskViewV2 extends ItemView {
);
}
// Persist and update header UI
this.saveFilterStateToWorkspace();
this.updateActionButtons();
// Apply filters with debouncing
debouncedApplyFilter();
}
@ -1789,7 +1800,9 @@ export class TaskViewV2 extends ItemView {
private async applyWorkspaceSettings() {
if (!this.plugin.workspaceManager || !this.workspaceId) return;
const settings = this.plugin.workspaceManager.getEffectiveSettings(this.workspaceId);
const settings = this.plugin.workspaceManager.getEffectiveSettings(
this.workspaceId
);
// Workspace settings are now restored via restoreFilterStateFromWorkspace
// This method is kept for future workspace-specific settings that are not filter-related
@ -1813,7 +1826,8 @@ export class TaskViewV2 extends ItemView {
// Update UI to reflect workspace change
if (this.sidebar) {
// Update sidebar workspace selection if needed
const workspace = this.plugin.workspaceManager?.getWorkspace(workspaceId);
const workspace =
this.plugin.workspaceManager?.getWorkspace(workspaceId);
if (workspace) {
this.sidebar.updateWorkspace(workspace);
}
@ -1833,6 +1847,9 @@ export class TaskViewV2 extends ItemView {
this.viewState.searchQuery = query;
this.applyFilters();
this.updateView();
// Persist and update header UI
this.saveFilterStateToWorkspace();
this.updateActionButtons();
}
private handleViewModeChange(mode: ViewMode) {
@ -2467,48 +2484,123 @@ export class TaskViewV2 extends ItemView {
}
// Save filter state to workspace - debounced to avoid infinite loops
private saveFilterStateToWorkspace = debounce(() => {
if (!this.plugin.workspaceManager || !this.workspaceId) return;
private saveFilterStateToWorkspace = debounce(
() => {
if (!this.plugin.workspaceManager || !this.workspaceId) return;
const effectiveSettings = this.plugin.workspaceManager.getEffectiveSettings(this.workspaceId);
const effectiveSettings =
this.plugin.workspaceManager.getEffectiveSettings(
this.workspaceId
);
// Save current filter state
if (!effectiveSettings.v2FilterState) {
effectiveSettings.v2FilterState = {};
}
// Save current filter state
if (!effectiveSettings.v2FilterState) {
effectiveSettings.v2FilterState = {};
}
effectiveSettings.v2FilterState[this.currentViewId] = {
filters: this.viewState.filters,
searchQuery: this.viewState.searchQuery,
selectedProject: this.viewState.selectedProject,
advancedFilter: this.currentFilterState,
viewMode: this.viewState.viewMode
};
const payload = {
filters: this.viewState.filters,
searchQuery: this.viewState.searchQuery,
selectedProject: this.viewState.selectedProject,
advancedFilter: this.currentFilterState,
viewMode: this.viewState.viewMode,
};
effectiveSettings.v2FilterState[this.currentViewId] = payload;
// Use saveOverridesQuietly to avoid triggering SETTINGS_CHANGED event
this.plugin.workspaceManager.saveOverridesQuietly(this.workspaceId, effectiveSettings);
}, 500, true)
console.log("[TG-WORKSPACE] saveFilterStateToWorkspace", {
workspaceId: this.workspaceId,
viewId: this.currentViewId,
searchQuery: this.viewState.searchQuery,
selectedProject: this.viewState.selectedProject,
hasAdvanced: !!this.currentFilterState,
groups:
(this.currentFilterState as any)?.filterGroups?.length ?? 0,
});
// Use saveOverridesQuietly to avoid triggering SETTINGS_CHANGED event
this.plugin.workspaceManager
.saveOverridesQuietly(this.workspaceId, effectiveSettings)
.then(() =>
console.log("[TG-WORKSPACE] overrides saved quietly", {
workspaceId: this.workspaceId,
viewId: this.currentViewId,
})
)
.catch((e) =>
console.warn("[TG-WORKSPACE] failed to save overrides", e)
);
},
500,
true
);
// Restore filter state from workspace
private restoreFilterStateFromWorkspace() {
if (!this.plugin.workspaceManager || !this.workspaceId) return;
const effectiveSettings = this.plugin.workspaceManager.getEffectiveSettings(this.workspaceId);
const effectiveSettings =
this.plugin.workspaceManager.getEffectiveSettings(this.workspaceId);
if (effectiveSettings.v2FilterState && effectiveSettings.v2FilterState[this.currentViewId]) {
const savedState = effectiveSettings.v2FilterState[this.currentViewId];
const saved =
effectiveSettings.v2FilterState?.[this.currentViewId] ?? null;
console.log("[TG-WORKSPACE] restoreFilterStateFromWorkspace", {
workspaceId: this.workspaceId,
viewId: this.currentViewId,
hasSaved: !!saved,
savedSearch: saved?.searchQuery ?? "",
savedProject: saved?.selectedProject ?? null,
hasAdvanced: !!saved?.advancedFilter,
groups: saved?.advancedFilter?.filterGroups?.length ?? 0,
});
if (saved) {
const savedState = saved;
// Restore filter state
this.viewState.filters = savedState.filters || {};
this.viewState.searchQuery = savedState.searchQuery || "";
this.viewState.selectedProject = savedState.selectedProject || null;
this.currentFilterState = savedState.advancedFilter || null;
this.liveFilterState = savedState.advancedFilter || null;
this.viewState.viewMode = savedState.viewMode || "list";
// Update UI elements
if (this.filterInputEl) {
this.filterInputEl.value = this.viewState.searchQuery || "";
}
// Broadcast so any open filter UI reacts and header button shows reset
this.app.workspace.trigger(
"task-genius:filter-changed",
this.liveFilterState as any
);
} else {
// No saved state for this view in this workspace; clear advanced filter UI state
this.currentFilterState = null;
this.liveFilterState = null;
// Let UI know filters are cleared
this.app.workspace.trigger("task-genius:filter-changed", {
rootCondition: "all",
filterGroups: [],
} as any);
}
// Refresh header action buttons (e.g., reset filter button)
this.updateActionButtons();
// Ensure data reflects the restored filters immediately when UI is ready
if (this.rootContainerEl) {
console.log("[TG-WORKSPACE] apply restored filters to view", {
workspaceId: this.workspaceId,
viewId: this.currentViewId,
});
this.applyFilters();
this.updateView();
} else {
console.log(
"[TG-WORKSPACE] UI not ready, skip applying filters/updateView"
);
}
}

View file

@ -4,7 +4,7 @@ import {
WorkspacesConfig,
EffectiveSettings,
WORKSPACE_SCOPED_KEYS,
WorkspaceOverrides
WorkspaceOverrides,
} from "../types/workspace";
import {
emitWorkspaceSwitched,
@ -13,7 +13,7 @@ import {
emitDefaultWorkspaceChanged,
emitWorkspaceCreated,
emitWorkspaceDeleted,
emitWorkspaceRenamed
emitWorkspaceRenamed,
} from "../events/ui-event";
import { Events } from "../../../dataflow/events/Events";
import { emit } from "../../../dataflow/events/Events";
@ -50,9 +50,9 @@ export class WorkspaceManager {
id: defaultId,
name: "Default",
updatedAt: Date.now(),
settings: {} // Default workspace has no overrides
}
}
settings: {}, // Default workspace has no overrides
},
},
};
return this.plugin.settings.workspaces;
}
@ -62,14 +62,17 @@ export class WorkspaceManager {
const config = this.getWorkspacesConfig();
// Ensure default workspace exists
if (!config.defaultWorkspaceId || !config.byId[config.defaultWorkspaceId]) {
if (
!config.defaultWorkspaceId ||
!config.byId[config.defaultWorkspaceId]
) {
const defaultId = this.generateId();
config.defaultWorkspaceId = defaultId;
config.byId[defaultId] = {
id: defaultId,
name: "Default",
updatedAt: Date.now(),
settings: {}
settings: {},
};
if (!config.order.includes(defaultId)) {
config.order.unshift(defaultId);
@ -85,7 +88,10 @@ export class WorkspaceManager {
}
// Ensure active workspace exists
if (!config.activeWorkspaceId || !config.byId[config.activeWorkspaceId]) {
if (
!config.activeWorkspaceId ||
!config.byId[config.activeWorkspaceId]
) {
config.activeWorkspaceId = config.defaultWorkspaceId;
}
}
@ -94,7 +100,9 @@ export class WorkspaceManager {
private mergeIntoGlobal(overrides: WorkspaceOverrides): void {
for (const key of Object.keys(overrides)) {
if (WORKSPACE_SCOPED_KEYS.includes(key as any)) {
(this.plugin.settings as any)[key] = structuredClone(overrides[key as keyof WorkspaceOverrides]);
(this.plugin.settings as any)[key] = structuredClone(
overrides[key as keyof WorkspaceOverrides]
);
}
}
}
@ -102,9 +110,21 @@ export class WorkspaceManager {
// Generate effective settings for a workspace
public getEffectiveSettings(workspaceId?: string): EffectiveSettings {
const config = this.getWorkspacesConfig();
const id = workspaceId || config.activeWorkspaceId || config.defaultWorkspaceId;
const id =
workspaceId ||
config.activeWorkspaceId ||
config.defaultWorkspaceId;
// Return from cache if available
console.log("[TG-WORKSPACE] getEffectiveSettings:start", {
requestId: workspaceId || null,
configActive: config.activeWorkspaceId,
defaultId: config.defaultWorkspaceId,
resolvedId: id,
cached: this.effectiveCache.has(id),
});
if (this.effectiveCache.has(id)) {
return this.effectiveCache.get(id)!;
}
@ -116,8 +136,10 @@ export class WorkspaceManager {
return this.getEffectiveSettings(config.defaultWorkspaceId);
}
// Start with global settings
// Start with global settings, but DO NOT inherit v2FilterState from global (workspace-scoped)
const effective: EffectiveSettings = { ...this.plugin.settings };
// Explicitly drop any global v2FilterState to avoid cross-workspace leakage
(effective as any).v2FilterState = undefined;
// Apply workspace overrides if not default
if (id !== config.defaultWorkspaceId && workspace.settings) {
@ -128,6 +150,28 @@ export class WorkspaceManager {
}
}
console.log("[TG-WORKSPACE] getEffectiveSettings:built", {
resolvedId: id,
hasWSv2: !!(
workspace.settings &&
(workspace.settings as any).v2FilterState !== undefined
),
hasEffV2: effective.v2FilterState !== undefined,
effViews: effective.v2FilterState
? Object.keys(effective.v2FilterState as any).length
: 0,
});
// Always apply v2FilterState from workspace settings (including default)
if (
workspace.settings &&
workspace.settings.v2FilterState !== undefined
) {
effective.v2FilterState = structuredClone(
workspace.settings.v2FilterState
);
}
// Cache the result
this.effectiveCache.set(id, effective);
return effective;
@ -138,9 +182,17 @@ export class WorkspaceManager {
const overrides: WorkspaceOverrides = {};
for (const key of WORKSPACE_SCOPED_KEYS) {
const effValue = effective[key];
const effValue = (effective as any)[key];
const globalValue = (this.plugin.settings as any)[key];
// v2FilterState is workspace-only. Always persist it per-workspace when defined.
if (key === "v2FilterState") {
if (effValue !== undefined) {
overrides[key] = structuredClone(effValue);
}
continue;
}
if (JSON.stringify(effValue) !== JSON.stringify(globalValue)) {
overrides[key] = structuredClone(effValue);
}
@ -162,7 +214,10 @@ export class WorkspaceManager {
for (const key of WORKSPACE_SCOPED_KEYS) {
if (workspace.settings[key] !== undefined) {
const globalValue = (this.plugin.settings as any)[key];
if (JSON.stringify(workspace.settings[key]) === JSON.stringify(globalValue)) {
if (
JSON.stringify(workspace.settings[key]) ===
JSON.stringify(globalValue)
) {
delete workspace.settings[key];
}
}
@ -178,7 +233,9 @@ export class WorkspaceManager {
// Get all workspaces
public getAllWorkspaces(): WorkspaceData[] {
const config = this.getWorkspacesConfig();
return config.order.map(id => config.byId[id]).filter(ws => ws !== undefined);
return config.order
.map((id) => config.byId[id])
.filter((ws) => ws !== undefined);
}
// Get workspace by ID
@ -196,6 +253,11 @@ export class WorkspaceManager {
// Set active workspace
public async setActiveWorkspace(workspaceId: string): Promise<void> {
console.log("[TG-WORKSPACE] setActiveWorkspace:start", {
from: this.getActiveWorkspace()?.id,
to: workspaceId,
});
const config = this.getWorkspacesConfig();
if (!config.byId[workspaceId]) {
@ -204,6 +266,10 @@ export class WorkspaceManager {
}
if (config.activeWorkspaceId === workspaceId) {
console.log(
"[TG-WORKSPACE] setActiveWorkspace:noop (already active)",
{ id: workspaceId }
);
return; // Already active
}
@ -211,16 +277,27 @@ export class WorkspaceManager {
this.clearCache();
await this.plugin.saveSettings();
console.log("[TG-WORKSPACE] setActiveWorkspace:done", {
active: config.activeWorkspaceId,
});
emitWorkspaceSwitched(this.app, workspaceId);
}
// Create new workspace (cloned from current or default)
public async createWorkspace(name: string, baseWorkspaceId?: string): Promise<WorkspaceData> {
public async createWorkspace(
name: string,
baseWorkspaceId?: string
): Promise<WorkspaceData> {
const config = this.getWorkspacesConfig();
const id = this.generateId();
// Use current active workspace as base if not specified
const baseId = baseWorkspaceId || config.activeWorkspaceId || config.defaultWorkspaceId;
const baseId =
baseWorkspaceId ||
config.activeWorkspaceId ||
config.defaultWorkspaceId;
const baseWorkspace = config.byId[baseId];
// Clone settings from base workspace (if not default)
@ -236,7 +313,7 @@ export class WorkspaceManager {
id,
name,
updatedAt: Date.now(),
settings
settings,
};
config.byId[id] = newWorkspace;
@ -281,7 +358,10 @@ export class WorkspaceManager {
}
// Rename workspace
public async renameWorkspace(workspaceId: string, newName: string): Promise<void> {
public async renameWorkspace(
workspaceId: string,
newName: string
): Promise<void> {
const config = this.getWorkspacesConfig();
const workspace = config.byId[workspaceId];
@ -297,18 +377,46 @@ export class WorkspaceManager {
}
// Save overrides for a workspace
public async saveOverrides(workspaceId: string, effective: EffectiveSettings): Promise<void> {
public async saveOverrides(
workspaceId: string,
effective: EffectiveSettings
): Promise<void> {
const config = this.getWorkspacesConfig();
// Cannot save overrides to default workspace
if (workspaceId === config.defaultWorkspaceId) {
// For default, write directly to global settings
// For default, write directly to global settings EXCEPT v2FilterState which is workspace-only
const changedKeys: string[] = [];
for (const key of WORKSPACE_SCOPED_KEYS) {
if (effective[key] !== undefined) {
(this.plugin.settings as any)[key] = structuredClone(effective[key]);
if (effective[key] !== undefined && key !== "v2FilterState") {
(this.plugin.settings as any)[key] = structuredClone(
effective[key]
);
changedKeys.push(key);
}
}
// Handle v2FilterState specially for default workspace
if (effective.v2FilterState !== undefined) {
const ws = config.byId[workspaceId];
ws.settings = (ws.settings || {}) as any;
(ws.settings as any).v2FilterState = structuredClone(
effective.v2FilterState
);
ws.updatedAt = Date.now();
changedKeys.push("v2FilterState");
}
console.log("[TG-WORKSPACE] saveOverrides(default)", {
workspaceId,
changedKeys,
});
this.clearCache();
await this.plugin.saveSettings();
// Emit overrides saved for UI to react; also emit SETTINGS_CHANGED for global changes
emitWorkspaceOverridesSaved(
this.app,
workspaceId,
changedKeys.length ? changedKeys : undefined
);
emit(this.app, Events.SETTINGS_CHANGED);
return;
}
@ -322,6 +430,10 @@ export class WorkspaceManager {
const overrides = this.toOverrides(effective);
const changedKeys = Object.keys(overrides);
console.log("[TG-WORKSPACE] saveOverrides", {
workspaceId,
changedKeys,
});
workspace.settings = overrides;
workspace.updatedAt = Date.now();
@ -333,17 +445,38 @@ export class WorkspaceManager {
}
// Save overrides quietly without triggering SETTINGS_CHANGED event
public async saveOverridesQuietly(workspaceId: string, effective: EffectiveSettings): Promise<void> {
public async saveOverridesQuietly(
workspaceId: string,
effective: EffectiveSettings
): Promise<void> {
const config = this.getWorkspacesConfig();
// Cannot save overrides to default workspace
if (workspaceId === config.defaultWorkspaceId) {
// For default, write directly to global settings
// For default, write directly to global settings EXCEPT v2FilterState which is workspace-only
for (const key of WORKSPACE_SCOPED_KEYS) {
if (effective[key] !== undefined) {
(this.plugin.settings as any)[key] = structuredClone(effective[key]);
if (effective[key] !== undefined && key !== "v2FilterState") {
(this.plugin.settings as any)[key] = structuredClone(
effective[key]
);
}
}
// Handle v2FilterState specially for default workspace (store under workspace.settings)
if (effective.v2FilterState !== undefined) {
const ws = config.byId[workspaceId];
ws.settings = (ws.settings || {}) as any;
(ws.settings as any).v2FilterState = structuredClone(
effective.v2FilterState
);
ws.updatedAt = Date.now();
}
console.log("[TG-WORKSPACE] saveOverridesQuietly(default)", {
workspaceId,
keys: WORKSPACE_SCOPED_KEYS.filter(
(k) => (effective as any)[k] !== undefined
),
});
this.clearCache();
await this.plugin.saveSettings();
return;
}
@ -355,6 +488,10 @@ export class WorkspaceManager {
// Calculate overrides
const overrides = this.toOverrides(effective);
console.log("[TG-WORKSPACE] saveOverridesQuietly", {
workspaceId,
keys: Object.keys(overrides),
});
workspace.settings = overrides;
workspace.updatedAt = Date.now();
@ -466,7 +603,7 @@ export class WorkspaceManager {
const config = this.getWorkspacesConfig();
// Validate that all IDs exist and default is first
const validOrder = newOrder.filter(id => config.byId[id]);
const validOrder = newOrder.filter((id) => config.byId[id]);
// Ensure default is always first
const defaultIndex = validOrder.indexOf(config.defaultWorkspaceId);
@ -496,17 +633,21 @@ export class WorkspaceManager {
name: workspace.name,
settings: workspace.settings,
exportedAt: Date.now(),
version: 1
version: 1,
};
return JSON.stringify(exportData, null, 2);
}
// Import workspace configuration
public async importWorkspace(jsonData: string, name?: string): Promise<WorkspaceData | null> {
public async importWorkspace(
jsonData: string,
name?: string
): Promise<WorkspaceData | null> {
try {
const importData = JSON.parse(jsonData);
const workspaceName = name || importData.name || "Imported Workspace";
const workspaceName =
name || importData.name || "Imported Workspace";
const settings = importData.settings || {};
const newWorkspace = await this.createWorkspace(workspaceName);
@ -520,4 +661,4 @@ export class WorkspaceManager {
return null;
}
}
}
}

View file

@ -234,7 +234,7 @@ export class TaskProgressBarSettingTab extends PluginSettingTab {
if (plugin.dataflowOrchestrator) {
// Call async updateSettings and await to ensure incremental reindex completes
await plugin.dataflowOrchestrator.updateSettings(
plugin.settings,
plugin.settings
);
}
@ -245,7 +245,7 @@ export class TaskProgressBarSettingTab extends PluginSettingTab {
await plugin.triggerViewUpdate();
},
100,
true,
true
);
this.debouncedApplyNotifications = debounce(
@ -256,7 +256,7 @@ export class TaskProgressBarSettingTab extends PluginSettingTab {
// Minimal view updates are unnecessary here
},
100,
true,
true
);
}
@ -276,7 +276,7 @@ export class TaskProgressBarSettingTab extends PluginSettingTab {
}
this.searchComponent = new SettingsSearchComponent(
this,
this.containerEl,
this.containerEl
);
}
@ -369,7 +369,7 @@ export class TaskProgressBarSettingTab extends PluginSettingTab {
tab.name +
(tab.id === "about"
? " v" + this.plugin.manifest.version
: ""),
: "")
);
// Add click handler
@ -402,7 +402,7 @@ export class TaskProgressBarSettingTab extends PluginSettingTab {
// Show active section, hide others
const sections = this.containerEl.querySelectorAll(
".settings-tab-section",
".settings-tab-section"
);
sections.forEach((section) => {
if (section.getAttribute("data-tab-id") === tabId) {
@ -416,10 +416,10 @@ export class TaskProgressBarSettingTab extends PluginSettingTab {
// Handle tab container and header visibility based on selected tab
const tabsContainer = this.containerEl.querySelector(
".settings-tabs-categorized-container",
".settings-tabs-categorized-container"
);
const settingsHeader = this.containerEl.querySelector(
".task-genius-settings-header",
".task-genius-settings-header"
);
if (tabId === "general") {
@ -452,7 +452,7 @@ export class TaskProgressBarSettingTab extends PluginSettingTab {
public navigateToTab(
tabId: string,
section?: string,
search?: string,
search?: string
): void {
// Set the current tab
this.currentTab = tabId;
@ -493,7 +493,7 @@ export class TaskProgressBarSettingTab extends PluginSettingTab {
// Special handling for MCP sections
if (sectionId === "cursor" && this.currentTab === "mcp-integration") {
const cursorSection = this.containerEl.querySelector(
".mcp-client-section",
".mcp-client-section"
);
if (cursorSection) {
const header =
@ -513,7 +513,7 @@ export class TaskProgressBarSettingTab extends PluginSettingTab {
private createTabSection(tabId: string): HTMLElement {
// Get the sections container
const sectionsContainer = this.containerEl.querySelector(
".settings-tab-sections",
".settings-tab-sections"
);
if (!sectionsContainer) return this.containerEl;
@ -546,7 +546,7 @@ export class TaskProgressBarSettingTab extends PluginSettingTab {
new IframeModal(
this.app,
url,
`How to use — ${tabInfo?.name ?? tabId}`,
`How to use — ${tabInfo?.name ?? tabId}`
).open();
} catch (e) {
window.open(url);
@ -670,7 +670,7 @@ export class TaskProgressBarSettingTab extends PluginSettingTab {
// Notifications Tab
const notificationsSection = this.createTabSection(
"desktop-integration",
"desktop-integration"
);
this.displayDesktopIntegrationSettings(notificationsSection);
@ -828,7 +828,7 @@ export class TaskProgressBarSettingTab extends PluginSettingTab {
() => {
this.currentTab = "general";
this.display();
},
}
);
icsSettingsComponent.display();
}
@ -839,7 +839,7 @@ export class TaskProgressBarSettingTab extends PluginSettingTab {
private displayMcpSettings(containerEl: HTMLElement): void {
renderMcpIntegrationSettingsTab(containerEl, this.plugin, () =>
this.applySettingsUpdate(),
this.applySettingsUpdate()
);
}
@ -890,7 +890,9 @@ export class TaskProgressBarSettingTab extends PluginSettingTab {
const descEl = workspacesSection.createDiv();
descEl.addClass("workspaces-description");
descEl.setText(
t("Manage workspaces to organize different contexts with their own settings and filters.")
t(
"Manage workspaces to organize different contexts with their own settings and filters."
)
);
if (!this.plugin.workspaceManager) {
@ -901,20 +903,23 @@ export class TaskProgressBarSettingTab extends PluginSettingTab {
}
// Current workspace info
const currentWorkspace = this.plugin.workspaceManager.getActiveWorkspace();
const isDefault = this.plugin.workspaceManager.isDefaultWorkspace(currentWorkspace.id);
const currentWorkspace =
this.plugin.workspaceManager.getActiveWorkspace();
const isDefault = this.plugin.workspaceManager.isDefaultWorkspace(
currentWorkspace.id
);
new Setting(workspacesSection)
.setName(t("Current Workspace"))
.setDesc(
`${currentWorkspace.name}${isDefault ? " (" + t("Default") + ")" : ""}`
`${currentWorkspace.name}${
isDefault ? " (" + t("Default") + ")" : ""
}`
)
.addButton((button) => {
button
.setButtonText(t("Switch Workspace"))
.onClick(() => {
this.showWorkspaceSelector();
});
button.setButtonText(t("Switch Workspace")).onClick(() => {
this.showWorkspaceSelector();
});
});
// Workspace list
@ -931,7 +936,8 @@ export class TaskProgressBarSettingTab extends PluginSettingTab {
workspaceItemEl.addClass("workspace-item");
const isCurrentActive = workspace.id === currentWorkspace.id;
const isDefaultWs = this.plugin.workspaceManager!.isDefaultWorkspace(workspace.id);
const isDefaultWs =
this.plugin.workspaceManager!.isDefaultWorkspace(workspace.id);
if (isCurrentActive) {
workspaceItemEl.addClass("workspace-item-active");
@ -943,7 +949,9 @@ export class TaskProgressBarSettingTab extends PluginSettingTab {
isDefaultWs
? t("Default workspace")
: t("Last updated: {{date}}", {
date: new Date(workspace.updatedAt).toLocaleDateString(),
date: new Date(
workspace.updatedAt
).toLocaleDateString(),
})
)
.addButton((button) => {
@ -951,6 +959,9 @@ export class TaskProgressBarSettingTab extends PluginSettingTab {
button.setButtonText(t("Active")).setDisabled(true);
} else {
button.setButtonText(t("Switch")).onClick(async () => {
console.log("[TG-WORKSPACE] settings:switch", {
to: workspace.id,
});
await this.plugin.workspaceManager!.setActiveWorkspace(
workspace.id
);
@ -959,9 +970,12 @@ export class TaskProgressBarSettingTab extends PluginSettingTab {
}
})
.addButton((button) => {
button.setIcon("edit").setTooltip(t("Rename")).onClick(() => {
this.showRenameWorkspaceDialog(workspace);
});
button
.setIcon("edit")
.setTooltip(t("Rename"))
.onClick(() => {
this.showRenameWorkspaceDialog(workspace);
});
})
.addButton((button) => {
if (isDefaultWs) {
@ -996,7 +1010,8 @@ export class TaskProgressBarSettingTab extends PluginSettingTab {
const menu = new Menu();
const workspaces = this.plugin.workspaceManager.getAllWorkspaces();
const currentWorkspace = this.plugin.workspaceManager.getActiveWorkspace();
const currentWorkspace =
this.plugin.workspaceManager.getActiveWorkspace();
workspaces.forEach((workspace) => {
menu.addItem((item) => {
@ -1006,6 +1021,10 @@ export class TaskProgressBarSettingTab extends PluginSettingTab {
await this.plugin.workspaceManager!.setActiveWorkspace(
workspace.id
);
console.log("[TG-WORKSPACE] settings:menu switch", {
to: workspace.id,
});
this.display();
});
@ -1073,6 +1092,10 @@ export class TaskProgressBarSettingTab extends PluginSettingTab {
const baseId = this.baseSelect.value || undefined;
if (name && this.plugin.workspaceManager) {
console.log("[TG-WORKSPACE] settings:create", {
name,
baseId,
});
await this.plugin.workspaceManager.createWorkspace(
name,
baseId
@ -1119,12 +1142,14 @@ export class TaskProgressBarSettingTab extends PluginSettingTab {
const { contentEl } = this;
contentEl.createEl("h2", { text: t("Rename Workspace") });
new Setting(contentEl).setName(t("New Name")).addText((text) => {
text
.setValue(this.workspace.name)
.setPlaceholder(t("Enter new name"));
this.nameInput = text.inputEl;
});
new Setting(contentEl)
.setName(t("New Name"))
.addText((text) => {
text.setValue(this.workspace.name).setPlaceholder(
t("Enter new name")
);
this.nameInput = text.inputEl;
});
const buttonContainer = contentEl.createDiv({
cls: "modal-button-container",
@ -1146,6 +1171,10 @@ export class TaskProgressBarSettingTab extends PluginSettingTab {
newName !== this.workspace.name &&
this.plugin.workspaceManager
) {
console.log("[TG-WORKSPACE] settings:rename", {
id: this.workspace.id,
to: newName,
});
await this.plugin.workspaceManager.renameWorkspace(
this.workspace.id,
newName
@ -1215,6 +1244,9 @@ export class TaskProgressBarSettingTab extends PluginSettingTab {
deleteButton.addEventListener("click", async () => {
if (this.plugin.workspaceManager) {
console.log("[TG-WORKSPACE] settings:delete", {
id: this.workspace.id,
});
await this.plugin.workspaceManager.deleteWorkspace(
this.workspace.id
);
@ -1255,14 +1287,14 @@ export class TaskProgressBarSettingTab extends PluginSettingTab {
warningEl.addClass("experimental-warning");
warningEl.createEl("strong").setText("⚠️ Warning: ");
warningEl.appendText(
"These features are experimental and may not be stable. Use at your own risk.",
"These features are experimental and may not be stable. Use at your own risk."
);
// Future experimental features will be added here
const placeholderEl = experimentalSection.createDiv();
placeholderEl.addClass("experimental-placeholder");
placeholderEl.setText(
"No experimental features are currently available. Check back in future updates for new experimental functionality.",
"No experimental features are currently available. Check back in future updates for new experimental functionality."
);
}
}