mirror of
https://github.com/taskgenius/taskgenius-plugin.git
synced 2026-07-22 06:40:25 +00:00
fix: tag input event issue
This commit is contained in:
parent
bc3a1d31d4
commit
4d04384b6e
15 changed files with 1007 additions and 434 deletions
5
.cursor/worktrees.json
Normal file
5
.cursor/worktrees.json
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"setup-worktree": [
|
||||
"pnpm install"
|
||||
]
|
||||
}
|
||||
|
|
@ -37,9 +37,9 @@ export class MinimalQuickCaptureModal extends BaseQuickCaptureModal {
|
|||
private targetFileEl: HTMLDivElement | null = null;
|
||||
private editorContainer: HTMLElement | null = null;
|
||||
|
||||
constructor(app: App, plugin: TaskProgressBarPlugin) {
|
||||
constructor(app: App, plugin: TaskProgressBarPlugin, metadata?: TaskMetadata) {
|
||||
// Default to checkbox mode for task creation
|
||||
super(app, plugin, "checkbox");
|
||||
super(app, plugin, "checkbox", metadata);
|
||||
|
||||
this.minimalSuggest = plugin.minimalQuickCaptureSuggest;
|
||||
|
||||
|
|
@ -48,6 +48,11 @@ export class MinimalQuickCaptureModal extends BaseQuickCaptureModal {
|
|||
this.taskMetadata.location =
|
||||
(targetType === "custom-file" ? "file" : targetType) || "fixed";
|
||||
this.taskMetadata.targetFile = this.getTargetFile();
|
||||
|
||||
// Merge passed metadata with existing taskMetadata
|
||||
if (metadata) {
|
||||
this.taskMetadata = { ...this.taskMetadata, ...metadata };
|
||||
}
|
||||
}
|
||||
|
||||
onOpen() {
|
||||
|
|
|
|||
|
|
@ -252,7 +252,6 @@ export class SettingsSearchComponent extends Component {
|
|||
nameEl.addClass("tg-settings-search-result-name");
|
||||
nameEl.textContent = result.item.name;
|
||||
|
||||
// 所属分类和标签页
|
||||
const metaEl = resultEl.createDiv();
|
||||
metaEl.addClass("tg-settings-search-result-meta");
|
||||
|
||||
|
|
@ -272,12 +271,10 @@ export class SettingsSearchComponent extends Component {
|
|||
);
|
||||
}
|
||||
|
||||
// 使用 registerDomEvent 注册点击事件
|
||||
this.registerDomEvent(resultEl, "click", () => {
|
||||
this.selectResult(result);
|
||||
});
|
||||
|
||||
// 使用 registerDomEvent 注册鼠标悬停事件
|
||||
this.registerDomEvent(resultEl, "mouseenter", () => {
|
||||
this.setSelectedIndex(index);
|
||||
});
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,4 +1,4 @@
|
|||
import { App, Component, setIcon } from "obsidian";
|
||||
import { App, Component, setIcon, debounce } from "obsidian";
|
||||
import { Task } from "@/types/task";
|
||||
import { TaskListItemComponent } from "./listItem"; // Re-import needed components
|
||||
import { ViewMode, getViewSettingOrDefault } from "@/common/setting-definition"; // 导入 SortCriterion
|
||||
|
|
@ -181,7 +181,7 @@ export class ContentComponent extends Component {
|
|||
console.log("toggle view mode");
|
||||
// Save the new view mode state
|
||||
saveViewMode(this.app, this.currentViewId, this.isTreeView);
|
||||
this.refreshTaskList(); // Refresh list completely on view mode change
|
||||
this.debounceRefreshTaskList(); // Refresh list completely on view mode change
|
||||
}
|
||||
|
||||
public setIsTreeView(isTree: boolean) {
|
||||
|
|
@ -193,7 +193,7 @@ export class ContentComponent extends Component {
|
|||
if (viewToggleBtn) {
|
||||
setIcon(viewToggleBtn, this.isTreeView ? "git-branch" : "list");
|
||||
}
|
||||
this.refreshTaskList();
|
||||
this.debounceRefreshTaskList();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -218,7 +218,7 @@ export class ContentComponent extends Component {
|
|||
this.notFilteredTasks = notFilteredTasks;
|
||||
updateSignatures();
|
||||
this.applyFilters();
|
||||
this.refreshTaskList();
|
||||
this.debounceRefreshTaskList();
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -248,7 +248,7 @@ export class ContentComponent extends Component {
|
|||
this.lastAllTasksSignature = nextAllSignature;
|
||||
this.lastNotFilteredTasksSignature = nextNotFilteredSignature;
|
||||
this.applyFilters();
|
||||
this.refreshTaskList();
|
||||
this.debounceRefreshTaskList();
|
||||
}
|
||||
|
||||
// Updated method signature
|
||||
|
|
@ -266,7 +266,7 @@ export class ContentComponent extends Component {
|
|||
this.initializeViewMode();
|
||||
|
||||
this.applyFilters();
|
||||
this.refreshTaskList();
|
||||
this.debounceRefreshTaskList();
|
||||
}
|
||||
|
||||
private applyFilters() {
|
||||
|
|
@ -329,7 +329,7 @@ export class ContentComponent extends Component {
|
|||
|
||||
private filterTasks(query: string) {
|
||||
this.applyFilters(); // Re-apply all filters including the new text query
|
||||
this.refreshTaskList();
|
||||
this.debounceRefreshTaskList();
|
||||
}
|
||||
|
||||
private computeTaskSignature(tasks: Task[]): string {
|
||||
|
|
@ -370,6 +370,8 @@ export class ContentComponent extends Component {
|
|||
return rect.width > 0 && rect.height > 0;
|
||||
}
|
||||
|
||||
private debounceRefreshTaskList = debounce(this.refreshTaskList, 700);
|
||||
|
||||
private refreshTaskList() {
|
||||
console.log("refreshing");
|
||||
// Defer rendering if container is not visible yet (e.g., view hidden during init)
|
||||
|
|
|
|||
|
|
@ -1,13 +1,9 @@
|
|||
import {
|
||||
Component,
|
||||
ExtraButtonComponent,
|
||||
TFile,
|
||||
ButtonComponent,
|
||||
DropdownComponent,
|
||||
TextComponent,
|
||||
moment,
|
||||
App,
|
||||
Menu,
|
||||
debounce,
|
||||
Platform,
|
||||
} from "obsidian";
|
||||
|
|
@ -22,6 +18,7 @@ import {
|
|||
ContextSuggest,
|
||||
ProjectSuggest,
|
||||
TagSuggest,
|
||||
TAG_COMMIT_EVENT,
|
||||
} from "@/components/ui/inputs/AutoComplete";
|
||||
import { FileTask } from "@/types/file-task";
|
||||
import {
|
||||
|
|
@ -108,8 +105,8 @@ export class TaskDetailsComponent extends Component {
|
|||
public containerEl: HTMLElement;
|
||||
private contentEl: HTMLElement;
|
||||
public currentTask: Task | null = null;
|
||||
private isVisible: boolean = true;
|
||||
private isEditing: boolean = false;
|
||||
private isVisible = true;
|
||||
private isEditing = false;
|
||||
private editFormEl: HTMLElement | null = null;
|
||||
|
||||
// Events
|
||||
|
|
@ -328,6 +325,8 @@ export class TaskDetailsComponent extends Component {
|
|||
cls: "details-edit-form",
|
||||
});
|
||||
|
||||
let requestSave = () => {};
|
||||
|
||||
// Task content/title
|
||||
const contentField = this.createFormField(
|
||||
this.editFormEl,
|
||||
|
|
@ -421,26 +420,124 @@ export class TaskDetailsComponent extends Component {
|
|||
new ProjectSuggest(this.app, projectInput.inputEl, this.plugin);
|
||||
|
||||
// Tags field
|
||||
let currentTags = (task.metadata.tags ?? [])
|
||||
.map((tag) => (tag.startsWith("#") ? tag.slice(1) : tag))
|
||||
.filter((tag) => tag);
|
||||
|
||||
const tagsField = this.createFormField(this.editFormEl, t("Tags"));
|
||||
const tagsInput = new TextComponent(tagsField);
|
||||
console.log("tagsInput", tagsInput, task.metadata.tags);
|
||||
// Remove # prefix from tags when displaying them
|
||||
tagsInput.setValue(
|
||||
task.metadata.tags
|
||||
? task.metadata.tags
|
||||
.map((tag) =>
|
||||
tag.startsWith("#") ? tag.slice(1) : tag
|
||||
)
|
||||
.join(", ")
|
||||
: ""
|
||||
const tagsContainer = tagsField.createDiv({ cls: "tags-editor" });
|
||||
const tagsList = tagsContainer.createDiv({ cls: "tags-editor__list" });
|
||||
const addTagButton = new ExtraButtonComponent(tagsContainer).setIcon(
|
||||
"plus"
|
||||
);
|
||||
const tagInputWrapper = tagsContainer.createDiv({
|
||||
cls: "tags-editor__input tags-editor__input--hidden",
|
||||
});
|
||||
const tagInput = new TextComponent(tagInputWrapper);
|
||||
tagInput.setPlaceholder(t("Add tag"));
|
||||
new TagSuggest(this.app, tagInput.inputEl, this.plugin, true);
|
||||
|
||||
const hideTagInput = () => {
|
||||
tagInput.setValue("");
|
||||
tagInputWrapper.addClass("tags-editor__input--hidden");
|
||||
};
|
||||
|
||||
const renderTags = () => {
|
||||
tagsList.empty();
|
||||
|
||||
if (currentTags.length === 0) {
|
||||
tagsList.createSpan({
|
||||
cls: "tags-editor__empty",
|
||||
text: t("No tags"),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
currentTags.forEach((tag, index) => {
|
||||
const tagChip = tagsList.createDiv({
|
||||
cls: "tags-editor__tag",
|
||||
});
|
||||
tagChip.createSpan({
|
||||
cls: "tags-editor__tag-label",
|
||||
text: `#${tag}`,
|
||||
});
|
||||
|
||||
new ExtraButtonComponent(tagChip)
|
||||
.setIcon("x")
|
||||
.setTooltip(t("Remove tag"))
|
||||
.onClick(() => {
|
||||
currentTags.splice(index, 1);
|
||||
renderTags();
|
||||
requestSave();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const addTag = (rawValue: string) => {
|
||||
const trimmedValue = rawValue.trim();
|
||||
if (!trimmedValue) {
|
||||
hideTagInput();
|
||||
return;
|
||||
}
|
||||
|
||||
const normalized = trimmedValue
|
||||
.replace(/^#+/, "")
|
||||
.replace(/[,\s]+$/g, "")
|
||||
.trim();
|
||||
|
||||
if (!normalized) {
|
||||
hideTagInput();
|
||||
return;
|
||||
}
|
||||
|
||||
const exists = currentTags.some(
|
||||
(existing) =>
|
||||
existing.toLowerCase() === normalized.toLowerCase()
|
||||
);
|
||||
if (exists) {
|
||||
hideTagInput();
|
||||
return;
|
||||
}
|
||||
|
||||
currentTags.push(normalized);
|
||||
renderTags();
|
||||
hideTagInput();
|
||||
requestSave();
|
||||
};
|
||||
|
||||
const addTagFromInput = () => addTag(tagInput.getValue());
|
||||
|
||||
this.registerDomEvent(
|
||||
tagInput.inputEl,
|
||||
TAG_COMMIT_EVENT as keyof HTMLElementEventMap,
|
||||
(event) => {
|
||||
event.stopPropagation();
|
||||
const customEvent = event as CustomEvent<{ tag: string }>;
|
||||
addTag(customEvent.detail?.tag ?? "");
|
||||
}
|
||||
);
|
||||
|
||||
addTagButton.onClick(() => {
|
||||
tagInputWrapper.removeClass("tags-editor__input--hidden");
|
||||
tagInput.inputEl.focus();
|
||||
});
|
||||
|
||||
this.registerDomEvent(tagInput.inputEl, "keydown", (event) => {
|
||||
if (event.key === "Escape") {
|
||||
event.preventDefault();
|
||||
hideTagInput();
|
||||
}
|
||||
});
|
||||
|
||||
this.registerDomEvent(tagInput.inputEl, "blur", () => {
|
||||
addTagFromInput();
|
||||
});
|
||||
|
||||
tagsField
|
||||
.createSpan({ cls: "field-description" })
|
||||
.setText(
|
||||
t("Comma separated") + " " + t("e.g. #tag1, #tag2, #tag3")
|
||||
);
|
||||
.setText(t("Click + to add tags. Click × on a tag to remove it."));
|
||||
|
||||
new TagSuggest(this.app, tagsInput.inputEl, this.plugin);
|
||||
renderTags();
|
||||
|
||||
// Context field
|
||||
const contextField = this.createFormField(
|
||||
|
|
@ -559,17 +656,10 @@ export class TaskDetailsComponent extends Component {
|
|||
metadata.project = task.metadata.project;
|
||||
}
|
||||
|
||||
// Parse and update tags (remove # prefix if present)
|
||||
const tagsValue = tagsInput.getValue();
|
||||
metadata.tags = tagsValue
|
||||
? tagsValue
|
||||
.split(",")
|
||||
.map((tag) => tag.trim())
|
||||
.map((tag) =>
|
||||
tag.startsWith("#") ? tag.slice(1) : tag
|
||||
) // Remove # prefix if present
|
||||
.filter((tag) => tag)
|
||||
: [];
|
||||
// Update tags from current list
|
||||
metadata.tags = currentTags
|
||||
.map((tag) => tag.trim())
|
||||
.filter((tag) => tag.length > 0);
|
||||
|
||||
// Update context
|
||||
const contextValue = contextInput.getValue();
|
||||
|
|
@ -700,6 +790,8 @@ export class TaskDetailsComponent extends Component {
|
|||
}
|
||||
}, 800); // 1500ms debounce time - allow time for multi-field editing
|
||||
|
||||
requestSave = () => saveTask();
|
||||
|
||||
// Use OnCompletionConfigurator directly
|
||||
const onCompletionConfigurator = new OnCompletionConfigurator(
|
||||
onCompletionField,
|
||||
|
|
@ -803,7 +895,7 @@ export class TaskDetailsComponent extends Component {
|
|||
// Register all input elements
|
||||
registerBlurEvent(contentInput.inputEl);
|
||||
registerBlurEvent(projectInput.inputEl);
|
||||
registerBlurEvent(tagsInput.inputEl);
|
||||
registerBlurEvent(tagInput.inputEl);
|
||||
registerBlurEvent(contextInput.inputEl);
|
||||
registerBlurEvent(priorityDropdown.selectEl);
|
||||
// Remove blur events for date inputs to prevent duplicate saves
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import {
|
|||
setIcon,
|
||||
ExtraButtonComponent,
|
||||
Platform,
|
||||
Notice,
|
||||
} from "obsidian";
|
||||
import { Task, TgProject } from "@/types/task";
|
||||
import { t } from "@/translations/helper";
|
||||
|
|
@ -23,6 +24,7 @@ import {
|
|||
formatProgressText,
|
||||
ProgressData,
|
||||
} from "@/editor-extensions/ui-widgets/progress-bar-widget";
|
||||
import { QuickCaptureModal } from "@/components/features/quick-capture/modals/QuickCaptureModalWithSwitch";
|
||||
|
||||
interface SelectedProjects {
|
||||
projects: string[];
|
||||
|
|
@ -271,6 +273,32 @@ export class ProjectsComponent extends Component {
|
|||
});
|
||||
taskCountEl.setText(`0 ${t("tasks")}`);
|
||||
|
||||
// Add quick capture button to add task to current project
|
||||
const addTaskButton = headerTopRightRow.createDiv({
|
||||
cls: "projects-add-task-btn clickable-icon",
|
||||
attr: { "aria-label": t("Add task to project") },
|
||||
});
|
||||
setIcon(addTaskButton, "plus");
|
||||
|
||||
this.registerDomEvent(addTaskButton, "click", () => {
|
||||
// Get the currently selected single project
|
||||
const selectedProject =
|
||||
this.selectedProjects.projects.length === 1
|
||||
? this.selectedProjects.projects[0]
|
||||
: undefined;
|
||||
|
||||
if (selectedProject) {
|
||||
new QuickCaptureModal(
|
||||
this.app,
|
||||
this.plugin,
|
||||
{ project: selectedProject },
|
||||
true
|
||||
).open();
|
||||
} else {
|
||||
new Notice(t("Please select a single project first"));
|
||||
}
|
||||
});
|
||||
|
||||
// Add view toggle button
|
||||
const viewToggleBtn = headerTopRightRow.createDiv({
|
||||
cls: "view-toggle-btn",
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ const CACHE_DURATION = 30000; // 30 seconds
|
|||
// Helper function to get cached data
|
||||
export async function getCachedData(
|
||||
plugin: TaskProgressBarPlugin,
|
||||
forceRefresh: boolean = false
|
||||
forceRefresh: boolean = false,
|
||||
): Promise<GlobalAutoCompleteCache> {
|
||||
const now = Date.now();
|
||||
|
||||
|
|
@ -33,7 +33,7 @@ export async function getCachedData(
|
|||
if (!globalCache || now - globalCache.lastUpdate > CACHE_DURATION) {
|
||||
// Fetch fresh data
|
||||
const tags = Object.keys(plugin.app.metadataCache.getTags() || {}).map(
|
||||
(tag) => tag.substring(1) // Remove # prefix
|
||||
(tag) => tag.substring(1), // Remove # prefix
|
||||
);
|
||||
|
||||
// Get projects and contexts from dataflow using the new convenience method
|
||||
|
|
@ -49,7 +49,7 @@ export async function getCachedData(
|
|||
} catch (error) {
|
||||
console.warn(
|
||||
"Failed to get projects/contexts from dataflow:",
|
||||
error
|
||||
error,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -92,7 +92,10 @@ export async function getCachedData(
|
|||
}
|
||||
|
||||
abstract class BaseSuggest<T> extends AbstractInputSuggest<T> {
|
||||
constructor(app: App, public inputEl: HTMLInputElement) {
|
||||
constructor(
|
||||
app: App,
|
||||
public inputEl: HTMLInputElement,
|
||||
) {
|
||||
super(app, inputEl);
|
||||
}
|
||||
|
||||
|
|
@ -124,7 +127,7 @@ class CustomSuggest extends BaseSuggest<string> {
|
|||
constructor(
|
||||
app: App,
|
||||
inputEl: HTMLInputElement,
|
||||
availableChoices: string[]
|
||||
availableChoices: string[],
|
||||
) {
|
||||
super(app, inputEl);
|
||||
this.availableChoices = availableChoices;
|
||||
|
|
@ -138,8 +141,8 @@ class CustomSuggest extends BaseSuggest<string> {
|
|||
return this.availableChoices
|
||||
.filter(
|
||||
(
|
||||
cmd: string // Add type to cmd
|
||||
) => fuzzySearch(cmd.toLowerCase()) // Call the returned function
|
||||
cmd: string, // Add type to cmd
|
||||
) => fuzzySearch(cmd.toLowerCase()), // Call the returned function
|
||||
)
|
||||
.slice(0, 100);
|
||||
}
|
||||
|
|
@ -160,7 +163,7 @@ export class ProjectSuggest extends CustomSuggest {
|
|||
constructor(
|
||||
app: App,
|
||||
inputEl: HTMLInputElement,
|
||||
plugin: TaskProgressBarPlugin
|
||||
plugin: TaskProgressBarPlugin,
|
||||
) {
|
||||
// Initialize with empty list, will be populated asynchronously
|
||||
super(app, inputEl, []);
|
||||
|
|
@ -178,7 +181,7 @@ export class ProjectSuggest extends CustomSuggest {
|
|||
} catch (e) {
|
||||
console.warn(
|
||||
"ProjectSuggest: failed to refresh projects on focus",
|
||||
e
|
||||
e,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
|
@ -192,7 +195,7 @@ export class ContextSuggest extends CustomSuggest {
|
|||
constructor(
|
||||
app: App,
|
||||
inputEl: HTMLInputElement,
|
||||
plugin: TaskProgressBarPlugin
|
||||
plugin: TaskProgressBarPlugin,
|
||||
) {
|
||||
// Initialize with empty list, will be populated asynchronously
|
||||
super(app, inputEl, []);
|
||||
|
|
@ -207,11 +210,22 @@ export class ContextSuggest extends CustomSuggest {
|
|||
/**
|
||||
* TagSuggest - Provides autocomplete for tag names
|
||||
*/
|
||||
export const TAG_COMMIT_EVENT = "task-progress-bar:tag-commit";
|
||||
|
||||
export class TagSuggest extends CustomSuggest {
|
||||
private readonly detailedKeydownHandler = (event: KeyboardEvent) => {
|
||||
if (event.key === "Enter") {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
this.commitDetailedTag();
|
||||
}
|
||||
};
|
||||
|
||||
constructor(
|
||||
app: App,
|
||||
inputEl: HTMLInputElement,
|
||||
plugin: TaskProgressBarPlugin
|
||||
plugin: TaskProgressBarPlugin,
|
||||
readonly isDetailed: boolean = false,
|
||||
) {
|
||||
// Initialize with empty list, will be populated asynchronously
|
||||
super(app, inputEl, []);
|
||||
|
|
@ -220,10 +234,30 @@ export class TagSuggest extends CustomSuggest {
|
|||
getCachedData(plugin).then((cachedData) => {
|
||||
this.availableChoices = cachedData.tags;
|
||||
});
|
||||
|
||||
if (this.isDetailed) {
|
||||
inputEl.addEventListener("keydown", this.detailedKeydownHandler);
|
||||
}
|
||||
}
|
||||
|
||||
// Override getSuggestions to handle comma-separated tags
|
||||
getSuggestions(query: string): string[] {
|
||||
if (this.isDetailed) {
|
||||
const currentTagInput = query
|
||||
.trim()
|
||||
.replace(/^#+/, "")
|
||||
.toLowerCase();
|
||||
|
||||
if (!currentTagInput) {
|
||||
return this.availableChoices.slice(0, 100);
|
||||
}
|
||||
|
||||
const fuzzySearch = prepareFuzzySearch(currentTagInput);
|
||||
return this.availableChoices
|
||||
.filter((tag) => fuzzySearch(tag.toLowerCase()))
|
||||
.slice(0, 100);
|
||||
}
|
||||
|
||||
const parts = query.split(",");
|
||||
const currentTagInput = parts[parts.length - 1].trim();
|
||||
|
||||
|
|
@ -239,6 +273,10 @@ export class TagSuggest extends CustomSuggest {
|
|||
|
||||
// Override to add # prefix and keep previous tags
|
||||
getSuggestionValue(item: string): string {
|
||||
if (this.isDetailed) {
|
||||
return `#${item}`;
|
||||
}
|
||||
|
||||
const currentValue = this.inputEl.value;
|
||||
const parts = currentValue.split(",");
|
||||
|
||||
|
|
@ -253,13 +291,58 @@ export class TagSuggest extends CustomSuggest {
|
|||
getSuggestionText(item: string): string {
|
||||
return `#${item}`;
|
||||
}
|
||||
|
||||
// Override to intercept selection in detailed mode
|
||||
selectSuggestion(item: string, evt: MouseEvent | KeyboardEvent): void {
|
||||
super.selectSuggestion(item, evt);
|
||||
if (this.isDetailed) {
|
||||
this.commitDetailedTag();
|
||||
}
|
||||
}
|
||||
|
||||
private commitDetailedTag(): void {
|
||||
if (!this.isDetailed) {
|
||||
return;
|
||||
}
|
||||
|
||||
const rawValue = this.inputEl.value.trim();
|
||||
if (!rawValue) {
|
||||
this.resetDetailedInput();
|
||||
return;
|
||||
}
|
||||
|
||||
const normalized = rawValue
|
||||
.replace(/^#+/, "")
|
||||
.replace(/[,\s]+$/g, "")
|
||||
.trim();
|
||||
|
||||
if (!normalized) {
|
||||
this.resetDetailedInput();
|
||||
return;
|
||||
}
|
||||
|
||||
this.inputEl.dispatchEvent(
|
||||
new CustomEvent(TAG_COMMIT_EVENT, {
|
||||
detail: { tag: normalized },
|
||||
bubbles: true,
|
||||
}),
|
||||
);
|
||||
|
||||
this.resetDetailedInput();
|
||||
}
|
||||
|
||||
private resetDetailedInput(): void {
|
||||
this.inputEl.value = "";
|
||||
this.inputEl.trigger("input");
|
||||
this.close();
|
||||
}
|
||||
}
|
||||
|
||||
export class SingleFolderSuggest extends CustomSuggest {
|
||||
constructor(
|
||||
app: App,
|
||||
inputEl: HTMLInputElement,
|
||||
plugin: TaskProgressBarPlugin
|
||||
plugin: TaskProgressBarPlugin,
|
||||
) {
|
||||
const folders = app.vault.getAllFolders();
|
||||
const paths = folders.map((file) => file.path);
|
||||
|
|
@ -278,7 +361,7 @@ export class FolderSuggest extends CustomSuggest {
|
|||
app: App,
|
||||
inputEl: HTMLInputElement,
|
||||
plugin: TaskProgressBarPlugin,
|
||||
outputType: "single" | "multiple" = "multiple"
|
||||
outputType: "single" | "multiple" = "multiple",
|
||||
) {
|
||||
// Get all markdown files in the vault
|
||||
const folders = app.vault.getAllFolders();
|
||||
|
|
@ -299,7 +382,7 @@ export class FolderSuggest extends CustomSuggest {
|
|||
}
|
||||
|
||||
const fuzzySearch = prepareFuzzySearch(
|
||||
currentPathInput.toLowerCase()
|
||||
currentPathInput.toLowerCase(),
|
||||
);
|
||||
return this.availableChoices
|
||||
.filter((path) => fuzzySearch(path.toLowerCase()))
|
||||
|
|
@ -357,7 +440,7 @@ export class ImageSuggest extends CustomSuggest {
|
|||
constructor(
|
||||
app: App,
|
||||
inputEl: HTMLInputElement,
|
||||
plugin: TaskProgressBarPlugin
|
||||
plugin: TaskProgressBarPlugin,
|
||||
) {
|
||||
// Get all images in the vault
|
||||
const images = app.vault
|
||||
|
|
@ -369,7 +452,7 @@ export class ImageSuggest extends CustomSuggest {
|
|||
file.extension === "jpeg" ||
|
||||
file.extension === "gif" ||
|
||||
file.extension === "svg" ||
|
||||
file.extension === "webp"
|
||||
file.extension === "webp",
|
||||
);
|
||||
const paths = images.map((file) => file.path);
|
||||
super(app, inputEl, paths);
|
||||
|
|
@ -388,7 +471,7 @@ export class FileSuggest extends AbstractInputSuggest<TFile> {
|
|||
app: App,
|
||||
inputEl: HTMLInputElement | HTMLDivElement,
|
||||
options: QuickCaptureOptions,
|
||||
onFileSelected?: (file: TFile) => void
|
||||
onFileSelected?: (file: TFile) => void,
|
||||
) {
|
||||
super(app, inputEl);
|
||||
this.suggestEl.addClass("quick-capture-file-suggest");
|
||||
|
|
@ -429,7 +512,7 @@ export class FileSuggest extends AbstractInputSuggest<TFile> {
|
|||
})
|
||||
.filter(
|
||||
(match): match is { file: TFile; score: number } =>
|
||||
match !== null
|
||||
match !== null,
|
||||
)
|
||||
.sort((a, b) => {
|
||||
// Sort by score (higher is better)
|
||||
|
|
@ -459,7 +542,7 @@ export class SimpleFileSuggest extends AbstractInputSuggest<TFile> {
|
|||
constructor(
|
||||
inputEl: HTMLInputElement,
|
||||
plugin: TaskProgressBarPlugin,
|
||||
onFileSelected?: (file: TFile) => void
|
||||
onFileSelected?: (file: TFile) => void,
|
||||
) {
|
||||
super(plugin.app, inputEl);
|
||||
this.onFileSelected = onFileSelected || (() => {});
|
||||
|
|
@ -480,7 +563,7 @@ export class SimpleFileSuggest extends AbstractInputSuggest<TFile> {
|
|||
})
|
||||
.filter(
|
||||
(match): match is { file: TFile; score: number } =>
|
||||
match !== null
|
||||
match !== null,
|
||||
)
|
||||
.sort((a, b) => {
|
||||
// Sort by score (higher is better)
|
||||
|
|
|
|||
|
|
@ -1333,7 +1333,7 @@ export class DataflowOrchestrator {
|
|||
|
||||
// Debug: log effective specialTagPrefixes for verification
|
||||
console.debug(
|
||||
"[TG] Parser specialTagPrefixes:",
|
||||
"[Task Genius] Parser specialTagPrefixes:",
|
||||
parserConfig.specialTagPrefixes,
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -35,14 +35,14 @@ function parseTasksWithConfigurableParser(
|
|||
|
||||
// Debug: show incoming prefixes and effective specialTagPrefixes keys
|
||||
try {
|
||||
console.debug("[TG][Worker] incoming prefixes", {
|
||||
console.debug("[Task Genius][Worker] incoming prefixes", {
|
||||
preferMetadataFormat: settings.preferMetadataFormat,
|
||||
projectTagPrefix: settings.projectTagPrefix,
|
||||
contextTagPrefix: settings.contextTagPrefix,
|
||||
areaTagPrefix: settings.areaTagPrefix,
|
||||
});
|
||||
console.debug(
|
||||
"[TG][Worker] specialTagPrefixes keys (before)",
|
||||
"[Task Genius][Worker] specialTagPrefixes keys (before)",
|
||||
Object.keys(config.specialTagPrefixes || {}),
|
||||
);
|
||||
} catch {}
|
||||
|
|
@ -63,7 +63,7 @@ function parseTasksWithConfigurableParser(
|
|||
};
|
||||
try {
|
||||
console.debug(
|
||||
"[TG][Worker] specialTagPrefixes keys (after)",
|
||||
"[Task Genius][Worker] specialTagPrefixes keys (after)",
|
||||
Object.keys(config.specialTagPrefixes),
|
||||
);
|
||||
} catch {}
|
||||
|
|
|
|||
27
src/index.ts
27
src/index.ts
|
|
@ -215,7 +215,7 @@ export default class TaskProgressBarPlugin extends Plugin {
|
|||
private iconsDeferred = false;
|
||||
|
||||
async onload() {
|
||||
console.time("[TG] onload");
|
||||
console.time("[Task Genius] onload");
|
||||
await this.loadSettings();
|
||||
|
||||
// Initialize version manager first
|
||||
|
|
@ -323,7 +323,7 @@ export default class TaskProgressBarPlugin extends Plugin {
|
|||
);
|
||||
|
||||
this.app.workspace.onLayoutReady(async () => {
|
||||
console.time("[TG] onLayoutReady");
|
||||
console.time("[Task Genius] onLayoutReady");
|
||||
|
||||
await this.initializeDeferredStartup();
|
||||
|
||||
|
|
@ -443,14 +443,14 @@ export default class TaskProgressBarPlugin extends Plugin {
|
|||
|
||||
this.maybeShowChangelog();
|
||||
|
||||
console.timeEnd("[TG] onLayoutReady");
|
||||
console.timeEnd("[Task Genius] onLayoutReady");
|
||||
});
|
||||
|
||||
await this.migratePresetTaskFiltersIfNeeded();
|
||||
|
||||
this.registerCoreCommands();
|
||||
|
||||
console.timeEnd("[TG] onload");
|
||||
console.timeEnd("[Task Genius] onload");
|
||||
}
|
||||
|
||||
private async initializeDeferredStartup(): Promise<void> {
|
||||
|
|
@ -461,7 +461,7 @@ export default class TaskProgressBarPlugin extends Plugin {
|
|||
return;
|
||||
}
|
||||
|
||||
console.time("[TG] initializeIndexer");
|
||||
console.time("[Task Genius] initializeIndexer");
|
||||
|
||||
await this.ensureFluentIntegration();
|
||||
|
||||
|
|
@ -475,7 +475,7 @@ export default class TaskProgressBarPlugin extends Plugin {
|
|||
const dataflowInitialized = await this.initializeDataflowOrchestrator();
|
||||
if (!dataflowInitialized) {
|
||||
this.scheduleExtendedCommands();
|
||||
console.timeEnd("[TG] initializeIndexer");
|
||||
console.timeEnd("[Task Genius] initializeIndexer");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -497,7 +497,7 @@ export default class TaskProgressBarPlugin extends Plugin {
|
|||
|
||||
this.scheduleExtendedCommands();
|
||||
|
||||
console.timeEnd("[TG] initializeIndexer");
|
||||
console.timeEnd("[Task Genius] initializeIndexer");
|
||||
}
|
||||
|
||||
private async ensureFluentIntegration(): Promise<void> {
|
||||
|
|
@ -829,7 +829,7 @@ export default class TaskProgressBarPlugin extends Plugin {
|
|||
return;
|
||||
}
|
||||
|
||||
console.time("[TG] migratePresetTaskFilters");
|
||||
console.time("[Task Genius] migratePresetTaskFilters");
|
||||
this.settings.taskFilter.presetTaskFilters = presets.map(
|
||||
(preset: any) => {
|
||||
if (preset.options) {
|
||||
|
|
@ -839,7 +839,7 @@ export default class TaskProgressBarPlugin extends Plugin {
|
|||
},
|
||||
);
|
||||
await this.saveSettings();
|
||||
console.timeEnd("[TG] migratePresetTaskFilters");
|
||||
console.timeEnd("[Task Genius] migratePresetTaskFilters");
|
||||
}
|
||||
|
||||
registerCommands() {
|
||||
|
|
@ -1814,7 +1814,7 @@ export default class TaskProgressBarPlugin extends Plugin {
|
|||
lastVersion !== "9.9.0"
|
||||
) {
|
||||
console.log(
|
||||
`[TG] Migration detected: ${previousVersion} -> ${currentVersion}, opening onboarding`,
|
||||
`[Task Genius] Migration detected: ${previousVersion} -> ${currentVersion}, opening onboarding`,
|
||||
);
|
||||
|
||||
// Directly open onboarding view (same pattern as maybeShowChangelog)
|
||||
|
|
@ -1825,7 +1825,10 @@ export default class TaskProgressBarPlugin extends Plugin {
|
|||
await this.saveSettings();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("[TG] Failed to check migration onboarding:", error);
|
||||
console.error(
|
||||
"[Task Genius] Failed to check migration onboarding:",
|
||||
error,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1853,7 +1856,7 @@ export default class TaskProgressBarPlugin extends Plugin {
|
|||
const isBeta = manifestVersion.toLowerCase().includes("beta");
|
||||
this.changelogManager.openChangelog(manifestVersion, isBeta);
|
||||
} catch (error) {
|
||||
console.error("[TG] Failed to show changelog:", error);
|
||||
console.error("[Task Genius] Failed to show changelog:", error);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { debounce, ItemView, Scope, WorkspaceLeaf } from "obsidian";
|
||||
import { debounce, ItemView, Scope, WorkspaceLeaf, setIcon } from "obsidian";
|
||||
import TaskProgressBarPlugin from "@/index";
|
||||
import { Task } from "@/types/task";
|
||||
import "@/styles/fluent/fluent-main.css";
|
||||
|
|
@ -29,6 +29,7 @@ import { FluentGestureManager } from "@/components/features/fluent/managers/Flue
|
|||
import { FluentWorkspaceStateManager } from "@/components/features/fluent/managers/FluentWorkspaceStateManager";
|
||||
import { FluentActionHandlers } from "@/components/features/fluent/managers/FluentActionHandlers";
|
||||
import { TaskSelectionManager } from "@/components/features/task/selection/TaskSelectionManager";
|
||||
import { getViewSettingOrDefault } from "@/common/setting-definition";
|
||||
|
||||
export const FLUENT_TASK_VIEW = "fluent-task-genius-view";
|
||||
|
||||
|
|
@ -156,12 +157,12 @@ export class FluentTaskView extends ItemView {
|
|||
!leafId.startsWith("view-config-") &&
|
||||
leafId !== "global-filter"
|
||||
) {
|
||||
console.log("[TG] Filter changed from live component");
|
||||
console.log("[Task Genius] Filter changed from live component");
|
||||
this.liveFilterState = filterState;
|
||||
this.currentFilterState = filterState;
|
||||
} else if (!leafId) {
|
||||
// No leafId means it's also a live filter change
|
||||
console.log("[TG] Filter changed (no leafId)");
|
||||
console.log("[Task Genius] Filter changed (no leafId)");
|
||||
this.liveFilterState = filterState;
|
||||
this.currentFilterState = filterState;
|
||||
}
|
||||
|
|
@ -189,7 +190,7 @@ export class FluentTaskView extends ItemView {
|
|||
}
|
||||
} catch (e) {
|
||||
console.warn(
|
||||
"[TG] Failed to sync selectedProject from filter state",
|
||||
"[Task Genius] Failed to sync selectedProject from filter state",
|
||||
e,
|
||||
);
|
||||
}
|
||||
|
|
@ -280,7 +281,7 @@ export class FluentTaskView extends ItemView {
|
|||
* Main initialization method
|
||||
*/
|
||||
async onOpen() {
|
||||
console.log("[TG] onOpen started");
|
||||
console.log("[Task Genius] onOpen started");
|
||||
this.isInitializing = true;
|
||||
|
||||
try {
|
||||
|
|
@ -289,7 +290,7 @@ export class FluentTaskView extends ItemView {
|
|||
// ====================
|
||||
if (this.DEBUG_MODE) {
|
||||
console.log(
|
||||
"[TG] Initializing UI, managers, structure, and events...",
|
||||
"[Task Genius] Initializing UI, managers, structure, and events...",
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -320,7 +321,7 @@ export class FluentTaskView extends ItemView {
|
|||
|
||||
if (this.DEBUG_MODE) {
|
||||
console.log(
|
||||
"[TG] ✅ UI, managers, structure, and events initialized",
|
||||
"[Task Genius] ✅ UI, managers, structure, and events initialized",
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -328,7 +329,7 @@ export class FluentTaskView extends ItemView {
|
|||
// PHASE 5: Restore Workspace State
|
||||
// ====================
|
||||
if (this.DEBUG_MODE) {
|
||||
console.log("[TG] Restoring workspace state...");
|
||||
console.log("[Task Genius] Restoring workspace state...");
|
||||
}
|
||||
|
||||
const savedWorkspaceId =
|
||||
|
|
@ -338,7 +339,7 @@ export class FluentTaskView extends ItemView {
|
|||
this.viewState.currentWorkspace = savedWorkspaceId;
|
||||
if (this.DEBUG_MODE) {
|
||||
console.log(
|
||||
`[TG] Restored workspace ID: ${savedWorkspaceId}`,
|
||||
`[Task Genius] Restored workspace ID: ${savedWorkspaceId}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -404,11 +405,11 @@ export class FluentTaskView extends ItemView {
|
|||
// ====================
|
||||
// PHASE 6: Load Data (KEY PHASE)
|
||||
// ====================
|
||||
console.log("[TG] Loading tasks...");
|
||||
console.log("[Task Genius] Loading tasks...");
|
||||
await this.dataManager.loadTasks(false); // Will trigger onTasksLoaded callback
|
||||
await this.dataManager.registerDataflowListeners();
|
||||
console.log(
|
||||
`[TG] ✅ Loaded ${this.tasks.length} tasks, ${this.filteredTasks.length} after filters`,
|
||||
`[Task Genius] ✅ Loaded ${this.tasks.length} tasks, ${this.filteredTasks.length} after filters`,
|
||||
);
|
||||
|
||||
// ====================
|
||||
|
|
@ -419,12 +420,12 @@ export class FluentTaskView extends ItemView {
|
|||
|
||||
// Check window size and auto-collapse sidebar if needed
|
||||
if (this.DEBUG_MODE) {
|
||||
console.log("[TG] Checking sidebar collapse...");
|
||||
console.log("[Task Genius] Checking sidebar collapse...");
|
||||
}
|
||||
this.layoutManager.checkAndCollapseSidebar();
|
||||
} catch (error) {
|
||||
console.error("[TG] ❌ Initialization error:", error);
|
||||
console.error("[TG] Error stack:", (error as Error).stack);
|
||||
console.error("[Task Genius] ❌ Initialization error:", error);
|
||||
console.error("[Task Genius] Error stack:", (error as Error).stack);
|
||||
this.loadError =
|
||||
(error as Error).message || "Failed to initialize view";
|
||||
} finally {
|
||||
|
|
@ -433,17 +434,17 @@ export class FluentTaskView extends ItemView {
|
|||
// ====================
|
||||
if (this.DEBUG_MODE) {
|
||||
console.log(
|
||||
`[TG] Finalizing (isInitializing was ${this.isInitializing})`,
|
||||
`[Task Genius] Finalizing (isInitializing was ${this.isInitializing})`,
|
||||
);
|
||||
}
|
||||
|
||||
this.isInitializing = false;
|
||||
|
||||
if (this.DEBUG_MODE) {
|
||||
console.log("[TG] Calling final updateView()...");
|
||||
console.log("[Task Genius] Calling final updateView()...");
|
||||
}
|
||||
this.updateView();
|
||||
console.log("[TG] ✅ Initialization complete");
|
||||
console.log("[Task Genius] ✅ Initialization complete");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -491,7 +492,9 @@ export class FluentTaskView extends ItemView {
|
|||
}
|
||||
},
|
||||
onUpdateNeeded: (source) => {
|
||||
console.log(`[TG] Update needed from source: ${source}`);
|
||||
console.log(
|
||||
`[Task Genius] Update needed from source: ${source}`,
|
||||
);
|
||||
// Re-apply filters and update view
|
||||
this.filteredTasks = this.dataManager.applyFilters(this.tasks);
|
||||
this.updateView();
|
||||
|
|
@ -559,7 +562,7 @@ export class FluentTaskView extends ItemView {
|
|||
// This enables the full project overview mode
|
||||
if (viewId === "projects") {
|
||||
console.log(
|
||||
"[TG] Navigating to projects overview - clearing project selection",
|
||||
"[Task Genius] Navigating to projects overview - clearing project selection",
|
||||
);
|
||||
this.viewState.selectedProject = undefined;
|
||||
|
||||
|
|
@ -595,7 +598,10 @@ export class FluentTaskView extends ItemView {
|
|||
);
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn("[TG] Failed to clear project filter", e);
|
||||
console.warn(
|
||||
"[Task Genius] Failed to clear project filter",
|
||||
e,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -617,7 +623,7 @@ export class FluentTaskView extends ItemView {
|
|||
this.updateView();
|
||||
},
|
||||
onProjectSelected: (projectId) => {
|
||||
console.log(`[TG] Project selected: ${projectId}`);
|
||||
console.log(`[Task Genius] Project selected: ${projectId}`);
|
||||
this.viewState.selectedProject = projectId;
|
||||
|
||||
// Switch to projects view
|
||||
|
|
@ -687,7 +693,7 @@ export class FluentTaskView extends ItemView {
|
|||
);
|
||||
} catch (e) {
|
||||
console.warn(
|
||||
"[TG] Failed to project-sync filter UI state",
|
||||
"[Task Genius] Failed to project-sync filter UI state",
|
||||
e,
|
||||
);
|
||||
// If filter sync fails, still update the view
|
||||
|
|
@ -751,14 +757,14 @@ export class FluentTaskView extends ItemView {
|
|||
// Trigger initial workspace leaf width update
|
||||
this.updateWorkspaceLeafWidth();
|
||||
|
||||
console.log("[TG] Managers initialized");
|
||||
console.log("[Task Genius] Managers initialized");
|
||||
}
|
||||
|
||||
/**
|
||||
* Build UI structure - MUST match original DOM structure for CSS
|
||||
*/
|
||||
private async buildUIStructure() {
|
||||
console.log("[TG] Building UI structure");
|
||||
console.log("[Task Genius] Building UI structure");
|
||||
|
||||
// Create layout structure (exact same as original)
|
||||
const layoutContainer = this.rootContainerEl.createDiv({
|
||||
|
|
@ -845,7 +851,7 @@ export class FluentTaskView extends ItemView {
|
|||
} else {
|
||||
sidebarEl.hide();
|
||||
console.log(
|
||||
"[TG] Using workspace side leaves: skip in-view sidebar",
|
||||
"[Task Genius] Using workspace side leaves: skip in-view sidebar",
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -868,7 +874,7 @@ export class FluentTaskView extends ItemView {
|
|||
this.addChild(this.topNavigation);
|
||||
|
||||
// Initialize view components
|
||||
console.log("[TG] Initializing view components");
|
||||
console.log("[Task Genius] Initializing view components");
|
||||
this.componentManager = new FluentComponentManager(
|
||||
this.app,
|
||||
this.plugin,
|
||||
|
|
@ -891,20 +897,33 @@ export class FluentTaskView extends ItemView {
|
|||
this.actionHandlers.handleTaskContextMenu(event, task);
|
||||
},
|
||||
onKanbanTaskStatusUpdate: (taskId, newStatusMark) => {
|
||||
console.log(`[FluentTaskView] Kanban status update callback received: taskId=${taskId}, mark=${newStatusMark}`);
|
||||
console.log(`[FluentTaskView] Current task list size: ${this.tasks.length}`);
|
||||
console.log(
|
||||
`[FluentTaskView] Kanban status update callback received: taskId=${taskId}, mark=${newStatusMark}`,
|
||||
);
|
||||
console.log(
|
||||
`[FluentTaskView] Current task list size: ${this.tasks.length}`,
|
||||
);
|
||||
|
||||
const task = this.tasks.find((t) => t.id === taskId);
|
||||
if (task) {
|
||||
console.log('[FluentTaskView] Task found, delegating to action handler');
|
||||
console.log(
|
||||
"[FluentTaskView] Task found, delegating to action handler",
|
||||
);
|
||||
this.actionHandlers.handleKanbanTaskStatusUpdate(
|
||||
task,
|
||||
newStatusMark,
|
||||
);
|
||||
} else {
|
||||
console.error(`[FluentTaskView] CRITICAL: Task ${taskId} not found in local task list`);
|
||||
console.error('[FluentTaskView] Available task IDs:', this.tasks.slice(0, 5).map(t => t.id));
|
||||
console.error('[FluentTaskView] This indicates a synchronization issue between kanban and main view');
|
||||
console.error(
|
||||
`[FluentTaskView] CRITICAL: Task ${taskId} not found in local task list`,
|
||||
);
|
||||
console.error(
|
||||
"[FluentTaskView] Available task IDs:",
|
||||
this.tasks.slice(0, 5).map((t) => t.id),
|
||||
);
|
||||
console.error(
|
||||
"[FluentTaskView] This indicates a synchronization issue between kanban and main view",
|
||||
);
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
@ -914,7 +933,7 @@ export class FluentTaskView extends ItemView {
|
|||
this.componentManager.initializeViewComponents();
|
||||
|
||||
// Sidebar toggle in header and responsive collapse
|
||||
console.log("[TG] Creating sidebar toggle");
|
||||
console.log("[Task Genius] Creating sidebar toggle");
|
||||
this.layoutManager.createSidebarToggle();
|
||||
|
||||
// Create task count mark
|
||||
|
|
@ -937,10 +956,10 @@ export class FluentTaskView extends ItemView {
|
|||
});
|
||||
|
||||
// Create action buttons in Obsidian view header
|
||||
console.log("[TG] Creating action buttons");
|
||||
console.log("[Task Genius] Creating action buttons");
|
||||
this.layoutManager.createActionButtons();
|
||||
|
||||
console.log("[TG] UI structure built");
|
||||
console.log("[Task Genius] UI structure built");
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1110,12 +1129,12 @@ export class FluentTaskView extends ItemView {
|
|||
*/
|
||||
private updateView() {
|
||||
if (this.isInitializing) {
|
||||
console.log("[TG] Skip update during initialization");
|
||||
console.log("[Task Genius] Skip update during initialization");
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(
|
||||
`[TG] Proceeding with view update for ${this.currentViewId}`,
|
||||
`[Task Genius] Proceeding with view update for ${this.currentViewId}`,
|
||||
);
|
||||
|
||||
// Update top navigation available modes based on current view
|
||||
|
|
@ -1186,7 +1205,7 @@ export class FluentTaskView extends ItemView {
|
|||
* Reset all active filters
|
||||
*/
|
||||
private resetCurrentFilter(): void {
|
||||
console.log("[TG] Resetting filter");
|
||||
console.log("[Task Genius] Resetting filter");
|
||||
|
||||
// Clear filter states
|
||||
this.liveFilterState = null;
|
||||
|
|
@ -1237,7 +1256,7 @@ export class FluentTaskView extends ItemView {
|
|||
* Clean up on close
|
||||
*/
|
||||
async onClose() {
|
||||
console.log("[TG] onClose started");
|
||||
console.log("[Task Genius] onClose started");
|
||||
|
||||
// Save workspace layout before closing
|
||||
this.workspaceStateManager.saveWorkspaceLayout();
|
||||
|
|
@ -1250,6 +1269,6 @@ export class FluentTaskView extends ItemView {
|
|||
// Clear selection
|
||||
this.actionHandlers.clearSelection();
|
||||
|
||||
console.log("[TG] onClose completed");
|
||||
console.log("[Task Genius] onClose completed");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,55 +40,83 @@ const KEY_GET_DROP = "task-genius/workspace-dnd:getDropLocation";
|
|||
* Install a runtime monkey-patch for Obsidian's internal drag handling,
|
||||
* using monkey-around for co-operative, removable patches.
|
||||
*/
|
||||
export function installWorkspaceDragMonitor(plugin: TaskProgressBarPlugin): void {
|
||||
export function installWorkspaceDragMonitor(
|
||||
plugin: TaskProgressBarPlugin,
|
||||
): void {
|
||||
const unpatch = around(Workspace.prototype as any, {
|
||||
onDragLeaf(old: Function | undefined) {
|
||||
return dedupe(KEY_ON_DRAG, old as Function, function (this: any, e: DragEvent, leaf: any) {
|
||||
const restricted = isRestrictedLeaf(leaf);
|
||||
// Mark workspace as currently dragging a restricted leaf until drop/dragend
|
||||
if (restricted) setRestrictState(this, true);
|
||||
if (restricted) {
|
||||
const vt = leaf?.view?.getViewType?.();
|
||||
console.debug("[TG][MonkeyPatch] onDragLeaf(restricted)", vt);
|
||||
} else {
|
||||
console.debug("[TG][MonkeyPatch] onDragLeaf");
|
||||
}
|
||||
// Install one-shot cleanup on drop/dragend
|
||||
const ws = this;
|
||||
if (restricted) {
|
||||
const cleanup = () => {
|
||||
setRestrictState(ws, false);
|
||||
window.removeEventListener("dragend", cleanup, true);
|
||||
window.removeEventListener("drop", cleanup, true);
|
||||
};
|
||||
window.addEventListener("dragend", cleanup, true);
|
||||
window.addEventListener("drop", cleanup, true);
|
||||
}
|
||||
return old && old.apply(this, [e, leaf]);
|
||||
});
|
||||
return dedupe(
|
||||
KEY_ON_DRAG,
|
||||
old as Function,
|
||||
function (this: any, e: DragEvent, leaf: any) {
|
||||
const restricted = isRestrictedLeaf(leaf);
|
||||
// Mark workspace as currently dragging a restricted leaf until drop/dragend
|
||||
if (restricted) setRestrictState(this, true);
|
||||
if (restricted) {
|
||||
const vt = leaf?.view?.getViewType?.();
|
||||
console.debug(
|
||||
"[Task Genius] onDragLeaf(restricted)",
|
||||
vt,
|
||||
);
|
||||
} else {
|
||||
console.debug("[Task Genius] onDragLeaf");
|
||||
}
|
||||
// Install one-shot cleanup on drop/dragend
|
||||
const ws = this;
|
||||
if (restricted) {
|
||||
const cleanup = () => {
|
||||
setRestrictState(ws, false);
|
||||
window.removeEventListener(
|
||||
"dragend",
|
||||
cleanup,
|
||||
true,
|
||||
);
|
||||
window.removeEventListener("drop", cleanup, true);
|
||||
};
|
||||
window.addEventListener("dragend", cleanup, true);
|
||||
window.addEventListener("drop", cleanup, true);
|
||||
}
|
||||
return old && old.apply(this, [e, leaf]);
|
||||
},
|
||||
);
|
||||
},
|
||||
|
||||
getDropLocation(old: Function | undefined) {
|
||||
return dedupe(KEY_GET_DROP, old as Function, function (this: any, ...args: any[]) {
|
||||
const target = old && old.apply(this, args);
|
||||
return dedupe(
|
||||
KEY_GET_DROP,
|
||||
old as Function,
|
||||
function (this: any, ...args: any[]) {
|
||||
const target = old && old.apply(this, args);
|
||||
|
||||
try {
|
||||
if (getRestrictState(this) && target) {
|
||||
const root = typeof target?.getRoot === "function" ? target.getRoot() : undefined;
|
||||
const isCenterRegion = root && root === this.rootSplit && target !== this.leftSplit && target !== this.rightSplit;
|
||||
if (isCenterRegion) {
|
||||
console.debug("[TG][MonkeyPatch] Blocked center drop location for restricted leaf");
|
||||
return null;
|
||||
try {
|
||||
if (getRestrictState(this) && target) {
|
||||
const root =
|
||||
typeof target?.getRoot === "function"
|
||||
? target.getRoot()
|
||||
: undefined;
|
||||
const isCenterRegion =
|
||||
root &&
|
||||
root === this.rootSplit &&
|
||||
target !== this.leftSplit &&
|
||||
target !== this.rightSplit;
|
||||
if (isCenterRegion) {
|
||||
console.debug(
|
||||
"[Task Genius] Blocked center drop location for restricted leaf",
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn(
|
||||
"[Task Genius] getDropLocation patch error",
|
||||
err,
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn("[TG][MonkeyPatch] getDropLocation patch error", err);
|
||||
}
|
||||
return target;
|
||||
});
|
||||
return target;
|
||||
},
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
plugin.register(unpatch);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -315,6 +315,91 @@
|
|||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.tags-editor {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.tags-editor__list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.tags-editor__tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding: 2px 8px;
|
||||
border-radius: 999px;
|
||||
background-color: var(--background-modifier-border);
|
||||
color: var(--text-muted);
|
||||
font-size: 0.8em;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.tags-editor__tag-label {
|
||||
color: var(--text-normal);
|
||||
}
|
||||
|
||||
.tags-editor__remove {
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--text-muted);
|
||||
cursor: pointer;
|
||||
font-size: 0.8em;
|
||||
line-height: 1;
|
||||
padding: 0 2px;
|
||||
}
|
||||
|
||||
.tags-editor__remove:hover {
|
||||
color: var(--text-error);
|
||||
}
|
||||
|
||||
.tags-editor__add {
|
||||
background: var(--interactive-accent);
|
||||
color: var(--text-on-accent);
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
.tags-editor__add:hover {
|
||||
background: var(--interactive-accent-hover);
|
||||
}
|
||||
|
||||
.tags-editor__input {
|
||||
min-width: 140px;
|
||||
}
|
||||
|
||||
.tags-editor__input input {
|
||||
width: 100%;
|
||||
padding: 4px 6px;
|
||||
}
|
||||
|
||||
.tags-editor__input--hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tags-editor__empty {
|
||||
font-size: 0.8em;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.tags-editor__tag div.clickable-icon {
|
||||
padding: 0;
|
||||
--icon-size: 12px;
|
||||
}
|
||||
|
||||
.details-form-buttons {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
|
|
|||
74
styles.css
74
styles.css
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue