mirror of
https://github.com/taskgenius/taskgenius-plugin.git
synced 2026-07-22 06:40:25 +00:00
feat(workspace): add custom icon selection for workspaces
- Add icon property to WorkspaceData type for custom workspace icons - Extract workspace modals to separate module for better organization - Implement icon selector in create/rename workspace dialogs - Update workspace selector to display custom icons - Add dedicated styles for workspace icon UI components - Support icon inheritance when cloning workspaces - Enhance workspace settings tab with icon display This allows users to visually distinguish workspaces beyond names, improving workspace identification and navigation experience.
This commit is contained in:
parent
6c282f976c
commit
b709c90f6f
10 changed files with 556 additions and 530 deletions
|
|
@ -1,8 +1,13 @@
|
|||
import { Menu, Modal, Notice, Setting } from "obsidian";
|
||||
import { Menu, Setting, setIcon } from "obsidian";
|
||||
import { TaskProgressBarSettingTab } from "@/setting";
|
||||
import TaskProgressBarPlugin from "@/index";
|
||||
import { WorkspaceData } from "@/experimental/v2/types/workspace";
|
||||
import { t } from "@/translations/helper";
|
||||
import {
|
||||
CreateWorkspaceModal,
|
||||
RenameWorkspaceModal,
|
||||
DeleteWorkspaceModal,
|
||||
} from "@/experimental/v2/components/modals/WorkspaceModals";
|
||||
|
||||
export function renderWorkspaceSettingsTab(settingTab: TaskProgressBarSettingTab, containerEl: HTMLElement) {
|
||||
const workspacesSection = containerEl.createDiv();
|
||||
|
|
@ -70,9 +75,15 @@ export function renderWorkspaceSettingsTab(settingTab: TaskProgressBarSettingTab
|
|||
workspaceItemEl.addClass("workspace-item-active");
|
||||
}
|
||||
|
||||
new Setting(workspaceItemEl)
|
||||
.setName(workspace.name)
|
||||
.setDesc(
|
||||
const setting = new Setting(workspaceItemEl);
|
||||
|
||||
// Add workspace icon to the name
|
||||
const nameWithIcon = setting.nameEl.createDiv({cls: "workspace-name-with-icon"});
|
||||
const iconEl = nameWithIcon.createDiv({cls: "workspace-list-icon"});
|
||||
setIcon(iconEl, workspace.icon || "layers");
|
||||
nameWithIcon.createSpan({text: workspace.name});
|
||||
|
||||
setting.setDesc(
|
||||
isDefaultWs
|
||||
? t("Default workspace")
|
||||
: t("Last updated: {{date}}", {
|
||||
|
|
@ -174,237 +185,23 @@ function showWorkspaceSelector(settingTab: TaskProgressBarSettingTab, event: Mou
|
|||
function showCreateWorkspaceDialog(settingTab: TaskProgressBarSettingTab) {
|
||||
if (!settingTab.plugin.workspaceManager) return;
|
||||
|
||||
|
||||
new CreateWorkspaceModal(settingTab.plugin, () => {
|
||||
this.display();
|
||||
settingTab.display();
|
||||
}).open();
|
||||
}
|
||||
|
||||
function showRenameWorkspaceDialog(settingTab: TaskProgressBarSettingTab, workspace: WorkspaceData) {
|
||||
if (!settingTab.plugin.workspaceManager) return;
|
||||
|
||||
|
||||
new RenameWorkspaceModal(settingTab.plugin, workspace, () => {
|
||||
this.display();
|
||||
settingTab.display();
|
||||
}).open();
|
||||
}
|
||||
|
||||
function showDeleteWorkspaceDialog(settingTab: TaskProgressBarSettingTab, workspace: WorkspaceData) {
|
||||
if (!settingTab.plugin.workspaceManager) return;
|
||||
|
||||
|
||||
new DeleteWorkspaceModal(settingTab.plugin, workspace, () => {
|
||||
this.display();
|
||||
settingTab.display();
|
||||
}).open();
|
||||
}
|
||||
|
||||
class CreateWorkspaceModal extends Modal {
|
||||
private nameInput: HTMLInputElement;
|
||||
private baseSelect: HTMLSelectElement;
|
||||
|
||||
constructor(
|
||||
private plugin: TaskProgressBarPlugin,
|
||||
private onCreated: () => void
|
||||
) {
|
||||
super(plugin.app);
|
||||
}
|
||||
|
||||
onOpen() {
|
||||
const {contentEl} = this;
|
||||
contentEl.createEl("h2", {text: t("Create New Workspace")});
|
||||
|
||||
new Setting(contentEl)
|
||||
.setName(t("Workspace Name"))
|
||||
.addText((text) => {
|
||||
text.setPlaceholder(t("Enter workspace name"));
|
||||
this.nameInput = text.inputEl;
|
||||
});
|
||||
|
||||
new Setting(contentEl)
|
||||
.setName(t("Copy Settings From"))
|
||||
.addDropdown((dropdown) => {
|
||||
dropdown.addOption("", t("Default settings"));
|
||||
this.plugin.workspaceManager
|
||||
?.getAllWorkspaces()
|
||||
.forEach((ws) => {
|
||||
dropdown.addOption(ws.id, ws.name);
|
||||
});
|
||||
this.baseSelect = dropdown.selectEl;
|
||||
});
|
||||
|
||||
const buttonContainer = contentEl.createDiv({
|
||||
cls: "modal-button-container",
|
||||
});
|
||||
|
||||
const createButton = buttonContainer.createEl("button", {
|
||||
text: t("Create"),
|
||||
cls: "mod-cta",
|
||||
});
|
||||
|
||||
const cancelButton = buttonContainer.createEl("button", {
|
||||
text: t("Cancel"),
|
||||
});
|
||||
|
||||
createButton.addEventListener("click", async () => {
|
||||
const name = this.nameInput.value.trim();
|
||||
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
|
||||
);
|
||||
new Notice(t("Workspace created"));
|
||||
this.onCreated();
|
||||
this.close();
|
||||
} else {
|
||||
new Notice(t("Please enter a workspace name"));
|
||||
}
|
||||
});
|
||||
|
||||
cancelButton.addEventListener("click", () => {
|
||||
this.close();
|
||||
});
|
||||
}
|
||||
|
||||
onClose() {
|
||||
const {contentEl} = this;
|
||||
contentEl.empty();
|
||||
}
|
||||
}
|
||||
|
||||
class RenameWorkspaceModal extends Modal {
|
||||
private nameInput: HTMLInputElement;
|
||||
|
||||
constructor(
|
||||
private plugin: TaskProgressBarPlugin,
|
||||
private workspace: WorkspaceData,
|
||||
private onRenamed: () => void
|
||||
) {
|
||||
super(plugin.app);
|
||||
}
|
||||
|
||||
onOpen() {
|
||||
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;
|
||||
});
|
||||
|
||||
const buttonContainer = contentEl.createDiv({
|
||||
cls: "modal-button-container",
|
||||
});
|
||||
|
||||
const renameButton = buttonContainer.createEl("button", {
|
||||
text: t("Rename"),
|
||||
cls: "mod-cta",
|
||||
});
|
||||
|
||||
const cancelButton = buttonContainer.createEl("button", {
|
||||
text: t("Cancel"),
|
||||
});
|
||||
|
||||
renameButton.addEventListener("click", async () => {
|
||||
const newName = this.nameInput.value.trim();
|
||||
if (
|
||||
newName &&
|
||||
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
|
||||
);
|
||||
new Notice(t("Workspace renamed"));
|
||||
this.onRenamed();
|
||||
this.close();
|
||||
} else {
|
||||
new Notice(t("Please enter a different name"));
|
||||
}
|
||||
});
|
||||
|
||||
cancelButton.addEventListener("click", () => {
|
||||
this.close();
|
||||
});
|
||||
|
||||
this.nameInput.focus();
|
||||
this.nameInput.select();
|
||||
}
|
||||
|
||||
onClose() {
|
||||
const {contentEl} = this;
|
||||
contentEl.empty();
|
||||
}
|
||||
}
|
||||
|
||||
class DeleteWorkspaceModal extends Modal {
|
||||
constructor(
|
||||
private plugin: TaskProgressBarPlugin,
|
||||
private workspace: WorkspaceData,
|
||||
private onDeleted: () => void
|
||||
) {
|
||||
super(plugin.app);
|
||||
}
|
||||
|
||||
onOpen() {
|
||||
const {contentEl} = this;
|
||||
contentEl.createEl("h2", {text: t("Delete Workspace")});
|
||||
|
||||
contentEl.createEl("p", {
|
||||
text: t(
|
||||
'Are you sure you want to delete "{{name}}"? This action cannot be undone.',
|
||||
{name: this.workspace.name}
|
||||
),
|
||||
});
|
||||
|
||||
const buttonContainer = contentEl.createDiv({
|
||||
cls: "modal-button-container",
|
||||
});
|
||||
|
||||
const deleteButton = buttonContainer.createEl("button", {
|
||||
text: t("Delete"),
|
||||
cls: "mod-warning",
|
||||
});
|
||||
|
||||
const cancelButton = buttonContainer.createEl("button", {
|
||||
text: t("Cancel"),
|
||||
});
|
||||
|
||||
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
|
||||
);
|
||||
new Notice(t("Workspace deleted"));
|
||||
this.onDeleted();
|
||||
this.close();
|
||||
}
|
||||
});
|
||||
|
||||
cancelButton.addEventListener("click", () => {
|
||||
this.close();
|
||||
});
|
||||
}
|
||||
|
||||
onClose() {
|
||||
const {contentEl} = this;
|
||||
contentEl.empty();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -157,16 +157,16 @@ export class TaskViewV2 extends ItemView {
|
|||
filters: {},
|
||||
};
|
||||
|
||||
private workspaceId: string = "";
|
||||
private isSavingFilterState: boolean = false;
|
||||
private workspaceId = "";
|
||||
private isSavingFilterState = false;
|
||||
|
||||
private tasks: Task[] = [];
|
||||
private filteredTasks: Task[] = [];
|
||||
private currentViewId: string = "inbox";
|
||||
private isLoading: boolean = false;
|
||||
private currentViewId = "inbox";
|
||||
private isLoading = false;
|
||||
private loadError: string | null = null;
|
||||
private isInitializing: boolean = true;
|
||||
private updateScheduled: boolean = false;
|
||||
private isInitializing = true;
|
||||
private updateScheduled = false;
|
||||
private pendingUpdates: Set<string> = new Set();
|
||||
|
||||
// Track the current active component for view mode switching
|
||||
|
|
@ -465,8 +465,10 @@ export class TaskViewV2 extends ItemView {
|
|||
this.plugin,
|
||||
(query) => this.handleSearch(query),
|
||||
(mode) => this.handleViewModeChange(mode),
|
||||
() => {}, // Filter is now in Obsidian view header
|
||||
() => {}, // Sort is now in Obsidian view header
|
||||
() => {
|
||||
}, // Filter is now in Obsidian view header
|
||||
() => {
|
||||
}, // Sort is now in Obsidian view header
|
||||
() => this.handleSettingsClick(),
|
||||
availableModes,
|
||||
() => this.toggleSidebar() // Pass sidebar toggle callback
|
||||
|
|
@ -645,12 +647,12 @@ export class TaskViewV2 extends ItemView {
|
|||
// Create legacy containers for backward compatibility
|
||||
this.listContainer = this.contentArea.createDiv({
|
||||
cls: "task-list-container",
|
||||
attr: { style: "display: none;" },
|
||||
attr: {style: "display: none;"},
|
||||
});
|
||||
|
||||
this.treeContainer = this.contentArea.createDiv({
|
||||
cls: "task-tree-container",
|
||||
attr: { style: "display: none;" },
|
||||
attr: {style: "display: none;"},
|
||||
});
|
||||
|
||||
// Hide all components initially (force hide all during initialization)
|
||||
|
|
@ -662,8 +664,8 @@ export class TaskViewV2 extends ItemView {
|
|||
private createSidebarToggle() {
|
||||
const headerBtns = !Platform.isPhone
|
||||
? (this.headerEl?.find(
|
||||
".view-header-nav-buttons"
|
||||
) as HTMLElement | null)
|
||||
".view-header-nav-buttons"
|
||||
) as HTMLElement | null)
|
||||
: (this.headerEl?.find(".view-header-left") as HTMLElement);
|
||||
if (!headerBtns) {
|
||||
console.warn("[TG-V2] header buttons container not found");
|
||||
|
|
@ -825,7 +827,8 @@ export class TaskViewV2 extends ItemView {
|
|||
this.sidebar?.setCollapsed(true);
|
||||
this.rootContainerEl?.addClass("v2-sidebar-collapsed");
|
||||
}
|
||||
} catch (_) {}
|
||||
} catch (_) {
|
||||
}
|
||||
}
|
||||
|
||||
private hideAllComponents(forceHideAll: boolean = false) {
|
||||
|
|
@ -902,7 +905,7 @@ export class TaskViewV2 extends ItemView {
|
|||
isDataflowEnabled(this.plugin) &&
|
||||
this.plugin.dataflowOrchestrator
|
||||
) {
|
||||
const { on, Events } = await import("../../dataflow/events/Events");
|
||||
const {on, Events} = await import("../../dataflow/events/Events");
|
||||
|
||||
// Listen for cache ready event
|
||||
this.registerEvent(
|
||||
|
|
@ -1224,8 +1227,8 @@ export class TaskViewV2 extends ItemView {
|
|||
|
||||
// Skip if we're switching to the same view (but never skip during initialization or if no filters/projects)
|
||||
// Also don't skip if we have no active filters - this ensures proper refresh when clearing filters
|
||||
const hasActiveFilters = (this.currentFilterState?.filterGroups?.length > 0) ||
|
||||
this.viewState.selectedProject;
|
||||
const hasActiveFilters = (this.currentFilterState?.filterGroups?.length ?? 0) > 0 ||
|
||||
!!this.viewState.selectedProject;
|
||||
if (
|
||||
!this.isInitializing &&
|
||||
this.currentViewId === viewId &&
|
||||
|
|
@ -1948,7 +1951,7 @@ export class TaskViewV2 extends ItemView {
|
|||
const loadingEl = this.contentArea.createDiv({
|
||||
cls: "tg-v2-loading",
|
||||
});
|
||||
loadingEl.createDiv({ cls: "tg-v2-spinner" });
|
||||
loadingEl.createDiv({cls: "tg-v2-spinner"});
|
||||
loadingEl.createDiv({
|
||||
cls: "tg-v2-loading-text",
|
||||
text: t("Loading tasks..."),
|
||||
|
|
@ -1967,7 +1970,7 @@ export class TaskViewV2 extends ItemView {
|
|||
const errorEl = this.contentArea.createDiv({
|
||||
cls: "tg-v2-error-state",
|
||||
});
|
||||
const errorIcon = errorEl.createDiv({ cls: "tg-v2-error-icon" });
|
||||
const errorIcon = errorEl.createDiv({cls: "tg-v2-error-icon"});
|
||||
setIcon(errorIcon, "alert-triangle");
|
||||
|
||||
errorEl.createDiv({
|
||||
|
|
@ -2003,7 +2006,7 @@ export class TaskViewV2 extends ItemView {
|
|||
const emptyEl = this.contentArea.createDiv({
|
||||
cls: "tg-v2-empty-state",
|
||||
});
|
||||
const emptyIcon = emptyEl.createDiv({ cls: "tg-v2-empty-icon" });
|
||||
const emptyIcon = emptyEl.createDiv({cls: "tg-v2-empty-icon"});
|
||||
setIcon(emptyIcon, "inbox");
|
||||
|
||||
emptyEl.createDiv({
|
||||
|
|
@ -2075,7 +2078,7 @@ export class TaskViewV2 extends ItemView {
|
|||
const total = projectTasks.length;
|
||||
const percentage = total > 0 ? (completed / total) * 100 : 0;
|
||||
|
||||
return { completed, total, percentage };
|
||||
return {completed, total, percentage};
|
||||
}
|
||||
|
||||
// Workspace management
|
||||
|
|
@ -2228,7 +2231,7 @@ export class TaskViewV2 extends ItemView {
|
|||
}, 100);
|
||||
});
|
||||
|
||||
popover.showAtPosition({ x: e.clientX, y: e.clientY });
|
||||
popover.showAtPosition({x: e.clientX, y: e.clientY});
|
||||
} else {
|
||||
const modal = new ViewTaskFilterModal(
|
||||
this.app,
|
||||
|
|
@ -2336,7 +2339,10 @@ export class TaskViewV2 extends ItemView {
|
|||
this.saveFilterStateToWorkspace();
|
||||
|
||||
// Broadcast filter change to ensure UI components update
|
||||
this.app.workspace.trigger("task-genius:filter-changed", null);
|
||||
this.app.workspace.trigger("task-genius:filter-changed", {
|
||||
rootCondition: "all",
|
||||
filterGroups: [],
|
||||
} as any);
|
||||
|
||||
// Clear any active project selection in sidebar
|
||||
this.sidebar?.projectList?.setActiveProject(null);
|
||||
|
|
@ -2428,7 +2434,7 @@ export class TaskViewV2 extends ItemView {
|
|||
if (this.detailsPanelEl) return;
|
||||
const wrapper = this.contentArea?.parentElement as HTMLElement;
|
||||
if (!wrapper) return;
|
||||
this.detailsPanelEl = wrapper.createDiv({ cls: "tg-v2-details-panel" });
|
||||
this.detailsPanelEl = wrapper.createDiv({cls: "tg-v2-details-panel"});
|
||||
const header = this.detailsPanelEl.createDiv({
|
||||
cls: "tg-v2-details-header",
|
||||
});
|
||||
|
|
@ -2443,7 +2449,7 @@ export class TaskViewV2 extends ItemView {
|
|||
this.ensureDetailsPanel();
|
||||
if (this.detailsBodyEl) {
|
||||
this.detailsBodyEl.empty();
|
||||
this.detailsBodyEl.createEl("div", { text: message });
|
||||
this.detailsBodyEl.createEl("div", {text: message});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2479,7 +2485,7 @@ export class TaskViewV2 extends ItemView {
|
|||
return;
|
||||
}
|
||||
|
||||
const updatedTask = { ...task, completed: !task.completed };
|
||||
const updatedTask = {...task, completed: !task.completed};
|
||||
|
||||
if (updatedTask.completed) {
|
||||
updatedTask.metadata.completedDate = Date.now();
|
||||
|
|
@ -2665,12 +2671,13 @@ export class TaskViewV2 extends ItemView {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async editTask(task: Task) {
|
||||
const file = this.app.vault.getFileByPath(task.filePath);
|
||||
if (!(file instanceof TFile)) return;
|
||||
const leaf = this.app.workspace.getLeaf(false);
|
||||
await leaf.openFile(file, {
|
||||
eState: { line: task.line },
|
||||
eState: {line: task.line},
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -2752,8 +2759,8 @@ export class TaskViewV2 extends ItemView {
|
|||
} else {
|
||||
new Notice(
|
||||
t("Failed to delete task") +
|
||||
": " +
|
||||
(result.error || "Unknown error")
|
||||
": " +
|
||||
(result.error || "Unknown error")
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,12 @@
|
|||
import { Menu, setIcon, Notice, Modal, Setting } from "obsidian";
|
||||
import { Menu, setIcon } from "obsidian";
|
||||
import type TaskProgressBarPlugin from "@/index";
|
||||
import { WorkspaceData } from "@/experimental/v2/types/workspace";
|
||||
import { t } from "@/translations/helper";
|
||||
import {
|
||||
CreateWorkspaceModal,
|
||||
RenameWorkspaceModal,
|
||||
DeleteWorkspaceModal,
|
||||
} from "@/experimental/v2/components/modals/WorkspaceModals";
|
||||
|
||||
export class WorkspaceSelector {
|
||||
private containerEl: HTMLElement;
|
||||
|
|
@ -49,7 +54,7 @@ export class WorkspaceSelector {
|
|||
// Use a color scheme for workspaces if needed
|
||||
workspaceIcon.style.backgroundColor =
|
||||
this.getWorkspaceColor(currentWorkspace);
|
||||
setIcon(workspaceIcon, "layers");
|
||||
setIcon(workspaceIcon, currentWorkspace.icon || "layers");
|
||||
|
||||
const workspaceDetails = workspaceInfo.createDiv({
|
||||
cls: "workspace-details",
|
||||
|
|
@ -119,7 +124,7 @@ export class WorkspaceSelector {
|
|||
const title = isDefault ? `${workspace.name}` : workspace.name;
|
||||
|
||||
item.setTitle(title)
|
||||
.setIcon("layers")
|
||||
.setIcon(workspace.icon || "layers")
|
||||
.onClick(async () => {
|
||||
await this.onWorkspaceChange(workspace.id);
|
||||
this.currentWorkspaceId = workspace.id;
|
||||
|
|
@ -186,111 +191,6 @@ export class WorkspaceSelector {
|
|||
}
|
||||
|
||||
private showCreateWorkspaceDialog() {
|
||||
class CreateWorkspaceModal extends Modal {
|
||||
private nameInput: HTMLInputElement;
|
||||
private baseWorkspaceSelect: HTMLSelectElement;
|
||||
private name: string = "";
|
||||
private selectWorkspaceId: string = "";
|
||||
|
||||
constructor(
|
||||
private plugin: TaskProgressBarPlugin,
|
||||
private onCreated: (workspace: WorkspaceData) => void
|
||||
) {
|
||||
super(plugin.app);
|
||||
}
|
||||
|
||||
onOpen() {
|
||||
const { contentEl } = this;
|
||||
contentEl.createEl("h2", { text: t("Create New Workspace") });
|
||||
|
||||
// Name input
|
||||
new Setting(contentEl)
|
||||
.setName(t("Workspace Name"))
|
||||
.setDesc(t("A descriptive name for the workspace"))
|
||||
.addText((text) => {
|
||||
text.setPlaceholder(t("Workspace Name"))
|
||||
.setValue("")
|
||||
.onChange((value) => {
|
||||
this.name = value;
|
||||
});
|
||||
});
|
||||
|
||||
const currentWorkspace =
|
||||
this.plugin.workspaceManager?.getActiveWorkspace();
|
||||
|
||||
// Base workspace selector
|
||||
new Setting(contentEl)
|
||||
.setName(t("Copy Settings From"))
|
||||
.setDesc(t("Copy settings from an existing workspace"))
|
||||
.addDropdown((dropdown) => {
|
||||
dropdown.addOption("", t("Select a workspace..."));
|
||||
if (currentWorkspace) {
|
||||
dropdown.addOption(
|
||||
currentWorkspace.id,
|
||||
`Current (${currentWorkspace.name})`
|
||||
);
|
||||
}
|
||||
this.plugin.workspaceManager
|
||||
?.getAllWorkspaces()
|
||||
.forEach((ws) => {
|
||||
dropdown.addOption(ws.id, ws.name);
|
||||
});
|
||||
|
||||
dropdown.onChange((value) => {
|
||||
this.selectWorkspaceId = value;
|
||||
});
|
||||
});
|
||||
|
||||
// Buttons
|
||||
const buttonContainer = contentEl.createDiv({
|
||||
cls: "modal-button-container",
|
||||
});
|
||||
const createButton = buttonContainer.createEl("button", {
|
||||
text: t("Create"),
|
||||
cls: "mod-cta",
|
||||
});
|
||||
const cancelButton = buttonContainer.createEl("button", {
|
||||
text: t("Cancel"),
|
||||
});
|
||||
|
||||
createButton.addEventListener("click", async () => {
|
||||
const name = this.name.trim();
|
||||
|
||||
if (!name) {
|
||||
new Notice(t("Please enter a workspace name"));
|
||||
return;
|
||||
}
|
||||
const baseId = this.selectWorkspaceId;
|
||||
|
||||
if (name && this.plugin.workspaceManager) {
|
||||
const workspace =
|
||||
await this.plugin.workspaceManager.createWorkspace(
|
||||
name,
|
||||
baseId
|
||||
);
|
||||
new Notice(
|
||||
t('Workspace "{{name}}" created', {
|
||||
interpolation: { name: name },
|
||||
})
|
||||
);
|
||||
this.onCreated(workspace);
|
||||
this.close();
|
||||
} else {
|
||||
new Notice(t("Please enter a workspace name"));
|
||||
}
|
||||
});
|
||||
|
||||
cancelButton.addEventListener("click", () => {
|
||||
this.close();
|
||||
});
|
||||
}
|
||||
|
||||
onClose() {
|
||||
const { contentEl } = this;
|
||||
contentEl.empty();
|
||||
}
|
||||
}
|
||||
|
||||
new CreateWorkspaceModal(this.plugin, (workspace) => {
|
||||
this.onWorkspaceChange(workspace.id);
|
||||
this.currentWorkspaceId = workspace.id;
|
||||
|
|
@ -299,144 +199,12 @@ export class WorkspaceSelector {
|
|||
}
|
||||
|
||||
private showRenameWorkspaceDialog(workspace: WorkspaceData) {
|
||||
class RenameWorkspaceModal extends Modal {
|
||||
private nameInput: HTMLInputElement;
|
||||
|
||||
constructor(
|
||||
private plugin: TaskProgressBarPlugin,
|
||||
private workspace: WorkspaceData,
|
||||
private onRenamed: () => void
|
||||
) {
|
||||
super(plugin.app);
|
||||
}
|
||||
|
||||
onOpen() {
|
||||
const { contentEl } = this;
|
||||
contentEl.createEl("h2", { text: t("Rename Workspace") });
|
||||
|
||||
const inputContainer = contentEl.createDiv({
|
||||
cls: "setting-item",
|
||||
});
|
||||
inputContainer.createEl("label", { text: t("New Name") + ":" });
|
||||
this.nameInput = inputContainer.createEl("input", {
|
||||
type: "text",
|
||||
value: this.workspace.name,
|
||||
placeholder: t("Enter workspace name..."),
|
||||
});
|
||||
|
||||
const buttonContainer = contentEl.createDiv({
|
||||
cls: "modal-button-container",
|
||||
});
|
||||
const renameButton = buttonContainer.createEl("button", {
|
||||
text: t("Rename"),
|
||||
cls: "mod-cta",
|
||||
});
|
||||
const cancelButton = buttonContainer.createEl("button", {
|
||||
text: t("Cancel"),
|
||||
});
|
||||
|
||||
renameButton.addEventListener("click", async () => {
|
||||
const newName = this.nameInput.value.trim();
|
||||
if (
|
||||
newName &&
|
||||
newName !== this.workspace.name &&
|
||||
this.plugin.workspaceManager
|
||||
) {
|
||||
await this.plugin.workspaceManager.renameWorkspace(
|
||||
this.workspace.id,
|
||||
newName
|
||||
);
|
||||
new Notice(
|
||||
t('Workspace renamed to "{{name}}"', {
|
||||
interpolation: { name: newName },
|
||||
})
|
||||
);
|
||||
this.onRenamed();
|
||||
this.close();
|
||||
} else {
|
||||
new Notice(t("Please enter a different name"));
|
||||
}
|
||||
});
|
||||
|
||||
cancelButton.addEventListener("click", () => {
|
||||
this.close();
|
||||
});
|
||||
|
||||
this.nameInput.focus();
|
||||
this.nameInput.select();
|
||||
}
|
||||
|
||||
onClose() {
|
||||
const { contentEl } = this;
|
||||
contentEl.empty();
|
||||
}
|
||||
}
|
||||
|
||||
new RenameWorkspaceModal(this.plugin, workspace, () => {
|
||||
this.render();
|
||||
}).open();
|
||||
}
|
||||
|
||||
private showDeleteWorkspaceDialog(workspace: WorkspaceData) {
|
||||
class DeleteWorkspaceModal extends Modal {
|
||||
constructor(
|
||||
private plugin: TaskProgressBarPlugin,
|
||||
private workspace: WorkspaceData,
|
||||
private onDeleted: () => void
|
||||
) {
|
||||
super(plugin.app);
|
||||
}
|
||||
|
||||
onOpen() {
|
||||
const { contentEl } = this;
|
||||
contentEl.createEl("h2", { text: t("Delete Workspace") });
|
||||
|
||||
contentEl.createEl("p", {
|
||||
text: t(
|
||||
'Are you sure you want to delete the workspace "{{name}}"? This action cannot be undone.',
|
||||
{ interpolation: { name: this.workspace.name } }
|
||||
),
|
||||
});
|
||||
|
||||
const buttonContainer = contentEl.createDiv({
|
||||
cls: "modal-button-container",
|
||||
});
|
||||
const deleteButton = buttonContainer.createEl("button", {
|
||||
text: t("Delete"),
|
||||
cls: "mod-warning",
|
||||
});
|
||||
const cancelButton = buttonContainer.createEl("button", {
|
||||
text: t("Cancel"),
|
||||
});
|
||||
|
||||
deleteButton.addEventListener("click", async () => {
|
||||
if (this.plugin.workspaceManager) {
|
||||
await this.plugin.workspaceManager.deleteWorkspace(
|
||||
this.workspace.id
|
||||
);
|
||||
new Notice(
|
||||
t('Workspace "{{name}}" deleted', {
|
||||
interpolation: {
|
||||
name: this.workspace.name,
|
||||
},
|
||||
})
|
||||
);
|
||||
this.onDeleted();
|
||||
this.close();
|
||||
}
|
||||
});
|
||||
|
||||
cancelButton.addEventListener("click", () => {
|
||||
this.close();
|
||||
});
|
||||
}
|
||||
|
||||
onClose() {
|
||||
const { contentEl } = this;
|
||||
contentEl.empty();
|
||||
}
|
||||
}
|
||||
|
||||
new DeleteWorkspaceModal(this.plugin, workspace, () => {
|
||||
// After deletion, workspace manager will automatically switch to default
|
||||
this.currentWorkspaceId =
|
||||
|
|
|
|||
345
src/experimental/v2/components/modals/WorkspaceModals.ts
Normal file
345
src/experimental/v2/components/modals/WorkspaceModals.ts
Normal file
|
|
@ -0,0 +1,345 @@
|
|||
import { Modal, Notice, Setting, ButtonComponent, setIcon } from "obsidian";
|
||||
import type TaskProgressBarPlugin from "@/index";
|
||||
import { WorkspaceData } from "@/experimental/v2/types/workspace";
|
||||
import { t } from "@/translations/helper";
|
||||
import { attachIconMenu } from "@/components/ui/menus/IconMenu";
|
||||
|
||||
/**
|
||||
* Helper function to create a workspace icon selector
|
||||
*/
|
||||
export function createWorkspaceIconSelector(
|
||||
containerEl: HTMLElement,
|
||||
plugin: TaskProgressBarPlugin,
|
||||
initialIcon: string,
|
||||
onIconSelected: (iconId: string) => void
|
||||
): ButtonComponent {
|
||||
const iconButton = new ButtonComponent(containerEl);
|
||||
iconButton.setIcon(initialIcon);
|
||||
|
||||
attachIconMenu(iconButton, {
|
||||
containerEl,
|
||||
plugin,
|
||||
onIconSelected: (iconId: string) => {
|
||||
iconButton.setIcon(iconId);
|
||||
onIconSelected(iconId);
|
||||
},
|
||||
});
|
||||
|
||||
return iconButton;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modal for creating a new workspace
|
||||
*/
|
||||
export class CreateWorkspaceModal extends Modal {
|
||||
private nameInput: HTMLInputElement;
|
||||
private baseSelect: HTMLSelectElement;
|
||||
private selectedIcon: string = "layers";
|
||||
private name: string = "";
|
||||
private selectWorkspaceId: string = "";
|
||||
|
||||
constructor(
|
||||
private plugin: TaskProgressBarPlugin,
|
||||
private onCreated: (workspace: WorkspaceData) => void
|
||||
) {
|
||||
super(plugin.app);
|
||||
}
|
||||
|
||||
onOpen() {
|
||||
const { contentEl } = this;
|
||||
contentEl.createEl("h2", { text: t("Create New Workspace") });
|
||||
|
||||
// Name input
|
||||
new Setting(contentEl)
|
||||
.setName(t("Workspace Name"))
|
||||
.setDesc(t("A descriptive name for the workspace"))
|
||||
.addText((text) => {
|
||||
text
|
||||
.setPlaceholder(t("Enter workspace name"))
|
||||
.setValue("")
|
||||
.onChange((value) => {
|
||||
this.name = value;
|
||||
});
|
||||
this.nameInput = text.inputEl;
|
||||
});
|
||||
|
||||
// Icon selection
|
||||
const iconSetting = new Setting(contentEl)
|
||||
.setName(t("Workspace Icon"))
|
||||
.setDesc(t("Choose an icon for this workspace"));
|
||||
|
||||
const iconContainer = iconSetting.controlEl.createDiv({
|
||||
cls: "workspace-icon-selector",
|
||||
});
|
||||
|
||||
createWorkspaceIconSelector(
|
||||
iconContainer,
|
||||
this.plugin,
|
||||
this.selectedIcon,
|
||||
(iconId) => {
|
||||
this.selectedIcon = iconId;
|
||||
}
|
||||
);
|
||||
|
||||
// Base workspace selector
|
||||
new Setting(contentEl)
|
||||
.setName(t("Copy Settings From"))
|
||||
.setDesc(t("Copy settings from an existing workspace"))
|
||||
.addDropdown((dropdown) => {
|
||||
dropdown.addOption("", t("Default settings"));
|
||||
|
||||
const currentWorkspace = this.plugin.workspaceManager?.getActiveWorkspace();
|
||||
if (currentWorkspace) {
|
||||
dropdown.addOption(
|
||||
currentWorkspace.id,
|
||||
`Current (${currentWorkspace.name})`
|
||||
);
|
||||
}
|
||||
|
||||
this.plugin.workspaceManager
|
||||
?.getAllWorkspaces()
|
||||
.forEach((ws) => {
|
||||
if (ws.id !== currentWorkspace?.id) {
|
||||
dropdown.addOption(ws.id, ws.name);
|
||||
}
|
||||
});
|
||||
|
||||
dropdown.onChange((value) => {
|
||||
this.selectWorkspaceId = value;
|
||||
});
|
||||
|
||||
this.baseSelect = dropdown.selectEl;
|
||||
});
|
||||
|
||||
// Buttons
|
||||
const buttonContainer = contentEl.createDiv({
|
||||
cls: "modal-button-container",
|
||||
});
|
||||
|
||||
const createButton = buttonContainer.createEl("button", {
|
||||
text: t("Create"),
|
||||
cls: "mod-cta",
|
||||
});
|
||||
|
||||
const cancelButton = buttonContainer.createEl("button", {
|
||||
text: t("Cancel"),
|
||||
});
|
||||
|
||||
createButton.addEventListener("click", async () => {
|
||||
const name = this.name.trim();
|
||||
const baseId = this.selectWorkspaceId || undefined;
|
||||
|
||||
if (!name) {
|
||||
new Notice(t("Please enter a workspace name"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.plugin.workspaceManager) {
|
||||
console.log("[TG-WORKSPACE] modal:create", {
|
||||
name,
|
||||
baseId,
|
||||
icon: this.selectedIcon,
|
||||
});
|
||||
|
||||
const workspace = await this.plugin.workspaceManager.createWorkspace(
|
||||
name,
|
||||
baseId,
|
||||
this.selectedIcon
|
||||
);
|
||||
|
||||
new Notice(
|
||||
t('Workspace "{{name}}" created', {
|
||||
interpolation: { name: name },
|
||||
})
|
||||
);
|
||||
|
||||
this.onCreated(workspace);
|
||||
this.close();
|
||||
}
|
||||
});
|
||||
|
||||
cancelButton.addEventListener("click", () => {
|
||||
this.close();
|
||||
});
|
||||
|
||||
// Focus the name input
|
||||
this.nameInput.focus();
|
||||
}
|
||||
|
||||
onClose() {
|
||||
const { contentEl } = this;
|
||||
contentEl.empty();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Modal for renaming a workspace
|
||||
*/
|
||||
export class RenameWorkspaceModal extends Modal {
|
||||
private nameInput: HTMLInputElement;
|
||||
private selectedIcon: string;
|
||||
|
||||
constructor(
|
||||
private plugin: TaskProgressBarPlugin,
|
||||
private workspace: WorkspaceData,
|
||||
private onRenamed: () => void
|
||||
) {
|
||||
super(plugin.app);
|
||||
this.selectedIcon = workspace.icon || "layers";
|
||||
}
|
||||
|
||||
onOpen() {
|
||||
const { contentEl } = this;
|
||||
contentEl.createEl("h2", { text: t("Rename Workspace") });
|
||||
|
||||
// Name input
|
||||
new Setting(contentEl)
|
||||
.setName(t("New Name"))
|
||||
.addText((text) => {
|
||||
text
|
||||
.setValue(this.workspace.name)
|
||||
.setPlaceholder(t("Enter new name"));
|
||||
this.nameInput = text.inputEl;
|
||||
});
|
||||
|
||||
// Icon selection
|
||||
const iconSetting = new Setting(contentEl)
|
||||
.setName(t("Workspace Icon"))
|
||||
.setDesc(t("Choose an icon for this workspace"));
|
||||
|
||||
const iconContainer = iconSetting.controlEl.createDiv({
|
||||
cls: "workspace-icon-selector",
|
||||
});
|
||||
|
||||
createWorkspaceIconSelector(
|
||||
iconContainer,
|
||||
this.plugin,
|
||||
this.selectedIcon,
|
||||
(iconId) => {
|
||||
this.selectedIcon = iconId;
|
||||
}
|
||||
);
|
||||
|
||||
// Buttons
|
||||
const buttonContainer = contentEl.createDiv({
|
||||
cls: "modal-button-container",
|
||||
});
|
||||
|
||||
const saveButton = buttonContainer.createEl("button", {
|
||||
text: t("Save"),
|
||||
cls: "mod-cta",
|
||||
});
|
||||
|
||||
const cancelButton = buttonContainer.createEl("button", {
|
||||
text: t("Cancel"),
|
||||
});
|
||||
|
||||
saveButton.addEventListener("click", async () => {
|
||||
const newName = this.nameInput.value.trim();
|
||||
|
||||
if (!newName) {
|
||||
new Notice(t("Please enter a name"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.plugin.workspaceManager) {
|
||||
console.log("[TG-WORKSPACE] modal:rename", {
|
||||
id: this.workspace.id,
|
||||
name: newName,
|
||||
icon: this.selectedIcon,
|
||||
});
|
||||
|
||||
await this.plugin.workspaceManager.renameWorkspace(
|
||||
this.workspace.id,
|
||||
newName,
|
||||
this.selectedIcon
|
||||
);
|
||||
|
||||
new Notice(t("Workspace updated"));
|
||||
this.onRenamed();
|
||||
this.close();
|
||||
}
|
||||
});
|
||||
|
||||
cancelButton.addEventListener("click", () => {
|
||||
this.close();
|
||||
});
|
||||
|
||||
// Focus and select the name input
|
||||
this.nameInput.focus();
|
||||
this.nameInput.select();
|
||||
}
|
||||
|
||||
onClose() {
|
||||
const { contentEl } = this;
|
||||
contentEl.empty();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Modal for deleting a workspace
|
||||
*/
|
||||
export class DeleteWorkspaceModal extends Modal {
|
||||
constructor(
|
||||
private plugin: TaskProgressBarPlugin,
|
||||
private workspace: WorkspaceData,
|
||||
private onDeleted: () => void
|
||||
) {
|
||||
super(plugin.app);
|
||||
}
|
||||
|
||||
onOpen() {
|
||||
const { contentEl } = this;
|
||||
contentEl.createEl("h2", { text: t("Delete Workspace") });
|
||||
|
||||
contentEl.createEl("p", {
|
||||
text: t(
|
||||
'Are you sure you want to delete "{{name}}"? This action cannot be undone.',
|
||||
{ interpolation: { name: this.workspace.name } }
|
||||
),
|
||||
});
|
||||
|
||||
const buttonContainer = contentEl.createDiv({
|
||||
cls: "modal-button-container",
|
||||
});
|
||||
|
||||
const deleteButton = buttonContainer.createEl("button", {
|
||||
text: t("Delete"),
|
||||
cls: "mod-warning",
|
||||
});
|
||||
|
||||
const cancelButton = buttonContainer.createEl("button", {
|
||||
text: t("Cancel"),
|
||||
});
|
||||
|
||||
deleteButton.addEventListener("click", async () => {
|
||||
if (this.plugin.workspaceManager) {
|
||||
console.log("[TG-WORKSPACE] modal:delete", {
|
||||
id: this.workspace.id,
|
||||
});
|
||||
|
||||
await this.plugin.workspaceManager.deleteWorkspace(
|
||||
this.workspace.id
|
||||
);
|
||||
|
||||
new Notice(
|
||||
t('Workspace "{{name}}" deleted', {
|
||||
interpolation: { name: this.workspace.name },
|
||||
})
|
||||
);
|
||||
|
||||
this.onDeleted();
|
||||
this.close();
|
||||
}
|
||||
});
|
||||
|
||||
cancelButton.addEventListener("click", () => {
|
||||
this.close();
|
||||
});
|
||||
}
|
||||
|
||||
onClose() {
|
||||
const { contentEl } = this;
|
||||
contentEl.empty();
|
||||
}
|
||||
}
|
||||
|
|
@ -288,7 +288,8 @@ export class WorkspaceManager {
|
|||
// Create new workspace (cloned from current or default)
|
||||
public async createWorkspace(
|
||||
name: string,
|
||||
baseWorkspaceId?: string
|
||||
baseWorkspaceId?: string,
|
||||
icon?: string
|
||||
): Promise<WorkspaceData> {
|
||||
const config = this.getWorkspacesConfig();
|
||||
const id = this.generateId();
|
||||
|
|
@ -316,6 +317,13 @@ export class WorkspaceManager {
|
|||
settings,
|
||||
};
|
||||
|
||||
// Add icon if provided, otherwise inherit from base workspace if cloning
|
||||
if (icon) {
|
||||
newWorkspace.icon = icon;
|
||||
} else if (baseWorkspace?.icon && baseId !== config.defaultWorkspaceId) {
|
||||
newWorkspace.icon = baseWorkspace.icon;
|
||||
}
|
||||
|
||||
config.byId[id] = newWorkspace;
|
||||
config.order.push(id);
|
||||
|
||||
|
|
@ -360,7 +368,8 @@ export class WorkspaceManager {
|
|||
// Rename workspace
|
||||
public async renameWorkspace(
|
||||
workspaceId: string,
|
||||
newName: string
|
||||
newName: string,
|
||||
icon?: string
|
||||
): Promise<void> {
|
||||
const config = this.getWorkspacesConfig();
|
||||
const workspace = config.byId[workspaceId];
|
||||
|
|
@ -370,6 +379,9 @@ export class WorkspaceManager {
|
|||
}
|
||||
|
||||
workspace.name = newName;
|
||||
if (icon !== undefined) {
|
||||
workspace.icon = icon;
|
||||
}
|
||||
workspace.updatedAt = Date.now();
|
||||
|
||||
await this.plugin.saveSettings();
|
||||
|
|
|
|||
|
|
@ -5,12 +5,9 @@
|
|||
/* Shadows */
|
||||
--tg-shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
|
||||
--tg-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
|
||||
--tg-shadow-md:
|
||||
0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
|
||||
--tg-shadow-lg:
|
||||
0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
|
||||
--tg-shadow-xl:
|
||||
0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
|
||||
--tg-shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
|
||||
--tg-shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
|
||||
--tg-shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
|
||||
|
||||
/* Animations */
|
||||
--tg-transition-fast: 150ms cubic-bezier(0.4, 0, 0.2, 1);
|
||||
|
|
@ -564,9 +561,8 @@
|
|||
border-radius: var(--tg-radius-full);
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
transform: translate(-50%, -50%);
|
||||
transition:
|
||||
width var(--tg-transition-slow),
|
||||
height var(--tg-transition-slow);
|
||||
transition: width var(--tg-transition-slow),
|
||||
height var(--tg-transition-slow);
|
||||
}
|
||||
|
||||
.tg-v2-button:active::before {
|
||||
|
|
@ -687,20 +683,16 @@
|
|||
.theme-dark {
|
||||
--tg-shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.3);
|
||||
--tg-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.4), 0 1px 2px -1px rgb(0 0 0 / 0.3);
|
||||
--tg-shadow-md:
|
||||
0 4px 6px -1px rgb(0 0 0 / 0.4), 0 2px 4px -2px rgb(0 0 0 / 0.3);
|
||||
--tg-shadow-lg:
|
||||
0 10px 15px -3px rgb(0 0 0 / 0.4), 0 4px 6px -4px rgb(0 0 0 / 0.3);
|
||||
--tg-shadow-xl:
|
||||
0 20px 25px -5px rgb(0 0 0 / 0.4), 0 8px 10px -6px rgb(0 0 0 / 0.3);
|
||||
--tg-shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.4), 0 2px 4px -2px rgb(0 0 0 / 0.3);
|
||||
--tg-shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.4), 0 4px 6px -4px rgb(0 0 0 / 0.3);
|
||||
--tg-shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.4), 0 8px 10px -6px rgb(0 0 0 / 0.3);
|
||||
}
|
||||
|
||||
.theme-dark .tg-v2-card:hover,
|
||||
.theme-dark .task-list-item:hover,
|
||||
.theme-dark .task-tree-project:hover {
|
||||
box-shadow:
|
||||
0 0 0 1px var(--interactive-accent),
|
||||
var(--tg-shadow-md);
|
||||
box-shadow: 0 0 0 1px var(--interactive-accent),
|
||||
var(--tg-shadow-md);
|
||||
}
|
||||
|
||||
/* ========== Progress Bars ========== */
|
||||
|
|
@ -790,3 +782,10 @@
|
|||
color: white;
|
||||
border-color: var(--text-error);
|
||||
}
|
||||
|
||||
.workspace-name-with-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
gap: var(--size-2-2);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ export interface WorkspaceData {
|
|||
id: string;
|
||||
name: string;
|
||||
color?: string;
|
||||
icon?: string; // Optional custom icon for the workspace
|
||||
updatedAt: number;
|
||||
order?: number;
|
||||
settings: WorkspaceOverrides; // Empty for Default workspace
|
||||
|
|
|
|||
|
|
@ -70,8 +70,6 @@ class PathTrie {
|
|||
.split("/")
|
||||
.filter((part) => part.length > 0);
|
||||
|
||||
console.log(parts, path);
|
||||
|
||||
// Try to match the rule starting at any segment in the input path
|
||||
for (let start = 0; start < parts.length; start++) {
|
||||
let current = this.root;
|
||||
|
|
@ -219,6 +217,7 @@ export class FileFilterManager {
|
|||
clearCache(): void {
|
||||
this.cache.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a cache key that is scoped by kind and scope to avoid cross-scope pollution
|
||||
*/
|
||||
|
|
@ -289,20 +288,20 @@ export class FileFilterManager {
|
|||
scope === "file"
|
||||
? this.fileSetFile
|
||||
: scope === "inline"
|
||||
? this.fileSetInline
|
||||
: this.fileSet;
|
||||
? this.fileSetInline
|
||||
: this.fileSet;
|
||||
const folderTrie =
|
||||
scope === "file"
|
||||
? this.folderTrieFile
|
||||
: scope === "inline"
|
||||
? this.folderTrieInline
|
||||
: this.folderTrie;
|
||||
? this.folderTrieInline
|
||||
: this.folderTrie;
|
||||
const patternRegexes =
|
||||
scope === "file"
|
||||
? this.patternRegexesFile
|
||||
: scope === "inline"
|
||||
? this.patternRegexesInline
|
||||
: this.patternRegexes;
|
||||
? this.patternRegexesInline
|
||||
: this.patternRegexes;
|
||||
|
||||
// Detailed match breakdown
|
||||
const fileHit = fileSet.has(normalizedPath);
|
||||
|
|
@ -341,28 +340,28 @@ export class FileFilterManager {
|
|||
switch (rule.type) {
|
||||
case "file":
|
||||
(bucket === "file"
|
||||
? this.fileSetFile
|
||||
: bucket === "inline"
|
||||
? this.fileSetInline
|
||||
: this.fileSet
|
||||
? this.fileSetFile
|
||||
: bucket === "inline"
|
||||
? this.fileSetInline
|
||||
: this.fileSet
|
||||
).add(this.normalizePath(rule.path));
|
||||
break;
|
||||
case "folder":
|
||||
(bucket === "file"
|
||||
? this.folderTrieFile
|
||||
: bucket === "inline"
|
||||
? this.folderTrieInline
|
||||
: this.folderTrie
|
||||
? this.folderTrieFile
|
||||
: bucket === "inline"
|
||||
? this.folderTrieInline
|
||||
: this.folderTrie
|
||||
).insert(rule.path, true);
|
||||
break;
|
||||
case "pattern":
|
||||
try {
|
||||
const regexPattern = this.globToRegex(rule.path);
|
||||
(bucket === "file"
|
||||
? this.patternRegexesFile
|
||||
: bucket === "inline"
|
||||
? this.patternRegexesInline
|
||||
: this.patternRegexes
|
||||
? this.patternRegexesFile
|
||||
: bucket === "inline"
|
||||
? this.patternRegexesInline
|
||||
: this.patternRegexes
|
||||
).push(new RegExp(regexPattern, "i"));
|
||||
} catch (error) {
|
||||
console.warn(
|
||||
|
|
|
|||
92
src/styles/workspace-icons.css
Normal file
92
src/styles/workspace-icons.css
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
/* Workspace Icon Selection Styles */
|
||||
|
||||
/* Icon selector container in modals */
|
||||
.workspace-icon-selector {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.workspace-icon-preview {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 6px;
|
||||
background-color: var(--background-modifier-hover);
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
}
|
||||
|
||||
.workspace-icon-preview svg {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
color: var(--text-normal);
|
||||
}
|
||||
|
||||
/* Workspace list in settings */
|
||||
.workspace-name-with-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.workspace-list-icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.workspace-list-icon svg {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.workspace-item-active .workspace-list-icon svg {
|
||||
color: var(--text-accent);
|
||||
}
|
||||
|
||||
/* Workspace selector button */
|
||||
.workspace-selector .workspace-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.workspace-selector .workspace-icon svg {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* Modal button container alignment */
|
||||
.modal-button-container {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 8px;
|
||||
margin-top: 20px;
|
||||
padding-top: 16px;
|
||||
border-top: 1px solid var(--background-modifier-border);
|
||||
}
|
||||
|
||||
/* Icon menu overrides for workspace selection */
|
||||
.tg-icon-menu {
|
||||
z-index: var(--layer-modal);
|
||||
}
|
||||
|
||||
/* Hover effects */
|
||||
.workspace-icon-preview:hover {
|
||||
background-color: var(--background-modifier-hover-more);
|
||||
}
|
||||
|
||||
/* Active workspace visual emphasis */
|
||||
.workspace-item-active {
|
||||
background-color: var(--background-secondary);
|
||||
border-left: 3px solid var(--text-accent);
|
||||
margin-left: -12px;
|
||||
padding-left: 9px;
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue