chore(settings): update workspace settings

This commit is contained in:
Quorafind 2025-09-24 16:09:11 +08:00
parent 5c3d19e95c
commit c0e694b8ee
2 changed files with 421 additions and 438 deletions

View file

@ -0,0 +1,410 @@
import { Menu, Modal, Notice, Setting } from "obsidian";
import { TaskProgressBarSettingTab } from "@/setting";
import TaskProgressBarPlugin from "@/index";
import { WorkspaceData } from "@/experimental/v2/types/workspace";
import { t } from "@/translations/helper";
export function renderWorkspaceSettingsTab(settingTab: TaskProgressBarSettingTab, containerEl: HTMLElement) {
const workspacesSection = containerEl.createDiv();
workspacesSection.addClass("workspaces-settings-section");
// Section header
const headerEl = workspacesSection.createEl("h2");
headerEl.setText(t("Workspace Management"));
headerEl.addClass("workspaces-section-heading");
// Description
const descEl = workspacesSection.createDiv();
descEl.addClass("workspaces-description");
descEl.setText(
t(
"Manage workspaces to organize different contexts with their own settings and filters."
)
);
if (!settingTab.plugin.workspaceManager) {
const warningEl = workspacesSection.createDiv();
warningEl.addClass("workspaces-warning");
warningEl.setText(t("Workspace manager is not available."));
return;
}
// Current workspace info
const currentWorkspace =
settingTab.plugin.workspaceManager.getActiveWorkspace();
const isDefault = settingTab.plugin.workspaceManager.isDefaultWorkspace(
currentWorkspace.id
);
new Setting(workspacesSection)
.setName(t("Current Workspace"))
.setDesc(
`${currentWorkspace.name}${
isDefault ? " (" + t("Default") + ")" : ""
}`
)
.addButton((button) => {
button.setButtonText(t("Switch Workspace")).onClick((evt) => {
showWorkspaceSelector(settingTab, evt);
});
});
// Workspace list
const allWorkspaces = settingTab.plugin.workspaceManager.getAllWorkspaces();
const workspaceListEl = workspacesSection.createDiv();
workspaceListEl.addClass("workspace-list");
const listHeaderEl = workspaceListEl.createEl("h3");
listHeaderEl.setText(t("All Workspaces"));
allWorkspaces.forEach((workspace) => {
const workspaceItemEl = workspaceListEl.createDiv();
workspaceItemEl.addClass("workspace-item");
const isCurrentActive = workspace.id === currentWorkspace.id;
const isDefaultWs =
settingTab.plugin.workspaceManager!.isDefaultWorkspace(workspace.id);
if (isCurrentActive) {
workspaceItemEl.addClass("workspace-item-active");
}
new Setting(workspaceItemEl)
.setName(workspace.name)
.setDesc(
isDefaultWs
? t("Default workspace")
: t("Last updated: {{date}}", {
interpolation: {
date: new Date(
workspace.updatedAt
).toLocaleDateString(),
},
})
)
.addButton((button) => {
if (isCurrentActive) {
button.setButtonText(t("Active")).setDisabled(true);
} else {
button.setButtonText(t("Switch")).onClick(async () => {
console.log("[TG-WORKSPACE] settings:switch", {
to: workspace.id,
});
await settingTab.plugin.workspaceManager!.setActiveWorkspace(
workspace.id
);
this.display();
});
}
})
.addButton((button) => {
button
.setIcon("edit")
.setTooltip(t("Rename"))
.onClick(() => {
showRenameWorkspaceDialog(settingTab, workspace);
});
})
.addButton((button) => {
if (isDefaultWs) {
button
.setIcon("trash")
.setTooltip(
t("Default workspace cannot be deleted")
);
button.setDisabled(true);
} else {
button
.setIcon("trash")
.setTooltip(t("Delete"))
.onClick(() => {
showDeleteWorkspaceDialog(settingTab, workspace);
});
}
});
});
// Create new workspace button
new Setting(workspacesSection)
.setName(t("Create New Workspace"))
.setDesc(t("Create a new workspace with custom settings"))
.addButton((button) => {
button
.setButtonText(t("Create"))
.setCta()
.onClick(() => {
showCreateWorkspaceDialog(settingTab);
});
});
}
function showWorkspaceSelector(settingTab: TaskProgressBarSettingTab, event: MouseEvent) {
if (!settingTab.plugin.workspaceManager) return;
const menu = new Menu();
const workspaces = settingTab.plugin.workspaceManager.getAllWorkspaces();
const currentWorkspace =
settingTab.plugin.workspaceManager.getActiveWorkspace();
workspaces.forEach((workspace) => {
menu.addItem((item) => {
item.setTitle(workspace.name)
.setIcon("layers")
.onClick(async () => {
await settingTab.plugin.workspaceManager?.setActiveWorkspace(
workspace.id
);
console.log("[TG-WORKSPACE] settings:menu switch", {
to: workspace.id,
});
this.display();
});
if (workspace.id === currentWorkspace.id) {
item.setChecked(true);
}
});
});
menu.showAtMouseEvent(event);
}
function showCreateWorkspaceDialog(settingTab: TaskProgressBarSettingTab) {
if (!settingTab.plugin.workspaceManager) return;
new CreateWorkspaceModal(settingTab.plugin, () => {
this.display();
}).open();
}
function showRenameWorkspaceDialog(settingTab: TaskProgressBarSettingTab, workspace: WorkspaceData) {
if (!settingTab.plugin.workspaceManager) return;
new RenameWorkspaceModal(settingTab.plugin, workspace, () => {
this.display();
}).open();
}
function showDeleteWorkspaceDialog(settingTab: TaskProgressBarSettingTab, workspace: WorkspaceData) {
if (!settingTab.plugin.workspaceManager) return;
new DeleteWorkspaceModal(settingTab.plugin, workspace, () => {
this.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();
}
}

View file

@ -46,6 +46,7 @@ import { IframeModal } from "@/components/ui/modals/IframeModal";
import { renderTaskTimerSettingTab } from "./components/features/settings/tabs/TaskTimerSettingsTab";
import { renderBasesSettingsTab } from "./components/features/settings/tabs/BasesSettingsTab";
import { WorkspaceData } from "./experimental/v2/types/workspace";
import { renderWorkspaceSettingsTab } from "@/components/features/settings/tabs/WorkspaceSettingTab";
export class TaskProgressBarSettingTab extends PluginSettingTab {
plugin: TaskProgressBarPlugin;
@ -218,7 +219,7 @@ export class TaskProgressBarSettingTab extends PluginSettingTab {
// icon: "beaker",
// category: "advanced",
// },
{ id: "about", name: t("About"), icon: "info", category: "info" },
{id: "about", name: t("About"), icon: "info", category: "info"},
];
constructor(app: App, plugin: TaskProgressBarPlugin) {
@ -289,7 +290,7 @@ export class TaskProgressBarSettingTab extends PluginSettingTab {
// Group tabs by category
const categories = {
core: { name: t("Core Settings"), tabs: [] as typeof this.tabs },
core: {name: t("Core Settings"), tabs: [] as typeof this.tabs},
display: {
name: t("Display & Progress"),
tabs: [] as typeof this.tabs,
@ -310,8 +311,8 @@ export class TaskProgressBarSettingTab extends PluginSettingTab {
name: t("Integration"),
tabs: [] as typeof this.tabs,
},
advanced: { name: t("Advanced"), tabs: [] as typeof this.tabs },
info: { name: t("Information"), tabs: [] as typeof this.tabs },
advanced: {name: t("Advanced"), tabs: [] as typeof this.tabs},
info: {name: t("Information"), tabs: [] as typeof this.tabs},
};
// Group tabs by category
@ -367,9 +368,9 @@ export class TaskProgressBarSettingTab extends PluginSettingTab {
labelEl.addClass("settings-tab-label");
labelEl.setText(
tab.name +
(tab.id === "about"
? " v" + this.plugin.manifest.version
: "")
(tab.id === "about"
? " v" + this.plugin.manifest.version
: "")
);
// Add click handler
@ -486,7 +487,7 @@ export class TaskProgressBarSettingTab extends PluginSettingTab {
headerText &&
headerText.includes(sectionId.replace("-", " "))
) {
header.scrollIntoView({ behavior: "smooth", block: "start" });
header.scrollIntoView({behavior: "smooth", block: "start"});
}
});
@ -583,7 +584,7 @@ export class TaskProgressBarSettingTab extends PluginSettingTab {
}
display(): void {
const { containerEl } = this;
const {containerEl} = this;
containerEl.empty();
@ -865,7 +866,7 @@ export class TaskProgressBarSettingTab extends PluginSettingTab {
}
private displayWorkspacesSettings(containerEl: HTMLElement): void {
this.renderWorkspacesSettingsTab(containerEl);
renderWorkspaceSettingsTab(this, containerEl);
}
// private displayExperimentalSettings(containerEl: HTMLElement): void {
@ -876,432 +877,4 @@ export class TaskProgressBarSettingTab extends PluginSettingTab {
renderTaskTimerSettingTab(this, containerEl);
}
private renderWorkspacesSettingsTab(containerEl: HTMLElement): void {
// Create workspaces settings section
const workspacesSection = containerEl.createDiv();
workspacesSection.addClass("workspaces-settings-section");
// Section header
const headerEl = workspacesSection.createEl("h2");
headerEl.setText(t("Workspace Management"));
headerEl.addClass("workspaces-section-heading");
// Description
const descEl = workspacesSection.createDiv();
descEl.addClass("workspaces-description");
descEl.setText(
t(
"Manage workspaces to organize different contexts with their own settings and filters."
)
);
if (!this.plugin.workspaceManager) {
const warningEl = workspacesSection.createDiv();
warningEl.addClass("workspaces-warning");
warningEl.setText(t("Workspace manager is not available."));
return;
}
// Current workspace info
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") + ")" : ""
}`
)
.addButton((button) => {
button.setButtonText(t("Switch Workspace")).onClick(() => {
this.showWorkspaceSelector();
});
});
// Workspace list
const allWorkspaces = this.plugin.workspaceManager.getAllWorkspaces();
const workspaceListEl = workspacesSection.createDiv();
workspaceListEl.addClass("workspace-list");
const listHeaderEl = workspaceListEl.createEl("h3");
listHeaderEl.setText(t("All Workspaces"));
allWorkspaces.forEach((workspace) => {
const workspaceItemEl = workspaceListEl.createDiv();
workspaceItemEl.addClass("workspace-item");
const isCurrentActive = workspace.id === currentWorkspace.id;
const isDefaultWs =
this.plugin.workspaceManager!.isDefaultWorkspace(workspace.id);
if (isCurrentActive) {
workspaceItemEl.addClass("workspace-item-active");
}
new Setting(workspaceItemEl)
.setName(workspace.name)
.setDesc(
isDefaultWs
? t("Default workspace")
: t("Last updated: {{date}}", {
interpolation: {
date: new Date(
workspace.updatedAt
).toLocaleDateString(),
},
})
)
.addButton((button) => {
if (isCurrentActive) {
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
);
this.display();
});
}
})
.addButton((button) => {
button
.setIcon("edit")
.setTooltip(t("Rename"))
.onClick(() => {
this.showRenameWorkspaceDialog(workspace);
});
})
.addButton((button) => {
if (isDefaultWs) {
button
.setIcon("trash")
.setTooltip(
t("Default workspace cannot be deleted")
);
button.setDisabled(true);
} else {
button
.setIcon("trash")
.setTooltip(t("Delete"))
.onClick(() => {
this.showDeleteWorkspaceDialog(workspace);
});
}
});
});
// Create new workspace button
new Setting(workspacesSection)
.setName(t("Create New Workspace"))
.setDesc(t("Create a new workspace with custom settings"))
.addButton((button) => {
button
.setButtonText(t("Create"))
.setCta()
.onClick(() => {
this.showCreateWorkspaceDialog();
});
});
}
private showWorkspaceSelector() {
if (!this.plugin.workspaceManager) return;
const menu = new Menu();
const workspaces = this.plugin.workspaceManager.getAllWorkspaces();
const currentWorkspace =
this.plugin.workspaceManager.getActiveWorkspace();
workspaces.forEach((workspace) => {
menu.addItem((item) => {
item.setTitle(workspace.name)
.setIcon("layers")
.onClick(async () => {
await this.plugin.workspaceManager!.setActiveWorkspace(
workspace.id
);
console.log("[TG-WORKSPACE] settings:menu switch", {
to: workspace.id,
});
this.display();
});
if (workspace.id === currentWorkspace.id) {
item.setChecked(true);
}
});
});
menu.showAtPosition({ x: 0, y: 0 });
}
private showCreateWorkspaceDialog() {
if (!this.plugin.workspaceManager) return;
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();
}
}
new CreateWorkspaceModal(this.plugin, () => {
this.display();
}).open();
}
private showRenameWorkspaceDialog(workspace: WorkspaceData) {
if (!this.plugin.workspaceManager) return;
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();
}
}
new RenameWorkspaceModal(this.plugin, workspace, () => {
this.display();
}).open();
}
private showDeleteWorkspaceDialog(workspace: WorkspaceData) {
if (!this.plugin.workspaceManager) return;
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();
}
}
new DeleteWorkspaceModal(this.plugin, workspace, () => {
this.display();
}).open();
}
private renderExperimentalSettingsTab(containerEl: HTMLElement): void {
// Create experimental settings section
const experimentalSection = containerEl.createDiv();
experimentalSection.addClass("experimental-settings-section");
// Section header
const headerEl = experimentalSection.createEl("h2");
headerEl.setText("Experimental Features");
headerEl.addClass("experimental-section-heading");
// Warning notice
const warningEl = experimentalSection.createDiv();
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."
);
// 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."
);
}
}