mirror of
https://github.com/taskgenius/taskgenius-plugin.git
synced 2026-07-22 06:40:25 +00:00
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:
parent
2b7179ed9e
commit
f617992d80
3 changed files with 358 additions and 93 deletions
|
|
@ -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"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
108
src/setting.ts
108
src/setting.ts
|
|
@ -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."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue