diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index e019f3ca..00000000 --- a/.eslintignore +++ /dev/null @@ -1,3 +0,0 @@ -node_modules/ - -main.js diff --git a/data.json b/data.json index 47303943..5c45a022 100644 --- a/data.json +++ b/data.json @@ -1,5 +1,5 @@ { - "version": "2.0.0-beta-4", + "version": "2.0.0-beta-5", "data": { "lang": "en", "openOnStartup": false, @@ -145,7 +145,7 @@ "compatiblePlugins": { "dailyNotesPlugin": false, "dayPlannerPlugin": false, - "tasksPlugin": false, + "tasksPlugin": true, "reminderPlugin": false, "quickAddPlugin": false }, @@ -248,8 +248,8 @@ ], "hiddenTaskProperties": [], "autoAddUniqueID": true, - "uniqueIdCounter": 2291, - "experimentalFeatures": false, + "uniqueIdCounter": 2295, + "experimentalFeatures": true, "safeGuardFeature": true, "lastViewHistory": { "viewedType": "kanban", @@ -278,18 +278,6 @@ "boardName": "My Project", "boardDescription": "This is my personal project. This is a default board created by Task Board for you to kick start your journey with Task Board. Feel free to edit or create new boards." }, - "board_1039059216": { - "boardId": "board_1039059216", - "filePath": "TaskBoard/A Test Board (copy).taskboard", - "boardName": "A Test Board (copy)", - "boardDescription": "This is my personal project. This is a default board created by Task Board for you to kick start your journey with Task Board. Feel free to edit or create new boards." - }, - "board_3907751366": { - "boardId": "board_3907751366", - "filePath": "TaskBoard/A Test Board.taskboard", - "boardName": "A Test Board", - "boardDescription": "This is my personal project. This is a default board created by Task Board for you to kick start your journey with Task Board. Feel free to edit or create new boards." - }, "board_2553700948": { "boardId": "board_2553700948", "filePath": "Meta/Task_Board/Backup Boards/TaskBoard-Template-1774097758616.taskboard", @@ -301,6 +289,18 @@ "filePath": "Meta/Task_Board/Backup Boards/TaskBoard-Template-1778856605183.taskboard", "boardName": "My Project", "boardDescription": "This is my personal project. This is a default board created by Task Board for you to kick start your journey with Task Board. Feel free to edit or create new boards." + }, + "board_1039059216": { + "boardId": "board_1039059216", + "filePath": "TaskBoard/A Test Board (copy).taskboard", + "boardName": "A Test Board (copy)", + "boardDescription": "This is my personal project. This is a default board created by Task Board for you to kick start your journey with Task Board. Feel free to edit or create new boards." + }, + "board_3907751366": { + "boardId": "board_3907751366", + "filePath": "TaskBoard/A Test Board.taskboard", + "boardName": "A Test Board", + "boardDescription": "This is my personal project. This is a default board created by Task Board for you to kick start your journey with Task Board. Feel free to edit or create new boards." } }, "filtersWarehouse": [ diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 00000000..c888a7aa --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,20 @@ +import tsparser from "@typescript-eslint/parser"; +import { defineConfig } from "eslint/config"; +import obsidianmd from "eslint-plugin-obsidianmd"; + +export default defineConfig([ + ...obsidianmd.configs.recommended, + { + files: ["**/*.ts", "**/*.tsx"], + ignores: ["node_modules/**", "main.js", "dist/**"], + languageOptions: { + parser: tsparser, + sourceType: "module", + ecmaVersion: "latest", + parserOptions: { + projectService: true, + tsconfigRootDir: import.meta.dirname, + }, + }, + }, +]); diff --git a/main.ts b/main.ts index f8fcbe91..439263f7 100644 --- a/main.ts +++ b/main.ts @@ -1,24 +1,41 @@ /** + * Task Board - Plugin for Obsidian + * Copyright (c) 2025-2026 tu2-atmanand + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * * @name main.ts * @path /main.ts - * The entry-point of this plugin. Initializes the plugin, initializes all the required + * @description The entry-point of this plugin. Initializes the plugin, initializes all the required * internal managers and utils. */ import { around } from "monkey-around"; import { - App, + type App, Menu, normalizePath, Notice, Plugin, - PluginManifest, + type PluginManifest, TAbstractFile, TFile, TFolder, + type ViewState, WorkspaceLeaf, } from "obsidian"; -import { EmbedRegistry } from "obsidian-typings"; +import { type EmbedRegistry } from "obsidian-typings"; import { parse } from "date-fns"; import { t } from "i18next"; import { @@ -39,9 +56,9 @@ import { RibbonIconActions, } from "./src/interfaces/Enums.js"; import { - PluginDataJson, + type PluginDataJson, DEFAULT_SETTINGS, - taskBoardFilesRegistryType, + type taskBoardFilesRegistryType, } from "./src/interfaces/GlobalSettings.js"; import { TaskBoardIcon } from "./src/interfaces/Icons.js"; import { bugReporterManagerInsatance } from "./src/managers/BugReporter.js"; @@ -77,9 +94,8 @@ import { TaskBoardSettingTab } from "./src/settings/TaskBoardSettingTab.js"; import { TaskBoardApi } from "./src/taskboardAPIs.js"; import { getCurrentLocalDateTimeString } from "./src/utils/DateTimeCalculations.js"; import { loadTranslationsOnStartup } from "./src/utils/lang/helper.js"; -import { Board, DEFAULT_BOARD } from "./src/interfaces/BoardConfigs.js"; +import { type Board, DEFAULT_BOARD } from "./src/interfaces/BoardConfigs.js"; import { generateRandomStringId } from "./src/utils/TaskItemUtils.js"; -import { getHideClassForProperty } from "./src/utils/UIHelpers.js"; import { TaskBoardEmbedComponent } from "./src/components/TaskBoardEmbedComponent.js"; /** @@ -87,8 +103,8 @@ import { TaskBoardEmbedComponent } from "./src/components/TaskBoardEmbedComponen */ export default class TaskBoard extends Plugin { app: App; - plugin: TaskBoard; - view: TaskBoardView | null; + // plugin: TaskBoard; + // view: TaskBoardView | null; settings: PluginDataJson = DEFAULT_SETTINGS; vaultScanner: VaultScanner; realTimeScanner: RealTimeScanner; @@ -120,9 +136,9 @@ export default class TaskBoard extends Plugin { private renameQueue: Array<{ file: TFile; oldPath: string }> = []; private deleteQueue: TFile[] = []; private createQueue: TFile[] = []; - private renameProcessingTimer: NodeJS.Timeout | null = null; - private deleteProcessingTimer: NodeJS.Timeout | null = null; - private createProcessingTimer: NodeJS.Timeout | null = null; + private renameProcessingTimer: number | null = null; + private deleteProcessingTimer: number | null = null; + private createProcessingTimer: number | null = null; private currentProgressNotice: Notice | null = null; private readonly QUEUE_DELAY = 2000; // Delay in ms before starting to process queue private readonly PROCESSING_INTERVAL = 100; // Delay between processing each file @@ -131,18 +147,18 @@ export default class TaskBoard extends Plugin { constructor(app: App, menifest: PluginManifest) { super(app, menifest); - this.plugin = this; + // this.plugin = this; this.app = app; - this.plugin.app = app; - this.view = null; + // this.plugin.app = app; + // this.view = null; this.settings = DEFAULT_SETTINGS; - this.vaultScanner = new VaultScanner(this.app, this.plugin); + this.vaultScanner = new VaultScanner(this.app, this); this.realTimeScanner = new RealTimeScanner( this.app, - this.plugin, + this, this.vaultScanner, ); - this.taskBoardFileManager = new TaskBoardFileManager(this.plugin); + this.taskBoardFileManager = new TaskBoardFileManager(this); this.editorModified = false; // this.currentModifiedFile = null; // this.fileUpdatedUsingModal = ""; @@ -153,11 +169,11 @@ export default class TaskBoard extends Plugin { } get api(): ReturnType { - return TaskBoardApi.GetApi(this.app, this.plugin); + return TaskBoardApi.GetApi(this.app, this); } async onload() { - console.log("Task Board : Loading..."); + // console.log("Task Board : Loading..."); // this.getLanguage(); await loadTranslationsOnStartup(this); @@ -178,24 +194,25 @@ export default class TaskBoard extends Plugin { // Register the Kanban view this.registerTaskBoardView(); + this.registerEmbedRegistry(); // Register events and commands only on Layout is ready this.app.workspace.onLayoutReady(() => { - this.compatiblePluginsAvailabilityCheck(); + void this.compatiblePluginsAvailabilityCheck(); dragDropTasksManagerInsatance.setPlugin(this); //Creates a Icon on Ribbon Bar (after i18n is initialized) - this.getRibbonIcon(); + void this.getRibbonIcon(); // Creating Few Events this.registerEvents(); // Register few commands - this.registerCommands(); + void this.registerCommands(); // For non-realtime scanning and scanning last modified files - this.createLocalStorageAndScanModifiedFiles(); + void this.createLocalStorageAndScanModifiedFiles(); // Run openAtStartup if openOnStartup is true this.openAtStartup(); @@ -209,18 +226,22 @@ export default class TaskBoard extends Plugin { // Register markdown post processor for hiding task properties this.registerReadingModePostProcessor(); - this.taskBoardFileManager.validateBoardFiles(); + void this.taskBoardFileManager.validateBoardFiles(); - setTimeout(() => this.findModifiedFilesOnAppAbsense(), 5000); + // Monkey-patch WorkspaceLeaf.setViewState to intercept .taskboard file clicks + this.registerMonkeyPatchForTaskboardFiles(); + + window.setTimeout( + () => void this.findModifiedFilesOnAppAbsense(), + 5000, + ); }); } onunload() { - console.log("Task Board : Uninstalling..."); - + // console.log("Task Board : Uninstalling..."); // deleteAllLocalStorageKeys(); // TODO : Enable this while production build. This is disabled for testing purpose because the data from localStorage is required for testing. // onUnloadSave(this.plugin); - // Obsidian already does this, no need to manually detach. // this.app.workspace.detachLeavesOfType(VIEW_TYPE_TASKBOARD); } @@ -316,12 +337,12 @@ export default class TaskBoard extends Plugin { }, }); - this.app.workspace.revealLeaf(leaf); + void this.app.workspace.revealLeaf(leaf); } - } catch (error) { + } catch (error: unknown) { bugReporterManagerInsatance.addToLogs( 202, - `Error opening the board: ${error}`, + `Error opening the board: ${String(error)}`, "main.ts/activateView", ); } @@ -346,7 +367,7 @@ export default class TaskBoard extends Plugin { duplicate: false, }); } else { - this.activateView("icon", false); + void this.activateView("icon", false); } break; case RibbonIconActions.allBoardsMenu: @@ -376,14 +397,14 @@ export default class TaskBoard extends Plugin { duplicate: false, }); } else { - this.activateView("icon", false); + void this.activateView("icon", false); } break; case RibbonIconActions.boardsExplorer: - openBoardsExplorerModal(this.plugin); + openBoardsExplorerModal(this); break; default: - openBoardsExplorerModal(this.plugin); + openBoardsExplorerModal(this); break; } @@ -407,13 +428,14 @@ export default class TaskBoard extends Plugin { } async loadSettings() { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment this.settings = Object.assign( {}, DEFAULT_SETTINGS, await this.loadData(), ); // this.migrateSettings(DEFAULT_SETTINGS, this.settings); - this.saveSettings(); + void this.saveSettings(); } async saveSettings(newSetting?: PluginDataJson) { @@ -449,46 +471,28 @@ export default class TaskBoard extends Plugin { // } // } - createLocalStorageAndScanModifiedFiles() { + async createLocalStorageAndScanModifiedFiles() { // Following line will create a localStorage. And then it will scan the previous files which didnt got scanned, becaues the Obsidian was closed before that or crashed. - this.realTimeScanner.initializeStack(); - this.realTimeScanner.processAllUpdatedFiles(); + await this.realTimeScanner.initializeStack(); + await this.realTimeScanner.processAllUpdatedFiles(); } registerTaskBoardView() { - this.registerView(VIEW_TYPE_TASKBOARD, (leaf) => { - this.view = new TaskBoardView(this, leaf); - return this.view; - }); + this.registerView( + VIEW_TYPE_TASKBOARD, + (leaf) => new TaskBoardView(this, leaf), + // { + // // eslint-disable-next-line obsidianmd/no-view-references-in-plugin + // // this.view = new TaskBoardView(this, leaf); + // // return new TaskBoardView(this, leaf); + // } + ); this.registerExtensions( [TASKBOARD_FILE_EXTENSION], VIEW_TYPE_TASKBOARD, ); - // Monkey-patch WorkspaceLeaf.setViewState to intercept .taskboard file clicks - this.registerMonkeyPatchForTaskboardFiles(); - - if (this.settings.data.experimentalFeatures) { - // @ts-ignore - const embedRegistry = this.app.embedRegistry as EmbedRegistry; - if ( - !embedRegistry?.isExtensionRegistered(TASKBOARD_FILE_EXTENSION) - ) { - embedRegistry?.registerExtension( - TASKBOARD_FILE_EXTENSION, - (context, file, _) => { - return new TaskBoardEmbedComponent( - context.containerEl, - this, - file, - context.containerEl.getAttr("alt") || undefined, - ) as any; - }, - ); - } - } - // Register TaskEditor view (can be opened in tabs or popout windows) // this.registerView(VIEW_TYPE_ADD_OR_EDIT_TASK, (leaf) => { // console.log("Leaf returned by registerView :", leaf); @@ -518,7 +522,11 @@ export default class TaskBoard extends Plugin { // This allows multiple plugins to patch the same method without conflicts const unregisterPatch = around(WorkspaceLeaf.prototype, { setViewState: (next) => - function (this: WorkspaceLeaf, state: any, eState?: any) { + function ( + this: WorkspaceLeaf, + state: ViewState, + eState?: unknown, + ) { const isTaskBoardView = state.type === VIEW_TYPE_TASKBOARD; const filePath = state.state?.file as string | undefined; const isTaskboardFile = @@ -526,7 +534,7 @@ export default class TaskBoard extends Plugin { if (isTaskBoardView && isTaskboardFile) { // Store the file path directly on the leaf instance for immediate access - (this as any).taskboardFilePath = filePath; + this.taskboardFilePath = filePath; // Also set ephemeral state for safety this.setEphemeralState({ taskboardFilePath: filePath }); @@ -542,6 +550,47 @@ export default class TaskBoard extends Plugin { this.register(unregisterPatch); } + private registerEmbedRegistry() { + try { + // feature : Embed `.taskboard` files inside notes + if (this.settings.data.experimentalFeatures) { + // @ts-ignore + const embedRegistry = this.app.embedRegistry as EmbedRegistry; + if ( + embedRegistry && + !embedRegistry?.isExtensionRegistered( + TASKBOARD_FILE_EXTENSION, + ) + ) { + embedRegistry?.registerExtension( + TASKBOARD_FILE_EXTENSION, + (context, file, subPath) => { + return new TaskBoardEmbedComponent( + this, + context, + file, + subPath, + ); + }, + ); + } + + this.register(() => { + embedRegistry?.unregisterExtension( + TASKBOARD_FILE_EXTENSION, + ); + }); + } + } catch (error) { + bugReporterManagerInsatance.showNotice( + 220, + "Failed to enable the embed boards feature", + `Below error message might provide more information about the issue: \nERROR : ${String(error)}`, + "main.ts/registerEmbedRegistry", + ); + } + } + registerEditorExtensions() { // TODO : The below editor extension will not going to be released in the upcoming version, will plan it for the next version. // Register task gutter extension @@ -589,7 +638,7 @@ export default class TaskBoard extends Plugin { // METHOD 1 // --------------------------------------------------------- // Find the view container or root element to apply CSS classes - // const viewContainer = document.querySelector(`.markdown-rendered`); + // const viewContainer = activeDocument.querySelector(`.markdown-rendered`); // if (viewContainer) { // // Remove all existing hide classes @@ -611,13 +660,13 @@ export default class TaskBoard extends Plugin { // METHOD 2 // --------------------------------------------------------- const styleId = "task-board-hide-task-properties-style"; - let styleEl = document.getElementById( + let styleEl = activeDocument.getElementById( styleId, ) as HTMLStyleElement | null; if (!styleEl) { - styleEl = document.createElement("style"); + styleEl = activeDocument.createElement("style"); styleEl.id = styleId; - document.head.appendChild(styleEl); + activeDocument.head.appendChild(styleEl); } let css = ""; const fadeInCSS = @@ -642,9 +691,10 @@ export default class TaskBoard extends Plugin { css += "span:hover .task-description>span>a.tag { opacity: 1; transform: scaleX(1); transition-delay: 0.3s; }"; css += fadeInCSS; - // css += - // "li:out-of-range .task-description>span>a.tag { display: none !important; animation: task-board-fade-out 0.5s ease-in-out; }"; - // css += fadeOutCSS; + // css += + // "li:out-of-range .task-description>span>a.tag { display: none !important; animation: task-board-fade-out 0.5s ease-in-out; }"; + // css += fadeOutCSS; + break; case taskPropertiesNames.CreatedDate: css += ".task-created { opacity: 0; transform: scaleX(0); transition: opacity 0.2s ease-in, transform 0.2s ease-in; }"; @@ -768,7 +818,7 @@ export default class TaskBoard extends Plugin { hiddenProperties: taskPropertiesNames[], ) { // Process text nodes to find and hide specific patterns - const walker = document.createTreeWalker( + const walker = activeDocument.createTreeWalker( element, NodeFilter.SHOW_TEXT, null, @@ -798,16 +848,15 @@ export default class TaskBoard extends Plugin { }); if (modified && textNode.parentElement) { - // Create a temporary element to hold the HTML - const tempDiv = document.createElement("div"); - // Use insertAdjacentHTML with proper sanitization (content already escaped via regex) - tempDiv.replaceChildren(); - tempDiv.insertAdjacentHTML("beforeend", content); - - // Replace the text node with the new content - while (tempDiv.firstChild) { + const parser = new DOMParser(); + const parsedDocument = parser.parseFromString( + content, + "text/html", + ); + + while (parsedDocument.body.firstChild) { textNode.parentNode?.insertBefore( - tempDiv.firstChild, + parsedDocument.body.firstChild, textNode, ); } @@ -819,7 +868,7 @@ export default class TaskBoard extends Plugin { openAtStartup() { if (!this.settings.data.openOnStartup) return; - this.activateView("icon", false); + void this.activateView("icon", false); } registerTaskBoardStatusBar() { @@ -834,14 +883,14 @@ export default class TaskBoard extends Plugin { id: "add-new-task", name: t("add-new-task"), callback: () => { - openAddNewTaskModal(this.plugin); + openAddNewTaskModal(this); }, }); this.addCommand({ id: "add-new-task-note", name: t("add-new-task-note"), callback: () => { - openAddNewTaskNoteModal(this.app, this.plugin); + openAddNewTaskNoteModal(this.app, this); }, }); this.addCommand({ @@ -854,7 +903,7 @@ export default class TaskBoard extends Plugin { if (activeEditor && activeFile) { openAddNewTaskInCurrentFileModal( this.app, - this.plugin, + this, activeFile, activeEditor?.getCursor(), ); @@ -867,17 +916,17 @@ export default class TaskBoard extends Plugin { }, }); this.addCommand({ - id: "open-task-board", + id: "open-recent-board", name: t("open-task-board"), callback: () => { - this.activateView("tab", false); + void this.activateView("tab", false); }, }); this.addCommand({ - id: "open-task-board-new-window", + id: "open-recent-board-new-window", name: t("open-task-board-in-new-window"), callback: () => { - this.activateView("window", false); + void this.activateView("window", false); }, }); this.addCommand({ @@ -891,7 +940,7 @@ export default class TaskBoard extends Plugin { id: "open-scan-vault-modal", name: t("open-scan-vault-modal"), callback: () => { - openScanVaultModal(this.plugin); + openScanVaultModal(this); }, }); this.addCommand({ @@ -907,7 +956,7 @@ export default class TaskBoard extends Plugin { this.addCommand({ id: "create-template-board", name: t("create-template-board"), - callback: async () => { + callback: () => { try { // Generate unique ID and filename for the new template board const boardId = generateRandomStringId("board"); @@ -915,40 +964,39 @@ export default class TaskBoard extends Plugin { const filePath = `TaskBoard-Template-${timestamp}.taskboard`; // Create a deep copy of DEFAULT_BOARD and update its properties - const newBoard: Board = JSON.parse( + const newBoard = JSON.parse( JSON.stringify(DEFAULT_BOARD), - ); + ) as Board; newBoard.id = boardId; // Save the board to disk - const saveSuccess = - await this.plugin.taskBoardFileManager.createNewBoardFile( - filePath, - newBoard, - ); - - if (!saveSuccess) { - bugReporterManagerInsatance.showNotice( - 187, - "Failed to create template board", - "saveBoardToDisk returned false", - "TaskBoardView.tsx/handleCreateTemplateBoard", - ); - return; - } + void this.taskBoardFileManager + .createNewBoardFile(filePath, newBoard) + .then((saveSuccess: boolean) => { + if (!saveSuccess) { + bugReporterManagerInsatance.showNotice( + 187, + "Failed to create template board", + "saveBoardToDisk returned false", + "main.ts/registerCommands/create-template-board", + ); + return; + } - // Show success notice - new Notice(t("board-created-successfully")); + // Show success notice + new Notice(t("board-created-successfully")); - const file = this.app.vault.getAbstractFileByPath(filePath); - if (file && file instanceof TFile) { - revealFileFolderInExplorer(this.plugin, file); - } + const file = + this.app.vault.getAbstractFileByPath(filePath); + if (file && file instanceof TFile) { + revealFileFolderInExplorer(this, file); + } - // Open the newly created board after some dalay. - setTimeout(() => { - this.activateView("tab", false, filePath); - }, 400); + // Open the newly created board after some dalay. + window.setTimeout(() => { + void this.activateView("tab", false, filePath); + }, 400); + }); } catch (error) { bugReporterManagerInsatance.showNotice( 187, @@ -965,7 +1013,7 @@ export default class TaskBoard extends Plugin { id: "open-migration-modal", name: "Open migration modal", callback: () => { - openMigrationModal(this.plugin); + openMigrationModal(this); }, }); } @@ -1002,11 +1050,11 @@ export default class TaskBoard extends Plugin { // Clear existing timer and set a new one if (this.renameProcessingTimer) { - clearTimeout(this.renameProcessingTimer); + window.clearTimeout(this.renameProcessingTimer); } - this.renameProcessingTimer = setTimeout(() => { - this.processRenameQueue(); + this.renameProcessingTimer = window.setTimeout(() => { + void this.processRenameQueue(); }, this.QUEUE_DELAY); } } @@ -1048,7 +1096,7 @@ export default class TaskBoard extends Plugin { const { file, oldPath } = allowedFiles.shift()!; if (file.extension === TASKBOARD_FILE_EXTENSION) { - this.taskBoardFileManager.onBoardFileRenamed( + await this.taskBoardFileManager.onBoardFileRenamed( file.path, oldPath, ); @@ -1080,7 +1128,7 @@ export default class TaskBoard extends Plugin { // Add delay between processing each file to prevent blocking UI if (allowedFiles.length > 0) { await new Promise((resolve) => - setTimeout(resolve, this.PROCESSING_INTERVAL), + window.setTimeout(resolve, this.PROCESSING_INTERVAL), ); } } @@ -1089,7 +1137,7 @@ export default class TaskBoard extends Plugin { this.currentProgressNotice?.hide(); this.currentProgressNotice = null; - this.plugin.vaultScanner.saveTasksToJsonCache(); + await this.vaultScanner.saveTasksToJsonCache(); eventEmitter.emit("REFRESH_BOARD"); if (processed > 20) { @@ -1100,7 +1148,7 @@ export default class TaskBoard extends Plugin { } if (this.renameProcessingTimer) - clearTimeout(this.renameProcessingTimer); + window.clearTimeout(this.renameProcessingTimer); } /** @@ -1113,8 +1161,8 @@ export default class TaskBoard extends Plugin { // Clear existing timer and set a new one if (!this.deleteProcessingTimer) { - this.deleteProcessingTimer = setTimeout(() => { - this.processDeleteQueue(); + this.deleteProcessingTimer = window.setTimeout(() => { + void this.processDeleteQueue(); }, this.QUEUE_DELAY); } else { // NOTE : I think there is no need to remove the Timout created, in 2 seconds, all the Obsidians triggers should finish, for the Task Board's processing to start. @@ -1154,7 +1202,9 @@ export default class TaskBoard extends Plugin { const file = allowedFiles.shift()!; if (file.extension === TASKBOARD_FILE_EXTENSION) { - this.taskBoardFileManager.onBoardFileDelete(file.path); + await this.taskBoardFileManager.onBoardFileDelete( + file.path, + ); continue; } @@ -1179,7 +1229,7 @@ export default class TaskBoard extends Plugin { // Add delay between processing each file to prevent blocking UI if (allowedFiles.length > 0) { await new Promise((resolve) => - setTimeout(resolve, this.PROCESSING_INTERVAL), + window.setTimeout(resolve, this.PROCESSING_INTERVAL), ); } } @@ -1187,7 +1237,7 @@ export default class TaskBoard extends Plugin { this.currentProgressNotice?.hide(); this.currentProgressNotice = null; - this.plugin.vaultScanner.saveTasksToJsonCache(); + await this.vaultScanner.saveTasksToJsonCache(); eventEmitter.emit("SOFT_REFRESH"); if (processed > 20) { @@ -1208,8 +1258,8 @@ export default class TaskBoard extends Plugin { // Clear existing timer and set a new one if (!this.createProcessingTimer) { - this.createProcessingTimer = setTimeout(() => { - this.processCreateQueue(); + this.createProcessingTimer = window.setTimeout(() => { + void this.processCreateQueue(); }, this.QUEUE_DELAY); } else { // NOTE : I think there is no need to remove the Timout created, in 2 seconds, all the Obsidians triggers should finish, for the Task Board's processing to start. @@ -1270,7 +1320,7 @@ export default class TaskBoard extends Plugin { // Add delay between processing each file to prevent blocking UI if (allowedFiles.length > 0) { await new Promise((resolve) => - setTimeout(resolve, this.PROCESSING_INTERVAL), + window.setTimeout(resolve, this.PROCESSING_INTERVAL), ); } } @@ -1320,8 +1370,8 @@ export default class TaskBoard extends Plugin { .filter((file) => { filesScannedCount++; return ( - file.stat.mtime > OBSIDIAN_CLOSED_TIME!.getTime() || - file.stat.ctime > OBSIDIAN_CLOSED_TIME!.getTime() + file.stat.mtime > OBSIDIAN_CLOSED_TIME.getTime() || + file.stat.ctime > OBSIDIAN_CLOSED_TIME.getTime() ); }); @@ -1345,18 +1395,18 @@ export default class TaskBoard extends Plugin { const deletedFilesList = [...deletedFiles]; const changed_files = modifiedCreatedRenamedFiles.filter((file) => - fileTypeAllowedForScanning(this.plugin.settings.data, file), + fileTypeAllowedForScanning(this.settings.data, file), ); const totalFilesLength = changed_files.length + deletedFilesList.length; if (totalFilesLength > 0) { const scanAllModifiedFiles = () => { - this.plugin.vaultScanner + void this.vaultScanner .refreshTasksFromFiles(changed_files, false) .then(async () => { if (deletedFilesList.length > 0) { - await this.plugin.vaultScanner.deleteCacheForFiles( + await this.vaultScanner.deleteCacheForFiles( deletedFilesList, ); } @@ -1388,7 +1438,7 @@ export default class TaskBoard extends Plugin { el.createEl("button", { text: t("scan-them"), cls: "ignoreBugButton", - onclick: async () => { + onclick: () => { try { modifiedFilesNotice.hide(); @@ -1421,9 +1471,9 @@ export default class TaskBoard extends Plugin { if ( modifiedFilesQueueLength > 0 ) { - await new Promise( + void new Promise( (resolve) => - setTimeout( + window.setTimeout( resolve, this .PROCESSING_INTERVAL, @@ -1472,16 +1522,14 @@ export default class TaskBoard extends Plugin { */ registerEvents() { this.registerEvent( - this.app.vault.on("modify", async (file: TAbstractFile) => { - if ( - fileTypeAllowedForScanning(this.plugin.settings.data, file) - ) { + this.app.vault.on("modify", (file: TAbstractFile) => { + if (fileTypeAllowedForScanning(this.settings.data, file)) { if (file instanceof TFile) { if ( - this.plugin.settings.data.scanMode === + this.settings.data.scanMode === scanModeOptions.REAL_TIME ) { - this.vaultScanner.refreshTasksFromFiles( + void this.vaultScanner.refreshTasksFromFiles( [file], false, ); @@ -1515,24 +1563,24 @@ export default class TaskBoard extends Plugin { }), ); - if (this.plugin.settings.data.scanMode !== scanModeOptions.MANUAL) { + if (this.settings.data.scanMode !== scanModeOptions.MANUAL) { // Listen for editor-blur event and trigger scanning if the editor was modified this.registerEvent( this.app.workspace.on( "active-leaf-change", (leaf: WorkspaceLeaf | null) => { - this.onFileModifiedAndLostFocus(); + void this.onFileModifiedAndLostFocus(); eventEmitter.emit("SAVE_MAP"); }, ), ); this.registerDomEvent(window, "blur", () => { - this.onFileModifiedAndLostFocus(); + void this.onFileModifiedAndLostFocus(); eventEmitter.emit("SAVE_MAP"); }); this.registerDomEvent(window, "focus", () => { - setTimeout(() => { - this.onFileModifiedAndLostFocus(); + window.setTimeout(() => { + void this.onFileModifiedAndLostFocus(); eventEmitter.emit("SAVE_MAP"); }, 200); }); @@ -1548,12 +1596,12 @@ export default class TaskBoard extends Plugin { }), ); - // const closeButton = document.querySelector( + // const closeButton = activeDocument.querySelector( // ".titlebar-button.mod-close" // ); // if (closeButton) { // this.registerDomEvent(closeButton, "mouseenter", () => { - // onUnloadSave(this.plugin); + // onUnloadSave(this); // }); // } @@ -1586,11 +1634,11 @@ export default class TaskBoard extends Plugin { .onClick(() => { if ( fileTypeAllowedForScanning( - this.plugin.settings.data, + this.settings.data, file, ) ) { - this.vaultScanner.refreshTasksFromFiles( + void this.vaultScanner.refreshTasksFromFiles( [file], true, ); @@ -1606,7 +1654,7 @@ export default class TaskBoard extends Plugin { this.settings.data.scanFilters.files.values.push( file.path, ); - this.saveSettings(); + void this.saveSettings(); }); }); } @@ -1619,7 +1667,7 @@ export default class TaskBoard extends Plugin { this.settings.data.scanFilters.files.values.push( file.path, ); - this.saveSettings(); + void this.saveSettings(); }); }); } @@ -1644,7 +1692,7 @@ export default class TaskBoard extends Plugin { this.settings.data.scanFilters.folders.values.push( file.path, ); - this.saveSettings(); + void this.saveSettings(); }); }); } @@ -1657,7 +1705,7 @@ export default class TaskBoard extends Plugin { this.settings.data.scanFilters.folders.values.push( file.path, ); - this.saveSettings(); + void this.saveSettings(); }); }); } @@ -1691,7 +1739,7 @@ export default class TaskBoard extends Plugin { // .setIcon(RefreshIcon) // .setSection("action") // .onClick(() => { - // onUnloadSave(this.plugin); + // onUnloadSave(this); // }); // }); // } @@ -1722,7 +1770,7 @@ export default class TaskBoard extends Plugin { filePath: string; duplicate: boolean; }) => { - this.activateView(data.layout, data.duplicate, data.filePath); + void this.activateView(data.layout, data.duplicate, data.filePath); }; eventEmitter.on("OPEN_BOARD", openBoardCallback); @@ -1746,12 +1794,12 @@ export default class TaskBoard extends Plugin { async compatiblePluginsAvailabilityCheck() { // Check if the Tasks plugin is installed and fetch the custom statuses - // await fetchTasksPluginCustomStatuses(this.plugin); - const tasksPlug = await isTasksPluginEnabled(this.plugin); - this.plugin.settings.data.compatiblePlugins.tasksPlugin = tasksPlug; + // await fetchTasksPluginCustomStatuses(this); + const tasksPlug = await isTasksPluginEnabled(this); + this.settings.data.compatiblePlugins.tasksPlugin = tasksPlug; // Check if the Reminder plugin is installed - isReminderPluginInstalled(this.plugin); + isReminderPluginInstalled(this); } private async runOnPluginUpdate() { @@ -1804,7 +1852,7 @@ export default class TaskBoard extends Plugin { // make the localStorage flag, 'manadatoryScan' to True if (previousVersion === "" || runMandatoryScan) { - localStorage.setItem(MANDATORY_SCAN_KEY, "true"); + this.app.saveLocalStorage(MANDATORY_SCAN_KEY, "true"); } this.settings.version = currentVersion; @@ -1813,7 +1861,7 @@ export default class TaskBoard extends Plugin { this.settings = migrateSettings(DEFAULT_SETTINGS, this.settings); this.settings.version = currentVersion; - this.saveSettings(); + void this.saveSettings(); // new Notice( // t("plugin-updated-notice", { @@ -1846,7 +1894,9 @@ export default class TaskBoard extends Plugin { 0, ); } else { - throw "Task Board: There was an issue while creating the template board file. Please check the logs."; + throw new Error( + "Task Board: There was an issue while creating the template board file. Please check the logs.", + ); } } catch (error) { bugReporterManagerInsatance.showNotice( diff --git a/manifest.json b/manifest.json index fa81dfa0..c83673b8 100644 --- a/manifest.json +++ b/manifest.json @@ -1,8 +1,8 @@ { "id": "task-board", "name": "Task Board", - "version": "2.0.0-beta-4", - "minAppVersion": "1.4.13", + "version": "2.0.0-beta-5", + "minAppVersion": "1.10.0", "description": "Manage all your tasks throughout your vault from a single board and much more...", "author": "Atmanand Gauns", "authorUrl": "https://github.com/tu2-atmanand", diff --git a/package-lock.json b/package-lock.json index dddbf74e..382541fc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "task-board", - "version": "2.0.0-beta-4", + "version": "2.0.0-beta-5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "task-board", - "version": "2.0.0-beta-4", + "version": "2.0.0-beta-5", "license": "GPL-3.0", "dependencies": { "@popperjs/core": "^2.11.8", @@ -30,11 +30,13 @@ "@types/react": "^18.2.0", "@types/react-dom": "^18.2.0", "@types/sortablejs": "^1.15.8", - "@typescript-eslint/eslint-plugin": "8.37.0", - "@typescript-eslint/parser": "8.37.0", + "@typescript-eslint/eslint-plugin": "^8.61.0", + "@typescript-eslint/parser": "^8.61.0", "builtin-modules": "5.0.0", "codemirror": "^6.0.0", "esbuild": "0.25.6", + "eslint": "^9.39.4", + "eslint-plugin-obsidianmd": "^0.3.0", "obsidian": "latest", "obsidian-typings": "obsidian-public-latest", "tslib": "2.8.1", @@ -624,7 +626,6 @@ "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", @@ -640,7 +641,6 @@ "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -652,7 +652,6 @@ "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -666,7 +665,6 @@ "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@eslint/core": "^0.17.0" }, @@ -680,7 +678,6 @@ "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@types/json-schema": "^7.0.15" }, @@ -694,7 +691,6 @@ "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ajv": "^6.14.0", "debug": "^4.3.2", @@ -719,33 +715,17 @@ "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/@eslint/eslintrc/node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">= 4" } @@ -756,7 +736,6 @@ "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -770,7 +749,6 @@ "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -778,13 +756,28 @@ "url": "https://eslint.org/donate" } }, + "node_modules/@eslint/json": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@eslint/json/-/json-0.14.0.tgz", + "integrity": "sha512-rvR/EZtvUG3p9uqrSmcDJPYSH7atmWr0RnFWN6m917MAPx82+zQgPUmDu0whPFG6XTyM0vB/hR6c1Q63OaYtCQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "@eslint/plugin-kit": "^0.4.1", + "@humanwhocodes/momoa": "^3.3.10", + "natural-compare": "^1.4.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/@eslint/object-schema": { "version": "2.1.7", "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", "dev": true, "license": "Apache-2.0", - "peer": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } @@ -795,7 +788,6 @@ "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@eslint/core": "^0.17.0", "levn": "^0.4.1" @@ -810,7 +802,6 @@ "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@humanfs/types": "^0.15.0" }, @@ -824,7 +815,6 @@ "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@humanfs/core": "^0.19.2", "@humanfs/types": "^0.15.0", @@ -840,7 +830,6 @@ "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", "dev": true, "license": "Apache-2.0", - "peer": true, "engines": { "node": ">=18.18.0" } @@ -851,7 +840,6 @@ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, "license": "Apache-2.0", - "peer": true, "engines": { "node": ">=12.22" }, @@ -860,13 +848,22 @@ "url": "https://github.com/sponsors/nzakas" } }, + "node_modules/@humanwhocodes/momoa": { + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@humanwhocodes/momoa/-/momoa-3.3.10.tgz", + "integrity": "sha512-KWiFQpSAqEIyrTXko3hFNLeQvSK8zXlJQzhhxsyVn58WFRYXST99b3Nqnu+ttOtjds2Pl2grUHGpe2NzhPynuQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, "node_modules/@humanwhocodes/retry": { "version": "0.4.3", "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", "dev": true, "license": "Apache-2.0", - "peer": true, "engines": { "node": ">=18.18" }, @@ -909,42 +906,55 @@ "dev": true, "license": "MIT" }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "node_modules/@microsoft/eslint-plugin-sdl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@microsoft/eslint-plugin-sdl/-/eslint-plugin-sdl-1.1.0.tgz", + "integrity": "sha512-dxdNHOemLnBhfY3eByrujX9KyLigcNtW8sU+axzWv5nLGcsSBeKW2YYyTpfPo1hV8YPOmIGnfA4fZHyKVtWqBQ==", "dev": true, "license": "MIT", "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" + "eslint-plugin-n": "17.10.3", + "eslint-plugin-react": "7.37.3", + "eslint-plugin-security": "1.4.0" }, "engines": { - "node": ">= 8" + "node": ">=18.0.0" + }, + "peerDependencies": { + "eslint": "^9" } }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "node_modules/@microsoft/eslint-plugin-sdl/node_modules/eslint-plugin-security": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-security/-/eslint-plugin-security-1.4.0.tgz", + "integrity": "sha512-xlS7P2PLMXeqfhyf3NpqbvbnW04kN8M9NtmhpR3XGyOvt/vNKS7XPXT5EDbwKW9vCjWH4PpfQvgD/+JgN0VJKA==", "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" + "license": "Apache-2.0", + "dependencies": { + "safe-regex": "^1.1.0" } }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "node_modules/@microsoft/eslint-plugin-sdl/node_modules/safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==", "dev": true, "license": "MIT", "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, + "ret": "~0.1.10" + } + }, + "node_modules/@pkgr/core": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.2.tgz", + "integrity": "sha512-fdDH1LSGfZdTH2sxdpVMw31BanV28K/Gry0cVFxaNP77neJSkd82mM8ErPNYs9e+0O7SdHBLTDzDgwUuy18RnQ==", + "dev": true, + "license": "MIT", "engines": { - "node": ">= 8" + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" } }, "node_modules/@popperjs/core": { @@ -957,6 +967,13 @@ "url": "https://opencollective.com/popperjs" } }, + "node_modules/@rtsao/scc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "dev": true, + "license": "MIT" + }, "node_modules/@simonwep/pickr": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/@simonwep/pickr/-/pickr-1.9.1.tgz", @@ -1026,6 +1043,17 @@ "@types/d3-selection": "*" } }, + "node_modules/@types/eslint": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", + "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", @@ -1038,8 +1066,14 @@ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true, + "license": "MIT" }, "node_modules/@types/node": { "version": "24.12.2", @@ -1097,21 +1131,20 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.37.0.tgz", - "integrity": "sha512-jsuVWeIkb6ggzB+wPCsR4e6loj+rM72ohW6IBn2C+5NCvfUVY8s33iFPySSVXqtm5Hu29Ne/9bnA0JmyLmgenA==", + "version": "8.61.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.61.0.tgz", + "integrity": "sha512-bFNvl9ZczlVb+wR2Akszf3gHfKVj/8WanXaGJ3UstTA7brNKg0cNdk6X1Psu5V7MZ2oQtzZKOEzIUehaoxbDGw==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.37.0", - "@typescript-eslint/type-utils": "8.37.0", - "@typescript-eslint/utils": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0", - "graphemer": "^1.4.0", - "ignore": "^7.0.0", + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.61.0", + "@typescript-eslint/type-utils": "8.61.0", + "@typescript-eslint/utils": "8.61.0", + "@typescript-eslint/visitor-keys": "8.61.0", + "ignore": "^7.0.5", "natural-compare": "^1.4.0", - "ts-api-utils": "^2.1.0" + "ts-api-utils": "^2.5.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1121,23 +1154,23 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.37.0", - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" + "@typescript-eslint/parser": "^8.61.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/parser": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.37.0.tgz", - "integrity": "sha512-kVIaQE9vrN9RLCQMQ3iyRlVJpTiDUY6woHGb30JDkfJErqrQEmtdWH3gV0PBAfGZgQXoqzXOO0T3K6ioApbbAA==", + "version": "8.61.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.61.0.tgz", + "integrity": "sha512-5B7PfA2e1NQGCnDHd/0lW7W3gvp3d59Ryw54FYO8Uswxo9f6ikw3AZV+Xj/TvpImmpsiYyUqAfhC6kJID1jF6w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.37.0", - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0", - "debug": "^4.3.4" + "@typescript-eslint/scope-manager": "8.61.0", + "@typescript-eslint/types": "8.61.0", + "@typescript-eslint/typescript-estree": "8.61.0", + "@typescript-eslint/visitor-keys": "8.61.0", + "debug": "^4.4.3" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1147,20 +1180,20 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.37.0.tgz", - "integrity": "sha512-BIUXYsbkl5A1aJDdYJCBAo8rCEbAvdquQ8AnLb6z5Lp1u3x5PNgSSx9A/zqYc++Xnr/0DVpls8iQ2cJs/izTXA==", + "version": "8.61.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.61.0.tgz", + "integrity": "sha512-DV42F7MLJO6Rax7SK1yg43tcnEfGUrurSpSxKuVX+a3RCTzBlH3fuxprrOJXKCJGAaw82xXocikJ0uQaqwXgGA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.37.0", - "@typescript-eslint/types": "^8.37.0", - "debug": "^4.3.4" + "@typescript-eslint/tsconfig-utils": "^8.61.0", + "@typescript-eslint/types": "^8.61.0", + "debug": "^4.4.3" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1170,18 +1203,18 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.37.0.tgz", - "integrity": "sha512-0vGq0yiU1gbjKob2q691ybTg9JX6ShiVXAAfm2jGf3q0hdP6/BruaFjL/ManAR/lj05AvYCH+5bbVo0VtzmjOA==", + "version": "8.61.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.61.0.tgz", + "integrity": "sha512-IWdXFHFSb6mlC3HPc7QsLDm5zYEbUla6trDEHf32D3/dnuUyXd87plScSNXSbm0/RxMvObpI17sv/EDTGrGZkA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0" + "@typescript-eslint/types": "8.61.0", + "@typescript-eslint/visitor-keys": "8.61.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1192,9 +1225,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.37.0.tgz", - "integrity": "sha512-1/YHvAVTimMM9mmlPvTec9NP4bobA1RkDbMydxG8omqwJJLEW/Iy2C4adsAESIXU3WGLXFHSZUU+C9EoFWl4Zg==", + "version": "8.61.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.61.0.tgz", + "integrity": "sha512-O5Amvdv9ztMpxpf+vmFULGG78IE6Qwdr3bCGvqwG4nwc9H2qXkOYJJnRbRHyMkQTjv1d03olqwwwzHLMqpFePQ==", "dev": true, "license": "MIT", "engines": { @@ -1205,21 +1238,21 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.37.0.tgz", - "integrity": "sha512-SPkXWIkVZxhgwSwVq9rqj/4VFo7MnWwVaRNznfQDc/xPYHjXnPfLWn+4L6FF1cAz6e7dsqBeMawgl7QjUMj4Ow==", + "version": "8.61.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.61.0.tgz", + "integrity": "sha512-TuBiQYIkd97yBfInHCTKVYMbX4kvEmpOEuixIuzCU9p8BGT1SfyyO0d0IfDMbPIHcjn/hWnusUX5e8v5Xg+X8A==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0", - "@typescript-eslint/utils": "8.37.0", - "debug": "^4.3.4", - "ts-api-utils": "^2.1.0" + "@typescript-eslint/types": "8.61.0", + "@typescript-eslint/typescript-estree": "8.61.0", + "@typescript-eslint/utils": "8.61.0", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1229,14 +1262,14 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/types": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.37.0.tgz", - "integrity": "sha512-ax0nv7PUF9NOVPs+lmQ7yIE7IQmAf8LGcXbMvHX5Gm+YJUYNAl340XkGnrimxZ0elXyoQJuN5sbg6C4evKA4SQ==", + "version": "8.61.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.61.0.tgz", + "integrity": "sha512-9QTQpZ5Iin4CdIodfbDQFSeiSJKidgYJYug1P9CC2xWgUTvlmixViqDZNciMjwLBZyJnG4tGmPl97rVAFb1AJg==", "dev": true, "license": "MIT", "engines": { @@ -1248,22 +1281,21 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.37.0.tgz", - "integrity": "sha512-zuWDMDuzMRbQOM+bHyU4/slw27bAUEcKSKKs3hcv2aNnc/tvE/h7w60dwVw8vnal2Pub6RT1T7BI8tFZ1fE+yg==", + "version": "8.61.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.61.0.tgz", + "integrity": "sha512-42zatd5qSvvcV1JdDBCLxYRznvP4eIHpPoZXdkPFnAmanA4FuZ5dibSnCBggY8hQnqajPpoGjXFdZ7fIJKQnlA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.37.0", - "@typescript-eslint/tsconfig-utils": "8.37.0", - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0", - "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^2.1.0" + "@typescript-eslint/project-service": "8.61.0", + "@typescript-eslint/tsconfig-utils": "8.61.0", + "@typescript-eslint/types": "8.61.0", + "@typescript-eslint/visitor-keys": "8.61.0", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1273,20 +1305,59 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/@typescript-eslint/utils": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.37.0.tgz", - "integrity": "sha512-TSFvkIW6gGjN2p6zbXo20FzCABbyUAuq6tBvNRGsKdsSQ6a7rnV6ADfZ7f4iI3lIiXc4F4WWvtUfDw9CJ9pO5A==", + "version": "8.61.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.61.0.tgz", + "integrity": "sha512-3bzFt7ImFMW/jVYwJamDoe/dMOdFLSC6pom6rRjdh4SZJEYupyMzem8e7vKZLclLfpHjlwSAXOUxtKxGXUiLqA==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.37.0", - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0" + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.61.0", + "@typescript-eslint/types": "8.61.0", + "@typescript-eslint/typescript-estree": "8.61.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1296,19 +1367,19 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.37.0.tgz", - "integrity": "sha512-YzfhzcTnZVPiLfP/oeKtDp2evwvHLMe0LOy7oe+hb9KKIumLNohYS9Hgp1ifwpu42YWxhZE8yieggz6JpqO/1w==", + "version": "8.61.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.61.0.tgz", + "integrity": "sha512-QVLZu3ZPQEE+HICQyAMZ2yLQhxf0meY/wx6Hx14YcTNj13JB3qHlX3lJ02L3fLGHgERRH71kvYDwiXIguT3AjQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.37.0", - "eslint-visitor-keys": "^4.2.1" + "@typescript-eslint/types": "8.61.0", + "eslint-visitor-keys": "^5.0.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1319,13 +1390,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", "dev": true, "license": "Apache-2.0", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { "url": "https://opencollective.com/eslint" @@ -1369,7 +1440,6 @@ "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -1383,7 +1453,6 @@ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, "license": "MIT", - "peer": true, "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } @@ -1394,7 +1463,6 @@ "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -1412,7 +1480,6 @@ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -1428,8 +1495,193 @@ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true, - "license": "Python-2.0", - "peer": true + "license": "Python-2.0" + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-includes": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz", + "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.0", + "es-object-atoms": "^1.1.1", + "get-intrinsic": "^1.3.0", + "is-string": "^1.1.1", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.findlast": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", + "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.findlastindex": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz", + "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-shim-unscopables": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", + "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", + "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.tosorted": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", + "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3", + "es-errors": "^1.3.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/balanced-match": { "version": "1.0.2", @@ -1448,30 +1700,67 @@ "balanced-match": "^1.0.0" } }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "node_modules/builtin-modules": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-5.0.0.tgz", + "integrity": "sha512-bkXY9WsVpY7CvMhKSR6pZilZu9Ln5WDrKVBUXf2S443etkmEO4V58heTecXcUIsNsi4Rx8JUO4NfX1IcQl4deg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/call-bind": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz", + "integrity": "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==", "dev": true, "license": "MIT", "dependencies": { - "fill-range": "^7.1.1" + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "get-intrinsic": "^1.3.0", + "set-function-length": "^1.2.2" }, "engines": { - "node": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/builtin-modules": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-5.0.0.tgz", - "integrity": "sha512-bkXY9WsVpY7CvMhKSR6pZilZu9Ln5WDrKVBUXf2S443etkmEO4V58heTecXcUIsNsi4Rx8JUO4NfX1IcQl4deg==", + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", "dev": true, "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, "engines": { - "node": ">=18.20" + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/callsites": { @@ -1480,7 +1769,6 @@ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6" } @@ -1491,7 +1779,6 @@ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -1531,7 +1818,6 @@ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "color-name": "~1.1.4" }, @@ -1544,16 +1830,14 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/core-js": { "version": "3.37.0", @@ -1579,7 +1863,6 @@ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -1701,6 +1984,60 @@ "node": ">=12" } }, + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/date-fns": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz", @@ -1734,21 +2071,285 @@ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, - "node_modules/esbuild": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.6.tgz", - "integrity": "sha512-GVuzuUwtdsghE3ocJ9Bs8PNoF13HNQ5TXbEi2AhvVb8xU1Iwt9Fos9FEamfoee+u/TOsn7GUWc04lz46n2bbTg==", + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "dev": true, - "hasInstallScript": true, "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" }, "engines": { - "node": ">=18" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/empathic": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/empathic/-/empathic-2.0.1.tgz", + "integrity": "sha512-YGRs8knHhKHVShLkFET/rWAU8kmHbOV5LwN938RHI0pljAJ1Gf6SzXsSmRaEzcXTtOOmVqJ5+WtQPL5uigY50Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.23.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.23.0.tgz", + "integrity": "sha512-yJN/BOOLxcOW2aQgeif9mSnaUB8KtvmMMp56oA1kx1CRfBKbhZm2pJ+NBY+3eOboHxix8lfjWpHE0Ei5U8RbSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/es-abstract": { + "version": "1.24.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.2.tgz", + "integrity": "sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.3.0", + "get-proto": "^1.0.1", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.2.1", + "is-set": "^2.0.3", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.1", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.4", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.4", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "stop-iteration-iterator": "^1.1.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.19" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-iterator-helpers": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.3.2.tgz", + "integrity": "sha512-HVLACW1TppGYjJ8H6/jqH/pqOtKRw6wMlrB23xfExmFWxFquAIWCmwoLsOyN96K4a5KbmOf5At9ZUO3GZbetAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.2", + "es-errors": "^1.3.0", + "es-set-tostringtag": "^2.1.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.3.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "iterator.prototype": "^1.1.5", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", + "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-to-primitive": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/esbuild": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.6.tgz", + "integrity": "sha512-GVuzuUwtdsghE3ocJ9Bs8PNoF13HNQ5TXbEi2AhvVb8xU1Iwt9Fos9FEamfoee+u/TOsn7GUWc04lz46n2bbTg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" }, "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.6", @@ -1785,7 +2386,6 @@ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=10" }, @@ -1799,7 +2399,6 @@ "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -1854,81 +2453,167 @@ } } }, - "node_modules/eslint-scope": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", - "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "node_modules/eslint-compat-utils": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.5.1.tgz", + "integrity": "sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q==", "dev": true, - "license": "BSD-2-Clause", - "peer": true, + "license": "MIT", "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" + "semver": "^7.5.4" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=12" }, - "funding": { - "url": "https://opencollective.com/eslint" + "peerDependencies": { + "eslint": ">=6.0.0" } }, - "node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "node_modules/eslint-import-resolver-node": { + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.10.tgz", + "integrity": "sha512-tRrKqFyCaKict5hOd244sL6EQFNycnMQnBe+j8uqGNXYzsImGbGUU4ibtoaBmv5FLwJwcFJNeg1GeVjQfbMrDQ==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.16.1", + "resolve": "^2.0.0-next.6" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-module-utils": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.13.0.tgz", + "integrity": "sha512-bLohSkT6469rRs8czj0tLTD8vaeIS/whvPRJVjDr7IuoTT1k5DYDERlNycjDj/HkOlvQdYurmfZ/g3fG5bgeLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.2.7" + }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=4" }, - "funding": { - "url": "https://opencollective.com/eslint" + "peerDependenciesMeta": { + "eslint": { + "optional": true + } } }, - "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", - "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "ms": "^2.1.1" } }, - "node_modules/eslint/node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "node_modules/eslint-plugin-depend": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-depend/-/eslint-plugin-depend-1.3.1.tgz", + "integrity": "sha512-1uo2rFAr9vzNrCYdp7IBZRB54LiyVxfaIso0R6/QV3t6Dax6DTbW/EV2Hktf0f4UtmGHK8UyzJWI382pwW04jw==", "dev": true, - "license": "Apache-2.0", - "peer": true, + "license": "MIT", + "dependencies": { + "empathic": "^2.0.0", + "module-replacements": "^2.8.0", + "semver": "^7.6.3" + } + }, + "node_modules/eslint-plugin-es-x": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-es-x/-/eslint-plugin-es-x-7.8.0.tgz", + "integrity": "sha512-7Ds8+wAAoV3T+LAKeu39Y5BzXCrGKrcISfgKEqTS4BDN8SFEDQd0S43jiQ8vIa3wUKD07qitZdfzlenSi8/0qQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/ota-meshi", + "https://opencollective.com/eslint" + ], + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.1.2", + "@eslint-community/regexpp": "^4.11.0", + "eslint-compat-utils": "^0.5.1" + }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^14.18.0 || >=16.0.0" }, - "funding": { - "url": "https://opencollective.com/eslint" + "peerDependencies": { + "eslint": ">=8" } }, - "node_modules/eslint/node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "node_modules/eslint-plugin-import": { + "version": "2.32.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz", + "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, "license": "MIT", - "peer": true, + "dependencies": { + "@rtsao/scc": "^1.1.0", + "array-includes": "^3.1.9", + "array.prototype.findlastindex": "^1.2.6", + "array.prototype.flat": "^1.3.3", + "array.prototype.flatmap": "^1.3.3", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.12.1", + "hasown": "^2.0.2", + "is-core-module": "^2.16.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "object.groupby": "^1.0.3", + "object.values": "^1.2.1", + "semver": "^6.3.1", + "string.prototype.trimend": "^1.0.9", + "tsconfig-paths": "^3.15.0" + }, "engines": { - "node": ">= 4" + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" } }, - "node_modules/eslint/node_modules/minimatch": { + "node_modules/eslint-plugin-import/node_modules/brace-expansion": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", + "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/minimatch": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -1936,363 +2621,1326 @@ "node": "*" } }, - "node_modules/espree": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", - "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "node_modules/eslint-plugin-import/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, - "license": "BSD-2-Clause", - "peer": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-plugin-json-schema-validator": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-json-schema-validator/-/eslint-plugin-json-schema-validator-5.1.0.tgz", + "integrity": "sha512-ZmVyxRIjm58oqe2kTuy90PpmZPrrKvOjRPXKzq8WCgRgAkidCgm5X8domL2KSfadZ3QFAmifMgGTcVNhZ5ez2g==", + "dev": true, + "license": "MIT", "dependencies": { - "acorn": "^8.15.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.1" + "@eslint-community/eslint-utils": "^4.3.0", + "ajv": "^8.0.0", + "debug": "^4.3.1", + "eslint-compat-utils": "^0.5.0", + "json-schema-migrate": "^2.0.0", + "jsonc-eslint-parser": "^2.0.0", + "minimatch": "^8.0.0", + "synckit": "^0.9.0", + "toml-eslint-parser": "^0.9.0", + "tunnel-agent": "^0.6.0", + "yaml-eslint-parser": "^1.0.0" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^14.18.0 || >=16.0.0" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://github.com/sponsors/ota-meshi" + }, + "peerDependencies": { + "eslint": ">=6.0.0" } }, - "node_modules/espree/node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "node_modules/eslint-plugin-json-schema-validator/node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", "dev": true, - "license": "Apache-2.0", - "peer": true, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" }, "funding": { - "url": "https://opencollective.com/eslint" + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/esquery": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", - "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "node_modules/eslint-plugin-json-schema-validator/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true, - "license": "BSD-3-Clause", - "peer": true, + "license": "MIT" + }, + "node_modules/eslint-plugin-json-schema-validator/node_modules/minimatch": { + "version": "8.0.7", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-8.0.7.tgz", + "integrity": "sha512-V+1uQNdzybxa14e/p00HZnQNNcTjnRJjDxg2V8wtkjFctq4M7hXFws4oekyTP0Jebeq7QYtpFyOeBAjc88zvYg==", + "dev": true, + "license": "ISC", "dependencies": { - "estraverse": "^5.1.0" + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=0.10" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/esrecurse": { - "version": "4.3.0", + "node_modules/eslint-plugin-n": { + "version": "17.10.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-17.10.3.tgz", + "integrity": "sha512-ySZBfKe49nQZWR1yFaA0v/GsH6Fgp8ah6XV0WDz6CN8WO0ek4McMzb7A2xnf4DCYV43frjCygvb9f/wx7UUxRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "enhanced-resolve": "^5.17.0", + "eslint-plugin-es-x": "^7.5.0", + "get-tsconfig": "^4.7.0", + "globals": "^15.8.0", + "ignore": "^5.2.4", + "minimatch": "^9.0.5", + "semver": "^7.5.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": ">=8.23.0" + } + }, + "node_modules/eslint-plugin-n/node_modules/globals": { + "version": "15.15.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz", + "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint-plugin-n/node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/eslint-plugin-no-unsanitized": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-no-unsanitized/-/eslint-plugin-no-unsanitized-4.1.5.tgz", + "integrity": "sha512-MSB4hXPVFQrI8weqzs6gzl7reP2k/qSjtCoL2vUMSDejIIq9YL1ZKvq5/ORBXab/PvfBBrWO2jWviYpL+4Ghfg==", + "dev": true, + "license": "MPL-2.0", + "peerDependencies": { + "eslint": "^9 || ^10" + } + }, + "node_modules/eslint-plugin-obsidianmd": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-obsidianmd/-/eslint-plugin-obsidianmd-0.3.0.tgz", + "integrity": "sha512-QvGDI6B2nxJBrsZKGTg31da2A/fEJNlnwN+fRZkaoPIu1QL3fYXUdpP7ThyMdr/0iTYQxifb9lt2X9cpydQx1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint/config-helpers": "^0.4.2", + "@eslint/js": "^9.30.1", + "@eslint/json": "0.14.0", + "@microsoft/eslint-plugin-sdl": "^1.1.0", + "@types/eslint": "9.6.1", + "@types/node": "20.12.12", + "@typescript-eslint/types": "^8.33.1", + "@typescript-eslint/utils": "^8.33.1", + "eslint": ">=9.0.0", + "eslint-plugin-depend": "1.3.1", + "eslint-plugin-import": "^2.31.0", + "eslint-plugin-json-schema-validator": "5.1.0", + "eslint-plugin-no-unsanitized": "^4.1.5", + "eslint-plugin-security": "2.1.1", + "globals": "14.0.0", + "obsidian": "1.12.3", + "semver": "^7.7.4", + "typescript": "5.4.5", + "typescript-eslint": "^8.35.1" + }, + "bin": { + "eslint-plugin-obsidian": "dist/lib/index.js" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@eslint/js": "^9.30.1", + "@eslint/json": "0.14.0", + "eslint": ">=9.0.0", + "obsidian": "1.8.7", + "typescript-eslint": "^8.35.1" + } + }, + "node_modules/eslint-plugin-obsidianmd/node_modules/@types/node": { + "version": "20.12.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.12.tgz", + "integrity": "sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/eslint-plugin-obsidianmd/node_modules/typescript": { + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/eslint-plugin-obsidianmd/node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true, + "license": "MIT" + }, + "node_modules/eslint-plugin-react": { + "version": "7.37.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.3.tgz", + "integrity": "sha512-DomWuTQPFYZwF/7c9W2fkKkStqZmBd3uugfqBYLdkZ3Hii23WzZuOLUskGxB8qkSKqftxEeGL1TB2kMhrce0jA==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-includes": "^3.1.8", + "array.prototype.findlast": "^1.2.5", + "array.prototype.flatmap": "^1.3.3", + "array.prototype.tosorted": "^1.1.4", + "doctrine": "^2.1.0", + "es-iterator-helpers": "^1.2.1", + "estraverse": "^5.3.0", + "hasown": "^2.0.2", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.1.2", + "object.entries": "^1.1.8", + "object.fromentries": "^2.0.8", + "object.values": "^1.2.1", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.5", + "semver": "^6.3.1", + "string.prototype.matchall": "^4.0.12", + "string.prototype.repeat": "^1.0.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" + } + }, + "node_modules/eslint-plugin-react/node_modules/brace-expansion": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", + "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint-plugin-react/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/eslint-plugin-react/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-plugin-security": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-security/-/eslint-plugin-security-2.1.1.tgz", + "integrity": "sha512-7cspIGj7WTfR3EhaILzAPcfCo5R9FbeWvbgsPYWivSurTBKW88VQxtP3c4aWMG9Hz/GfJlJVdXEJ3c8LqS+u2w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "safe-regex": "^2.1.1" + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", + "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, - "license": "BSD-2-Clause", - "peer": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", + "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true, + "license": "ISC" + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/generator-function": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", + "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-tsconfig": { + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.14.0.tgz", + "integrity": "sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "dev": true, + "license": "MIT", "dependencies": { - "estraverse": "^5.2.0" + "dunder-proto": "^1.0.0" }, "engines": { - "node": ">=4.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "dev": true, - "license": "BSD-2-Clause", - "peer": true, + "license": "MIT", "engines": { - "node": ">=4.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "dev": true, - "license": "BSD-2-Clause", - "peer": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/i18next": { + "version": "25.10.10", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-25.10.10.tgz", + "integrity": "sha512-cqUW2Z3EkRx7NqSyywjkgCLK7KLCL6IFVFcONG7nVYIJ3ekZ1/N5jUsihHV6Bq37NfhgtczxJcxduELtjTwkuQ==", + "funding": [ + { + "type": "individual", + "url": "https://www.locize.com/i18next" + }, + { + "type": "individual", + "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" + }, + { + "type": "individual", + "url": "https://www.locize.com" + } + ], + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.2" + }, + "peerDependencies": { + "typescript": "^5 || ^6" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", "dev": true, "license": "MIT", - "peer": true + "engines": { + "node": ">= 4" + } }, - "node_modules/fast-glob": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "dev": true, "license": "MIT", "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=8.6.0" + "node": ">=0.8.19" } }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "is-glob": "^4.0.1" + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" }, "engines": { - "node": ">= 6" + "node": ">= 0.4" } }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", "dev": true, "license": "MIT", - "peer": true + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", "dev": true, "license": "MIT", - "peer": true + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "node_modules/fastq": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", - "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "node_modules/is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "reusify": "^1.0.4" + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/file-entry-cache": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", + "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "flat-cache": "^4.0.0" + "hasown": "^2.0.3" }, "engines": { - "node": ">=16.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", "dev": true, "license": "MIT", "dependencies": { - "to-regex-range": "^5.0.1" + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" }, "engines": { - "node": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" }, "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/flat-cache": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.4" + "call-bound": "^1.0.3" }, "engines": { - "node": ">=16" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/flatted": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", - "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "node_modules/is-generator-function": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", + "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==", "dev": true, - "license": "ISC", - "peer": true + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.4", + "generator-function": "^2.0.0", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, - "license": "ISC", - "peer": true, + "license": "MIT", "dependencies": { - "is-glob": "^4.0.3" + "is-extglob": "^2.1.1" }, "engines": { - "node": ">=10.13.0" + "node": ">=0.10.0" } }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", "dev": true, "license": "MIT", - "peer": true, "engines": { - "node": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/i18next": { - "version": "25.10.10", - "resolved": "https://registry.npmjs.org/i18next/-/i18next-25.10.10.tgz", - "integrity": "sha512-cqUW2Z3EkRx7NqSyywjkgCLK7KLCL6IFVFcONG7nVYIJ3ekZ1/N5jUsihHV6Bq37NfhgtczxJcxduELtjTwkuQ==", - "funding": [ - { - "type": "individual", - "url": "https://www.locize.com/i18next" - }, - { - "type": "individual", - "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" - }, - { - "type": "individual", - "url": "https://www.locize.com" - } - ], + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "dev": true, "license": "MIT", "dependencies": { - "@babel/runtime": "^7.29.2" + "call-bound": "^1.0.3" }, - "peerDependencies": { - "typescript": "^5 || ^6" + "engines": { + "node": ">= 0.4" }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/ignore": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", - "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", "dev": true, "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, "engines": { - "node": ">= 4" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/import-fresh": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" }, "engines": { - "node": ">=6" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", "dev": true, "license": "MIT", - "peer": true, + "dependencies": { + "which-typed-array": "^1.1.16" + }, "engines": { - "node": ">=0.8.19" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", "dev": true, "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "node_modules/is-weakref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", "dev": true, "license": "MIT", "dependencies": { - "is-extglob": "^2.1.1" + "call-bound": "^1.0.3" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", "dev": true, "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, "engines": { - "node": ">=0.12.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true, - "license": "ISC", - "peer": true + "license": "ISC" + }, + "node_modules/iterator.prototype": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", + "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "get-proto": "^1.0.0", + "has-symbols": "^1.1.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } }, "node_modules/js-tokens": { "version": "4.0.0", @@ -2316,7 +3964,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "argparse": "^2.0.1" }, @@ -2329,24 +3976,121 @@ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-migrate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/json-schema-migrate/-/json-schema-migrate-2.0.0.tgz", + "integrity": "sha512-r38SVTtojDRp4eD6WsCqiE0eNDt4v1WalBXb9cyZYw9ai5cGtBwzRNWjHzJl38w6TxFkXAIA7h+fyX3tnrAFhQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + } + }, + "node_modules/json-schema-migrate/node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "dev": true, "license": "MIT", - "peer": true + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/json-schema-migrate/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/jsonc-eslint-parser": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/jsonc-eslint-parser/-/jsonc-eslint-parser-2.4.2.tgz", + "integrity": "sha512-1e4qoRgnn448pRuMvKGsFFymUCquZV0mpGgOyIKNgD3JVDTsVJyRBGH/Fm0tBb8WsWGgmB1mDe6/yJMQM37DUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.5.0", + "eslint-visitor-keys": "^3.0.0", + "espree": "^9.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ota-meshi" + } + }, + "node_modules/jsonc-eslint-parser/node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/jsx-ast-utils": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", + "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", + "dev": true, "license": "MIT", - "peer": true + "dependencies": { + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "object.assign": "^4.1.4", + "object.values": "^1.1.6" + }, + "engines": { + "node": ">=4.0" + } }, "node_modules/keyv": { "version": "4.5.4", @@ -2354,7 +4098,6 @@ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "json-buffer": "3.0.1" } @@ -2365,7 +4108,6 @@ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" @@ -2380,7 +4122,6 @@ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "p-locate": "^5.0.0" }, @@ -2396,8 +4137,7 @@ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/loose-envify": { "version": "1.4.0", @@ -2420,28 +4160,14 @@ "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", "dev": true, "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, "engines": { - "node": ">=8.6" + "node": ">= 0.4" } }, "node_modules/minimatch": { @@ -2460,6 +4186,23 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/module-replacements": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/module-replacements/-/module-replacements-2.11.0.tgz", + "integrity": "sha512-j5sNQm3VCpQQ7nTqGeOZtoJtV3uKERgCBm9QRhmGRiXiqkf7iRFOkfxdJRZWLkqYY8PNf4cDQF/WfXUYLENrRA==", + "dev": true, + "license": "MIT" + }, "node_modules/moment": { "version": "2.29.4", "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", @@ -2496,6 +4239,158 @@ "dev": true, "license": "MIT" }, + "node_modules/node-exports-info": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/node-exports-info/-/node-exports-info-1.6.0.tgz", + "integrity": "sha512-pyFS63ptit/P5WqUkt+UUfe+4oevH+bFeIiPPdfb0pFeYEu/1ELnJu5l+5EcTKYL5M7zaAa7S8ddywgXypqKCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "array.prototype.flatmap": "^1.3.3", + "es-errors": "^1.3.0", + "object.entries": "^1.1.9", + "semver": "^6.3.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/node-exports-info/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.entries": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz", + "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.groupby": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.values": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", + "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/obsidian": { "version": "1.12.3", "resolved": "https://registry.npmjs.org/obsidian/-/obsidian-1.12.3.tgz", @@ -2524,7 +4419,6 @@ "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", @@ -2534,7 +4428,25 @@ "word-wrap": "^1.2.5" }, "engines": { - "node": ">= 0.8.0" + "node": ">= 0.8.0" + } + }, + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/p-limit": { @@ -2543,7 +4455,6 @@ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "yocto-queue": "^0.1.0" }, @@ -2560,7 +4471,6 @@ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "p-limit": "^3.0.2" }, @@ -2577,7 +4487,6 @@ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "callsites": "^3.0.0" }, @@ -2591,7 +4500,6 @@ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -2602,22 +4510,25 @@ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=8" } }, - "node_modules/picomatch": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", "dev": true, "license": "MIT", "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "node": ">= 0.4" } }, "node_modules/prelude-ls": { @@ -2626,43 +4537,32 @@ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">= 0.8.0" } }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dev": true, + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6" } }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, "node_modules/react": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", @@ -2697,112 +4597,522 @@ "react": "*" } }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexp-tree": { + "version": "0.1.27", + "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz", + "integrity": "sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==", + "dev": true, + "license": "MIT", + "bin": { + "regexp-tree": "bin/regexp-tree" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "2.0.0-next.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.7.tgz", + "integrity": "sha512-tqt+NBWwyaMgw3zDsnygx4CByWjQEJHOPMdslYhppaQSJUtL/D4JO9CcBBlhPoI8lz9oJIDXkwXfhF4aWqP8xQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "is-core-module": "^2.16.2", + "node-exports-info": "^1.6.0", + "object-keys": "^1.1.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, "license": "MIT", - "peer": true, "engines": { - "node": ">=4" + "node": ">=4" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12" + } + }, + "node_modules/safe-array-concat": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.4.tgz", + "integrity": "sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "get-intrinsic": "^1.3.0", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-2.1.1.tgz", + "integrity": "sha512-rx+x8AMzKb5Q5lQ95Zoi6ZbJqwCLkqi3XuJXp5P3rT8OEc6sZCJG5AE5dU3lsgRr/F4Bs31jSlVN+j5KrsGu9A==", + "dev": true, + "license": "MIT", + "dependencies": { + "regexp-tree": "~0.1.1" + } + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", + "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4", + "side-channel-list": "^1.0.1", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/sortablejs": { + "version": "1.15.7", + "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.15.7.tgz", + "integrity": "sha512-Kk8wLQPlS+yi1ZEf48a4+fzHa4yxjC30M/Sr2AnQu+f/MPwvvX9XjZ6OWejiz8crBsLwSq8GHqaxaET7u6ux0A==", + "license": "MIT" + }, + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" } }, - "node_modules/reusify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", - "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "node_modules/string.prototype.matchall": { + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", + "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==", "dev": true, "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.6", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "regexp.prototype.flags": "^1.5.3", + "set-function-name": "^2.0.2", + "side-channel": "^1.1.0" + }, "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "node_modules/string.prototype.repeat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", + "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], "license": "MIT", "dependencies": { - "queue-microtask": "^1.2.2" + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" } }, - "node_modules/scheduler": { - "version": "0.23.2", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", - "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "node_modules/string.prototype.trim": { + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.11.tgz", + "integrity": "sha512-PwvK7BU+CMTJGYQCTZb5RWXIML92lftJLhQz1tBzgKiqGxJaMlBAa48POXaNAC2s4y8jr3EFqrkF9+44neS46w==", + "dev": true, "license": "MIT", "dependencies": { - "loose-envify": "^1.1.0" + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.2", + "es-object-atoms": "^1.1.2", + "has-property-descriptors": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "node_modules/string.prototype.trimend": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.10.tgz", + "integrity": "sha512-2+3aDAOmPTmuFwjDnmJG2ctEkQKVki7vOSqaxkv42Mowj1V6PnvuwFCRrR5lChUux1TBskPjfkeTOhqczDMxTw==", "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.1.2" }, "engines": { - "node": ">=10" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "shebang-regex": "^3.0.0" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" }, "engines": { - "node": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/shebang-regex": { + "node_modules/strip-bom": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true, "license": "MIT", - "peer": true, "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/sortablejs": { - "version": "1.15.7", - "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.15.7.tgz", - "integrity": "sha512-Kk8wLQPlS+yi1ZEf48a4+fzHa4yxjC30M/Sr2AnQu+f/MPwvvX9XjZ6OWejiz8crBsLwSq8GHqaxaET7u6ux0A==", - "license": "MIT" - }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=8" }, @@ -2823,7 +5133,6 @@ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -2831,17 +5140,112 @@ "node": ">=8" } }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/synckit": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.3.tgz", + "integrity": "sha512-JJoOEKTfL1urb1mDoEblhD9NhEbWmq9jHEMEnxoC4ujUaZ4itA8vKgwkFAyNClgxplLi9tsUKX+EduK0p/l7sg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pkgr/core": "^0.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/tapable": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/toml-eslint-parser": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/toml-eslint-parser/-/toml-eslint-parser-0.9.3.tgz", + "integrity": "sha512-moYoCvkNUAPCxSW9jmHmRElhm4tVJpHL8ItC/+uYD0EpPSFXbck7yREz9tNdJVTSpHVod8+HoipcpbQ0oE6gsw==", "dev": true, "license": "MIT", "dependencies": { - "is-number": "^7.0.0" + "eslint-visitor-keys": "^3.0.0" }, "engines": { - "node": ">=8.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ota-meshi" } }, "node_modules/ts-api-utils": { @@ -2857,6 +5261,19 @@ "typescript": ">=4.8.4" } }, + "node_modules/tsconfig-paths": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", @@ -2864,13 +5281,25 @@ "dev": true, "license": "0BSD" }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "prelude-ls": "^1.2.1" }, @@ -2878,6 +5307,84 @@ "node": ">= 0.8.0" } }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.8.tgz", + "integrity": "sha512-phPGCwqr2+Qo0fwniCE8e4pKnGu/yFb5nD5Y8bf0EEeiI5GklnACYA9GFy/DrAeRrKHXvHn+1SUsOWgJp6RO+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "for-each": "^0.3.5", + "gopd": "^1.2.0", + "is-typed-array": "^1.1.15", + "possible-typed-array-names": "^1.1.0", + "reflect.getprototypeof": "^1.0.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/typescript": { "version": "5.8.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", @@ -2892,6 +5399,49 @@ "node": ">=14.17" } }, + "node_modules/typescript-eslint": { + "version": "8.61.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.61.0.tgz", + "integrity": "sha512-8y31Rd0eGTrDKqhy6vT0HtzhN+YLjQizwX3aA3hPXP/ynSfnrBXcQY5IzsP9/DM7+klX4IUncZZjkchP0z+rUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.61.0", + "@typescript-eslint/parser": "8.61.0", + "@typescript-eslint/typescript-estree": "8.61.0", + "@typescript-eslint/utils": "8.61.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/undici-types": { "version": "7.16.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", @@ -2905,7 +5455,6 @@ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, "license": "BSD-2-Clause", - "peer": true, "dependencies": { "punycode": "^2.1.0" } @@ -2932,7 +5481,6 @@ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "isexe": "^2.0.0" }, @@ -2943,24 +5491,144 @@ "node": ">= 8" } }, + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.22", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.22.tgz", + "integrity": "sha512-fvO4ExWMFsqyhG3AiPAObMuY1lxaqgYcxbc49CNdWDDECOJNgQyvsOWVwbZc+qf3rzRtxojBK+CMEv0Ld5CYpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } }, + "node_modules/yaml": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", + "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, + "node_modules/yaml-eslint-parser": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/yaml-eslint-parser/-/yaml-eslint-parser-1.3.2.tgz", + "integrity": "sha512-odxVsHAkZYYglR30aPYRY4nUGJnoJ2y1ww2HDvZALo0BDETv9kWbi16J52eHs+PWRNmF4ub6nZqfVOeesOvntg==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.0.0", + "yaml": "^2.0.0" + }, + "engines": { + "node": "^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ota-meshi" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=10" }, diff --git a/package.json b/package.json index f67b381e..3873ce11 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,14 @@ { "name": "task-board", - "version": "2.0.0-beta-4", + "version": "2.0.0-beta-5", "description": "An Obsidian plugin to manage small to large projects using tasks from the whole vault on a centralized board using various kinds of views like Kanban, map, list, etc.", "main": "main.js", "type": "module", "scripts": { "dev": "node esbuild.config.mjs", "build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production", + "lint": "eslint main.ts src --ext .ts,.tsx", + "lint:fix": "eslint \"**/*.{ts,tsx}\" --fix", "version": "node version-bump.mjs && git add manifest.json versions.json" }, "keywords": [], @@ -21,11 +23,13 @@ "@types/react": "^18.2.0", "@types/react-dom": "^18.2.0", "@types/sortablejs": "^1.15.8", - "@typescript-eslint/eslint-plugin": "8.37.0", - "@typescript-eslint/parser": "8.37.0", + "@typescript-eslint/eslint-plugin": "^8.61.0", + "@typescript-eslint/parser": "^8.61.0", "builtin-modules": "5.0.0", "codemirror": "^6.0.0", "esbuild": "0.25.6", + "eslint": "^9.39.4", + "eslint-plugin-obsidianmd": "^0.3.0", "obsidian": "latest", "obsidian-typings": "obsidian-public-latest", "tslib": "2.8.1", diff --git a/src/components/AdvancedFilterer/Component.ts b/src/components/AdvancedFilterer/Component.ts index 17175030..c7d10b74 100644 --- a/src/components/AdvancedFilterer/Component.ts +++ b/src/components/AdvancedFilterer/Component.ts @@ -12,7 +12,7 @@ import { ToggleComponent, Menu, } from "obsidian"; -import Sortable from "sortablejs"; +import Sortable, { SortableEvent } from "sortablejs"; import TaskBoard from "../../../main.js"; import { Filter, @@ -104,9 +104,10 @@ export class AdvancedFilterComponent extends Component { onload() { if (this.initialFilterState) { - this.advancedFilter = JSON.parse( + const parsedData: unknown = JSON.parse( JSON.stringify(this.initialFilterState), ); + this.advancedFilter = parsedData as AdvancedFilter; this.advancedFilter.filters = this.advancedFilter.filters.filter( (filter) => @@ -328,8 +329,8 @@ export class AdvancedFilterComponent extends Component { }); setTooltip(el, t("import-filter-from-filter-warehouse")); - this.registerDomEvent(el, "click", async () => { - this.openFiltersWarehouseModal(); + this.registerDomEvent(el, "click", () => { + void this.openFiltersWarehouseModal(); }); }, ); @@ -445,9 +446,11 @@ export class AdvancedFilterComponent extends Component { const warehouse = this.plugin.settings.data.filtersWarehouse || []; - const filterCopy = JSON.parse( + + const parsedData: unknown = JSON.parse( JSON.stringify(filter), ); + const filterCopy = parsedData as Filter; warehouse.push(filterCopy); this.plugin.settings.data.filtersWarehouse = @@ -527,10 +530,8 @@ export class AdvancedFilterComponent extends Component { expandableArea: HTMLElement, expandBtn: HTMLDivElement, ): void { - expandableArea.style.maxHeight = "0"; - expandableArea.style.opacity = "0"; - expandableArea.style.paddingTop = "0"; - expandableArea.style.paddingBottom = "0"; + expandableArea.toggleClass("expand", false); + expandableArea.toggleClass("collapse", true); expandBtn.replaceChildren(); expandBtn.createEl( @@ -556,7 +557,7 @@ export class AdvancedFilterComponent extends Component { ); this.expandedFilters.set(filter, false); - setTimeout(() => { + window.setTimeout(() => { expandableArea.hide(); }, 300); } @@ -570,15 +571,8 @@ export class AdvancedFilterComponent extends Component { this.renderExpandedFilterContent(filter, expandableArea); } expandableArea.show(); - expandableArea.style.maxHeight = "0"; - expandableArea.style.opacity = "0"; - expandableArea.style.paddingTop = "0"; - expandableArea.style.paddingBottom = "0"; - void expandableArea.offsetHeight; - expandableArea.style.maxHeight = "2000px"; - expandableArea.style.opacity = "1"; - expandableArea.style.paddingTop = "var(--size-2-2)"; - expandableArea.style.paddingBottom = "var(--size-2-2)"; + expandableArea.toggleClass("collapse", false); + expandableArea.toggleClass("expand", true); expandBtn.replaceChildren(); expandBtn.createEl( @@ -835,7 +829,8 @@ export class AdvancedFilterComponent extends Component { const index = this.advancedFilter.filters.indexOf(filter); if (index === -1) return; - const newFilter: Filter = JSON.parse(JSON.stringify(filter)); + const parsedData: unknown = JSON.parse(JSON.stringify(filter)); + const newFilter = parsedData as Filter; newFilter.name = `${filter.name} ${t("copy-suffix")}`; this.advancedFilter.filters.splice(index + 1, 0, newFilter); @@ -919,7 +914,7 @@ export class AdvancedFilterComponent extends Component { groupData.groupCondition = selectedValue; this.updateFilterConjunctions( - newGroupEl.querySelector(".filters-list") as HTMLElement, + newGroupEl.querySelector(".filters-list"), selectedValue, ); }) @@ -968,19 +963,6 @@ export class AdvancedFilterComponent extends Component { .setIcon("trash-2") .setTooltip(t("remove-criterion-group")) .onClick(() => { - const filtersListElForSortable = newGroupEl.querySelector( - ".filters-list", - ) as HTMLElement; - if ( - filtersListElForSortable && - (filtersListElForSortable as any).sortableInstance - ) { - ( - (filtersListElForSortable as any) - .sortableInstance as Sortable - ).destroy(); - } - filter.filterGroups = filter.filterGroups.filter( (g) => g.id !== groupData.id, ); @@ -1278,7 +1260,7 @@ export class AdvancedFilterComponent extends Component { newFilterEl.remove(); this.updateFilterConjunctions( - newFilterEl.parentElement as HTMLElement, + newFilterEl.parentElement, groupData.groupCondition, ); }); @@ -1411,7 +1393,7 @@ export class AdvancedFilterComponent extends Component { }, ]; break; - case "status": + case "status": { valueInput.hide(); const statusOptions = getCustomStatusOptionsForDropdown( this.plugin.settings.data.customStatuses, @@ -1432,7 +1414,7 @@ export class AdvancedFilterComponent extends Component { }); } valueSelect.addOptions(optionsRecord); - setTimeout(() => { + window.setTimeout(() => { Array.from(valueSelect.selectEl.options).forEach( (option) => { if (option.value.startsWith("__group_")) { @@ -1461,6 +1443,7 @@ export class AdvancedFilterComponent extends Component { }, ]; break; + } case "project": valueInput.type = "text"; conditionOptions = [ @@ -1514,7 +1497,7 @@ export class AdvancedFilterComponent extends Component { getPriorityOptionsForDropdown()[0].value.toString(), ); valueSelect.onChange((newValue) => { - filterData.value = Number(newValue); + filterData.value = newValue; }); conditionOptions = [ { @@ -1710,8 +1693,8 @@ export class AdvancedFilterComponent extends Component { } conditionSelect.selectEl.empty(); - conditionOptions.forEach((opt) => - conditionSelect.addOption(opt.value, opt.text), + conditionOptions.forEach( + (opt) => void conditionSelect.addOption(opt.value, opt.text), ); const currentSelectedCondition = filterData.condition; @@ -1764,7 +1747,7 @@ export class AdvancedFilterComponent extends Component { } } - if (valueInput instanceof HTMLInputElement) { + if (valueInput.instanceOf(HTMLInputElement)) { this.setupMultiSuggest(property, valueInput, filterData); } } @@ -1897,8 +1880,8 @@ export class AdvancedFilterComponent extends Component { animation: 150, handle: ".drag-handle", ghostClass: "dragging-placeholder", - onEnd: (evt: Event) => { - const sortableEvent = evt as any; + onEnd: (evt: SortableEvent) => { + const sortableEvent = evt; if ( sortableEvent.oldDraggableIndex === undefined || sortableEvent.newDraggableIndex === undefined @@ -1927,7 +1910,10 @@ export class AdvancedFilterComponent extends Component { rootCondition: "all", }; } - return JSON.parse(JSON.stringify(this.advancedFilter)); + const parsedData: unknown = JSON.parse( + JSON.stringify(this.advancedFilter), + ); + return parsedData as AdvancedFilter; } public loadFilterState(state: AdvancedFilter): void { @@ -1936,7 +1922,8 @@ export class AdvancedFilterComponent extends Component { this.filtersSortableInstance = null; } - this.advancedFilter = JSON.parse(JSON.stringify(state)); + const parsedData: unknown = JSON.parse(JSON.stringify(state)); + this.advancedFilter = parsedData as AdvancedFilter; if ( this.advancedFilter.filters && @@ -1975,9 +1962,10 @@ export class AdvancedFilterComponent extends Component { this.plugin, (importedFilters: Filter[]) => { for (const importedFilter of importedFilters) { - const filterCopy = JSON.parse( + const parsedData: unknown = JSON.parse( JSON.stringify(importedFilter), ); + const filterCopy = parsedData as Filter; this.advancedFilter.filters.push(filterCopy); } this.expandedFilters = new WeakMap(); diff --git a/src/components/AdvancedFilterer/FiltersWarehouse.ts b/src/components/AdvancedFilterer/FiltersWarehouse.ts index 3aac9edd..f653be61 100644 --- a/src/components/AdvancedFilterer/FiltersWarehouse.ts +++ b/src/components/AdvancedFilterer/FiltersWarehouse.ts @@ -39,6 +39,8 @@ export class FiltersWarehouseModal extends Modal { } private _saveBtn: HTMLButtonElement | null = null; + private _importBtn: HTMLButtonElement | null = null; + private _clearBtn: HTMLButtonElement | null = null; private expandedFilters = new WeakMap(); @@ -83,7 +85,7 @@ export class FiltersWarehouseModal extends Modal { const { contentEl } = this; contentEl.empty(); - this.setTitle("Task Board Filters Warehouse"); + this.setTitle(t("task-board-filters-warehouse")); this.render(); } @@ -127,14 +129,10 @@ export class FiltersWarehouseModal extends Modal { const footer = contentEl.createDiv({ cls: "filters-warehouse-footer", }); - footer.style.cssText = - "position: sticky; bottom: 0; background: var(--background-primary); border-top: 1px solid var(--background-modifier-border); padding: var(--size-4-2); margin-top: var(--size-4-2); display: flex; justify-content: space-between; align-items: center; gap: var(--size-2-2);"; const leftSection = footer.createDiv({ cls: "filters-warehouse-footer-left", }); - leftSection.style.cssText = - "display: flex; gap: var(--size-2-2); align-items: center;"; const importBtn = leftSection.createEl("button", { cls: ["compact-btn"], @@ -175,8 +173,6 @@ export class FiltersWarehouseModal extends Modal { const rightSection = footer.createDiv({ cls: "filters-warehouse-footer-right", }); - rightSection.style.cssText = - "display: flex; gap: var(--size-2-2); align-items: center;"; const saveBtn = rightSection.createEl("button", { cls: ["compact-btn"], @@ -200,18 +196,18 @@ export class FiltersWarehouseModal extends Modal { // this.close(); // }); - (this as any)._importBtn = importBtn; - (this as any)._clearBtn = clearBtn; - (this as any)._saveBtn = saveBtn; + this._importBtn = importBtn; + this._clearBtn = clearBtn; + this._saveBtn = saveBtn; } private updateActionButtonsState( importBtn?: HTMLButtonElement, clearBtn?: HTMLButtonElement, ): void { - const btnImport = importBtn || (this as any)._importBtn; - const btnClear = clearBtn || (this as any)._clearBtn; - if (!btnImport) return; + const btnImport = importBtn || this._importBtn; + const btnClear = clearBtn || this._clearBtn; + if (!btnImport || !btnClear) return; const hasSelection = this.selectedFilterIds.size > 0; btnImport.disabled = !hasSelection; @@ -327,10 +323,8 @@ export class FiltersWarehouseModal extends Modal { expandableArea: HTMLElement, expandBtn: HTMLDivElement, ): void { - expandableArea.style.maxHeight = "0"; - expandableArea.style.opacity = "0"; - expandableArea.style.paddingTop = "0"; - expandableArea.style.paddingBottom = "0"; + expandableArea.toggleClass("expand", false); + expandableArea.toggleClass("collapse", true); expandBtn.replaceChildren(); expandBtn.createEl( @@ -356,7 +350,7 @@ export class FiltersWarehouseModal extends Modal { ); this.expandedFilters.set(filter, false); - setTimeout(() => { + window.setTimeout(() => { expandableArea.hide(); }, 300); } @@ -370,15 +364,8 @@ export class FiltersWarehouseModal extends Modal { this.renderExpandedFilterContent(filter, expandableArea); } expandableArea.show(); - expandableArea.style.maxHeight = "0"; - expandableArea.style.opacity = "0"; - expandableArea.style.paddingTop = "0"; - expandableArea.style.paddingBottom = "0"; - void expandableArea.offsetHeight; - expandableArea.style.maxHeight = "2000px"; - expandableArea.style.opacity = "1"; - expandableArea.style.paddingTop = "var(--size-2-2)"; - expandableArea.style.paddingBottom = "var(--size-2-2)"; + expandableArea.toggleClass("collapse", false); + expandableArea.toggleClass("expand", true); expandBtn.replaceChildren(); expandBtn.createEl( @@ -465,7 +452,7 @@ export class FiltersWarehouseModal extends Modal { ".advanced-filter-top-left", ); if (leftSection) { - const newDesc = leftSection.createEl("span", { + leftSection.createEl("span", { cls: "filter-description-text", text: filter.description, }); @@ -609,7 +596,7 @@ export class FiltersWarehouseModal extends Modal { .onChange((value) => { groupData.groupCondition = value as "all" | "any" | "none"; this.updateFilterConjunctions( - newGroupEl.querySelector(".filters-list") as HTMLElement, + newGroupEl.querySelector(".filters-list"), groupData.groupCondition, ); this.markAsEdited(); @@ -788,7 +775,7 @@ export class FiltersWarehouseModal extends Modal { valueInput.hide(); valueInput.addEventListener("click", () => { this.isMultiSuggestDropdownActive = true; - setTimeout(() => { + window.setTimeout(() => { this.isMultiSuggestDropdownActive = false; }, 100); }); @@ -871,7 +858,7 @@ export class FiltersWarehouseModal extends Modal { ); newFilterEl.remove(); this.updateFilterConjunctions( - newFilterEl.parentElement as HTMLElement, + newFilterEl.parentElement, groupData.groupCondition, ); this.markAsEdited(); @@ -1001,7 +988,7 @@ export class FiltersWarehouseModal extends Modal { }); } valueSelect.addOptions(optionsRecord); - setTimeout(() => { + window.setTimeout(() => { Array.from(valueSelect.selectEl.options).forEach( (option) => { if (option.value.startsWith("__group_")) { @@ -1048,7 +1035,7 @@ export class FiltersWarehouseModal extends Modal { getPriorityOptionsForDropdown()[0].value.toString(), ); valueSelect.onChange((newValue) => { - filterData.value = Number(newValue); + filterData.value = newValue; this.markAsEdited(); }); } @@ -1132,8 +1119,8 @@ export class FiltersWarehouseModal extends Modal { } conditionSelect.selectEl.empty(); - conditionOptions.forEach((opt) => - conditionSelect.addOption(opt.value, opt.text), + conditionOptions.forEach( + (opt) => void conditionSelect.addOption(opt.value, opt.text), ); const currentSelectedCondition = filterData.condition; @@ -1184,7 +1171,7 @@ export class FiltersWarehouseModal extends Modal { } } - if (valueInput instanceof HTMLInputElement) { + if (valueInput.instanceOf(HTMLInputElement)) { this.setupMultiSuggest(property, valueInput, filterData); } } @@ -1320,7 +1307,7 @@ export class FiltersWarehouseModal extends Modal { private saveWarehouse(): void { let newSettings = this.plugin.settings; newSettings.data.filtersWarehouse = this.filtersWarehouseData; - this.plugin.saveSettings(newSettings); + void this.plugin.saveSettings(newSettings); if (this._saveBtn) { this._saveBtn.hide(); } diff --git a/src/components/AdvancedFilterer/LoadSavedFiltersModal.ts b/src/components/AdvancedFilterer/LoadSavedFiltersModal.ts deleted file mode 100644 index 7a54e6aa..00000000 --- a/src/components/AdvancedFilterer/LoadSavedFiltersModal.ts +++ /dev/null @@ -1,417 +0,0 @@ -/** - * @deprecated This component has been deprecated and will be replaced by "Filters Warehouse" in the future. - * Dont use this component. - */ - -import { t } from "i18next"; -import { App, Modal, Setting, Notice, DropdownComponent } from "obsidian"; -import type { Filter, SavedFilterConfig, FilterCriterionGroup, Board } from "../../interfaces/BoardConfigs.js"; -import { bugReporterManagerInsatance } from "../../managers/BugReporter.js"; -import { generateRandomStringId } from "../../utils/TaskItemUtils.js"; -import type TaskBoard from "../../../main.js"; - -export class BoardFiltersStoreModal extends Modal { - private plugin: TaskBoard; - private mode: "save" | "load"; - private currentBoardID: string; - private currentFilterState?: Filter; - private onSave?: (config: SavedFilterConfig) => void; - private onLoad?: (config: SavedFilterConfig) => void; - - constructor( - app: App, - plugin: TaskBoard, - mode: "save" | "load", - currentBoardID: string, - currentFilterState?: Filter, - onSave?: (config: SavedFilterConfig) => void, - onLoad?: (config: SavedFilterConfig) => void, - ) { - super(app); - this.plugin = plugin; - this.mode = mode; - this.currentBoardID = currentBoardID; - this.currentFilterState = currentFilterState; - this.onSave = onSave; - this.onLoad = onLoad; - } - - onOpen() { - const { contentEl } = this; - contentEl.empty(); - - if (this.mode === "save") { - this.renderSaveMode(); - } else { - this.renderLoadMode(); - } - } - - private renderSaveMode() { - const { contentEl } = this; - - contentEl.createEl("h2", { text: t("save-filter-configuration") }); - - let nameValue = ""; - let descriptionValue = ""; - - new Setting(contentEl) - .setName(t("filter-configuration-name")) - .setDesc(t("filter-configuration-name-info")) - .addText((text) => { - text.setPlaceholder(t("filter-configuration-name")) - .setValue(nameValue) - .onChange((value) => { - nameValue = value; - }); - }); - - new Setting(contentEl) - .setName(t("filter-configuration-description")) - .setDesc(t("filter-configuration-description-info")) - .addTextArea((text) => { - text.setPlaceholder(t("filter-configuration-description")) - .setValue(descriptionValue) - .onChange((value) => { - descriptionValue = value; - }); - text.inputEl.rows = 3; - }); - - new Setting(contentEl) - .addButton((btn) => { - btn.setButtonText(t("save")) - .setCta() - .onClick(() => { - this.saveConfiguration(nameValue, descriptionValue); - }); - }) - .addButton((btn) => { - btn.setButtonText(t("cancel")).onClick(() => { - this.close(); - }); - }); - } - - private async renderLoadMode() { - const { contentEl } = this; - - contentEl.createEl("h2", { text: t("load-filter-configuration") }); - - const board: Board | null = - await this.plugin.taskBoardFileManager.loadBoardUsingID(this.currentBoardID); - if (board && !board?.filterConfig) { - board.filterConfig = { - enableSavedFilters: true, - savedConfigs: [], - }; - } - const savedConfigs = board!.filterConfig!.savedConfigs; - - if (savedConfigs.length === 0) { - contentEl.createEl("p", { - text: t("no-saved-filter-configurations"), - }); - new Setting(contentEl).addButton((btn) => { - btn.setButtonText(t("close")).onClick(() => { - this.close(); - }); - }); - return; - } - - let selectedConfigId = ""; - - new Setting(contentEl) - .setName(t("select-a-saved-filter-configuration")) - .addDropdown((dropdown: DropdownComponent) => { - dropdown.addOption( - "", - t("select-a-saved-filter-configuration"), - ); - - savedConfigs.forEach((config) => { - dropdown.addOption(config.id, config.name); - }); - - dropdown.onChange((value) => { - selectedConfigId = value; - this.updateConfigDetails(value); - }); - }); - - // Container for config details - const detailsContainer = contentEl.createDiv({ - cls: "filter-config-details", - }); - - // Buttons container - const buttonsContainer = contentEl.createDiv({ - cls: "filter-config-buttons", - }); - - new Setting(buttonsContainer) - .addButton((btn) => { - btn.setButtonText(t("load")) - .setCta() - .onClick(() => { - this.loadConfiguration(selectedConfigId); - }); - }) - .addButton((btn) => { - btn.setButtonText(t("delete")) - .setWarning() - .onClick(() => { - this.deleteConfiguration(selectedConfigId); - }); - }) - .addButton((btn) => { - btn.setButtonText(t("cancel")).onClick(() => { - this.close(); - }); - }); - - // Store references for updating - (this as any).detailsContainer = detailsContainer; - } - - private async updateConfigDetails(configId: string) { - const detailsContainer = (this as any).detailsContainer; - if (!detailsContainer) return; - - detailsContainer.empty(); - - if (!configId) return; - - const board = - await this.plugin.taskBoardFileManager.loadBoardUsingID(this.currentBoardID); - if (board && !board.filterConfig) return; - - const config = board!.filterConfig!.savedConfigs.find( - (c: SavedFilterConfig) => c.id === configId, - ); - - if (!config) return; - - detailsContainer.createEl("h3", { text: config.name }); - - if (config.description) { - detailsContainer.createEl("p", { text: config.description }); - } - - detailsContainer.createEl("p", { - text: `${t("created")}: ${new Date( - config.createdAt, - ).toLocaleString()}`, - cls: "filter-config-meta", - }); - - detailsContainer.createEl("p", { - text: `${t("updated")}: ${new Date( - config.updatedAt, - ).toLocaleString()}`, - cls: "filter-config-meta", - }); - - // Show filter summary - const filterSummary = detailsContainer.createDiv({ - cls: "filter-config-summary", - }); - filterSummary.createEl("h4", { text: t("filter-summary") }); - - const groupCount = config.filterState.filterGroups.length; - const totalFilters = config.filterState.filterGroups.reduce( - (sum: number, group: FilterCriterionGroup) => sum + group.filters.length, - 0, - ); - - filterSummary.createEl("p", { - text: `${groupCount} ${t("filter-group")}${ - groupCount !== 1 ? "s" : "" - }, ${totalFilters} ${t("filter")}${totalFilters !== 1 ? "s" : ""}`, - }); - - filterSummary.createEl("p", { - text: `${t("root-condition")}: ${config.filterState.rootCondition}`, - }); - } - - private async saveConfiguration(name: string, description: string) { - if (!name.trim()) { - new Notice(t("filter-configuration-name-is-required")); - return; - } - - if (!this.currentFilterState) { - new Notice(t("failed-to-save-filter-configuration")); - return; - } - - const now = new Date().toISOString(); - const config: SavedFilterConfig = { - id: generateRandomStringId(`filter_saved_${Date.now()}`), - name: name.trim(), - description: description.trim() || undefined, - filterState: JSON.parse(JSON.stringify(this.currentFilterState)), - createdAt: now, - updatedAt: now, - }; - - try { - const board = - await this.plugin.taskBoardFileManager.loadBoardUsingID(this.currentBoardID); - if (board && !board.filterConfig) { - board.filterConfig = { - enableSavedFilters: true, - savedConfigs: [], - }; - } - board!.filterConfig!.savedConfigs.push(config); - await this.plugin.saveSettings(); - - if (this.onSave) { - this.onSave(config); - } - - this.close(); - } catch (error) { - bugReporterManagerInsatance.addToLogs( - 111, - String(error), - "BoardFiltersStoreModal.ts/saveConfiguration", - ); - new Notice(t("failed-to-save-filter-configuration")); - } - } - - private async loadConfiguration(configId: string) { - if (!configId) { - new Notice(t("select-a-saved-filter-configuration")); - return; - } - - const board = - await this.plugin.taskBoardFileManager.loadBoardUsingID(this.currentBoardID); - if (!board || !board.filterConfig) return; - - const config = board.filterConfig.savedConfigs.find( - (c: SavedFilterConfig) => c.id === configId, - ); - - if (!config) { - new Notice(t("failed-to-load-filter-configuration")); - return; - } - - try { - if (this.onLoad) { - this.onLoad(config); - } - - this.close(); - } catch (error) { - bugReporterManagerInsatance.addToLogs( - 112, - String(error), - "BoardFiltersStoreModal.ts/loadConfiguration", - ); - new Notice(t("failed-to-load-filter-configuration")); - } - } - - private async deleteConfiguration(configId: string) { - if (!configId) { - new Notice(t("Select a saved filter configuration")); - return; - } - - const board = await this.plugin.taskBoardFileManager.loadBoardUsingID(this.currentBoardID); - if (!board || !board.filterConfig) return; - - const config = board.filterConfig.savedConfigs.find( - (c: SavedFilterConfig) => c.id === configId, - ); - - if (!config) { - new Notice(t("failed-to-delete-filter-configuration")); - return; - } - - // Confirm deletion - const confirmed = await new Promise((resolve) => { - const confirmModal = new Modal(this.app); - confirmModal.contentEl.createEl("h2", { - text: t("delete-filter-configuration"), - }); - confirmModal.contentEl.createEl("p", { - text: t("delete-filter-configuration-question"), - }); - confirmModal.contentEl.createEl("p", { - text: `"${config.name}"`, - cls: "filter-config-name-highlight", - }); - - new Setting(confirmModal.contentEl) - .addButton((btn) => { - btn.setButtonText(t("delete")) - .setWarning() - .onClick(() => { - resolve(true); - confirmModal.close(); - }); - }) - .addButton((btn) => { - btn.setButtonText(t("cancel")).onClick(() => { - resolve(false); - confirmModal.close(); - }); - }); - - confirmModal.open(); - }); - - if (!confirmed) return; - - try { - const board = - await this.plugin.taskBoardFileManager.loadBoardUsingID(this.currentBoardID); - if (!board || !board.filterConfig) return; - - board.filterConfig.savedConfigs = - board.filterConfig.savedConfigs.filter( - (c: SavedFilterConfig) => c.id !== configId, - ); - - await this.plugin.saveSettings(); - - new Notice(t("filter-configuration-deleted-successfully")); - - // Refresh the load mode display - this.close(); - - // Reopen in load mode to refresh the list - const newModal = new BoardFiltersStoreModal( - this.app, - this.plugin, - "load", - this.currentBoardID, - undefined, - this.onSave, - this.onLoad, - ); - newModal.open(); - } catch (error) { - bugReporterManagerInsatance.addToLogs( - 113, - String(error), - "BoardFiltersStoreModal.ts/deleteConfiguration", - ); - new Notice(t("failed-to-delete-filter-configuration")); - } - } - - onClose() { - const { contentEl } = this; - contentEl.empty(); - } -} diff --git a/src/components/AdvancedFilterer/Popover.ts b/src/components/AdvancedFilterer/Popover.ts index ba5d37bb..14c2c97f 100644 --- a/src/components/AdvancedFilterer/Popover.ts +++ b/src/components/AdvancedFilterer/Popover.ts @@ -148,7 +148,7 @@ export class AdvancedFilterPopover }); this.popoverRef.appendChild(contentEl); - document.body.appendChild(this.popoverRef); + activeDocument.body.appendChild(this.popoverRef); // Create a virtual element for Popper.js const virtualElement = { diff --git a/src/components/KanbanView/KanbanBoardView.tsx b/src/components/KanbanView/KanbanBoardView.tsx index b97201f7..2d77b251 100644 --- a/src/components/KanbanView/KanbanBoardView.tsx +++ b/src/components/KanbanView/KanbanBoardView.tsx @@ -8,9 +8,11 @@ import type { taskJsonMerged, taskItem } from "../../interfaces/TaskItem.js"; import { columnSegregator } from "../../utils/algorithms/ColumnSegregator.js"; import KanbanSwimlanesContainer from "./KanbanSwimlanesContainer.js"; import LazyColumn from "./LazyColumn.js"; +import { Component, View } from "obsidian"; interface KanbanBoardProps { plugin: TaskBoard; + parentComponent: Component | View; currentBoardData: Board; currentView: TaskBoardViewType; currentViewIndex: number; @@ -18,7 +20,7 @@ interface KanbanBoardProps { freshInstall: boolean; } -const KanbanBoard: React.FC = ({ plugin, currentBoardData, currentView, currentViewIndex, filteredAndSearchedTasks, freshInstall }) => { +const KanbanBoard: React.FC = ({ plugin, parentComponent, currentBoardData, currentView, currentViewIndex, filteredAndSearchedTasks, freshInstall }) => { // console.log("Lets see how many times this is running....\ncurrentViewIndex = ", currentViewIndex, "\ncurrentViewName = ", currentView.viewName); const [loading, setLoading] = useState(true); @@ -61,6 +63,7 @@ const KanbanBoard: React.FC = ({ plugin, currentBoardData, cur = ({ plugin, currentBoardData, cur return null; }, [freshInstall]); - const isSwimlanesEnabled = currentView.kanbanView!.swimlanes?.enabled === true; + const isSwimlanesEnabled = currentView.kanbanView.swimlanes?.enabled === true; return (
@@ -120,9 +123,10 @@ const KanbanBoard: React.FC = ({ plugin, currentBoardData, cur {isSwimlanesEnabled ? ( ) : ( diff --git a/src/components/KanbanView/KanbanSwimlanesContainer.tsx b/src/components/KanbanView/KanbanSwimlanesContainer.tsx index 1c63a176..a90ec581 100644 --- a/src/components/KanbanView/KanbanSwimlanesContainer.tsx +++ b/src/components/KanbanView/KanbanSwimlanesContainer.tsx @@ -12,9 +12,11 @@ import { eventEmitter } from '../../services/EventEmitter.js'; import { getAllTaskTags } from '../../utils/TaskItemUtils.js'; import LazyColumn from './LazyColumn.js'; import { getStatusNameFromStatusSymbol } from '../../utils/taskNote/TaskNoteUtils.js'; +import { Component, View } from 'obsidian'; interface KanbanSwimlanesContainerProps { plugin: TaskBoard; + parentComponent: Component | View; currentBoardData: Board; currentViewIndex: number; kanbanViewData: KanbanView; @@ -30,6 +32,7 @@ interface SwimlaneRow { const KanbanSwimlanesContainer: React.FC = ({ plugin, + parentComponent, currentBoardData, currentViewIndex, kanbanViewData, @@ -248,6 +251,7 @@ const KanbanSwimlanesContainer: React.FC = ({ = ({ // ); // } - async function handleSwimlaneMinimize(rowIndex: number) { + function handleSwimlaneMinimize(rowIndex: number) { try { const swimlaneName = swimlanes[rowIndex]?.swimlaneName; if (!swimlaneName) return; @@ -324,7 +328,7 @@ const KanbanSwimlanesContainer: React.FC = ({ } - await plugin.taskBoardFileManager.saveBoard(updatedBoardData); + void plugin.taskBoardFileManager.saveBoard(updatedBoardData); eventEmitter.emit('REFRESH_BOARD'); } catch (err) { bugReporterManagerInsatance.addToLogs( @@ -360,6 +364,7 @@ const KanbanSwimlanesContainer: React.FC = ({ = ({ = ({ 0) { - values = allTags.map((tag: string) => { - if (typeof tag === 'string') return tag.replace('#', ''); - return ''; - }).filter((v: string) => v.trim()); + { + const allTags = getAllTaskTags(task); + if (allTags && allTags.length > 0) { + values = allTags.map((tag: string) => { + if (typeof tag === 'string') return tag.replace('#', ''); + return ''; + }).filter((v: string) => v.trim()); + } + break; } - break; case 'priority': if (typeof task.priority === 'number') { diff --git a/src/components/KanbanView/LazyColumn.tsx b/src/components/KanbanView/LazyColumn.tsx index 80353d7d..f54ac165 100644 --- a/src/components/KanbanView/LazyColumn.tsx +++ b/src/components/KanbanView/LazyColumn.tsx @@ -1,24 +1,25 @@ // src/components/KanbanView/LazyColumn.tsx import React, { memo, useMemo, useState, useEffect, useRef, useCallback } from 'react'; -import { CSSProperties } from 'react'; -import { Menu, Notice, Platform } from 'obsidian'; +import { type CSSProperties } from 'react'; +import { Component, Menu, Notice, Platform, View } from 'obsidian'; import { t } from 'i18next'; -import { AlertOctagon, EllipsisVertical, MenuIcon } from 'lucide-react'; +import { AlertOctagon, EllipsisVertical } from 'lucide-react'; import TaskBoard from '../../../main.js'; -import { Board, KanbanView, ColumnData, Filter, AdvancedFilter } from '../../interfaces/BoardConfigs.js'; +import type { Board, KanbanView, ColumnData, AdvancedFilter } from '../../interfaces/BoardConfigs.js'; import { taskCardStyleNames, viewTypeNames } from '../../interfaces/Enums.js'; -import { taskItem } from '../../interfaces/TaskItem.js'; +import type { taskItem } from '../../interfaces/TaskItem.js'; import { bugReporterManagerInsatance } from '../../managers/BugReporter.js'; import { dragDropTasksManagerInsatance } from '../../managers/DragDropTasksManager.js'; import { ConfigureColumnSortingModal } from '../../modals/ConfigureColumnSortingModal.js'; import { eventEmitter } from '../../services/EventEmitter.js'; -import { isRootFilterStateEmpty } from '../../utils/algorithms/AdvancedFilterer.js'; +import { isAdvancedFilterEmpty } from '../../utils/algorithms/AdvancedFilterer.js'; import { matchTagsWithWildcards } from '../../utils/algorithms/ScanningFilterer.js'; import { AdvancedFilterModal } from '../AdvancedFilterer/index.js'; import { AdvancedFilterPopover } from '../AdvancedFilterer/Popover.js'; -import TaskItem, { swimlaneDataProp } from '../TaskCard/TaskItem.js'; +import TaskItem, { type swimlaneDataProp } from '../TaskCard/TaskItem.js'; import TaskItemV2 from '../TaskCard/TaskItemV2.js'; +import { DEFAULT_SETTINGS } from '../../interfaces/GlobalSettings.js'; type CustomCSSProperties = CSSProperties & { '--task-board-column-width': string; @@ -26,6 +27,7 @@ type CustomCSSProperties = CSSProperties & { export interface LazyColumnProps { plugin: TaskBoard; + parentComponent: Component | View; activeBoardData: Board; currentViewIndex: number; kanbanViewData: KanbanView; @@ -40,6 +42,7 @@ export interface LazyColumnProps { const LazyColumn: React.FC = ({ plugin, + parentComponent, activeBoardData, currentViewIndex, kanbanViewData, @@ -80,7 +83,7 @@ const LazyColumn: React.FC = ({ cancelAnimationFrame(rafRef.current); rafRef.current = null; } - rafRef.current = requestAnimationFrame(() => { + rafRef.current = window.requestAnimationFrame(() => { insertIndexRef.current = pos; setInsertIndex(pos); rafRef.current = null; @@ -117,7 +120,7 @@ const LazyColumn: React.FC = ({ if (scrollDifference < 1) return; - const htmlElement = document.documentElement; + const htmlElement = activeDocument.documentElement; if (isScrollingDown) { // User is scrolling down - hide navigation @@ -139,6 +142,7 @@ const LazyColumn: React.FC = ({ // Scroll event handler const handleScroll = useCallback(() => { + debugger; const container = tasksContainerRef.current; if (!container) return; @@ -160,30 +164,28 @@ const LazyColumn: React.FC = ({ if (!container) return; // Throttle scroll events for performance - let throttleTimeout: NodeJS.Timeout | null = null; + let throttleTimeout: number | null; const throttledScroll = () => { if (throttleTimeout) return; - throttleTimeout = setTimeout(() => { + throttleTimeout = window.setTimeout(() => { handleScroll(); if (Platform.isMobile) handleNavVisibility(); - - throttleTimeout = null; }, 100); }; container.addEventListener('scroll', throttledScroll); return () => { container.removeEventListener('scroll', throttledScroll); - if (throttleTimeout) clearTimeout(throttleTimeout); + if (throttleTimeout) window.clearTimeout(throttleTimeout); }; }, [handleScroll]); // ------------------------------------------------- // ALL DRAG AND DROP RELATED FUNCTIONS // - // All these drag-drop handlers has been moved at the top of this file + // All these drag-drop handler function must be kept here at the top of this file // so that useCallback can be initiazlied BEFORE any early returns // ------------------------------------------------- @@ -248,7 +250,7 @@ const LazyColumn: React.FC = ({ } const targetColumnContainer = tasksContainerRef.current as HTMLDivElement; - dragDropTasksManagerInsatance.handleCardDragOverEvent(e.nativeEvent as DragEvent, e.currentTarget as HTMLDivElement, targetColumnContainer, columnData); + dragDropTasksManagerInsatance.handleCardDragOverEvent(e.nativeEvent, e.currentTarget, targetColumnContainer, columnData); } } catch (error) { bugReporterManagerInsatance.addToLogs(119, String(error), "LazyColumn.tsx/handleTaskItemDragOver"); @@ -264,7 +266,7 @@ const LazyColumn: React.FC = ({ // setIsDragOver(true); try { // Get the target column container - const targetColumnContainer = (e.currentTarget) as HTMLDivElement; + const targetColumnContainer = (e.currentTarget); dragDropTasksManagerInsatance.handleColumnDragOverEvent(e.nativeEvent, columnData, targetColumnContainer); } catch (error) { bugReporterManagerInsatance.addToLogs(120, String(error), "LazyColumn.tsx/handleDragOver"); @@ -295,7 +297,7 @@ const LazyColumn: React.FC = ({ // setIsDragOver(false); setInsertIndex(null); // Let manager clean up the column highlight - dragDropTasksManagerInsatance.handleDragLeaveEvent(e.currentTarget as HTMLDivElement); + dragDropTasksManagerInsatance.handleDragLeaveEvent(e.currentTarget); dragDropTasksManagerInsatance.clearDesiredDropIndex(); }, []); @@ -320,7 +322,7 @@ const LazyColumn: React.FC = ({ const targetColumnContainer = tasksContainerRef.current; // const targetColumnContainer = (e.currentTarget) as HTMLDivElement; if (!targetColumnContainer) { - throw `tasksContainerRef.current not found : ${JSON.stringify(targetColumnContainer)}`; + throw new Error(`tasksContainerRef.current not found : ${JSON.stringify(targetColumnContainer)}`); } // We will have to do this calculation here once again to ensure that we correctly captures the task index. @@ -363,7 +365,7 @@ const LazyColumn: React.FC = ({ // Clean up navigation visibility class when component unmounts // if (isNavHiddenRef.current) { - document.documentElement.classList.remove('is-hidden-nav'); + activeDocument.documentElement.classList.remove('is-hidden-nav'); // isNavHiddenRef.current = false; // } }; @@ -374,7 +376,7 @@ const LazyColumn: React.FC = ({ // return null; // } - const columnWidth = plugin.settings.data.columnWidth || '273px'; + const columnWidth = plugin.settings.data.columnWidth || DEFAULT_SETTINGS.data.columnWidth; // Extra code to provide special data-types for theme support. const tagColors = plugin.settings.data.tagColors; @@ -388,7 +390,7 @@ const LazyColumn: React.FC = ({ } // Determine whether an advanced filter is applied (used by header count UI) - const isAdvancedFilterApplied = !isRootFilterStateEmpty(columnData.filters); + const isAdvancedFilterApplied = !isAdvancedFilterEmpty(columnData.columnFilters); async function handleMinimizeColumn() { // const boardIndex = plugin.settings.data.boardConfigs.findIndex( @@ -406,14 +408,14 @@ const LazyColumn: React.FC = ({ if (columnIndex !== -1) { let newBoardData = activeBoardData; newBoardData.views[currentViewIndex].kanbanView!.columns[columnIndex].minimized = !newBoardData.views[currentViewIndex].kanbanView!.columns[columnIndex].minimized; - plugin.taskBoardFileManager.saveBoard(newBoardData); + await plugin.taskBoardFileManager.saveBoard(newBoardData); eventEmitter.emit('REFRESH_BOARD'); } // } } - async function handleAlertButtonClick() { + function handleAlertButtonClick() { const message = "You have set a work limit of " + columnData.workLimit + " for this column. Dont be so hard on yourself. Limit your work to reduce workload burden."; new Notice(message, 0); } @@ -451,7 +453,7 @@ const LazyColumn: React.FC = ({ // Update the column configuration let newBoardData = activeBoardData; newBoardData.views[currentViewIndex].kanbanView!.columns[columnIndex] = updatedColumnConfiguration; - plugin.taskBoardFileManager.saveBoard(newBoardData); + void plugin.taskBoardFileManager.saveBoard(newBoardData); // Refresh the board view eventEmitter.emit('REFRESH_BOARD'); @@ -489,14 +491,14 @@ const LazyColumn: React.FC = ({ ); // Set the close callback - mainly used for handling cancel actions - filterModal.filterCloseCallback = async (filterState: AdvancedFilter | undefined) => { + filterModal.filterCloseCallback = (filterState: AdvancedFilter | undefined) => { if (filterState) { if (columnIndex !== -1) { // Update the column filters let newBoardData = activeBoardData; newBoardData.views[currentViewIndex].kanbanView!.columns[columnIndex].columnFilters = filterState; - plugin.taskBoardFileManager.saveBoard(newBoardData); + void plugin.taskBoardFileManager.saveBoard(newBoardData); // Refresh the board view eventEmitter.emit('REFRESH_BOARD'); @@ -509,7 +511,7 @@ const LazyColumn: React.FC = ({ // Get the position of the menu (approximate column position) // Use CSS.escape to properly escape the selector value const escapedTag = columnData.coltag ? CSS.escape(columnData.coltag) : ''; - const columnElement = document.querySelector(`[data-column-tag-name="${escapedTag}"]`) as HTMLElement; + const columnElement = activeDocument.querySelector(`[data-column-tag-name="${escapedTag}"]`) as HTMLElement; const position = columnElement ? { x: columnElement.getBoundingClientRect().left, y: columnElement.getBoundingClientRect().top + 40 } : { x: 100, y: 100 }; // Fallback position @@ -527,14 +529,14 @@ const LazyColumn: React.FC = ({ ); // Set up close callback to save filter state - popover.onClose = async (filterState?: AdvancedFilter) => { + popover.onClose = (filterState?: AdvancedFilter) => { if (filterState) { if (columnIndex !== -1) { // Update the column filters let newBoardData = activeBoardData; newBoardData.views[currentViewIndex].kanbanView!.columns[columnIndex].columnFilters = filterState; - plugin.taskBoardFileManager.saveBoard(newBoardData); + void plugin.taskBoardFileManager.saveBoard(newBoardData); // Refresh the board view eventEmitter.emit('REFRESH_BOARD'); @@ -577,7 +579,7 @@ const LazyColumn: React.FC = ({ let newBoardData = activeBoardData; newBoardData.views[currentViewIndex].kanbanView!.columns[columnIndex].active = false; - plugin.taskBoardFileManager.saveBoard(newBoardData); + void plugin.taskBoardFileManager.saveBoard(newBoardData); // Refresh the board view eventEmitter.emit('REFRESH_BOARD'); @@ -620,7 +622,7 @@ const LazyColumn: React.FC = ({ let updatedBoardData = { ...activeBoardData }; if (updatedBoardData.views[currentViewIndex].kanbanView) { updatedBoardData.views[currentViewIndex].kanbanView = updatedViewData; - plugin.taskBoardFileManager.saveBoard(updatedBoardData); + void plugin.taskBoardFileManager.saveBoard(updatedBoardData); eventEmitter.emit('REFRESH_BOARD'); } @@ -699,9 +701,8 @@ const LazyColumn: React.FC = ({
openColumnMenu(evt)} aria-label={t("open-column-menu")}> {allTasks?.length ?? 0}
-
{ - await handleMinimizeColumn(); - eventEmitter.emit('REFRESH_BOARD'); +
{ + void handleMinimizeColumn(); }}>{columnData.name}
) : ( @@ -768,6 +769,7 @@ const LazyColumn: React.FC = ({ dataAttributeIndex: i, plugin: plugin, task: task, + parentComponent: parentComponent, activeBoardID: activeBoardData.id, activeViewIndex: currentViewIndex, activeViewType: viewTypeNames.kanban, // Since LazyColumn will be always rendered inside a Kanban view. diff --git a/src/components/MapView/CustomNodeResizer.tsx b/src/components/MapView/CustomNodeResizer.tsx index f655c8da..002bf436 100644 --- a/src/components/MapView/CustomNodeResizer.tsx +++ b/src/components/MapView/CustomNodeResizer.tsx @@ -1,15 +1,15 @@ -import { memo } from 'react'; +import { memo, ReactElement } from 'react'; import { Handle, Position, NodeResizeControl, NodeProps } from '@xyflow/react'; import TaskBoard from '../../../main.js'; -interface dataProps extends React.ReactElement { +interface dataProps extends ReactElement { props: { plugin: TaskBoard }; } interface CustomNodeProps { data: { label: dataProps; - [key: string]: any; + [key: string]: unknown; }; // selected: boolean; // width: number | undefined; diff --git a/src/components/MapView/EdgeWithToolbar.tsx b/src/components/MapView/EdgeWithToolbar.tsx index 1e078074..6e9f50a4 100644 --- a/src/components/MapView/EdgeWithToolbar.tsx +++ b/src/components/MapView/EdgeWithToolbar.tsx @@ -1,3 +1,4 @@ +/* eslint-disable obsidianmd/rule-custom-message */ /** * This component creates a custom edge which provides a nice toolbar * when left or right clicked on the edge. This functionalities are provided by @@ -129,7 +130,7 @@ export function EdgeWithToolbar(props: EdgeProps) { > )} - @@ -1429,6 +1439,7 @@ const TaskBoardViewContainer: React.FC<{ plugin: TaskBoard, currentBoardData: Bo currentView.viewType === viewTypeNames.kanban ? ( 0 ) { - const pr = document.createElement("div"); + const pr = activeDocument.createElement("div"); pr.className = "taskItemPrio"; - pr.textContent = priorityEmojis[task.priority as number] || ""; + pr.textContent = priorityEmojis[task.priority] || ""; headerLeft.appendChild(pr); } @@ -87,11 +87,11 @@ export function createTaskCard(plugin: TaskBoard, task: taskItem): HTMLElement { ) && (task.tags.length > 0 || task.frontmatterTags.length > 0) ) { - const tagsDiv = document.createElement("div"); + const tagsDiv = activeDocument.createElement("div"); tagsDiv.className = "taskItemTags"; task.tags.forEach((tag) => { - const tagEl = document.createElement("div"); + const tagEl = activeDocument.createElement("div"); tagEl.className = "taskItemTag"; const isTagBg = globalSettings.tagColorsType === TagColorType.TagBg; const isCardBg = @@ -107,7 +107,7 @@ export function createTaskCard(plugin: TaskBoard, task: taskItem): HTMLElement { const dimmedTagColor = customTag ? updateRGBAOpacity(customTag.color, 0.1) : undefined; - if (isTagBg && tagColor) tagEl.style.color = "white"; + if (isTagBg && tagColor) tagEl.addClass("tb_color_white "); if (isTagBg) tagEl.style.backgroundColor = tagColor ?? ""; else if (dimmedTagColor) tagEl.style.backgroundColor = dimmedTagColor; @@ -117,7 +117,7 @@ export function createTaskCard(plugin: TaskBoard, task: taskItem): HTMLElement { }); task.frontmatterTags.forEach((tag) => { - const fm = document.createElement("div"); + const fm = activeDocument.createElement("div"); fm.className = "taskItemTagFrontmatter"; fm.title = "Tag from note's frontmatter (read-only)"; fm.textContent = tag; @@ -129,7 +129,7 @@ export function createTaskCard(plugin: TaskBoard, task: taskItem): HTMLElement { headerBottom.appendChild(headerLeft); - const headerRight = document.createElement("div"); + const headerRight = activeDocument.createElement("div"); headerRight.className = "taskItemHeaderRight"; if ( globalSettings.visiblePropertiesList?.includes( @@ -137,12 +137,12 @@ export function createTaskCard(plugin: TaskBoard, task: taskItem): HTMLElement { ) && task.legacyId ) { - const idCont = document.createElement("div"); + const idCont = activeDocument.createElement("div"); idCont.className = "taskItemPropertyID"; - const label = document.createElement("div"); + const label = activeDocument.createElement("div"); label.className = "taskItemPropertyIDLabel"; label.textContent = "ID"; - const value = document.createElement("div"); + const value = activeDocument.createElement("div"); value.className = "taskItemPropertyIDValue"; value.textContent = String(task.legacyId); idCont.appendChild(label); @@ -155,9 +155,9 @@ export function createTaskCard(plugin: TaskBoard, task: taskItem): HTMLElement { main.appendChild(header); // Body (title) - const body = document.createElement("div"); + const body = activeDocument.createElement("div"); body.className = "taskItemMainBody"; - const titleWrap = document.createElement("div"); + const titleWrap = activeDocument.createElement("div"); titleWrap.className = "taskItemMainBodyTitleNsubTasks"; if ( @@ -165,7 +165,7 @@ export function createTaskCard(plugin: TaskBoard, task: taskItem): HTMLElement { taskPropertiesNames.Checkbox, ) ) { - const cb = document.createElement("input"); + const cb = activeDocument.createElement("input"); cb.type = "checkbox"; cb.checked = task.status === " " ? false : true; cb.className = "taskItemCheckbox"; @@ -173,9 +173,9 @@ export function createTaskCard(plugin: TaskBoard, task: taskItem): HTMLElement { titleWrap.appendChild(cb); } - const bodyContent = document.createElement("div"); + const bodyContent = activeDocument.createElement("div"); bodyContent.className = "taskItemBodyContent"; - const title = document.createElement("div"); + const title = activeDocument.createElement("div"); title.className = "taskItemTitle"; title.textContent = task.title || ""; bodyContent.appendChild(title); @@ -184,7 +184,7 @@ export function createTaskCard(plugin: TaskBoard, task: taskItem): HTMLElement { main.appendChild(body); // Footer - const footer = document.createElement("div"); + const footer = activeDocument.createElement("div"); footer.className = "taskItemFooter"; if ( globalSettings.visiblePropertiesList?.includes( @@ -192,9 +192,9 @@ export function createTaskCard(plugin: TaskBoard, task: taskItem): HTMLElement { ) && task.status ) { - const stat = document.createElement("div"); + const stat = activeDocument.createElement("div"); stat.className = "taskItemFooterPropertyContainerEmoji"; - const val = document.createElement("div"); + const val = activeDocument.createElement("div"); val.className = "taskItemFooterPropertyContainerValue"; val.textContent = getStatusNameFromStatusSymbol( task.status, @@ -209,9 +209,9 @@ export function createTaskCard(plugin: TaskBoard, task: taskItem): HTMLElement { ) && task.filePath ) { - const fp = document.createElement("div"); + const fp = activeDocument.createElement("div"); fp.className = "taskItemFooterPropertyContainerEmoji"; - const val = document.createElement("div"); + const val = activeDocument.createElement("div"); val.className = "taskItemFooterPropertyContainerValue"; val.setAttribute("aria-label", task.filePath); val.textContent = task.filePath.split("/").pop() || ""; diff --git a/src/components/TaskCard/TaskItem.tsx b/src/components/TaskCard/TaskItem.tsx index fc2d489b..0863d616 100644 --- a/src/components/TaskCard/TaskItem.tsx +++ b/src/components/TaskCard/TaskItem.tsx @@ -36,7 +36,7 @@ export interface swimlaneDataProp { value: string; } -const TaskItem: React.FC = ({ dataAttributeIndex, plugin, task, activeBoardID, activeViewIndex, activeViewType, kanbanViewData, columnIndex, swimlaneData }) => { +const TaskItem: React.FC = ({ dataAttributeIndex, plugin, task, parentComponent, activeBoardID, activeViewIndex, activeViewType, kanbanViewData, columnIndex, swimlaneData }) => { const globalSettings = plugin.settings.data; const taskNoteIdentifierTag = plugin.settings.data.taskNoteIdentifierTag; const isTaskNote = isTaskNotePresentInTags(taskNoteIdentifierTag, task.tags); @@ -72,12 +72,11 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin, task, a // ); const taskIdKey = task.id; // for rendering unique title - const componentRef = useRef(null); - useEffect(() => { - // Initialize TaskBoardView Component on mount - componentRef.current = plugin.view; - }, []); - + // const componentRef = useRef(null); + // useEffect(() => { + // // Initialize TaskBoardView Component on mount + // componentRef.current = plugin.view; + // }, []); // Ref to access the DOM element of the task item const taskItemRef = useRef(null); @@ -111,7 +110,7 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin, task, a // titleElement.empty(); // // Call the MarkdownUIRenderer to render the description // console.log("Obsidian Renderer : Will render following task : ", cleanedTitle); - // MarkdownUIRenderer.renderTaskDisc( + // MarkdownUIRenderer.strictRender( // plugin.app, // cleanedTitle, // titleElement, @@ -129,19 +128,20 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin, task, a // ======================================== useEffect(() => { const el = taskTitleRendererRef.current; - if (!el || !componentRef.current) return; + // if (!el || !componentRef.current) return; + if (!el) return; try { if (task.title === "") return; const cleanedTitle = isTaskNote ? task.title : cleanTaskTitleLegacy(task); - MarkdownUIRenderer.renderTaskDisc( + void MarkdownUIRenderer.strictRender( plugin.app, cleanedTitle, el, task.filePath, - componentRef.current + parentComponent ); hookMarkdownLinkMouseEventHandlers(plugin.app, plugin, el, task.filePath, task.filePath); @@ -175,7 +175,7 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin, task, a // // strippedSubtaskText = searchQuery ? strippedSubtaskText.replace(regex, `$1`) : strippedSubtaskText; // // } - // MarkdownUIRenderer.renderSubtaskText( + // MarkdownUIRenderer.safeRender( // plugin.app, // strippedSubtaskText, // element, @@ -192,7 +192,7 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin, task, a // SUBTASKS RENDERING WITH STABLE useEffect // ======================================== useEffect(() => { - if (!componentRef.current) return; + // if (!componentRef.current) return; const allSubTasks = task.body.filter(line => isTaskLine(line.trim())); for (const [index, subtaskText] of allSubTasks.entries()) { @@ -204,12 +204,12 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin, task, a try { const match = subtaskText.match(TaskRegularExpressions.taskRegex); let strippedSubtaskText = match ? match?.length >= 5 ? match[4].trim() : subtaskText.trim() : subtaskText.trim(); - MarkdownUIRenderer.renderSubtaskText( + void MarkdownUIRenderer.safeRender( plugin.app, strippedSubtaskText, element, task.filePath, - componentRef.current + parentComponent ); hookMarkdownLinkMouseEventHandlers(plugin.app, plugin, element, task.filePath, task.filePath); @@ -251,12 +251,12 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin, task, a container.empty(); - MarkdownUIRenderer.renderTaskDisc( + void MarkdownUIRenderer.strictRender( plugin.app, descriptionContent, container, task.filePath, - componentRef.current + parentComponent ); hookMarkdownLinkMouseEventHandlers( @@ -302,20 +302,20 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin, task, a return null; }; - const toggleDescription = async () => { + const toggleDescription = () => { const status = isDescriptionExpanded; setIsDescriptionExpanded((prev) => !prev); if (!status) { - await renderDescriptionSection(); + renderDescriptionSection(); if (descriptionRef.current) { descriptionRef.current.style.height = `${descriptionRef.current.scrollHeight}px`; - descriptionRef.current.style.opacity = "1"; // Add fade-in effect + descriptionRef.current.toggleClass('tb_opacity_1', true); } } else { if (descriptionRef.current) { - descriptionRef.current.style.height = "0"; - descriptionRef.current.style.opacity = "0"; // Add fade-out effect + descriptionRef.current.style.height = `0`; + descriptionRef.current.toggleClass('tb_opacity_0', true); } const uniqueKey = `${task.id}-desc`; const descElement = taskItemBodyDescriptionRef.current[uniqueKey]; @@ -323,7 +323,7 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin, task, a } }; - // const renderDescriptionByDefault = async () => { + // const renderDescriptionByDefault = () => { // if (showDescriptionSection) { // await renderTaskDescriptionWithObsidianAPI(); // return true; @@ -386,6 +386,7 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin, task, a return '#f23a3ab8'; } } catch (error) { + console.warn("[Task board] : Error while fetching correct color for the due indicator bar for tasks scheduled for today : ", error); // If time parsing fails, return yellow for "due today" return 'var(--color-yellow)'; } @@ -520,7 +521,7 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin, task, a // The component might be unmounted by the time this runs, but this is a safeguard. // The event-based system should ideally handle the final state. // A short delay can prevent a flicker if the re-render is immediate, hence providing 1 second. - setTimeout(() => { + window.setTimeout(() => { setCardLoadingAnimation(false); // const isTaskCompletedNow = isTaskNote // ? isTaskCompleted(task.status, true, plugin.settings) @@ -569,18 +570,18 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin, task, a handleSubTasksChange(plugin, task, updatedTask); } else { // If it's a task note, open the note for editing - handleTaskNoteBodyChange(plugin, task, updatedTask); + void handleTaskNoteBodyChange(plugin, task, updatedTask); } } finally { // The component might be unmounted by the time this runs, but this is a safeguard. // The event-based system should ideally handle the final state. // A short delay can prevent a flicker if the re-render is immediate, hence providing 1 second. - setTimeout(() => setCardLoadingAnimation(false), 2000); + window.setTimeout(() => setCardLoadingAnimation(false), 2000); } }; const handleMouseEnter = (event: React.MouseEvent) => { - const element = document.getElementById('taskItemFooterBtns'); + const element = activeDocument.getElementById('taskItemFooterBtns'); if (element && event.ctrlKey) { markdownButtonHoverPreviewEvent(plugin.app, event, task.filePath); } @@ -659,7 +660,7 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin, task, a options.forEach((status) => { statusMenu.addItem((item) => { // Render status with markdown formatting - MarkdownUIRenderer.renderSubtaskText( + void MarkdownUIRenderer.safeRender( plugin.app, `- [${status.value}] ${status.label}`, item.titleEl, @@ -668,7 +669,7 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin, task, a ); item.onClick(() => { - updateTaskItemStatus(plugin, task, task, status.value); + void updateTaskItemStatus(plugin, task, task, status.value); }); }); }); @@ -712,8 +713,8 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin, task, a taskItemMenu.addItem((it) => { it.setIcon("calendar-plus") it.setTitle(t("start-date")); - it.onClick(async () => { - openDateInputModal(plugin, t("start"), (newDate: string) => { + it.onClick(() => { + void openDateInputModal(plugin, t("start"), (newDate: string) => { updateTaskItemDate(plugin, task, task, UniversalDateOptions.startDate, newDate); }, task.startDate) }); @@ -721,8 +722,8 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin, task, a taskItemMenu.addItem((it) => { it.setIcon("calendar-clock") it.setTitle(t("scheduled-date")); - it.onClick(async () => { - openDateInputModal(plugin, t("scheduled"), (newDate: string) => { + it.onClick(() => { + void openDateInputModal(plugin, t("scheduled"), (newDate: string) => { updateTaskItemDate(plugin, task, task, UniversalDateOptions.scheduledDate, newDate); }, task.scheduledDate) }); @@ -730,8 +731,8 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin, task, a taskItemMenu.addItem((it) => { it.setIcon("calendar") it.setTitle(t("due-date")); - it.onClick(async () => { - openDateInputModal(plugin, t("due"), (newDate: string) => { + it.onClick(() => { + void openDateInputModal(plugin, t("due"), (newDate: string) => { updateTaskItemDate(plugin, task, task, UniversalDateOptions.dueDate, newDate); }, task.due) }); @@ -741,7 +742,7 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin, task, a taskItemMenu.addItem((item) => { item.setIcon("clock"); item.setTitle(t("reminder")); - item.onClick(async () => { + item.onClick(() => { const modal = new DateTimePickerModal(plugin, t("reminder"), task.reminder); modal.onDateTimeSelected = (dateTime) => { // e.g., "2024-01-15T14:30" or "14:30" updateTaskItemReminder(plugin, task, task, dateTime); @@ -759,11 +760,16 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin, task, a taskItemMenu.addItem((item) => { item.setIcon("copy"); item.setTitle(t("copy-task-title")); - item.onClick(async () => { + item.onClick(() => { try { - await navigator.clipboard.writeText(cleanTaskTitleLegacy(task)); + void navigator.clipboard.writeText(cleanTaskTitleLegacy(task)); new Notice(t("copy-task-title-successful")); } catch (error) { + bugReporterManagerInsatance.addToLogs( + 125, + String(error), + "TaskItem.tsx/handleMenuButtonClicked/copy-task-title", + ); new Notice(t("copy-task-title-unsuccessful")); } }); @@ -771,7 +777,7 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin, task, a taskItemMenu.addItem((item) => { item.setIcon("square-pen"); item.setTitle(t("open-task-editor")); - item.onClick(async () => { + item.onClick(() => { handleEditTask(plugin, task, EditButtonMode.Modal); }); }); @@ -802,14 +808,14 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin, task, a taskItemMenu.addItem((item) => { item.setIcon("file-input"); item.setTitle(t("open-note")); - item.onClick(async () => { + item.onClick(() => { handleEditTask(plugin, task, EditButtonMode.NoteInTab) }); }); taskItemMenu.addItem((item) => { item.setIcon("columns-2"); item.setTitle(t("open-note-to-right")); - item.onClick(async () => { + item.onClick(() => { handleEditTask(plugin, task, EditButtonMode.NoteInSplit) }); }); @@ -819,7 +825,7 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin, task, a item.setIcon("file-text"); item.setTitle(t("more-note-actions")); - const submenu = (item as any).setSubmenu(); + const submenu = item.setSubmenu(); // Get the file for the task const file = plugin.app.vault.getAbstractFileByPath(task.filePath); @@ -829,6 +835,11 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin, task, a // Trigger the file-menu event to populate with default actions plugin.app.workspace.trigger("file-menu", submenu, file, "file-explorer"); } catch (error) { + bugReporterManagerInsatance.addToLogs( + 125, + String(error), + "TaskItem.tsx/handleMenuButtonClicked/more-note-actions", + ); console.debug("Native file menu not available, using fallback"); } @@ -836,32 +847,33 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin, task, a submenu.addItem((subItem: MenuItem) => { subItem.setIcon("pencil"); subItem.setTitle(t("rename-note")); - subItem.onClick(async () => { + subItem.onClick(() => { try { // Modal-based rename const currentName = file.basename; - const newName = await showTextInputModal(plugin.app, { + void showTextInputModal(plugin.app, { title: t("rename-note"), placeholder: t("rename-note-placeholder"), initialValue: currentName, - }); - - if (newName && newName.trim() !== "" && newName !== currentName) { - // Ensure the new name has the correct extension - const extension = file.extension; - const finalName = newName.endsWith(`.${extension}`) - ? newName - : `${newName}.${extension}`; - - // Construct the new path - const newPath = file.parent - ? `${file.parent.path}/${finalName}` - : finalName; - - // Rename the file - await plugin.app.vault.rename(file, newPath); - new Notice("File renamed successfully."); - } + }).then((newName: string | null) => { + if (newName && newName.trim() !== "" && newName !== currentName) { + // Ensure the new name has the correct extension + const extension = file.extension; + const finalName = newName.endsWith(`.${extension}`) + ? newName + : `${newName}.${extension}`; + + // Construct the new path + const newPath = file.parent + ? `${file.parent.path}/${finalName}` + : finalName; + + // Rename the file + void plugin.app.vault.rename(file, newPath); + new Notice("File renamed successfully."); + } + }) + } catch (error) { new Notice("There was an error while renaming the file."); bugReporterManagerInsatance.addToLogs( @@ -876,11 +888,16 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin, task, a submenu.addItem((subItem: MenuItem) => { subItem.setIcon("trash"); subItem.setTitle(t("delete-note")); - subItem.onClick(async () => { - plugin.app.vault.trash(file, true).then(() => { + subItem.onClick(() => { + void plugin.app.fileManager.trashFile(file).then(() => { new Notice("File deleted successfully. Moved to system trash."); + }).catch((error) => { + bugReporterManagerInsatance.addToLogs( + 125, + String(error), + "TaskItem.tsx/handleMenuButtonClicked/renaming/trashFile", + ); }) - // handleDeleteTask(plugin, task, true); }); }); } @@ -913,7 +930,7 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin, task, a swimlaneData: swimlaneData }; // Delegate to manager for standardized behavior (sets current payload and dims element) - dragDropTasksManagerInsatance.handleDragStartEvent(e.nativeEvent as DragEvent, el, payload); + dragDropTasksManagerInsatance.handleDragStartEvent(e.nativeEvent, el, payload); } catch (err) { // fallback minimal behavior // try { @@ -976,7 +993,7 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin, task, a
{/* Render priority */} {globalSettings.visiblePropertiesList?.includes(taskPropertiesNames.Priority) && task.priority > 0 && ( -
{priorityEmojis[task.priority as number]}
+
{priorityEmojis[task.priority]}
)} {/* Render tags individually */} @@ -1250,15 +1267,15 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin, task, a // Effect to load child tasks asynchronously useEffect(() => { if (task?.dependsOn && task.dependsOn.length > 0) { - const loadChildTasks = async () => { + const loadChildTasks = () => { const childTasksMap: Record = {}; - await Promise.all((task?.dependsOn ?? []).map(async (dependsOnId) => { + void Promise.all((task?.dependsOn ?? []).map(async (dependsOnId) => { const childTask = await getTaskFromId(plugin, dependsOnId); childTasksMap[dependsOnId] = childTask; })); setChildTasksData(childTasksMap); }; - loadChildTasks(); + void loadChildTasks(); } else { setChildTasksData({}); } @@ -1284,7 +1301,7 @@ const TaskItem: React.FC = ({ dataAttributeIndex, plugin, task, a // Simple version just showing the ID and a symbol return (
-
handleOpenChildTaskModal(event, dependsOnId)}> +
void handleOpenChildTaskModal(event, dependsOnId)}> {isChildTaskCompleted ? TASKS_PLUGIN_DEFAULT_SYMBOLS.dependsOnCompletedSymbol : TASKS_PLUGIN_DEFAULT_SYMBOLS.dependsOnSymbol} = ({ dataAttributeIndex, plugin, task, a className={`taskItemCheckbox${cardLoadingAnimation ? '-checked' : ''}`} data-task={cardLoadingAnimation ? 'x' : task.status} dir='auto' - onChange={handleMainCheckBoxClick} + onChange={() => handleMainCheckBoxClick} onClick={(e) => { if (cardLoadingAnimation) { e.preventDefault(); diff --git a/src/components/TaskCard/TaskItemV2.tsx b/src/components/TaskCard/TaskItemV2.tsx index 5627ce45..92c0b2f8 100644 --- a/src/components/TaskCard/TaskItemV2.tsx +++ b/src/components/TaskCard/TaskItemV2.tsx @@ -2,7 +2,7 @@ import { FaEdit, FaTrash } from 'react-icons/fa'; import React, { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'; -import { Component, Notice, Platform, Menu, TFile, MenuItem } from 'obsidian'; +import { Notice, Platform, Menu, TFile, MenuItem, Component, View } from 'obsidian'; import { ChevronDown, EllipsisVertical, Grip } from 'lucide-react'; import { isToday, isBefore, isAfter, startOfDay, compareAsc } from 'date-fns'; import { t } from 'i18next'; @@ -41,16 +41,17 @@ export interface TaskCardProps { dataAttributeIndex: number; plugin: TaskBoard; task: taskItem; + parentComponent: Component | View; // This is only require for the Markdown.render API to track its lifecycle activeBoardID: string; activeViewIndex: number; - activeViewType: string; + activeViewType: viewTypeNames; // These are optional as this TaskItem can be shown either on Kanban view or Map view. kanbanViewData?: KanbanView; columnIndex?: number; swimlaneData?: swimlaneDataProp; } -const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, activeBoardID, activeViewIndex, activeViewType, kanbanViewData, columnIndex, swimlaneData }) => { +const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, parentComponent, activeBoardID, activeViewIndex, activeViewType, kanbanViewData, columnIndex, swimlaneData }) => { const globalSettings = plugin.settings.data; const taskNoteIdentifierTag = plugin.settings.data.taskNoteIdentifierTag; const isTaskNote = isTaskNotePresentInTags(taskNoteIdentifierTag, task.tags); @@ -86,11 +87,11 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, // ); const taskIdKey = task.id; // for rendering unique title - const componentRef = useRef(null); - useEffect(() => { - // Initialize TaskBoardView Component on mount - componentRef.current = plugin.view; - }, []); + // const componentRef = useRef(null); + // useEffect(() => { + // // Initialize TaskBoardView Component on mount + // componentRef.current = plugin.view; + // }, []); // Ref to access the DOM element of the task item @@ -125,7 +126,7 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, // titleElement.empty(); // // Call the MarkdownUIRenderer to render the description // console.log("Obsidian Renderer : Will render following task : ", cleanedTitle); - // MarkdownUIRenderer.renderTaskDisc( + // MarkdownUIRenderer.strictRender( // plugin.app, // cleanedTitle, // titleElement, @@ -143,19 +144,20 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, // ======================================== useEffect(() => { const el = taskTitleRendererRef.current; - if (!el || !componentRef.current) return; + // if (!el || !componentRef.current) return; + if (!el) return; try { if (task.title === "") return; const cleanedTitle = isTaskNote ? task.title : cleanTaskTitleLegacy(task); - MarkdownUIRenderer.renderTaskDisc( + void MarkdownUIRenderer.strictRender( plugin.app, cleanedTitle, el, task.filePath, - componentRef.current + parentComponent, ); hookMarkdownLinkMouseEventHandlers(plugin.app, plugin, el, task.filePath, task.filePath); @@ -189,7 +191,7 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, // // strippedSubtaskText = searchQuery ? strippedSubtaskText.replace(regex, `$1`) : strippedSubtaskText; // // } - // MarkdownUIRenderer.renderSubtaskText( + // MarkdownUIRenderer.safeRender( // plugin.app, // strippedSubtaskText, // element, @@ -206,7 +208,7 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, // SUBTASKS RENDERING WITH STABLE useEffect // ======================================== useEffect(() => { - if (!componentRef.current) return; + // if (!componentRef.current) return; const allSubTasks = task.body.filter(line => isTaskLine(line.trim())); for (const [index, subtaskText] of allSubTasks.entries()) { @@ -218,12 +220,12 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, try { const match = subtaskText.match(TaskRegularExpressions.taskRegex); let strippedSubtaskText = match ? match?.length >= 5 ? match[4].trim() : subtaskText.trim() : subtaskText.trim(); - MarkdownUIRenderer.renderSubtaskText( + void MarkdownUIRenderer.safeRender( plugin.app, strippedSubtaskText, element, task.filePath, - componentRef.current + parentComponent ); hookMarkdownLinkMouseEventHandlers(plugin.app, plugin, element, task.filePath, task.filePath); @@ -265,12 +267,12 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, container.empty(); - MarkdownUIRenderer.renderTaskDisc( + void MarkdownUIRenderer.strictRender( plugin.app, descriptionContent, container, task.filePath, - componentRef.current + parentComponent ); hookMarkdownLinkMouseEventHandlers( @@ -316,20 +318,20 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, return null; }; - const toggleDescription = async () => { + const toggleDescription = () => { const status = isDescriptionExpanded; setIsDescriptionExpanded((prev) => !prev); if (!status) { - await renderDescriptionSection(); + renderDescriptionSection(); if (descriptionRef.current) { descriptionRef.current.style.height = `${descriptionRef.current.scrollHeight}px`; - descriptionRef.current.style.opacity = "1"; // Add fade-in effect + descriptionRef.current.toggleClass('tb_opacity_1', true); } } else { if (descriptionRef.current) { - descriptionRef.current.style.height = "0"; - descriptionRef.current.style.opacity = "0"; // Add fade-out effect + descriptionRef.current.style.height = `0`; + descriptionRef.current.toggleClass('tb_opacity_0', true); } const uniqueKey = `${task.id}-desc`; const descElement = taskItemBodyDescriptionRef.current[uniqueKey]; @@ -337,7 +339,7 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, } }; - // const renderDescriptionByDefault = async () => { + // const renderDescriptionByDefault = () => { // if (showDescriptionSection) { // await renderTaskDescriptionWithObsidianAPI(); // return true; @@ -534,7 +536,7 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, // The component might be unmounted by the time this runs, but this is a safeguard. // The event-based system should ideally handle the final state. // A short delay can prevent a flicker if the re-render is immediate, hence providing 1 second. - setTimeout(() => { + window.setTimeout(() => { setCardLoadingAnimation(false); // const isTaskCompletedNow = isTaskNote // ? isTaskCompleted(task.status, true, plugin.settings) @@ -583,18 +585,18 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, handleSubTasksChange(plugin, task, updatedTask); } else { // If it's a task note, open the note for editing - handleTaskNoteBodyChange(plugin, task, updatedTask); + void handleTaskNoteBodyChange(plugin, task, updatedTask); } } finally { // The component might be unmounted by the time this runs, but this is a safeguard. // The event-based system should ideally handle the final state. // A short delay can prevent a flicker if the re-render is immediate, hence providing 1 second. - setTimeout(() => setCardLoadingAnimation(false), 2000); + window.setTimeout(() => setCardLoadingAnimation(false), 2000); } }; const handleMouseEnter = (event: React.MouseEvent) => { - const element = document.getElementById('taskItemFooterBtns'); + const element = activeDocument.getElementById('taskItemFooterBtns'); if (element && event.ctrlKey) { markdownButtonHoverPreviewEvent(plugin.app, event, task.filePath); } @@ -673,16 +675,16 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, options.forEach((status) => { statusMenu.addItem((item) => { // Render status with markdown formatting - MarkdownUIRenderer.renderSubtaskText( + void MarkdownUIRenderer.safeRender( plugin.app, `- [${status.value}] ${status.label}`, item.titleEl, '', - null + parentComponent ); item.onClick(() => { - updateTaskItemStatus(plugin, task, task, status.value); + void updateTaskItemStatus(plugin, task, task, status.value); }); }); }); @@ -726,8 +728,8 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, taskItemMenu.addItem((it) => { it.setIcon("calendar-plus") it.setTitle(t("start-date")); - it.onClick(async () => { - openDateInputModal(plugin, t("start"), (newDate: string) => { + it.onClick(() => { + void openDateInputModal(plugin, t("start"), (newDate: string) => { updateTaskItemDate(plugin, task, task, UniversalDateOptions.startDate, newDate); }, task.startDate) }); @@ -735,8 +737,8 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, taskItemMenu.addItem((it) => { it.setIcon("calendar-clock") it.setTitle(t("scheduled-date")); - it.onClick(async () => { - openDateInputModal(plugin, t("scheduled"), (newDate: string) => { + it.onClick(() => { + void openDateInputModal(plugin, t("scheduled"), (newDate: string) => { updateTaskItemDate(plugin, task, task, UniversalDateOptions.scheduledDate, newDate); }, task.scheduledDate) }); @@ -744,8 +746,8 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, taskItemMenu.addItem((it) => { it.setIcon("calendar") it.setTitle(t("due-date")); - it.onClick(async () => { - openDateInputModal(plugin, t("due"), (newDate: string) => { + it.onClick(() => { + void openDateInputModal(plugin, t("due"), (newDate: string) => { updateTaskItemDate(plugin, task, task, UniversalDateOptions.dueDate, newDate); }, task.due) }); @@ -755,7 +757,7 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, taskItemMenu.addItem((item) => { item.setIcon("clock"); item.setTitle(t("reminder")); - item.onClick(async () => { + item.onClick(() => { const modal = new DateTimePickerModal(plugin, t("reminder"), task.reminder); modal.onDateTimeSelected = (dateTime) => { // e.g., "2024-01-15T14:30" or "14:30" updateTaskItemReminder(plugin, task, task, dateTime); @@ -773,9 +775,9 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, taskItemMenu.addItem((item) => { item.setIcon("copy"); item.setTitle(t("copy-task-title")); - item.onClick(async () => { + item.onClick(() => { try { - await navigator.clipboard.writeText(cleanTaskTitleLegacy(task)); + void navigator.clipboard.writeText(cleanTaskTitleLegacy(task)); new Notice(t("copy-task-title-successful")); } catch (error) { new Notice(t("copy-task-title-unsuccessful")); @@ -785,7 +787,7 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, taskItemMenu.addItem((item) => { item.setIcon("square-pen"); item.setTitle(t("open-task-editor")); - item.onClick(async () => { + item.onClick(() => { handleEditTask(plugin, task, EditButtonMode.Modal); }); }); @@ -816,14 +818,14 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, taskItemMenu.addItem((item) => { item.setIcon("file-input"); item.setTitle(t("open-note")); - item.onClick(async () => { + item.onClick(() => { handleEditTask(plugin, task, EditButtonMode.NoteInTab) }); }); taskItemMenu.addItem((item) => { item.setIcon("columns-2"); item.setTitle(t("open-note-to-right")); - item.onClick(async () => { + item.onClick(() => { handleEditTask(plugin, task, EditButtonMode.NoteInSplit) }); }); @@ -833,7 +835,7 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, item.setIcon("file-text"); item.setTitle(t("more-note-actions")); - const submenu = (item as any).setSubmenu(); + const submenu = item.setSubmenu(); // Get the file for the task const file = plugin.app.vault.getAbstractFileByPath(task.filePath); @@ -850,32 +852,33 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, submenu.addItem((subItem: MenuItem) => { subItem.setIcon("pencil"); subItem.setTitle(t("rename-note")); - subItem.onClick(async () => { + subItem.onClick(() => { try { // Modal-based rename const currentName = file.basename; - const newName = await showTextInputModal(plugin.app, { + void showTextInputModal(plugin.app, { title: t("rename-note"), placeholder: t("rename-note-placeholder"), initialValue: currentName, - }); - - if (newName && newName.trim() !== "" && newName !== currentName) { - // Ensure the new name has the correct extension - const extension = file.extension; - const finalName = newName.endsWith(`.${extension}`) - ? newName - : `${newName}.${extension}`; - - // Construct the new path - const newPath = file.parent - ? `${file.parent.path}/${finalName}` - : finalName; - - // Rename the file - await plugin.app.vault.rename(file, newPath); - new Notice("File renamed successfully."); - } + }).then((newName: string | null) => { + if (newName && newName.trim() !== "" && newName !== currentName) { + // Ensure the new name has the correct extension + const extension = file.extension; + const finalName = newName.endsWith(`.${extension}`) + ? newName + : `${newName}.${extension}`; + + // Construct the new path + const newPath = file.parent + ? `${file.parent.path}/${finalName}` + : finalName; + + // Rename the file + void plugin.app.vault.rename(file, newPath); + new Notice("File renamed successfully."); + } + }) + } catch (error) { new Notice("There was an error while renaming the file."); bugReporterManagerInsatance.addToLogs( @@ -890,8 +893,8 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, submenu.addItem((subItem: MenuItem) => { subItem.setIcon("trash"); subItem.setTitle(t("delete-note")); - subItem.onClick(async () => { - plugin.app.vault.trash(file, true).then(() => { + subItem.onClick(() => { + void plugin.app.fileManager.trashFile(file).then(() => { new Notice("File deleted successfully. Moved to system trash."); }) // handleDeleteTask(plugin, task, true); @@ -927,7 +930,7 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, swimlaneData: swimlaneData }; // Delegate to manager for standardized behavior (sets current payload and dims element) - dragDropTasksManagerInsatance.handleDragStartEvent(e.nativeEvent as DragEvent, el, payload); + dragDropTasksManagerInsatance.handleDragStartEvent(e.nativeEvent, el, payload); } catch (err) { // fallback minimal behavior // try { @@ -1273,9 +1276,9 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, // Effect to load child tasks asynchronously useEffect(() => { if (task?.dependsOn && task.dependsOn.length > 0) { - const loadChildTasks = async () => { + const loadChildTasks = () => { const childTasksMap: Record = {}; - await Promise.all((task?.dependsOn ?? []).map(async (dependsOnId) => { + void Promise.all((task?.dependsOn ?? []).map(async (dependsOnId) => { const childTask = await getTaskFromId(plugin, dependsOnId); childTasksMap[dependsOnId] = childTask; })); @@ -1307,7 +1310,7 @@ const TaskItemV2: React.FC = ({ dataAttributeIndex, plugin, task, // Simple version just showing the ID and a symbol return (
-
handleOpenChildTaskModal(event, dependsOnId)}> +
void handleOpenChildTaskModal(event, dependsOnId)}> {isChildTaskCompleted ? TASKS_PLUGIN_DEFAULT_SYMBOLS.dependsOnCompletedSymbol : TASKS_PLUGIN_DEFAULT_SYMBOLS.dependsOnSymbol} = ({ dataAttributeIndex, plugin, task, className={`taskItemCheckbox${cardLoadingAnimation ? '-checked' : ''}`} data-task={cardLoadingAnimation ? 'x' : task.status} dir='auto' - onChange={handleMainCheckBoxClick} + onChange={() => handleMainCheckBoxClick} onClick={(e) => { if (cardLoadingAnimation) { e.preventDefault(); diff --git a/src/components/TaskEditorRC.tsx b/src/components/TaskEditorRC.tsx index 8a917921..1d19cc6b 100644 --- a/src/components/TaskEditorRC.tsx +++ b/src/components/TaskEditorRC.tsx @@ -9,7 +9,7 @@ import { DeleteIcon, EditIcon, FileInput, Network, PanelRightOpenIcon } from "lu import { ViewUpdate } from "@codemirror/view"; import { RxDragHandleHorizontal } from "react-icons/rx"; import TaskBoard from "../../main.js"; -import { statusTypeNames, UniversalDateOptions, viewTypeNames, EditButtonMode, NotificationService } from "../interfaces/Enums.js"; +import { statusTypeNames, UniversalDateOptions, EditButtonMode, NotificationService } from "../interfaces/Enums.js"; import { taskItemEmpty, getPriorityOptionsForDropdown } from "../interfaces/Mapping.js"; import { taskItem, cursorLocationInterface } from "../interfaces/TaskItem.js"; import { bugReporterManagerInsatance } from "../managers/BugReporter.js"; @@ -24,7 +24,7 @@ import { getTagSuggestions, MultiSuggest, getQuickAddPluginChoices, getFileSugge import { openEditTaskView } from "../services/OpenModals.js"; import { compareTwoTags, verifySubtasksAndChildtasksAreComplete } from "../utils/algorithms/ScanningFilterer.js"; import { getObsidianIndentationSetting, isTaskLine } from "../utils/CheckBoxUtils.js"; -import { applyIdToTaskItem, getTaskFromId } from "../utils/TaskItemUtils.js"; +import { applyIdToTaskItem } from "../utils/TaskItemUtils.js"; import { getFormattedTaskContentSync, cleanTaskTitleLegacy, sanitizeStatus, sanitizeCreatedDate, sanitizeStartDate, sanitizeScheduledDate, sanitizeDueDate, sanitizeReminder, sanitizePriority, sanitizeTime, sanitizeTags, sanitizeDependsOn } from "../utils/taskLine/TaskContentFormatter.js"; import { formatTaskNoteContent, isTaskNotePresentInTags } from "../utils/taskNote/TaskNoteUtils.js"; import { updateRGBAOpacity } from "../utils/UIHelpers.js"; @@ -47,7 +47,7 @@ export const TaskEditorRC: React.FC<{ noteContent: string; task?: taskItem, taskExists?: boolean, - onSave: (updatedTask: taskItem, quickAddPluginChoice: string, updatedNoteContent?: string) => void; + onSave: (updatedTask: taskItem, quickAddPluginChoice: string, updatedNoteContent?: string) => Promise; onClose: () => void; setIsEdited: (value: boolean) => void; }> = ({ plugin, root, isTaskNote, noteContent, task = taskItemEmpty, taskExists, activeNote, filePath, onSave, onClose, setIsEdited }) => { @@ -80,7 +80,7 @@ export const TaskEditorRC: React.FC<{ const [quickAddPluginChoice, setQuickAddPluginChoice] = useState(globalSettings.quickAddPluginDefaultChoice || ''); const [markdownEditor, setMarkdownEditor] = useState(null); - const [isEditorContentChanged, setIsEditorContentChanged] = useState(true); + const [isEditorContentChanged, setIsEditorContentChanged] = useState(true); const cursorLocationRef = useRef(null); const indentationString = getObsidianIndentationSetting(plugin); @@ -97,11 +97,11 @@ export const TaskEditorRC: React.FC<{ useEffect(() => { if (isRightSecVisible) { - document.addEventListener("mousedown", handleClickOutside); + activeDocument.addEventListener("mousedown", handleClickOutside); } else { - document.removeEventListener("mousedown", handleClickOutside); + activeDocument.removeEventListener("mousedown", handleClickOutside); } - return () => document.removeEventListener("mousedown", handleClickOutside); + return () => activeDocument.removeEventListener("mousedown", handleClickOutside); }, [isRightSecVisible]); // Load statuses dynamically @@ -132,7 +132,7 @@ export const TaskEditorRC: React.FC<{ // Clear previous content before rendering new markdown titleComponentRef.current.empty(); - MarkdownUIRenderer.renderTaskDisc( + void MarkdownUIRenderer.strictRender( plugin.app, cleanedTaskTitle, titleComponentRef.current, @@ -439,7 +439,7 @@ export const TaskEditorRC: React.FC<{ setIsEditorContentChanged(true); // Clear the input field after MultiSuggest has finished processing - setTimeout(() => { + window.setTimeout(() => { if (tagsInputFieldRef.current) { tagsInputFieldRef.current.value = ''; } @@ -519,14 +519,14 @@ export const TaskEditorRC: React.FC<{ tags, time: newTime, priority, - filePath: newFilePath, + filePath: editedFilePath, taskLocation: task.taskLocation, cancelledDate: task.cancelledDate || '', status, reminder, }; - onSave(updatedTask, quickAddPluginChoice); + void onSave(updatedTask, quickAddPluginChoice); // onClose(); } @@ -578,7 +578,7 @@ export const TaskEditorRC: React.FC<{ const newFormattedNoteContent = formatTaskNoteContent(plugin, taskNoteItem, formattedTaskContent); // Call onSave with the task note item - onSave(taskNoteItem, quickAddPluginChoice, newFormattedNoteContent.newContent ? newFormattedNoteContent.newContent : undefined); + void onSave(taskNoteItem, quickAddPluginChoice, newFormattedNoteContent.newContent ? newFormattedNoteContent.newContent : undefined); }; let modifiedTask: taskItem = { @@ -636,22 +636,28 @@ export const TaskEditorRC: React.FC<{ } }, [plugin.app]); - const handleOpenTaskInMapView = () => { + const handleOpenTaskInMapView = async () => { // if (!globalSettings.experimentalFeatures) { // new Notice(t("enable-experimental-features-message")); // return; // } - applyIdToTaskItem(plugin, task).then((newId) => { + await applyIdToTaskItem(plugin, task).then((newId) => { plugin.settings.data.lastViewHistory.taskId = newId ? String(newId) : (task.legacyId ? task.legacyId : String(globalSettings.uniqueIdCounter)); // console.log("Preparing to open task in kanban view. Current file path:", newFilePath, "\nTask ID:", task.id, "\nLegacy ID:", task.legacyId, "\nnewId:", newId); plugin.realTimeScanner.processAllUpdatedFiles(filePath).then(() => { onClose(); - sleep(1000).then(() => { + window.setTimeout(() => { eventEmitter.emit("SWITCH_VIEW", 'first-map'); - }); + }, 1000) + }).catch((error) => { + bugReporterManagerInsatance.addToLogs( + 217, + String(error), + "TaskEditorRC.tss/handleOpenTaskInMapView", + ); }); }); @@ -726,7 +732,7 @@ export const TaskEditorRC: React.FC<{ const componentRef = useRef(null); // Reference to the HTML element where markdown will be rendered useEffect(() => { // Initialize Obsidian Component on mount - componentRef.current = plugin.view; + componentRef.current = null; // @todo - Assign the current Modal parent as component to maintain its lifecycle. }, []); // TODO : This function should be optimized to avoid excessive parsing on every keystroke. @@ -943,7 +949,7 @@ export const TaskEditorRC: React.FC<{ useEffect(() => { if (isEditorContentChanged) { if (isTaskNote) { - const { newContent, newFrontmatter, contentWithoutFrontmatter } = formatTaskNoteContent(plugin, modifiedTask, formattedTaskContent); + const { newContent, newFrontmatter } = formatTaskNoteContent(plugin, modifiedTask, formattedTaskContent); const newFormattedTaskNoteContent = newContent; // console.log("Updating embedded markdown editor for task note with content:\n", newFormattedTaskNoteContent, // "\nFrontmatter:\n", newFrontmatter, "\nContent without frontmatter:\n", contentWithoutFrontmatter @@ -978,9 +984,8 @@ export const TaskEditorRC: React.FC<{ const handleOpenChildTaskSelector = async (): Promise => { try { - let pendingTaskItems = getPendingTasksSuggestions(plugin).filter(t => t.id !== task.id); - pendingTaskItems = pendingTaskItems.filter((t) => !dependsOn.includes(t.legacyId)); - openTaskSelector(plugin, pendingTaskItems, async (selected) => { + + const handleOnChooseTask = async (selected: taskItem | null) => { if (!selected) return; const newId = await applyIdToTaskItem(plugin, selected); @@ -1026,6 +1031,12 @@ export const TaskEditorRC: React.FC<{ // .catch(err => { // bugReporterManagerInsatance.showNotice(25, "Error updating task in file", `An error occurred while updating the task in file: ${err.message}`, "AddOrEditTaskModal.tsx/handleOpenChildTaskSelector"); // }); + } + + let pendingTaskItems = getPendingTasksSuggestions(plugin).filter(t => t.id !== task.id); + pendingTaskItems = pendingTaskItems.filter((t) => !dependsOn.includes(t.legacyId)); + openTaskSelector(plugin, pendingTaskItems, (selected) => { + void handleOnChooseTask(selected); }, { placeholder: t("search-child-task"), title: t("select-child-task") @@ -1061,7 +1072,7 @@ export const TaskEditorRC: React.FC<{ if (!element) return; const childTaskTitle = childTask.title; - MarkdownUIRenderer.renderSubtaskText( + void MarkdownUIRenderer.safeRender( plugin.app, childTaskTitle, element, @@ -1101,9 +1112,11 @@ export const TaskEditorRC: React.FC<{ case EditButtonMode.ViewInWindow: case EditButtonMode.TasksPluginModal: default: - const isTaskNotePresent = isTaskNotePresentInTags(globalSettings.taskNoteIdentifierTag, childTask.tags); - openEditTaskView(plugin, isTaskNotePresent, false, true, childTask, childTask.filePath, "window"); - break; + { + const isTaskNotePresent = isTaskNotePresentInTags(globalSettings.taskNoteIdentifierTag, childTask.tags); + void openEditTaskView(plugin, isTaskNotePresent, false, true, childTask, childTask.filePath, "window"); + break; + } } // if (settingOption !== EditButtonMode.NoteInHover && settingOption !== EditButtonMode.Modal) { @@ -1249,14 +1262,14 @@ export const TaskEditorRC: React.FC<{ {taskExists && (
@@ -1302,7 +1315,7 @@ export const TaskEditorRC: React.FC<{
handleOpenChildTaskSelector()} + onClick={() => { void handleOpenChildTaskSelector(); }} aria-label={t("child-tasks-section-description")} > {t("add-child-task")} @@ -1321,7 +1334,7 @@ export const TaskEditorRC: React.FC<{
{!Platform.isMobileApp && ( - + )}
@@ -1356,7 +1369,7 @@ export const TaskEditorRC: React.FC<{ {/* Task Status */}
- void handleStatusChange(e.target.value)}> {filteredStatusesDropdown.map((option) => ( ))} @@ -1447,14 +1460,14 @@ export const TaskEditorRC: React.FC<{ /> {/* Render tags with cross icon */}
- {tags.map((tag: string) => { + {tags.map((tag: string, index: number) => { const customTagData = globalSettings.tagColors.find(t => compareTwoTags(t.name, tag)); const tagColor = customTagData?.color; const backgroundColor = tagColor ? updateRGBAOpacity(tagColor, 0.1) : `var(--tag-background)`; const borderColor = tagColor ? updateRGBAOpacity(tagColor, 0.5) : `var(--tag-color-hover)`; return (
=', 'isEmpty' - value?: any; // Value for the condition, type depends on property and operator + value?: unknown; // Value for the condition, type depends on property and operator } // Represents a group of filter conditions in the UI from focus.md diff --git a/src/interfaces/Constants.ts b/src/interfaces/Constants.ts index 99e90c35..8d8d52b4 100644 --- a/src/interfaces/Constants.ts +++ b/src/interfaces/Constants.ts @@ -1,4 +1,4 @@ -export const CURRENT_PLUGIN_VERSION = "2.0.0-beta-4"; +export const CURRENT_PLUGIN_VERSION = "2.0.0-beta-5"; export const CURRENT_REVISION = 4; // Plugin view type identifiers export const VIEW_TYPE_TASKBOARD = "task-board-view"; diff --git a/src/interfaces/Enums.ts b/src/interfaces/Enums.ts index a471f86e..4668c87f 100644 --- a/src/interfaces/Enums.ts +++ b/src/interfaces/Enums.ts @@ -104,20 +104,27 @@ export enum viewTypeNames { export enum defaultTaskStatuses { unchecked = " ", + // eslint-disable-next-line @typescript-eslint/no-duplicate-enum-values incomplete = " ", + // eslint-disable-next-line @typescript-eslint/no-duplicate-enum-values pending = " ", + // eslint-disable-next-line @typescript-eslint/no-duplicate-enum-values todo = " ", regular = "x", checked = "X", + // eslint-disable-next-line @typescript-eslint/no-duplicate-enum-values done = "x", dropped = "-", forward = ">", migrated = "<", + // eslint-disable-next-line @typescript-eslint/no-duplicate-enum-values rescheduled = ">", + // eslint-disable-next-line @typescript-eslint/no-duplicate-enum-values scheduled = "<", date = "D", question = "?", halfDone = "/", + // eslint-disable-next-line @typescript-eslint/no-duplicate-enum-values inprogress = "/", add = "+", research = "R", @@ -150,6 +157,9 @@ export enum defaultTaskStatuses { secret = "s", } +/** + * Collection of status types supported by the plugin. + */ export enum statusTypeNames { TODO = "TODO", DONE = "DONE", diff --git a/src/interfaces/GlobalSettings.ts b/src/interfaces/GlobalSettings.ts index ccce0bd8..739a426a 100644 --- a/src/interfaces/GlobalSettings.ts +++ b/src/interfaces/GlobalSettings.ts @@ -17,6 +17,7 @@ import { mapViewBackgrounVariantTypes, mapViewNodeMapOrientation, RibbonIconActions, + statusTypeNames, } from "./Enums.js"; import { taskItemKeyToNameMapping } from "./Mapping.js"; @@ -50,7 +51,7 @@ export interface CustomStatus { name: string; // The human-readable name of the status (e.g., "In Progress") nextStatusSymbol: string; // The symbol representing the next status in the workflow (e.g., "x") availableAsCommand: boolean; // Whether this status is available as a command in Obsidian - type: string; // The type/category of the status (e.g., "IN_PROGRESS", "CANCELLED") + type: statusTypeNames; // The type/category of the status (e.g., "IN_PROGRESS", "CANCELLED") } export interface TaskBoardAction { @@ -82,7 +83,7 @@ export interface globalSettingsData { scanFilters: ScanFilters; firstDayOfWeek?: string; ignoreFileNameDates: boolean; - taskPropertyFormat: string; + taskPropertyFormat: taskPropertyFormatOptions; dailyNotesPluginComp: boolean; dateFormat: string; dateTimeFormat: string; @@ -95,16 +96,16 @@ export interface globalSettingsData { autoAddCancelledDate: boolean; // scanVaultAtStartup: boolean; - @deprecated v1.9.0 - A better approach has been used using showModifiedFilesNotice feature. showModifiedFilesNotice: boolean; - scanMode: string; + scanMode: scanModeOptions; columnWidth: string; visiblePropertiesList: string[]; - taskCardStyle: string; + taskCardStyle: taskCardStyleNames; showVerticalScroll: boolean; dragAutoScrollEdgePercent: number; tagColors: TagColor[]; editButtonAction: EditButtonMode; doubleClickCardToEdit: EditButtonMode; - universalDate: string; + universalDate: UniversalDateOptions; customStatuses: CustomStatus[]; showTaskWithoutMetadata: boolean; tagColorsType: TagColorType; @@ -124,7 +125,7 @@ export interface globalSettingsData { frontmatterFormatting: FrontmatterFormattingInterface[]; showFrontmatterTagsOnCards: boolean; tasksCacheFilePath: string; - notificationService: string; + notificationService: NotificationService; actions: TaskBoardAction[]; searchQuery?: string; hiddenTaskProperties: taskPropertiesNames[]; @@ -132,7 +133,6 @@ export interface globalSettingsData { uniqueIdCounter: number; // Counter to generate unique IDs for tasks. This will keep track of the last used ID. experimentalFeatures: boolean; safeGuardFeature: boolean; - taskBoardFilesRegistry: taskBoardFilesRegistryType; lastViewHistory: { boardFilePath: string; settingTab: number; @@ -140,8 +140,8 @@ export interface globalSettingsData { }; boundTaskCompletionToChildTasks: boolean; mapView: { - background: string; - mapOrientation: string; + background: mapViewBackgrounVariantTypes; + mapOrientation: mapViewNodeMapOrientation; optimizedRender: boolean; arrowDirection: mapViewArrowDirection; animatedEdges: boolean; @@ -150,9 +150,11 @@ export interface globalSettingsData { renderVisibleNodes: boolean; edgeType: mapViewEdgeType; }; + // New settings introduced in 2.x.x series + taskBoardFilesRegistry: taskBoardFilesRegistryType; enableDragnDropTouch: boolean; filtersWarehouse: Filter[]; - ribbonIconAction: string; + ribbonIconAction: RibbonIconActions; } // Define the interface for GlobalSettings based on your JSON structure @@ -261,49 +263,49 @@ export const DEFAULT_SETTINGS: PluginDataJson = { name: "Todo", nextStatusSymbol: defaultTaskStatuses.done, availableAsCommand: false, - type: "TODO", + type: statusTypeNames.TODO, }, { symbol: defaultTaskStatuses.scheduled, name: "Ready to start", nextStatusSymbol: defaultTaskStatuses.done, availableAsCommand: false, - type: "TODO", + type: statusTypeNames.TODO, }, { symbol: defaultTaskStatuses.question, name: "In Review", nextStatusSymbol: defaultTaskStatuses.done, availableAsCommand: false, - type: "TODO", + type: statusTypeNames.TODO, }, { symbol: defaultTaskStatuses.inprogress, name: "In Progress", nextStatusSymbol: defaultTaskStatuses.done, availableAsCommand: true, - type: "IN_PROGRESS", + type: statusTypeNames.IN_PROGRESS, }, { symbol: defaultTaskStatuses.done, name: "Done", nextStatusSymbol: defaultTaskStatuses.todo, availableAsCommand: true, - type: "DONE", + type: statusTypeNames.DONE, }, { symbol: defaultTaskStatuses.checked, name: "Completed", nextStatusSymbol: defaultTaskStatuses.todo, availableAsCommand: true, - type: "DONE", + type: statusTypeNames.DONE, }, { symbol: defaultTaskStatuses.dropped, name: "Cancelled", nextStatusSymbol: defaultTaskStatuses.done, availableAsCommand: true, - type: "CANCELLED", + type: statusTypeNames.CANCELLED, }, ], compatiblePlugins: { diff --git a/src/interfaces/Mapping.ts b/src/interfaces/Mapping.ts index f2e7ef37..9e96525b 100644 --- a/src/interfaces/Mapping.ts +++ b/src/interfaces/Mapping.ts @@ -1,6 +1,6 @@ import { bugReporterManagerInsatance } from "../managers/BugReporter.js"; import { t } from "../utils/lang/helper.js"; -import { defaultTaskStatuses } from "./Enums.js"; +import { defaultTaskStatuses, statusTypeNames } from "./Enums.js"; import { CustomStatus } from "./GlobalSettings.js"; import { taskItem } from "./TaskItem.js"; @@ -125,12 +125,12 @@ export const getCustomStatusOptionsForDropdown = ( typeof status.symbol === "string" && typeof status.name === "string" && status.name.trim() !== "" && - typeof status.type === "string"; + typeof status.type === typeof statusTypeNames; if (!isValid) { bugReporterManagerInsatance.addToLogs( 190, - `Invalid status at index ${index}: ${status}`, + `Invalid status at index ${index}: ${JSON.stringify(status)}`, "Mapping.ts/getCustomStatusOptionsForDropdown", ); } @@ -206,7 +206,7 @@ export const getCustomStatusOptionsForDropdown = ( } return acc; }, - [] as Array<{ type: string; statuses: CustomStatus[] }>, + [] as Array<{ type: statusTypeNames; statuses: CustomStatus[] }>, ); const groups: GroupedStatusOptions[] = grouped.map((group) => ({ diff --git a/src/interfaces/StatusConfiguration.ts b/src/interfaces/StatusConfiguration.ts index e1f9d7c5..3575d55e 100644 --- a/src/interfaces/StatusConfiguration.ts +++ b/src/interfaces/StatusConfiguration.ts @@ -1,15 +1,4 @@ -/** - * Collection of status types supported by the plugin. - */ -export enum StatusType { - TODO = 'TODO', - DONE = 'DONE', - IN_PROGRESS = 'IN_PROGRESS', - ON_HOLD = 'ON_HOLD', - CANCELLED = 'CANCELLED', - NON_TASK = 'NON_TASK', - EMPTY = 'EMPTY', -} +import { statusTypeNames } from "./Enums.js"; /** * This is the object stored by the Obsidian configuration and used to create the status @@ -18,60 +7,60 @@ export enum StatusType { * @class StatusConfiguration */ export class StatusConfiguration { - /** - * The character used between the two square brackets in the markdown task. - * - * @type {string} - */ - public readonly symbol: string; + /** + * The character used between the two square brackets in the markdown task. + * + * @type {string} + */ + public readonly symbol: string; - /** - * Returns the name of the status for display purposes. - * - * @type {string} - */ - public readonly name: string; + /** + * Returns the name of the status for display purposes. + * + * @type {string} + */ + public readonly name: string; - /** - * Returns the next status for a task when toggled. - * - * @type {string} - */ - public readonly nextStatusSymbol: string; + /** + * Returns the next status for a task when toggled. + * + * @type {string} + */ + public readonly nextStatusSymbol: string; - /** - * If true then it is registered as a command that the user can map to. - * - * @type {boolean} - */ - public readonly availableAsCommand: boolean; + /** + * If true then it is registered as a command that the user can map to. + * + * @type {boolean} + */ + public readonly availableAsCommand: boolean; - /** - * Returns the status type. See {@link StatusType} for details. - */ - public readonly type: StatusType; + /** + * Returns the status type. See {@link StatusType} for details. + */ + public readonly type: statusTypeNames; - /** - * Creates an instance of Status. The registry will be added later in the case - * of the default statuses. - * - * @param {string} symbol - * @param {string} name - * @param {Status} nextStatusSymbol - * @param {boolean} availableAsCommand - * @param {StatusType} type - */ - constructor( - symbol: string, - name: string, - nextStatusSymbol: string, - availableAsCommand: boolean, - type: StatusType = StatusType.TODO, // TODO Remove default value - ) { - this.symbol = symbol; - this.name = name; - this.nextStatusSymbol = nextStatusSymbol; - this.availableAsCommand = availableAsCommand; - this.type = type; - } + /** + * Creates an instance of Status. The registry will be added later in the case + * of the default statuses. + * + * @param {string} symbol + * @param {string} name + * @param {Status} nextStatusSymbol + * @param {boolean} availableAsCommand + * @param {statusTypeNames} type + */ + constructor( + symbol: string, + name: string, + nextStatusSymbol: string, + availableAsCommand: boolean, + type: statusTypeNames = statusTypeNames.TODO, // TODO Remove default value + ) { + this.symbol = symbol; + this.name = name; + this.nextStatusSymbol = nextStatusSymbol; + this.availableAsCommand = availableAsCommand; + this.type = type; + } } diff --git a/src/interfaces/TaskItem.ts b/src/interfaces/TaskItem.ts index cb267c27..48289cbf 100644 --- a/src/interfaces/TaskItem.ts +++ b/src/interfaces/TaskItem.ts @@ -46,7 +46,7 @@ export interface customFrontmatterCache extends FrontMatterCache { export interface noteItem { filePath: string; - frontmatter: any; // The frontmatter of the note + frontmatter: unknown; // The frontmatter of the note reminder: string; // A date-time value. } diff --git a/src/managers/BugReporter.ts b/src/managers/BugReporter.ts index cfb30f3a..83dcf87b 100644 --- a/src/managers/BugReporter.ts +++ b/src/managers/BugReporter.ts @@ -6,6 +6,7 @@ import { BugReporterModal } from "../modals/BugReporterModal.js"; import { fsPromises } from "../services/FileSystem.js"; import { getObsidianDebugInfo } from "../services/ObsidianDebugInfo.js"; import { getCurrentLocalDateTimeString } from "../utils/DateTimeCalculations.js"; +import { type ElectronOpenDialogReturnValue } from "obsidian-typings"; /** * Interface for bug report entries @@ -32,7 +33,7 @@ class BugReporterManager { private alreadyShownBugsIDs: number[] = []; private LOG_FILE_PATH = ""; private readonly MAX_RECENT_LOGS = 20; - private readonly MAX_USED_ID = 216; // This constant will not be used anywhere, its simply to keep track of the the recent ID used. + private readonly MAX_USED_ID = 221; // This constant will not be used anywhere, its simply to keep track of the the recent ID used. private constructor() { // Private constructor to enforce singleton pattern @@ -95,15 +96,15 @@ class BugReporterManager { /** * Format system information for the log file */ - private formatSystemInfo(systemInfo: Record): string { + private formatSystemInfo(systemInfo: Record): string { return Object.entries(systemInfo) .map(([key, value]) => { if (Array.isArray(value)) { return `- **${key}**: \n${value - .map((v) => ` - ${v}`) + .map((v: unknown) => ` - ${String(v)}`) .join("\n")}`; } - return `- **${key}**: ${value}`; + return `- **${key}**: ${String(value)}`; }) .join("\n"); } @@ -347,7 +348,7 @@ ${entry.bugContent} }); // STEP 3 - Append the bug report to the task-board-logs.md file - this.appendBugReport(id, message, bugContent, context); + void this.appendBugReport(id, message, bugContent, context); }; /** @@ -364,7 +365,7 @@ ${entry.bugContent} this.alreadyShownBugsIDs.push(id); // STEP 3 - Append the bug report to the task-board-logs.md file - this.appendBugReport(id, "", bugContent, context); + void this.appendBugReport(id, "", bugContent, context); }; async exportLogFile(): Promise { @@ -380,21 +381,25 @@ ${entry.bugContent} // Desktop folder picker if ( - (window as any).electron && - (window as any).electron.remote && - (window as any).electron.remote.dialog + window?.electron && + window.electron?.remote && + window.electron.dialog ) { - let folderPaths: string[] = ( - window as any - ).electron.remote.dialog.showOpenDialogSync({ - title: "Pick folder to export settings", - properties: ["openDirectory", "dontAddToRecent"], - }); - if (!folderPaths || folderPaths.length === 0) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call + let pickFolderDialogReturn: ElectronOpenDialogReturnValue = + await window.electron.dialog.showOpenDialog({ + title: "Pick folder to export log file", + properties: ["openDirectory", "dontAddToRecent"], + }); + if ( + pickFolderDialogReturn.canceled || + !pickFolderDialogReturn?.filePaths || + pickFolderDialogReturn.filePaths.length === 0 + ) { new Notice("Export cancelled or folder not selected."); return; } - const folderPath = folderPaths[0]; + const folderPath = pickFolderDialogReturn.filePaths[0]; const exportPath = folderPath.endsWith("/") || folderPath.endsWith("\\") ? folderPath + exportFileName @@ -405,15 +410,15 @@ ${entry.bugContent} new Notice(`Log file exported to ${exportPath}`); } else { // Web: use file save dialog - let a = document.createElement("a"); + let a = activeDocument.createElement("a"); a.href = URL.createObjectURL( new Blob([data], { type: "application/json" }), ); a.download = exportFileName; - document.body.appendChild(a); + activeDocument.body.appendChild(a); a.click(); - setTimeout(() => { - document.body.removeChild(a); + window.setTimeout(() => { + activeDocument.body.removeChild(a); URL.revokeObjectURL(a.href); }, 1000); new Notice( @@ -423,7 +428,7 @@ ${entry.bugContent} } catch (err) { bugReporterManagerInsatance.addToLogs( 198, - `Failed to export the log file: ${err}`, + `Failed to export the log file: ${String(err)}`, "BugReporter.ts/exportLogFile", ); } diff --git a/src/managers/DragDropTasksManager.ts b/src/managers/DragDropTasksManager.ts index a8c70a11..2e01950f 100644 --- a/src/managers/DragDropTasksManager.ts +++ b/src/managers/DragDropTasksManager.ts @@ -234,16 +234,20 @@ class DragDropTasksManager { this.autoScrollLastY = e.clientY; }; this.autoScrollDragOverHandler = trackPosition; - document.addEventListener( + activeDocument.addEventListener( "dragover", trackPosition as EventListener, true, ); // Auto-cleanup on drag end - document.addEventListener("dragend", () => this.stopAutoScroll(), { - once: true, - }); + activeDocument.addEventListener( + "dragend", + () => this.stopAutoScroll(), + { + once: true, + }, + ); this.startAutoScrollLoop(); } @@ -255,7 +259,7 @@ class DragDropTasksManager { this.isAutoScrolling = false; this.autoScrollActive = false; if (this.autoScrollDragOverHandler) { - document.removeEventListener( + activeDocument.removeEventListener( "dragover", this.autoScrollDragOverHandler as EventListener, true, @@ -304,7 +308,7 @@ class DragDropTasksManager { return; } - const boardEl = document.querySelector(".kanbanBoard"); + const boardEl = activeDocument.querySelector(".kanbanBoard"); if (!boardEl) { this.autoScrollRAFId = window.requestAnimationFrame(step); return; @@ -336,12 +340,12 @@ class DragDropTasksManager { // Gather all plausible scroll containers (safe to try all — out-of-range scroll is a no-op) const scrollTargets: HTMLElement[] = []; - const columnsContainer = document.querySelector( + const columnsContainer = activeDocument.querySelector( ".columnsContainer", ) as HTMLElement; if (columnsContainer) scrollTargets.push(columnsContainer); - const swimlanesContainer = document.querySelector( + const swimlanesContainer = activeDocument.querySelector( ".swimlanesContainer", ) as HTMLElement; if (swimlanesContainer) scrollTargets.push(swimlanesContainer); @@ -408,7 +412,7 @@ class DragDropTasksManager { targetColumnSwimlaneData: swimlaneDataProp | undefined, ): Promise => { // This means, user either wants to change the order of the taskItems within the column or is changing the swimlanes. - this.handleTasksOrderChange( + void this.handleTasksOrderChange( this.plugin!, currentDragData, sourceColumnData, @@ -439,24 +443,39 @@ class DragDropTasksManager { ); if (isThisTaskNote) { - updateFrontmatterInMarkdownFile(plugin, newTask).then(() => { - sleep(1000).then(() => { - plugin.realTimeScanner.processAllUpdatedFiles( - oldTask.filePath, - oldTask.id, - ); - }); - }); + void updateFrontmatterInMarkdownFile(plugin, newTask).then( + () => { + window.setTimeout(() => { + plugin.realTimeScanner + .processAllUpdatedFiles( + oldTask.filePath, + oldTask.id, + ) + .catch((error) => { + bugReporterManagerInsatance.addToLogs( + 217, + String(error), + "OpenModals.ts/openAddNewTaskInCurrentFileModal", + ); + }); + }, 1000); + }, + ); } else { - updateTaskInFile(plugin, newTask, oldTask).then(() => { - plugin.realTimeScanner.processAllUpdatedFiles( - oldTask.filePath, - oldTask.id, - ); + void updateTaskInFile(plugin, newTask, oldTask).then(() => { + plugin.realTimeScanner + .processAllUpdatedFiles(oldTask.filePath, oldTask.id) + .catch((error) => { + bugReporterManagerInsatance.addToLogs( + 217, + String(error), + "OpenModals.ts/openAddNewTaskInCurrentFileModal", + ); + }); }); } } else { - setTimeout(() => { + window.setTimeout(() => { eventEmitter.emit("REFRESH_BOARD"); }, 200); } @@ -493,13 +512,13 @@ class DragDropTasksManager { } const oldTask = currentDragData.task; - let newTask = { ...oldTask } as taskItem; + let newTask = { ...oldTask }; // ----------------------------------------------- // STEP 1 - If the target column has "manualOrder" sorting criteria, update the task-order-config in the target column. // This is moved above STEP-1 because, the parent function is async. // ---------------------------------------------- - this.handleTasksOrderChange( + void this.handleTasksOrderChange( plugin, currentDragData, targetColumn, @@ -606,9 +625,9 @@ class DragDropTasksManager { } const oldTask = currentDragData.task; - let newTask = { ...oldTask } as taskItem; + let newTask = { ...oldTask }; - this.handleTasksOrderChange( + void this.handleTasksOrderChange( plugin, currentDragData, targetColumn, @@ -656,22 +675,18 @@ class DragDropTasksManager { // const datePicker = new DatePickerModal(plugin, dateType); // datePicker.onDateSelected = async (date: string | null) => {}; - openDateInputModal( - plugin, - dateType, - async (date: string | null) => { - if (date) { - // newTask[dateType] = date; - updateTaskItemDate( - plugin, - oldTask, - newTask, - dateType, - date, - ); - } - }, - ); + void openDateInputModal(plugin, dateType, (date: string | null) => { + if (date) { + // newTask[dateType] = date; + updateTaskItemDate( + plugin, + oldTask, + newTask, + dateType, + date, + ); + } + }); // datePicker.open(); } else { @@ -712,9 +727,9 @@ class DragDropTasksManager { } const oldTask = currentDragData.task; - let newTask = { ...oldTask } as taskItem; + let newTask = { ...oldTask }; - this.handleTasksOrderChange( + void this.handleTasksOrderChange( plugin, currentDragData, targetColumn, @@ -729,7 +744,7 @@ class DragDropTasksManager { ); // Extract the priority value from the source column - const targetColumnPrioirty = (targetColumn.taskPriority as number) || 0; + const targetColumnPrioirty = targetColumn.taskPriority || 0; updateTaskItemPriority(plugin, oldTask, newTask, targetColumnPrioirty); }; @@ -761,9 +776,9 @@ class DragDropTasksManager { } const oldTask = currentDragData.task; - let newTask = { ...oldTask } as taskItem; + let newTask = { ...oldTask }; - this.handleTasksOrderChange( + void this.handleTasksOrderChange( plugin, currentDragData, targetColumn, @@ -778,10 +793,14 @@ class DragDropTasksManager { ); // Extract the status value from the source column - const targetColumnStatusValue = - (targetColumn.taskStatus as string) || ""; + const targetColumnStatusValue = targetColumn.taskStatus || ""; - updateTaskItemStatus(plugin, oldTask, newTask, targetColumnStatusValue); + void updateTaskItemStatus( + plugin, + oldTask, + newTask, + targetColumnStatusValue, + ); }; handleTaskMove_DONE_to_TODO = ( @@ -836,9 +855,9 @@ class DragDropTasksManager { } const oldTask = currentDragData.task; - let newTask = { ...oldTask } as taskItem; + let newTask = { ...oldTask }; - this.handleTasksOrderChange( + void this.handleTasksOrderChange( plugin, currentDragData, targetColumn, @@ -901,22 +920,18 @@ class DragDropTasksManager { // }; // datePicker.open(); - openDateInputModal( - plugin, - dateType, - async (date: string | null) => { - if (date) { - // newTask[dateType] = date; - updateTaskItemDate( - plugin, - oldTask, - newTask, - dateType, - date, - ); - } - }, - ); + void openDateInputModal(plugin, dateType, (date: string | null) => { + if (date) { + // newTask[dateType] = date; + updateTaskItemDate( + plugin, + oldTask, + newTask, + dateType, + date, + ); + } + }); } else { // This code-block should technically not run, since we are not allowing to drop task in dated type column with a range of dates. bugReporterManagerInsatance.showNotice( @@ -953,10 +968,10 @@ class DragDropTasksManager { } const oldTask = currentDragData.task; - let newTask = { ...oldTask } as taskItem; + let newTask = { ...oldTask }; // STEP 1 - Check if the target column has 'manualOrder' sorting criteria. - this.handleTasksOrderChange( + void this.handleTasksOrderChange( plugin, currentDragData, targetColumn, @@ -1017,9 +1032,9 @@ class DragDropTasksManager { } const oldTask = currentDragData.task; - let newTask = { ...oldTask } as taskItem; + let newTask = { ...oldTask }; - this.handleTasksOrderChange( + void this.handleTasksOrderChange( plugin, currentDragData, targetColumn, @@ -1038,7 +1053,7 @@ class DragDropTasksManager { } // Extract the priority value from the source column - const targetColumnPrioirty = (targetColumn.taskPriority as number) || 0; + const targetColumnPrioirty = targetColumn.taskPriority || 0; updateTaskItemPriority(plugin, oldTask, newTask, targetColumnPrioirty); }; @@ -1068,9 +1083,9 @@ class DragDropTasksManager { } const oldTask = currentDragData.task; - let newTask = { ...oldTask } as taskItem; + let newTask = { ...oldTask }; - this.handleTasksOrderChange( + void this.handleTasksOrderChange( plugin, currentDragData, targetColumn, @@ -1089,10 +1104,14 @@ class DragDropTasksManager { } // Extract the status value from the source column - const targetColumnStatusValue = - (targetColumn.taskStatus as string) || ""; + const targetColumnStatusValue = targetColumn.taskStatus || ""; - updateTaskItemStatus(plugin, oldTask, newTask, targetColumnStatusValue); + void updateTaskItemStatus( + plugin, + oldTask, + newTask, + targetColumnStatusValue, + ); }; /** @@ -1111,10 +1130,10 @@ class DragDropTasksManager { targetColumnSwimlaneData: swimlaneDataProp | undefined, ): Promise => { const oldTask = currentDragData.task; - let newTask = { ...oldTask } as taskItem; + let newTask = { ...oldTask }; // STEP 1 - Check if the target column has 'manualOrder' sorting criteria. - this.handleTasksOrderChange( + void this.handleTasksOrderChange( plugin, currentDragData, targetColumn, @@ -1134,7 +1153,7 @@ class DragDropTasksManager { ); // FINALLY - Update the task in the note. - updateTaskItemStatus( + void updateTaskItemStatus( plugin, oldTask, newTask, @@ -1201,7 +1220,7 @@ class DragDropTasksManager { ); if (!newBoardData) { - throw "Board data not found"; + throw new Error("Board data not found"); } newBoardData.views[ @@ -1209,7 +1228,7 @@ class DragDropTasksManager { ].kanbanView!.columns[targetColumnData.index - 1] = updatedTargetColumnData; - plugin.taskBoardFileManager.saveBoard(newBoardData); + void plugin.taskBoardFileManager.saveBoard(newBoardData); }; /** @@ -1405,8 +1424,8 @@ class DragDropTasksManager { // For column we can do this kind of heavy DOM traversing, // since there will be less columns, so querySelecting them all is not so big issue. const allColumnContainers = Array.from( - document.querySelectorAll(".tasksContainer"), - ) as HTMLDivElement[]; + activeDocument.querySelectorAll(".tasksContainer"), + ); allColumnContainers.forEach((container) => { container.classList.remove( "drag-over-allowed", @@ -1436,12 +1455,12 @@ class DragDropTasksManager { * Clear column dragover feedback classes from all columns and swimlane columns. */ private clearDragoverFeedback(): void { - document + activeDocument .querySelectorAll(".tasksContainer.drag-over-allowed") .forEach((el) => { el.classList.remove("drag-over-allowed"); }); - document + activeDocument .querySelectorAll(".tasksContainer.drag-over-not-allowed") .forEach((el) => { el.classList.remove("drag-over-not-allowed"); @@ -1523,7 +1542,7 @@ class DragDropTasksManager { const touch = e.changedTouches[0]; if (touch) { - this.handleTouchDrop(touch.clientX, touch.clientY); + void this.handleTouchDrop(touch.clientX, touch.clientY); } this.clearTouchDragState(); @@ -1570,12 +1589,12 @@ class DragDropTasksManager { this.touchDragElement = cardWrapper; this.setCurrentDragData(dragData); - document.addEventListener( + activeDocument.addEventListener( "contextmenu", this.boundContextMenuBlocker, true, ); - document.body.classList.add("taskboard-touch-dragging"); + activeDocument.body.classList.add("taskboard-touch-dragging"); cardWrapper.classList.add("task-item-dragging"); this.touchDragGhost = this.createTouchDragGhost(cardWrapper, x, y); @@ -1622,12 +1641,12 @@ class DragDropTasksManager { private clearTouchDragState(): void { this.touchDragActive = false; - document.removeEventListener( + activeDocument.removeEventListener( "contextmenu", this.boundContextMenuBlocker, true, ); - document.body.classList.remove("taskboard-touch-dragging"); + activeDocument.body.classList.remove("taskboard-touch-dragging"); this.removeTouchDragGhost(); this.stopTouchAutoScroll(); @@ -1657,17 +1676,15 @@ class DragDropTasksManager { // window.requestAnimationFrame(() => { this.clearDragoverFeedback(); - const elementUnder = document.elementFromPoint(x, y); + const elementUnder = activeDocument.elementFromPoint(x, y); if (!elementUnder) return; const columnEl = (elementUnder as HTMLElement).closest( ".TaskBoardColumnsSection", - ) as HTMLElement | null; + ); if (!columnEl) return; - const tasksContainer = columnEl.querySelector( - ".tasksContainer", - ) as HTMLElement | null; + const tasksContainer = columnEl.querySelector(".tasksContainer"); if (!tasksContainer) return; const currentDragData = this.getCurrentDragData(); @@ -1707,17 +1724,15 @@ class DragDropTasksManager { * Handle the drop action at the end of a touch drag. */ private async handleTouchDrop(x: number, y: number): Promise { - const elementUnder = document.elementFromPoint(x, y); + const elementUnder = activeDocument.elementFromPoint(x, y); if (!elementUnder) return; const columnEl = (elementUnder as HTMLElement).closest( ".TaskBoardColumnsSection", - ) as HTMLElement | null; + ); if (!columnEl) return; - const tasksContainer = columnEl.querySelector( - ".tasksContainer", - ) as HTMLElement | null; + const tasksContainer = columnEl.querySelector(".tasksContainer"); if (!tasksContainer) return; const currentDragData = this.getCurrentDragData(); @@ -1750,7 +1765,7 @@ class DragDropTasksManager { // Find insertion index if hovering over a task card const taskCardEl = (elementUnder as HTMLElement).closest( "[data-taskitem-index]", - ) as HTMLElement | null; + ); if ( taskCardEl && targetColumnData.sortCriteria?.some( @@ -2006,7 +2021,7 @@ class DragDropTasksManager { if (!this.currentDragData) { bugReporterManagerInsatance.addToLogs( 142, - `No current drag data available for drop operation.\currentDragData=${JSON.stringify(this.currentDragData)}`, + `No current drag data available for drop operation.\ncurrentDragData=${JSON.stringify(this.currentDragData)}`, "DragDropTasksManager.ts/handleDropEvent", ); return; @@ -2017,7 +2032,7 @@ class DragDropTasksManager { if (!sourceColumnData) { bugReporterManagerInsatance.addToLogs( 143, - `There was an error while capturing the source column data.\sourceColumnData=${JSON.stringify(sourceColumnData)}`, + `There was an error while capturing the source column data.\nsourceColumnData=${JSON.stringify(sourceColumnData)}`, "DragDropTasksManager.ts/handleDropEvent", ); return; @@ -2063,7 +2078,13 @@ class DragDropTasksManager { targetColumnData, sourceColumnSwimlaneData, targetColumnSwimlaneData, - ); + ).catch((error) => { + bugReporterManagerInsatance.addToLogs( + 218, + String(error), + "DragDropTasksManager.tshandleDropEvent/handleTaskMove_same_column", + ); + }); return; } else if (targetColumnData.colType === colTypeNames.namedTag) { this.handleTaskMove_namedTag_to_namedTag( @@ -2073,7 +2094,13 @@ class DragDropTasksManager { targetColumnData, sourceColumnSwimlaneData, targetColumnSwimlaneData, - ); + ).catch((error) => { + bugReporterManagerInsatance.addToLogs( + 218, + String(error), + "DragDropTasksManager.tshandleDropEvent/handleTaskMove_namedTag_to_namedTag", + ); + }); } else if (targetColumnData.colType === colTypeNames.dated) { this.handleTaskMove_to_dated( this.plugin!, @@ -2082,7 +2109,13 @@ class DragDropTasksManager { targetColumnData, sourceColumnSwimlaneData, targetColumnSwimlaneData, - ); + ).catch((error) => { + bugReporterManagerInsatance.addToLogs( + 218, + String(error), + "DragDropTasksManager.tshandleDropEvent/handleTaskMove_to_dated", + ); + }); } else if (targetColumnData.colType === colTypeNames.taskPriority) { this.handleTaskMove_priority_to_priority( this.plugin!, @@ -2091,7 +2124,13 @@ class DragDropTasksManager { targetColumnData, sourceColumnSwimlaneData, targetColumnSwimlaneData, - ); + ).catch((error) => { + bugReporterManagerInsatance.addToLogs( + 218, + String(error), + "DragDropTasksManager.tshandleDropEvent/handleTaskMove_priority_to_priority", + ); + }); } else if (targetColumnData.colType === colTypeNames.taskStatus) { this.handleTaskMove_status_to_status( this.plugin!, @@ -2100,7 +2139,13 @@ class DragDropTasksManager { targetColumnData, sourceColumnSwimlaneData, targetColumnSwimlaneData, - ); + ).catch((error) => { + bugReporterManagerInsatance.addToLogs( + 218, + String(error), + "DragDropTasksManager.tshandleDropEvent/handleTaskMove_status_to_status", + ); + }); } else { new Notice( "This operation is not possible in the current version. Please request this idea to the developer.", @@ -2116,7 +2161,13 @@ class DragDropTasksManager { targetColumnData, sourceColumnSwimlaneData, targetColumnSwimlaneData, - ); + ).catch((error) => { + bugReporterManagerInsatance.addToLogs( + 218, + String(error), + "DragDropTasksManager.tshandleDropEvent/handleTaskMove_to_completed", + ); + }); } else if (targetColumnData.colType === colTypeNames.dated) { // This means user is moving task to a dated column from any other type of column. // This operation should basically add a date property to the task based on the target column's dateType @@ -2127,7 +2178,13 @@ class DragDropTasksManager { targetColumnData, sourceColumnSwimlaneData, targetColumnSwimlaneData, - ); + ).catch((error) => { + bugReporterManagerInsatance.addToLogs( + 218, + String(error), + "DragDropTasksManager.tshandleDropEvent/handleTaskMove_to_dated", + ); + }); } else if (targetColumnData.colType === colTypeNames.namedTag) { // This means user is moving task to a namedTag column from any other type of column. // This operation should basically add the target column's tag to the task @@ -2138,7 +2195,13 @@ class DragDropTasksManager { targetColumnData, sourceColumnSwimlaneData, targetColumnSwimlaneData, - ); + ).catch((error) => { + bugReporterManagerInsatance.addToLogs( + 218, + String(error), + "DragDropTasksManager.tshandleDropEvent/handleTaskMove_to_namedTag", + ); + }); } else if (targetColumnData.colType === colTypeNames.taskPriority) { // This means user is moving task to a priority column from any other type of column. // This operation should basically update the task's priority based on the target column's taskPriority @@ -2149,7 +2212,13 @@ class DragDropTasksManager { targetColumnData, sourceColumnSwimlaneData, targetColumnSwimlaneData, - ); + ).catch((error) => { + bugReporterManagerInsatance.addToLogs( + 218, + String(error), + "DragDropTasksManager.tshandleDropEvent/handleTaskMove_to_priority", + ); + }); } else if (targetColumnData.colType === colTypeNames.taskStatus) { // This means user is moving task to a status column from any other type of column. // This operation should basically update the task's status based on the target column's taskStatus @@ -2160,7 +2229,13 @@ class DragDropTasksManager { targetColumnData, sourceColumnSwimlaneData, targetColumnSwimlaneData, - ); + ).catch((error) => { + bugReporterManagerInsatance.addToLogs( + 218, + String(error), + "DragDropTasksManager.tshandleDropEvent/handleTaskMove_to_status", + ); + }); } else { new Notice( "This operation is not possible in the current version. Please request this idea to the developer.", diff --git a/src/managers/RealTimeScanner.ts b/src/managers/RealTimeScanner.ts index c423cfa4..016525e4 100644 --- a/src/managers/RealTimeScanner.ts +++ b/src/managers/RealTimeScanner.ts @@ -21,9 +21,12 @@ export class RealTimeScanner { async initializeStack() { try { - const storedStack = localStorage.getItem(PENDING_SCAN_FILE_STACK); + const storedStack = this.app.loadLocalStorage( + PENDING_SCAN_FILE_STACK, + ) as string; if (storedStack) { - this.taskBoardFileStack = JSON.parse(storedStack); + const parsedData: unknown = JSON.parse(storedStack); + this.taskBoardFileStack = parsedData as string[]; } // this.startScanTimer(); } catch (error) { @@ -37,7 +40,7 @@ export class RealTimeScanner { saveStack() { try { - localStorage.setItem( + this.app.saveLocalStorage( PENDING_SCAN_FILE_STACK, JSON.stringify(this.taskBoardFileStack), ); @@ -56,61 +59,67 @@ export class RealTimeScanner { * @param currentFile The file that was modified, or undefined if no file was modified. * @param updatedTaskId The ID of the task that was updated, or undefined if no task was updated. * @returns A Promise that resolves to a boolean indicating if the process was successful. + * + * @error_code 217 */ async processAllUpdatedFiles( - currentFile?: TFile | string | undefined, - updatedTaskId?: string | undefined, - ) { - // If a current file is provided, ensure it's included in the processing - let newFile: TFile | null | undefined = null; - if (currentFile && typeof currentFile === "string") { - newFile = this.plugin.app.vault.getFileByPath(currentFile); - } else { - newFile = currentFile as TFile | undefined; - } + currentFile?: TFile | string, + updatedTaskId?: string, + ): Promise { + try { + // If a current file is provided, ensure it's included in the processing + let newFile: TFile | null | undefined = null; + if (currentFile && typeof currentFile === "string") { + newFile = this.plugin.app.vault.getFileByPath(currentFile); + } else { + newFile = currentFile as TFile | undefined; + } - const filesToProcess = this.taskBoardFileStack.slice(); - const files = filesToProcess - .map((filePath) => this.getFileFromPath(filePath)) - .filter((file) => !!file); + const filesToProcess = this.taskBoardFileStack.slice(); + const files = filesToProcess + .map((filePath) => this.getFileFromPath(filePath)) + .filter((file) => !!file); - if (newFile) { - // If a current file is provided, ensure it's included in the processing - const currentFilePath = newFile.path; - if (!filesToProcess.includes(currentFilePath)) { - filesToProcess.push(currentFilePath); - files.push(newFile); + if (newFile) { + // If a current file is provided, ensure it's included in the processing + const currentFilePath = newFile.path; + if (!filesToProcess.includes(currentFilePath)) { + filesToProcess.push(currentFilePath); + files.push(newFile); + } } - } - let result = false; - if (filesToProcess.length > 0) { - // Send all files for scanning and updating tasks - result = await this.vaultScanner.refreshTasksFromFiles( - files, - false, - ); + let result = false; + if (filesToProcess.length > 0) { + // Send all files for scanning and updating tasks + result = await this.vaultScanner.refreshTasksFromFiles( + files, + false, + ); - if (result) { - // Clear the stack to avoid re-processing during this run. - this.taskBoardFileStack = []; - // Save updated stack (which should now be empty) - this.saveStack(); + if (result) { + // Clear the stack to avoid re-processing during this run. + this.taskBoardFileStack = []; + // Save updated stack (which should now be empty) + this.saveStack(); - // Reset the editorModified flag after the scan. - this.plugin.editorModified = false; - } else { - new Notice("Few files didnt got scanned..."); + // Reset the editorModified flag after the scan. + this.plugin.editorModified = false; + } else { + new Notice("Few files didnt got scanned..."); + } } + } catch (error) { + throw new Error(String(error)); + } finally { + window.setTimeout(() => { + // This event emmitter will stop any loading animation of ongoing task-card. + eventEmitter.emit("UPDATE_TASK", { + taskID: updatedTaskId, + state: false, + }); + }, 500); } - - setTimeout(() => { - // This event emmitter will stop any loading animation of ongoing task-card. - eventEmitter.emit("UPDATE_TASK", { - taskID: updatedTaskId, - state: false, - }); - }, 500); } getFileFromPath(filePath: string): TFile | null { @@ -149,7 +158,8 @@ export class RealTimeScanner { [Pending, Completed].forEach((cache) => { if (cache && typeof cache === "object") { - if (file instanceof TFile && cache.hasOwnProperty(oldPath)) { + // Use Object.hasOwn() (ES2022) instead of hasOwnProperty to avoid no-prototype-builtins lint error + if (file instanceof TFile && Object.hasOwn(cache, oldPath)) { if ( !file.path .toLowerCase() @@ -247,7 +257,8 @@ export class RealTimeScanner { const { Pending, Completed } = this.plugin.vaultScanner.tasksCache; [Pending, Completed].forEach((cache) => { if (cache && typeof cache === "object") { - if (file instanceof TFile && cache.hasOwnProperty(file.path)) { + // Use Object.hasOwn() (ES2022) instead of hasOwnProperty to avoid no-prototype-builtins lint error + if (file instanceof TFile && Object.hasOwn(cache, file.path)) { delete cache[file.path]; foundFlag = true; } else if (file instanceof TFolder) { diff --git a/src/managers/TaskBoardFileManager.ts b/src/managers/TaskBoardFileManager.ts index 5f5212f7..159f9c15 100644 --- a/src/managers/TaskBoardFileManager.ts +++ b/src/managers/TaskBoardFileManager.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-deprecated */ /** * @name TaskBoardFileManager.ts * @path /src/managers/TaskBoardFileManager.ts @@ -7,7 +8,11 @@ import { App, TFile, Notice, normalizePath } from "obsidian"; import TaskBoard from "../../main.js"; -import { Board } from "../interfaces/BoardConfigs.js"; +import { + Board, + ColumnData, + TaskBoardViewType, +} from "../interfaces/BoardConfigs.js"; import { CURRENT_REVISION, LEAFID_FILEPATH_MAPPING_KEY, @@ -37,7 +42,7 @@ export default class TaskBoardFileManager { */ private recentBoardsData: recentBoardsDataType = {}; private taskBoardFilesRegistry: taskBoardFilesRegistryType = {}; - private debouncedSaveBoardTimers: Map = new Map(); // Track debounce timers per board + private debouncedSaveBoardTimers: Map = new Map(); // Track debounce timers per board /** * @deprecated @@ -70,17 +75,18 @@ export default class TaskBoardFileManager { // Check if file exists const fileExists = await this.app.vault.adapter.exists(filePath); if (!fileExists) { - throw `TaskBoard file not found: ${filePath}`; + throw new Error(`TaskBoard file not found: ${filePath}`); } // Read the file const fileContent = await this.app.vault.adapter.read(filePath); if (!fileContent) { - throw `TaskBoard file is empty: ${filePath}`; + throw new Error(`TaskBoard file is empty: ${filePath}`); } // Parse JSON content - let boardData: Board = JSON.parse(fileContent); + const parsedData: unknown = JSON.parse(fileContent); + let boardData = parsedData as Board; // Same Board ID ChecK : Check if board with this ID already exists in registry // This is to ensure that, when a .taskboard file has been simply duplicated externally @@ -107,7 +113,7 @@ export default class TaskBoardFileManager { } catch (error) { bugReporterManagerInsatance.addToLogs( 192, - `Error loading board from file ${filePath} : ${error}`, + `Error loading board from file ${filePath} : ${String(error)}`, "TaskBoardFileManager.ts/loadBoardFromDisk", ); return null; @@ -131,7 +137,7 @@ export default class TaskBoardFileManager { // Check if file exists const fileExists = await this.app.vault.adapter.exists(filePath); if (!fileExists) { - throw `TaskBoard file not found: ${filePath}`; + throw new Error(`TaskBoard file not found: ${filePath}`); } // Read the file @@ -139,11 +145,12 @@ export default class TaskBoardFileManager { const decodedData = new TextDecoder().decode(file); if (!decodedData) { - throw `TaskBoard file is empty: ${filePath}`; + throw new Error(`TaskBoard file is empty: ${filePath}`); } // Parse JSON content - let boardData: Board = JSON.parse(decodedData); + const parsedData: unknown = JSON.parse(decodedData); + let boardData = parsedData as Board; // Check if board with this ID already exists in registry const existingRegistryEntry = Object.entries( @@ -168,7 +175,7 @@ export default class TaskBoardFileManager { } catch (error) { bugReporterManagerInsatance.addToLogs( 193, - `Error loading board from file ${filePath} : ${error}`, + `Error loading board from file ${filePath} : ${String(error)}`, "TaskBoardFileManager.ts/loadBoardFromDisk", ); return null; @@ -183,7 +190,7 @@ export default class TaskBoardFileManager { async loadBoardUsingID(boardId: string): Promise { try { if (!boardId || boardId.trim() === "") { - throw `No board ID provided to load the board`; + throw new Error(`No board ID provided to load the board`); } // Search for board by ID in the cached data @@ -195,11 +202,11 @@ export default class TaskBoardFileManager { return cachedBoard; } - throw `Board with ID "${boardId}" not found in cache`; + throw new Error(`Board with ID "${boardId}" not found in cache`); } catch (error) { bugReporterManagerInsatance.addToLogs( 194, - `Error loading board with ID ${boardId}: ${error}`, + `Error loading board with ID ${boardId}: ${String(error)}`, "TaskBoardFileManager.ts/loadBoardFromDisk", ); return null; @@ -216,7 +223,9 @@ export default class TaskBoardFileManager { async loadBoardUsingPath(filePath: string): Promise { try { if (!filePath || filePath.trim() === "") { - throw `No board file path provided to load the board`; + throw new Error( + `No board file path provided to load the board`, + ); } // Check if board is already cached in memory by file path @@ -225,7 +234,11 @@ export default class TaskBoardFileManager { // `Board "${this.recentBoardsData[filePath].name}" already exists in cache for file: ${filePath}`, // ); const foundBoard = this.recentBoardsData[filePath]; - this.addNewBoardToRegistry(foundBoard.id, filePath, foundBoard); + void this.addNewBoardToRegistry( + foundBoard.id, + filePath, + foundBoard, + ); return this.recentBoardsData[filePath]; } @@ -237,18 +250,22 @@ export default class TaskBoardFileManager { // Cache the board data in memory using file path as key this.recentBoardsData[filePath] = boardData; // Update the registry to move this board on top - this.addNewBoardToRegistry(boardData.id, filePath, boardData); + void this.addNewBoardToRegistry( + boardData.id, + filePath, + boardData, + ); // console.log( // `Loaded and cached board "${boardData.name}" from: ${filePath}`, // ); return boardData; } - throw `Board data is not valid : ${boardData}`; + throw new Error(`Board data is not valid : ${boardData}`); } catch (error) { bugReporterManagerInsatance.addToLogs( 204, - `Error loading board with file ${filePath}: ${error}`, + `Error loading board with file ${filePath}: ${String(error)}`, "TaskBoardFileManager.ts/loadBoardFromDisk", ); return undefined; @@ -276,7 +293,7 @@ export default class TaskBoardFileManager { } catch (error) { bugReporterManagerInsatance.addToLogs( 205, - `Error saving board to file ${filePath}: ${error}`, + `Error saving board to file ${filePath}: ${String(error)}`, "TaskBoardFileManager.ts/loadBoardFromDisk", ); return false; @@ -319,7 +336,7 @@ export default class TaskBoardFileManager { // Update existing file with binary data const file = this.app.vault.getAbstractFileByPath(filePath); if (!file || !(file instanceof TFile)) { - throw `Cannot find file at the path to update`; + throw new Error(`Cannot find file at the path to update`); } await this.app.vault.modifyBinary( file, @@ -332,7 +349,7 @@ export default class TaskBoardFileManager { } catch (error) { bugReporterManagerInsatance.addToLogs( 206, - `Error saving board to file ${filePath}: ${error}`, + `Error saving board to file ${filePath}: ${String(error)}`, "TaskBoardFileManager.ts/saveBoardToDiskEncoded", ); return false; @@ -353,7 +370,7 @@ export default class TaskBoardFileManager { ): Promise { try { if (!updatedBoardData.id || updatedBoardData.id.trim() === "") { - throw `Board data does not contain a valid ID.`; + throw new Error(`Board data does not contain a valid ID.`); } let filepathLocal: string; @@ -380,7 +397,9 @@ export default class TaskBoardFileManager { !registryEntry.filePath || registryEntry.filePath.trim() === "" ) { - throw `No file path configured for board ID.`; + throw new Error( + `No file path configured for board ID.`, + ); } filepathLocal = registryEntry.filePath; @@ -417,7 +436,7 @@ export default class TaskBoardFileManager { } catch (error) { bugReporterManagerInsatance.addToLogs( 207, - `Error saving board with ID ${updatedBoardData.id}: ${error}`, + `Error saving board with ID ${updatedBoardData.id}: ${String(error)}`, "TaskBoardFileManager.ts/saveBoard", ); return false; @@ -440,28 +459,30 @@ export default class TaskBoardFileManager { const boardId = updatedBoardData.id; if (!boardId || boardId.trim() === "") { - throw `Cannot debounce save: Board data does not contain a valid ID`; + throw new Error( + `Cannot debounce save: Board data does not contain a valid ID`, + ); } // Clear any existing timer for this board const existingTimer = this.debouncedSaveBoardTimers.get(boardId); if (existingTimer) { - clearTimeout(existingTimer); + window.clearTimeout(existingTimer); } // Set a new timer to save after the debounce delay - const newTimer = setTimeout(async () => { - try { - await this.saveBoard(updatedBoardData, filePath); - this.debouncedSaveBoardTimers.delete(boardId); - } catch (error) { - bugReporterManagerInsatance.addToLogs( - 208, - `Error in debounced save for board ID ${boardId} and filePath ${filePath}: ${error}`, - "TaskBoardFileManager.ts/debouncedSaveBoard", - ); - this.debouncedSaveBoardTimers.delete(boardId); - } + const newTimer = window.setTimeout(() => { + void this.saveBoard(updatedBoardData, filePath) + .catch((error) => { + bugReporterManagerInsatance.addToLogs( + 208, + `Error in debounced save for board ID ${boardId} and filePath ${filePath}: ${String(error)}`, + "TaskBoardFileManager.ts/debouncedSaveBoard", + ); + }) + .finally(() => { + this.debouncedSaveBoardTimers.delete(boardId); + }); }, delayMs); // Store the timer for potential cancellation @@ -473,7 +494,7 @@ export default class TaskBoardFileManager { */ clearAllDebouncedSaves(): void { this.debouncedSaveBoardTimers.forEach((timer) => { - clearTimeout(timer); + window.clearTimeout(timer); }); this.debouncedSaveBoardTimers.clear(); } @@ -493,7 +514,7 @@ export default class TaskBoardFileManager { // Cancel any pending debounced save for this board const existingTimer = this.debouncedSaveBoardTimers.get(boardId); if (existingTimer) { - clearTimeout(existingTimer); + window.clearTimeout(existingTimer); this.debouncedSaveBoardTimers.delete(boardId); } @@ -562,7 +583,7 @@ export default class TaskBoardFileManager { [boardId]: newEntry, ...updatedTaskBoardFilesRegistry, }; - this.plugin.saveSettings(this.plugin.settings); + void this.plugin.saveSettings(this.plugin.settings); this.taskBoardFilesRegistry = this.plugin.settings.data.taskBoardFilesRegistry; @@ -572,7 +593,7 @@ export default class TaskBoardFileManager { } catch (error) { bugReporterManagerInsatance.addToLogs( 209, - `Error adding board to registry: ${error}`, + `Error adding board to registry: ${String(error)}`, "TaskBoardFileManager.ts/addNewBoardToRegistry", ); } @@ -612,13 +633,13 @@ export default class TaskBoardFileManager { } if (flag) { - this.plugin.saveSettings(); + void this.plugin.saveSettings(); this.taskBoardFilesRegistry = updatedTaskBoardFilesRegistry; } } catch (error) { bugReporterManagerInsatance.addToLogs( 209, - `Error removing board from registry: ${error}`, + `Error removing board from registry: ${String(error)}`, "TaskBoardFileManager.ts/removeBoardFromRegistry", ); } @@ -635,7 +656,7 @@ export default class TaskBoardFileManager { } catch (error) { bugReporterManagerInsatance.addToLogs( 210, - `Error checking if file exists ${filePath}: ${error}`, + `Error checking if file exists ${filePath}: ${String(error)}`, "TaskBoardFileManager.ts/addNewBoardToRegistry", ); return false; @@ -670,7 +691,7 @@ export default class TaskBoardFileManager { } catch (error) { bugReporterManagerInsatance.addToLogs( 212, - `Error validating board files from the registry: ${error}`, + `Error validating board files from the registry: ${String(error)}`, "TaskBoardFileManager.ts/validateBoardFiles", ); } @@ -722,13 +743,13 @@ export default class TaskBoardFileManager { // Cache the board data in memory using file path as key this.recentBoardsData[filePath] = boardData; // Update the registry to move this board on top - this.addNewBoardToRegistry(boardData.id, filePath, boardData); + void this.addNewBoardToRegistry(boardData.id, filePath, boardData); return await this.saveBoardToDisk(filePath, boardData); } catch (error) { bugReporterManagerInsatance.addToLogs( 211, - `Error creating new board file ${filePath}: ${error}`, + `Error creating new board file ${filePath}: ${String(error)}`, "TaskBoardFileManager.ts/createNewBoardFile", ); return false; @@ -744,17 +765,17 @@ export default class TaskBoardFileManager { try { const file = this.app.vault.getAbstractFileByPath(filePath); if (!file || !(file instanceof TFile)) { - throw `Cannot find file to delete.`; + throw new Error(`Cannot find file to delete.`); } // Remove from the fileRegistry and the recentBoardsCache - await this.app.vault.trash(file, false); + await this.app.fileManager.trashFile(file); return true; } catch (error) { bugReporterManagerInsatance.addToLogs( 195, - `Error deleting board file ${filePath}: ${error}`, + `Error deleting board file ${filePath}: ${String(error)}`, "TaskBoardFileManager.ts/loadBoardFromDisk", ); return false; @@ -783,7 +804,7 @@ export default class TaskBoardFileManager { const leaves = this.app.workspace.getLeavesOfType(VIEW_TYPE_TASKBOARD); const leafToClose = leaves.find( - (leaf) => (leaf as any).taskboardFilePath === filePath, + (leaf) => leaf.taskboardFilePath === filePath, ); if (leafToClose) { @@ -797,7 +818,7 @@ export default class TaskBoardFileManager { } catch (error) { bugReporterManagerInsatance.addToLogs( 215, - `Error handling board file deletion for ${filePath}: ${error}`, + `Error handling board file deletion for ${filePath}: ${String(error)}`, "TaskBoardFileManager.ts/onBoardFileDelete", ); } @@ -817,7 +838,7 @@ export default class TaskBoardFileManager { // Get the boardId using the old file path const boardId = this.getBoardIdhUsingBoardFilepath(oldFilePath); if (!boardId) { - throw `Board ID not found for file: ${oldFilePath}`; + throw new Error(`Board ID not found for file: ${oldFilePath}`); } // Get the board data from cache @@ -839,7 +860,7 @@ export default class TaskBoardFileManager { const leaves = this.app.workspace.getLeavesOfType(VIEW_TYPE_TASKBOARD); const leafToClose = leaves.find( - (leaf) => (leaf as any).taskboardFilePath === oldFilePath, + (leaf) => leaf.taskboardFilePath === oldFilePath, ); if (leafToClose) { @@ -853,7 +874,7 @@ export default class TaskBoardFileManager { } catch (error) { bugReporterManagerInsatance.addToLogs( 216, - `Error handling board file rename for ${oldFilePath} to ${newFilePath}: ${error}`, + `Error handling board file rename for ${oldFilePath} to ${newFilePath}: ${String(error)}`, "TaskBoardFileManager.ts/onBoardFileRenamed", ); } @@ -877,7 +898,7 @@ export default class TaskBoardFileManager { file instanceof TFile && file.extension === TASKBOARD_FILE_EXTENSION, ) - .map((file) => (file as TFile).path); + .map((file) => file.path); // console.log( // `Found ${taskboardFiles.length} .taskboard files:`, @@ -887,7 +908,7 @@ export default class TaskBoardFileManager { } catch (error) { bugReporterManagerInsatance.addToLogs( 214, - `Error getting all .taskboard files: ${error}`, + `Error getting all .taskboard files: ${String(error)}`, "TaskBoardFileManager.ts/addNewBoardToRegistry", ); return []; @@ -923,7 +944,7 @@ export default class TaskBoardFileManager { if (Object.keys(newTaskBoardFilesRegistry).length > 0) { this.plugin.settings.data.taskBoardFilesRegistry = newTaskBoardFilesRegistry; - this.plugin.saveSettings(); + void this.plugin.saveSettings(); this.taskBoardFilesRegistry = newTaskBoardFilesRegistry; } @@ -957,7 +978,9 @@ export default class TaskBoardFileManager { )[0]; if (!firstItemFromRegistry?.filePath) { - throw `First registry entry does not have a valid filePath.`; + throw new Error( + `First registry entry does not have a valid filePath.`, + ); } let boardData: Board | undefined; @@ -979,7 +1002,7 @@ export default class TaskBoardFileManager { } catch (error) { bugReporterManagerInsatance.addToLogs( 215, - `Error loading the last opened board: ${error}`, + `Error loading the last opened board: ${String(error)}`, "TaskBoardFileManager.ts/addNewBoardToRegistry", ); return undefined; @@ -1141,7 +1164,8 @@ export default class TaskBoardFileManager { * from older boards to be lost. */ runMigrationForRevision_4(oldBoardData: Board): Board { - let newBoardData = JSON.parse(JSON.stringify(oldBoardData)); // Deep clone to avoid mutations + const parsedData: unknown = JSON.parse(JSON.stringify(oldBoardData)); // Deep clone to avoid mutations= + let newBoardData = parsedData as Board; // Ensure views array exists; initialize empty if missing if (!newBoardData.views || !Array.isArray(newBoardData.views)) { @@ -1149,73 +1173,79 @@ export default class TaskBoardFileManager { } // Process each view - if (newBoardData.views && Array.isArray(newBoardData.views)) { - newBoardData.views = newBoardData.views.map((view: any) => { - // Initialize viewFilters if missing - if (!view.viewFilters) { - // If deprecated viewFilter exists, convert it to AdvancedFilter - if (view.viewFilter && view.viewFilter.filterGroups) { - const deprecatedViewFilter = view.viewFilter; - view.viewFilters = { - filters: [ - { - id: generateRandomStringId("filter"), - name: "Migrated Filter", - status: true, // Enable the migrated filter - description: - "Auto-migrated from legacy viewFilter", - rootCondition: - deprecatedViewFilter.rootCondition || - "all", - filterGroups: - deprecatedViewFilter.filterGroups || [], - }, - ], - rootCondition: - deprecatedViewFilter.rootCondition || "all", - }; - } else { - // No deprecated filter, create empty AdvancedFilter - view.viewFilters = { - filters: [], - rootCondition: "all", - }; - } - } - - // Process kanban view columns - if (view.kanbanView && Array.isArray(view.kanbanView.columns)) { - view.kanbanView.columns = view.kanbanView.columns.map( - (column: any) => { - // Initialize columnFilters if missing - if (!column.columnFilters) { - // If deprecated filters (single Filter) exists, convert it to AdvancedFilter - if ( - column.filters && - column.filters.filterGroups - ) { - const deprecatedFilter = column.filters; - column.columnFilters = { - filters: [deprecatedFilter], // Wrap the single filter in an array + if (newBoardData?.views && Array.isArray(newBoardData.views)) { + newBoardData.views = newBoardData.views.map( + (view: TaskBoardViewType) => { + // Initialize viewFilters if missing + if (!view.viewFilters) { + // If deprecated viewFilter exists, convert it to AdvancedFilter + if (view.viewFilter && view.viewFilter.filterGroups) { + const deprecatedViewFilter = view.viewFilter; + view.viewFilters = { + filters: [ + { + id: generateRandomStringId("filter"), + name: "Migrated Filter", + status: true, // Enable the migrated filter + description: + "Auto-migrated from legacy viewFilter", rootCondition: - deprecatedFilter.rootCondition || + deprecatedViewFilter.rootCondition || "all", - }; - } else { - // No deprecated filter, create empty AdvancedFilter - column.columnFilters = { - filters: [], - rootCondition: "all", - }; + filterGroups: + deprecatedViewFilter.filterGroups || + [], + }, + ], + rootCondition: + deprecatedViewFilter.rootCondition || "all", + }; + } else { + // No deprecated filter, create empty AdvancedFilter + view.viewFilters = { + filters: [], + rootCondition: "all", + }; + } + } + + // Process kanban view columns + if ( + view.kanbanView && + Array.isArray(view.kanbanView.columns) + ) { + view.kanbanView.columns = view.kanbanView.columns.map( + (column: ColumnData) => { + // Initialize columnFilters if missing + if (!column.columnFilters) { + // If deprecated filters (single Filter) exists, convert it to AdvancedFilter + if ( + column.filters && + column.filters.filterGroups + ) { + const deprecatedFilter = column.filters; + column.columnFilters = { + filters: [deprecatedFilter], // Wrap the single filter in an array + rootCondition: + deprecatedFilter.rootCondition || + "all", + }; + } else { + // No deprecated filter, create empty AdvancedFilter + column.columnFilters = { + filters: [], + rootCondition: "all", + }; + } } - } - return column; - }, - ); - } + return column; + }, + ); + } - return view; - }); + return view; + }, + ); } return newBoardData; @@ -1264,13 +1294,13 @@ export default class TaskBoardFileManager { // After applying necessary migrations, update the revision in the board data updatedBoardData.revision = CURRENT_REVISION; - this.saveBoard(updatedBoardData, filePath); + void this.saveBoard(updatedBoardData, filePath); return updatedBoardData; } catch (error) { bugReporterManagerInsatance.addToLogs( 213, - `Error applying migration to board data: ${error}`, + `Error applying migration to board data: ${String(error)}`, "TaskBoardFileManager.ts/applyMigrationIfNeeded", ); return boardData; // Return original data if migration fails to prevent data loss @@ -1323,14 +1353,16 @@ export default class TaskBoardFileManager { // Validate board index if (boardIndex < 0 || boardIndex > boardsArray.length - 1) { - throw `Invalid board index: ${boardIndex}. Available boards: ${boardsArray.length}`; + throw new Error( + `Invalid board index: ${boardIndex}. Available boards: ${boardsArray.length}`, + ); } return boardsArray[boardIndex] || null; } catch (error) { bugReporterManagerInsatance.addToLogs( 216, - `Error loading board at index ${boardIndex}: ${error}`, + `Error loading board at index ${boardIndex}: ${String(error)}`, "TaskBoardFileManager.ts/loadBoardUsingIndex", ); return null; @@ -1560,7 +1592,7 @@ export default class TaskBoardFileManager { * as per the file paths stored in the task board file path registry saved in the global settings. * @returns All boards data as an object keyed by file path or an empty object if failed to load the boards. */ - async loadAllBoards(): Promise { + async loadAllBoards(): Promise { try { let loadedBoardsData: recentBoardsDataType = {}; @@ -1596,7 +1628,7 @@ export default class TaskBoardFileManager { return loadedBoardsData; } catch (error) { console.error(`Error loading all boards:`, error); - return {}; + return; } } } diff --git a/src/managers/VaultScanner.ts b/src/managers/VaultScanner.ts index 4fff4b48..7009fa2b 100644 --- a/src/managers/VaultScanner.ts +++ b/src/managers/VaultScanner.ts @@ -1,13 +1,6 @@ // /src/utils/ScanningVaults.ts -import { - App, - Notice, - TAbstractFile, - TFile, - TFolder, - moment as _moment, -} from "obsidian"; +import { App, Notice, TAbstractFile, TFile, TFolder } from "obsidian"; import { isValid, parse } from "date-fns"; import { t } from "i18next"; import type TaskBoard from "../../main.js"; @@ -604,7 +597,9 @@ export default class VaultScanner { if (this.tasksDetectedOrUpdated) { let result = await this.saveTasksToJsonCache(); if (!result) { - throw "There was an error while saving the tasks cache."; + throw new Error( + "There was an error while saving the tasks cache.", + ); } } @@ -674,7 +669,7 @@ export default class VaultScanner { this.tasksCache, ); - setTimeout(() => { + window.setTimeout(() => { eventEmitter.emit("SOFT_REFRESH"); // if (this.plugin.settings.data.searchQuery) { // console.log( @@ -876,7 +871,7 @@ export function extractTaskId(text: string): RegExpMatchArray | null { return idMatch; } - idMatch = text.match(/\@id\(\s*(.*?)\)/); + idMatch = text.match(/@id\(\s*(.*?)\)/); if (idMatch && idMatch[1]) { return idMatch; } @@ -914,7 +909,7 @@ export function extractBody( let tempLine = prevLine; while (tempLine && tempLine.startsWith(indentationString)) { n++; - tempLine = tempLine!.slice(indentationString.length); + tempLine = tempLine.slice(indentationString.length); } const requiredIndent = indentationString.repeat(n + 1); if (!sanitizedLine.startsWith(requiredIndent)) { @@ -1006,7 +1001,7 @@ export function extractCreatedDate(text: string): RegExpMatchArray | null { return match; } - match = text.match(/\@created\(\s*([^ ]+)\)/); + match = text.match(/@created\(\s*([^ ]+)\)/); if (match && match[1]) { return match; } @@ -1031,7 +1026,7 @@ export function extractStartDate(text: string): RegExpMatchArray | null { return match; } - match = text.match(/\@start\(\s*([^ ]+)\)/); + match = text.match(/@start\(\s*([^ ]+)\)/); if (match && match[1]) { return match; } @@ -1057,7 +1052,7 @@ export function extractScheduledDate(text: string): RegExpMatchArray | null { return match; } - match = text.match(/\@scheduled\(\s*([^ ]+)\)/); + match = text.match(/@scheduled\(\s*([^ ]+)\)/); if (match && match[1]) { return match; } @@ -1081,7 +1076,7 @@ export function extractDueDate(text: string): RegExpMatchArray | null { return match; } - match = text.match(/\@due\(\s*([^ ]+)\)/); + match = text.match(/@due\(\s*([^ ]+)\)/); if (match && match[1]) { return match; } @@ -1165,7 +1160,7 @@ export function extractReminder( // } // New patterns - // match = text.match(/\(\@(\d{4}-\d{2}-\d{2}( \d{2}:\d{2})?)\)/); + // match = text.match(/\(@(\d{4}-\d{2}-\d{2}( \d{2}:\d{2})?)\)/); // if (match) { // const dateStr = match[1]; // if (dateStr.includes(" ")) { @@ -1176,7 +1171,7 @@ export function extractReminder( // } // } - match = text.match(/\(\@(\d{2}:\d{2})\)/); + match = text.match(/\(@(\d{2}:\d{2})\)/); if (match) { const baseDate = startDate || scheduledDate || dueDate; if (baseDate) { @@ -1210,7 +1205,7 @@ export function extractDependsOn(text: string): RegExpMatchArray | null { return match; } - match = text.match(/\@dependsOn\(\s*(.*?)\s*\)/); + match = text.match(/@dependsOn\(\s*(.*?)\s*\)/); if (match && match[1]) { return match; } @@ -1234,7 +1229,7 @@ export function extractCompletionDate(text: string): RegExpMatchArray | null { return match; } - match = text.match(/\@completion\(\s*([^ ]+)\)/); + match = text.match(/@completion\(\s*([^ ]+)\)/); if (match && match[1]) { return match; } @@ -1259,7 +1254,7 @@ export function extractCancelledDate(text: string): RegExpMatchArray | null { return match; } - match = text.match(/\@cancelled\(\s*([^ ]+)\)/); + match = text.match(/@cancelled\(\s*([^ ]+)\)/); if (match && match[1]) { return match; } diff --git a/src/modals/AddColumnModal.ts b/src/modals/AddColumnModal.ts index 85407215..39a0b0f7 100644 --- a/src/modals/AddColumnModal.ts +++ b/src/modals/AddColumnModal.ts @@ -16,7 +16,7 @@ interface AddColumnModalProps { export class AddColumnModal extends Modal { private onSubmit: (columnData: ColumnData) => void; private onCancel: () => void; - private colType: string; + private colType: colTypeNames; private name: string; constructor(app: App, { onCancel, onSubmit }: AddColumnModalProps) { @@ -99,7 +99,7 @@ export class AddColumnModal extends Modal { colTypeSelect.addEventListener("change", (event: Event) => { const target = event.target as HTMLSelectElement; - this.colType = target.value; + this.colType = target.value as colTypeNames; }); // Name Field @@ -171,7 +171,7 @@ export class AddColumnModal extends Modal { name: this.name, taskPriority: 1, }); - } else if (this.colType === "completed") { + } else if (this.colType === colTypeNames.completed) { this.onSubmit({ id: generateRandomNumber(), index: 9999, diff --git a/src/modals/AddViewModal.ts b/src/modals/AddViewModal.ts index 237cb7d2..64e14eae 100644 --- a/src/modals/AddViewModal.ts +++ b/src/modals/AddViewModal.ts @@ -15,7 +15,7 @@ interface AddViewModalProps { export class AddViewModal extends Modal { private onSubmit: (updatedBoardData: Board) => void; private onCancel: () => void; - private viewType: string; + private viewType: viewTypeNames; private viewName: string; private boardData: Board; @@ -55,7 +55,7 @@ export class AddViewModal extends Modal { }) .setValue(this.viewType) .onChange(async (value) => { - this.viewType = value; + this.viewType = value as viewTypeNames; }), ); diff --git a/src/modals/BoardConfigModal.tsx b/src/modals/BoardConfigModal.tsx index 88d3cdfa..c1ea44c8 100644 --- a/src/modals/BoardConfigModal.tsx +++ b/src/modals/BoardConfigModal.tsx @@ -2,11 +2,11 @@ import { Modal, normalizePath, Notice } from "obsidian"; import Sortable from "sortablejs"; -import { BrickWall, EyeIcon, EyeOffIcon, Network, SquareKanban, GripHorizontalIcon } from "lucide-react"; +import { BrickWall, EyeIcon, EyeOffIcon, Network, SquareKanban, GripHorizontalIcon, Copy } from "lucide-react"; import React, { useEffect, useRef, useState } from "react"; import { FaAlignJustify, FaTrash } from 'react-icons/fa'; import ReactDOM from "react-dom/client"; -import { RxDragHandleDots2, RxDragHandleHorizontal } from "react-icons/rx"; +import { RxDragHandleDots2 } from "react-icons/rx"; import { t } from "i18next"; import TaskBoard from "../../main.js"; import { Board, TaskBoardViewType, ColumnData, swimlaneConfigs } from "../interfaces/BoardConfigs.js"; @@ -45,7 +45,8 @@ const ConfigModalContent: React.FC = ({ }) => { const [allViewsData, setAllViewsData] = useState(() => { try { - return currentBoardData.views ? JSON.parse(JSON.stringify(currentBoardData.views)) : []; + const parsedData: unknown = JSON.parse(JSON.stringify(currentBoardData.views)); + return currentBoardData.views ? parsedData as TaskBoardViewType[] : []; } catch (e) { bugReporterManagerInsatance.showNotice(34, "Error parsing boards data", e as string, "BoardConfigModal.tsx/allViewsData"); return []; @@ -80,7 +81,7 @@ const ConfigModalContent: React.FC = ({ // Kanban Column - Function to handle adding a new column to the selected Kanban specific view using the data submitted from the add column modal const handleAddColumn = (viewIndex: number, columnData: ColumnData) => { - const updatedViewsData: any = [...allViewsData]; + const updatedViewsData: TaskBoardViewType[] = [...allViewsData]; updatedViewsData[viewIndex].kanbanView!.columns.push({ id: columnData.id, index: updatedViewsData[viewIndex].kanbanView!.columns.length + 1, @@ -91,8 +92,8 @@ const ConfigModalContent: React.FC = ({ coltag: columnData.coltag, datedBasedColumn: { dateType: plugin.settings.data.universalDate, - from: "", - to: "" + from: 0, + to: 0 }, taskStatus: columnData.taskStatus, taskPriority: columnData.taskPriority, @@ -199,13 +200,13 @@ const ConfigModalContent: React.FC = ({ viewIndex: number, columnIndex: number, field: string, - value: any + value: unknown ) => { // evt?.preventDefault(); // evt?.stopPropagation(); // console.log(`Updating column at viewIndex: ${viewIndex}, columnIndex: ${columnIndex}, field: ${field}, value:`, value); const updatedViewsData = [...allViewsData]; - (updatedViewsData[viewIndex].kanbanView!.columns[columnIndex] as any)[field] = value; + (updatedViewsData[viewIndex].kanbanView!.columns[columnIndex] as Record)[field] = value; setAllViewsData(updatedViewsData); setIsEdited(true); @@ -313,15 +314,16 @@ const ConfigModalContent: React.FC = ({ return; } const viewToDuplicate = allViewsData[selectedViewIndex]; + const parsedData: unknown = JSON.parse(JSON.stringify(viewToDuplicate)); // Deep copy const duplicatedView: TaskBoardViewType = { - ...JSON.parse(JSON.stringify(viewToDuplicate)), // Deep copy + ...parsedData as TaskBoardViewType, viewId: generateRandomStringId('view'), viewName: `${viewToDuplicate.viewName} ${t("copy-suffix")}`, }; // Regenerate IDs for all columns to ensure uniqueness - if (duplicatedView?.kanbanView && duplicatedView.kanbanView!.columns && duplicatedView.kanbanView!.columns.length > 0) { - duplicatedView.kanbanView!.columns = duplicatedView.kanbanView!.columns.map((column: ColumnData) => ({ + if (duplicatedView?.kanbanView && duplicatedView.kanbanView.columns && duplicatedView.kanbanView.columns.length > 0) { + duplicatedView.kanbanView.columns = duplicatedView.kanbanView.columns.map((column: ColumnData) => ({ ...column, id: generateRandomNumber(), // Generate new numeric ID for each column })); @@ -406,23 +408,23 @@ const ConfigModalContent: React.FC = ({ ); // Set initial filter state - if (currentBoardData!.boardFilters) { - setTimeout(() => { + if (currentBoardData.boardFilters) { + window.setTimeout(() => { // Use type assertion to resolve non-null issues // const filterState = filterModal.liveFilterState as Filter; if (filterModal.advancedFilterComponent) { - filterModal.advancedFilterComponent.loadFilterState(currentBoardData!.boardFilters); + filterModal.advancedFilterComponent.loadFilterState(currentBoardData.boardFilters); } }, 100); } // Set the close callback - mainly used for handling cancel actions - filterModal.filterCloseCallback = async (filtersState) => { + filterModal.filterCloseCallback = (filtersState) => { if (filtersState) { // Save the filter state to the board let updatedcurrentBoardData = currentBoardData; - updatedcurrentBoardData!.boardFilters = filtersState; - plugin.taskBoardFileManager.saveBoard(updatedcurrentBoardData); + updatedcurrentBoardData.boardFilters = filtersState; + void plugin.taskBoardFileManager.saveBoard(updatedcurrentBoardData); // Refresh the board view eventEmitter.emit('REFRESH_BOARD'); @@ -433,9 +435,10 @@ const ConfigModalContent: React.FC = ({ } // Board Management - Function to handle duplicating the currently active board by creating a copy of the board data with a new name and adding it to the file system. After duplication, the new board is opened in a new view. - const handleDuplicateCurrentBoard = async () => { + const handleDuplicateCurrentBoard = () => { + const parsedData: unknown = JSON.parse(JSON.stringify(activeBoardData)); // Deep copy const duplicatedBoard: Board = { - ...JSON.parse(JSON.stringify(activeBoardData)), // Deep copy + ...parsedData as Board, id: generateRandomStringId('board'), name: `${activeBoardData.name} ${t("copy-suffix")}`, views: activeBoardData.views ? activeBoardData.views.map((view: TaskBoardViewType) => ({ @@ -453,9 +456,9 @@ const ConfigModalContent: React.FC = ({ newFilePath = normalizePath(duplicatedBoard.name + ".taskboard"); } - await plugin.taskBoardFileManager.saveBoardToDisk(newFilePath, duplicatedBoard); + void plugin.taskBoardFileManager.saveBoardToDisk(newFilePath, duplicatedBoard); - setTimeout(() => { + window.setTimeout(() => { eventEmitter.emit("OPEN_BOARD", { layout: "tab", filePath: newFilePath, @@ -472,7 +475,7 @@ const ConfigModalContent: React.FC = ({ const handleToggleBoardSettings = (viewIndex: number, field: BooleanBoardProperties, value: boolean) => { const updatedViewsData = [...allViewsData]; if (updatedViewsData[viewIndex]) { - (updatedViewsData[viewIndex] as any)[field] = value as boolean; + updatedViewsData[viewIndex][field] = value; } setAllViewsData(updatedViewsData); setIsEdited(true); @@ -480,7 +483,7 @@ const ConfigModalContent: React.FC = ({ const handleToggleKanbanViewSettings = (viewIndex: number, field: BooleanKanbanProperties, value: boolean) => { const updatedViewsData = [...allViewsData]; if (updatedViewsData[viewIndex] && updatedViewsData[viewIndex].kanbanView) { - (updatedViewsData[viewIndex].kanbanView as any)[field] = value as boolean; + updatedViewsData[viewIndex].kanbanView[field] = value; } setAllViewsData(updatedViewsData); setIsEdited(true); @@ -510,6 +513,7 @@ const ConfigModalContent: React.FC = ({ // Find and return the updated current view data // const updatedCurrentBoard = allViewsData.find(view => view.id === currentBoardData.id); // if (updatedCurrentBoard) { + onSave(boardToSave); // } }; @@ -518,7 +522,7 @@ const ConfigModalContent: React.FC = ({ // ALL RENDERING LOGIC BELOW // -------------------------------------------------------------- - const getViewTypeIconComponent = (viewTypeName: string | undefined, size: number) => { + const getViewTypeIconComponent = (viewTypeName: viewTypeNames | undefined, size: number) => { let viewType = viewTypeName ?? currentBoardData.views[currentViewIndex].viewType; switch (viewType) { case viewTypeNames.kanban: @@ -541,14 +545,14 @@ const ConfigModalContent: React.FC = ({ }; useEffect(() => { if (isSidebarVisible) { - document.addEventListener("mousedown", handleClickOutside); + activeDocument.addEventListener("mousedown", handleClickOutside); } else { - document.removeEventListener("mousedown", handleClickOutside); + activeDocument.removeEventListener("mousedown", handleClickOutside); } return () => { if (isSidebarVisible) { // Cleanup event listener when the component unmounts or sidebar visibility changes - document.removeEventListener("mousedown", handleClickOutside); + activeDocument.removeEventListener("mousedown", handleClickOutside); } } }, [isSidebarVisible]); @@ -573,7 +577,7 @@ const ConfigModalContent: React.FC = ({ settingManager.cleanUp(); globalSettingsHTMLSection.current.empty(); // Render global settings - settingManager.constructUI(globalSettingsHTMLSection.current, t("plugin-global-settings")); + void settingManager.constructUI(globalSettingsHTMLSection.current, t("plugin-global-settings")); } }, [selectedViewIndex]); const renderGlobalSettingsTab = (viewIndex: number) => { @@ -647,7 +651,7 @@ const ConfigModalContent: React.FC = ({
- +
@@ -692,6 +696,28 @@ const ConfigModalContent: React.FC = ({

{view.viewName} {t("configurations")}


+
+
+
{t("view-id")}
+
{t("view-id-info")}
+
+
+
{view.viewId}
+ { + try { + void navigator.clipboard.writeText(view.viewId); + new Notice(t("copy-view-id-successful")); + } catch (error) { + bugReporterManagerInsatance.addToLogs( + 221, + String(error), + "BoardConfigModal.tsx/renderViewSettings/copy-view-id", + ); + new Notice(t("copy-task-title-unsuccessful")); + } + }} /> +
+
{t("view-name")}
@@ -1282,7 +1308,7 @@ const ConfigModalContent: React.FC = ({

- +
@@ -1350,7 +1376,14 @@ const ConfigModalContent: React.FC = ({

- +
@@ -1365,7 +1398,14 @@ const ConfigModalContent: React.FC = ({ )}
- + ); }; diff --git a/src/modals/BoardsExplorer.ts b/src/modals/BoardsExplorer.ts index 18449ed9..33a1b33c 100644 --- a/src/modals/BoardsExplorer.ts +++ b/src/modals/BoardsExplorer.ts @@ -58,9 +58,9 @@ export class BoardsExplorerModal extends Modal { text: t("refresh-boards"), cls: "boardsExplorerScanButton", }); - scanButton.addEventListener("click", async () => { + scanButton.addEventListener("click", () => { if (!this.isScanning) { - await this.handleScanBoards( + void this.handleScanBoards( mainContent, headerSection, scanButton, @@ -121,8 +121,8 @@ export class BoardsExplorerModal extends Modal { cls: "boardsExplorerCard", }); - card.addEventListener("click", async () => { - await this.openBoard(boardId, board.filePath); + card.addEventListener("click", () => { + void this.openBoard(boardId, board.filePath); this.close(); }); @@ -170,7 +170,7 @@ export class BoardsExplorerModal extends Modal { }); pathRow.createEl("span", { - text: "File Path:", + text: t("file-path") + " : ", cls: "boardsExplorerCardRowLabel", }); @@ -231,57 +231,10 @@ export class BoardsExplorerModal extends Modal { } private createLoadingBar(): HTMLElement { - const container = document.createElement("div"); - container.className = "boardsExplorerLoadingBarContainer"; - - const style = document.createElement("style"); - style.textContent = ` - .boardsExplorerLoadingBarContainer { - width: calc(100% + 40px); - margin: 0 -20px 12px -20px; - padding: 0 20px; - overflow: hidden; - } - - .boardsExplorerLoadingBar { - width: 100%; - height: 4px; - background: linear-gradient( - 90deg, - #0066cc 0%, - #0066cc 20%, - transparent 20%, - transparent 40%, - #0066cc 40%, - #0066cc 60%, - transparent 60%, - transparent 80%, - #0066cc 80%, - #0066cc 100% - ); - background-size: 100px 4px; - animation: boardsExplorerLoadingPendulum 2s ease-in-out infinite; - border-radius: 2px; - } - - @keyframes boardsExplorerLoadingPendulum { - 0% { - background-position: -100px 0; - } - 50% { - background-position: calc(100% + 100px) 0; - } - 100% { - background-position: -100px 0; - } - } - `; - - container.appendChild(style); - - const bar = document.createElement("div"); - bar.className = "boardsExplorerLoadingBar"; - + const container = activeDocument.createElement("div"); + container.addClass("boardsExplorerLoadingBarContainer"); + const bar = activeDocument.createElement("div"); + bar.addClass("boardsExplorerLoadingBar"); container.appendChild(bar); return container; diff --git a/src/modals/BugReporterModal.ts b/src/modals/BugReporterModal.ts index d00dc232..67c85a46 100644 --- a/src/modals/BugReporterModal.ts +++ b/src/modals/BugReporterModal.ts @@ -120,9 +120,9 @@ export class BugReporterModal extends Modal { } async sanitizeBugReportContent( - message: String, - bugContent: String, - context: String, + message: string, + bugContent: string, + context: string, ) { // Sanitize the bug report content to prevent XSS attacks let sanitizedErrorContent = bugContent; @@ -189,7 +189,7 @@ export class BugReporterModal extends Modal { handleCopyBtnEvent(bugReportContent: string) { // Copy the bug report content to clipboard - navigator.clipboard.writeText(bugReportContent).then(() => { + void navigator.clipboard.writeText(bugReportContent).then(() => { new Notice(t("copied-to-clipboard")); }); } diff --git a/src/modals/ConfigureColumnSortingModal.ts b/src/modals/ConfigureColumnSortingModal.ts index 6789aa0f..7899df8b 100644 --- a/src/modals/ConfigureColumnSortingModal.ts +++ b/src/modals/ConfigureColumnSortingModal.ts @@ -31,9 +31,10 @@ export class ConfigureColumnSortingModal extends Modal { // Deep-copy columnConfiguration to avoid mutating caller's object (avoid stale/unsaved changes) // This ensures that edits in the modal don't affect the original object until Save is clicked try { - this.columnConfiguration = JSON.parse( + const parsedData: unknown = JSON.parse( JSON.stringify(columnConfiguration), ); + this.columnConfiguration = parsedData as ColumnData; } catch (e) { // Fallback to shallow copy if stringify fails this.columnConfiguration = { ...columnConfiguration }; @@ -95,7 +96,7 @@ export class ConfigureColumnSortingModal extends Modal { forceFallback: true, fallbackClass: "task-board-sortable-fallback", easing: "cubic-bezier(1, 0, 0, 1)", - onSort: async () => { + onSort: () => { // Use stable uid attributes on DOM rows to determine new order const uidsInDom = Array.from(sortingCriteriaList.children).map( (child) => child.getAttribute("data-uid"), @@ -104,7 +105,7 @@ export class ConfigureColumnSortingModal extends Modal { const newOrder: ColumnData["sortCriteria"] = []; uidsInDom.forEach((uid, idx) => { if (!uid) return; - const found = existing.find((c) => (c as any).uid === uid); + const found = existing.find((c) => c.uid === uid); if (found) { found.priority = idx + 1; newOrder.push(found); @@ -129,7 +130,7 @@ export class ConfigureColumnSortingModal extends Modal { cls: "configureColumnSortingModalHomeSortingCriteriaListItemRow", attr: { "data-criteria-name": sortCriteria.criteria, - "data-uid": (sortCriteria as any).uid, + "data-uid": sortCriteria.uid, }, }); @@ -257,7 +258,7 @@ export class ConfigureColumnSortingModal extends Modal { }) .addButton((del) => del - .setButtonText("delete") + .setButtonText(t("delete")) .setIcon("trash") .setClass( "configureColumnSortingModalHomeSortingCriteriaListItemDeleteCriterion", @@ -301,7 +302,7 @@ export class ConfigureColumnSortingModal extends Modal { text: t("add-new-sorting-criterion"), cls: "configureColumnSortingModalHomeAddSortingBtn", }); - addNewSortingButton.addEventListener("click", async () => { + addNewSortingButton.addEventListener("click", () => { if ( this.columnConfiguration.sortCriteria?.some( (c) => c.criteria === "manualOrder", diff --git a/src/modals/CustomStatusConfigurator.ts b/src/modals/CustomStatusConfigurator.ts index 51b93409..e9d46241 100644 --- a/src/modals/CustomStatusConfigurator.ts +++ b/src/modals/CustomStatusConfigurator.ts @@ -3,14 +3,14 @@ import { Modal, Setting, TextComponent } from "obsidian"; import type { Plugin } from "obsidian"; import { statusTypeNames } from "../interfaces/Enums.js"; import { CustomStatus } from "../interfaces/GlobalSettings.js"; -import { StatusType, StatusConfiguration } from "../interfaces/StatusConfiguration.js"; +import { StatusConfiguration } from "../interfaces/StatusConfiguration.js"; export class CustomStatusModal extends Modal { statusSymbol: string; statusName: string; statusNextSymbol: string; statusAvailableAsCommand: boolean; - type: StatusType; + type: statusTypeNames; saved: boolean = false; error: boolean = false; @@ -21,14 +21,14 @@ export class CustomStatusModal extends Modal { isCoreStatus: boolean, ) { super(plugin.app); - const status = statusType as any; + let status = statusType; this.statusSymbol = status.symbol; this.statusName = status.name; this.statusNextSymbol = status.nextStatusSymbol; this.statusAvailableAsCommand = status.availableAsCommand; - // Ensure type is a valid StatusType enum value + // Ensure type is a valid statusTypeNames enum value if (typeof status.type === "string") { - this.type = status.type as StatusType; + this.type = status.type; } else { this.type = status.type; } @@ -57,12 +57,10 @@ export class CustomStatusModal extends Modal { const settingDiv = contentEl.createDiv(); //const title = this.title ?? '...'; - let statusSymbolText: TextComponent; new Setting(settingDiv) .setName(t("task-status-symbol")) .setDesc(t("task-status-symbol-info")) .addText((text) => { - statusSymbolText = text; text.setValue(this.statusSymbol).onChange((v) => { this.statusSymbol = v; }); @@ -72,12 +70,10 @@ export class CustomStatusModal extends Modal { // Show any error if the initial value loaded is incorrect. }); - let statusNameText: TextComponent; new Setting(settingDiv) .setName(t("task-status-name")) .setDesc(t("task-status-name-info")) .addText((text) => { - statusNameText = text; text.setValue(this.statusName).onChange((v) => { this.statusName = v; }); @@ -100,17 +96,15 @@ export class CustomStatusModal extends Modal { dropdown.addOption(s, s); }); dropdown.setValue(this.type).onChange((v) => { - this.type = v as StatusType; + this.type = v as statusTypeNames; }); }); - let statusNextSymbolText: TextComponent; new Setting(settingDiv) .setName(t("cycle-to-following-status")) .setDesc(t("cycle-to-following-status-info")) .addText((text) => { - text.setPlaceholder("eg.: /"); - statusNextSymbolText = text; + text.setPlaceholder("Eg.: /"); text.setValue(this.statusNextSymbol).onChange((v) => { this.statusNextSymbol = v; }); @@ -145,7 +139,7 @@ export class CustomStatusModal extends Modal { // titleSpan.prepend(iconEl); // } onOpen() { - this.display(); + void this.display(); } static setValidationError(textInput: TextComponent) { diff --git a/src/modals/DiffContentCompareModal.ts b/src/modals/DiffContentCompareModal.ts index d8a56ca5..3e2ab307 100644 --- a/src/modals/DiffContentCompareModal.ts +++ b/src/modals/DiffContentCompareModal.ts @@ -2,6 +2,7 @@ import { Modal, ButtonComponent } from "obsidian"; import TaskBoard from "../../main.js"; +import { t } from "i18next"; export type DiffSelection = "old" | "new"; @@ -33,13 +34,13 @@ export class DiffContentCompareModal extends Modal { "data-type", "task-board-diff-content-compare", ); - this.setTitle("Task Board Safe Guard"); + this.setTitle(t("task-board-safe-guard")); contentEl.classList.add("taskboard-diff-content-compare-modal"); contentEl.createEl("h2", { text: "Content mismatch detected" }); contentEl.createEl("p", { - text: "Looks like the task content in your file is a little different from the content you just edited through Task Board. Choose which version of content you would like to keep:", + text: "Looks like the task content in your file is a little different from the content you just edited through task board. Choose which version of content you would like to keep:", }); const comparisonContainer = contentEl.createDiv({ @@ -53,13 +54,18 @@ export class DiffContentCompareModal extends Modal { const newContentDiv = rightDiv.createDiv({ cls: "taskboard-diff-content-compare-modal-content", }); - newContentDiv.insertAdjacentHTML( - "beforeend", - this.getHighlightedDiff( - this.cachedTaskContent, - this.taskContentFromFile, - "left", - ), + // newContentDiv.insertAdjacentHTML( + // "beforeend", + // this.getHighlightedDiff( + // this.cachedTaskContent, + // this.taskContentFromFile, + // "left", + // ), + // ); + newContentDiv.textContent = this.getHighlightedDiff( + this.cachedTaskContent, + this.taskContentFromFile, + "left", ); new ButtonComponent(rightDiv) .setButtonText("Keep this as it is") @@ -76,13 +82,18 @@ export class DiffContentCompareModal extends Modal { const oldContentDiv = leftDiv.createDiv({ cls: "taskboard-diff-content-compare-modal-content", }); - oldContentDiv.insertAdjacentHTML( - "beforeend", - this.getHighlightedDiff( - this.cachedTaskContent, - this.EditedTaskContent, - "right", - ), + // oldContentDiv.insertAdjacentHTML( + // "beforeend", + // this.getHighlightedDiff( + // this.cachedTaskContent, + // this.EditedTaskContent, + // "right", + // ), + // ); + oldContentDiv.textContent = this.getHighlightedDiff( + this.cachedTaskContent, + this.EditedTaskContent, + "right", ); new ButtonComponent(leftDiv) .setButtonText("Use this") @@ -93,9 +104,10 @@ export class DiffContentCompareModal extends Modal { }); const noteOnSetting = contentEl.createEl("p", { + // eslint-disable-next-line obsidianmd/ui/sentence-case text: "NOTE : If you think this safe guard modal shown is irrelevant and its also popping-up too many times, you can by-pass it and disable this from the settings under the 'general' tab.", }); - noteOnSetting.style.color = "red"; + noteOnSetting.addClass("tb_color_white"); const infoDiv = contentEl.createDiv({ cls: "taskboard-diff-content-compare-modal-info", @@ -108,10 +120,10 @@ export class DiffContentCompareModal extends Modal { text: "Green highlighted content indicates the characters which are different by comparing the content you just now edited and the content from the task-board-cache..", }); ul.createEl("li", { - text: "Select the version of content you want Task Board to write in your note.", + text: "Select the version of content you want task board to write in your note.", }); ul.createEl("li", { - text: "If the content shown on left-side is completely different from the content of the task you just edited. This probably means that, Task Board couldnt able to find the task you just edited through its cache inside the current file at the particular line. Either some other plugin updated the content or during sync the content was tempered. Use the abort button below to close this modal, to avoid updating any data. And scan the file using file-menu option or update the task manually.", + text: "If the content shown on left-side is completely different from the content of the task you just edited. This probably means that, task board couldnt able to find the task you just edited through its cache inside the current file at the particular line. Either some other plugin updated the content or during sync the content was tempered. Use the abort button below to close this modal, to avoid updating any data. And scan the file using file-menu option or update the task manually.", }); }); @@ -127,7 +139,7 @@ export class DiffContentCompareModal extends Modal { contentEl.createEl("hr"); - contentEl.createEl("h2", { text: "Debug Info" }); + contentEl.createEl("h2", { text: "Debug info" }); contentEl.createEl("p", { text: "Here is why this modal was shown in the first place. If you think this modal shown was unnecessary and if possible kindly share this information with the developer to improve the plugin.", }); @@ -146,37 +158,48 @@ export class DiffContentCompareModal extends Modal { debuInfoComparisonContainerLefttDiv.createDiv({ cls: "taskboard-diff-content-compare-modal-content", }); - debuInfoComparisonContainerLefttDivContent.insertAdjacentHTML( - "beforeend", + // debuInfoComparisonContainerLefttDivContent.insertAdjacentHTML( + // "beforeend", + // this.getHighlightedDiff( + // this.cachedTaskContent, + // this.taskContentFromFile, + // "left", + // ), + // ); + debuInfoComparisonContainerLefttDivContent.textContent = this.getHighlightedDiff( this.cachedTaskContent, this.taskContentFromFile, "left", - ), - ); - + ); const debuInfoComparisonContainerRightDiv = debuInfoComparisonContainer.createDiv({ cls: "taskboard-diff-content-compare-modal-side", }); debuInfoComparisonContainerRightDiv.createEl("h3", { - text: "Task Board Cache", + text: t("task-board-cache"), }); const debuInfoComparisonContainerRightDivContent = debuInfoComparisonContainerRightDiv.createDiv({ cls: "taskboard-diff-content-compare-modal-content", }); - debuInfoComparisonContainerRightDivContent.insertAdjacentHTML( - "beforeend", + // debuInfoComparisonContainerRightDivContent.insertAdjacentHTML( + // "beforeend", + // this.getHighlightedDiff( + // this.taskContentFromFile, + // this.cachedTaskContent, + // "right", + // ), + // ); + debuInfoComparisonContainerRightDivContent.textContent = this.getHighlightedDiff( this.taskContentFromFile, this.cachedTaskContent, "right", - ), - ); + ); contentEl.createEl("p", { - text: "This 'Debug info' section is just provided to find out whether the Safe Guard genuinely captured the difference correctly or whether this was a false alarm. Will remove this section in the future, if this safe guard feature is working as expected.", + text: "This 'debug info' section is just provided to find out whether the safe guard genuinely captured the difference correctly or whether this was a false alarm. Will remove this section in the future, if this safe guard feature is working as expected.", }); } @@ -264,7 +287,7 @@ export class DiffContentCompareModal extends Modal { .replace(/&/g, "&") .replace(//g, ">") - .replace(/\"/g, """) + .replace(/"/g, """) .replace(/'/g, "'"); } } diff --git a/src/modals/EditTagsModal.ts b/src/modals/EditTagsModal.ts index 96519682..fc761a3c 100644 --- a/src/modals/EditTagsModal.ts +++ b/src/modals/EditTagsModal.ts @@ -1,93 +1,112 @@ -import { Modal } from 'obsidian'; -import TaskBoard from '../../main.js'; +import { Modal } from "obsidian"; +import TaskBoard from "../../main.js"; +import { MultiSuggest } from "../services/MultiSuggest.js"; export default class EditTagsModal extends Modal { - plugin: TaskBoard; - initialTags: string[]; - onSave: (tags: string[]) => void; - - private tagsContainer: HTMLElement | null = null; - private inputEl: HTMLInputElement | null = null; - - constructor(plugin: TaskBoard, initialTags: string[], onSave: (tags: string[]) => void) { - super(plugin.app); - this.plugin = plugin; - this.initialTags = initialTags || []; - this.onSave = onSave; - } - - onOpen() { - const { contentEl } = this; - contentEl.empty(); - contentEl.addClass('task-board-edit-tags-modal'); - - const title = contentEl.createEl('h3', { text: 'Edit tags' }); - this.tagsContainer = contentEl.createDiv({ cls: 'tags-container' }); - - const inputWrap = contentEl.createDiv({ cls: 'tag-input-wrap' }); - this.inputEl = inputWrap.createEl('input', { type: 'text' }) as HTMLInputElement; - this.inputEl.placeholder = 'Type to add tag and press Enter (use #prefix optional)'; - - const info = contentEl.createEl('div', { text: 'Existing tags:' }); - - // Render initial tags as capsules - this.initialTags.forEach(t => this.addTagPill(t)); - - // MultiSuggest for tag suggestions (best-effort) - try { - // @ts-ignore - MultiSuggest is available globally in project - const suggestionContent = this.plugin.settings.data.tagColors?.map(tc => tc.name) || []; - // @ts-ignore - new (window as any).MultiSuggest(this.inputEl, new Set(suggestionContent), (choice: string) => { - this.handleAddTag(choice); - }, this.plugin.app); - } catch (error) { - // ignore if MultiSuggest not available in this scope - } - - this.inputEl.addEventListener('keydown', (ev) => { - if (ev.key === 'Enter') { - ev.preventDefault(); - const v = this.inputEl!.value.trim(); - if (v) this.handleAddTag(v); - this.inputEl!.value = ''; - } - }); - - const footer = contentEl.createDiv({ cls: 'modal-footer' }); - const saveBtn = footer.createEl('button', { text: 'Save' }); - saveBtn.addEventListener('click', () => { - const tags = Array.from(this.tagsContainer!.querySelectorAll('.tag-pill')).map((el: any) => el.getAttribute('data-tag')); - this.onSave(tags); - this.close(); - }); - - const cancelBtn = footer.createEl('button', { text: 'Cancel' }); - cancelBtn.addEventListener('click', () => this.close()); - } - - addTagPill(tag: string) { - if (!this.tagsContainer) return; - const normalized = tag.startsWith('#') ? tag : `#${tag}`; - // avoid duplicates - const exists = Array.from(this.tagsContainer.querySelectorAll('.tag-pill')).some((el: any) => el.getAttribute('data-tag') === normalized); - if (exists) return; - const pill = this.tagsContainer.createDiv({ cls: 'tag-pill' }); - pill.setAttr('data-tag', normalized); - pill.createSpan({ text: normalized }); - const removeBtn = pill.createEl('button', { text: '✕' }); - removeBtn.addEventListener('click', () => pill.remove()); - } - - handleAddTag(tag: string) { - if (!this.tagsContainer) return; - const t = tag.trim(); - if (!t) return; - this.addTagPill(t); - } - - onClose() { - const { contentEl } = this; - contentEl.empty(); - } + plugin: TaskBoard; + initialTags: string[]; + onSave: (tags: string[]) => void; + + private tagsContainer: HTMLElement | null = null; + private inputEl: HTMLInputElement | null = null; + + constructor( + plugin: TaskBoard, + initialTags: string[], + onSave: (tags: string[]) => void, + ) { + super(plugin.app); + this.plugin = plugin; + this.initialTags = initialTags || []; + this.onSave = onSave; + } + + onOpen() { + const { contentEl } = this; + contentEl.empty(); + contentEl.addClass("task-board-edit-tags-modal"); + + const title = contentEl.createEl("h3", { text: "Edit tags" }); + this.tagsContainer = contentEl.createDiv({ cls: "tags-container" }); + + const inputWrap = contentEl.createDiv({ cls: "tag-input-wrap" }); + this.inputEl = inputWrap.createEl("input", { type: "text" }); + this.inputEl.placeholder = "Hit enter key after typing the tag"; + + const info = contentEl.createEl("div", { text: "Existing tags:" }); + + // Render initial tags as capsules + this.initialTags.forEach((t) => this.addTagPill(t)); + + // MultiSuggest for tag suggestions (best-effort) + try { + // @ts-ignore - MultiSuggest is available globally in project + const suggestionContent = + this.plugin.settings.data.tagColors?.map((tc) => tc.name) || []; + // @ts-ignore + new MultiSuggest( + this.inputEl, + new Set(suggestionContent), + (choice: string) => { + this.handleAddTag(choice); + }, + this.plugin.app, + ); + } catch (error) { + // ignore if MultiSuggest not available in this scope + } + + this.inputEl.addEventListener("keydown", (ev) => { + if (ev.key === "Enter") { + ev.preventDefault(); + const v = this.inputEl!.value.trim(); + if (v) this.handleAddTag(v); + this.inputEl!.value = ""; + } + }); + + const footer = contentEl.createDiv({ cls: "modal-footer" }); + const saveBtn = footer.createEl("button", { text: "Save" }); + saveBtn.addEventListener("click", () => { + const tags = Array.from( + this.tagsContainer!.querySelectorAll(".tag-pill"), + ).map((el: Element) => el.getAttribute("data-tag")); + + const validTags = tags.filter( + (tag: unknown) => typeof tag === "string", + ); + this.onSave(validTags); + this.close(); + }); + + const cancelBtn = footer.createEl("button", { text: "Cancel" }); + cancelBtn.addEventListener("click", () => this.close()); + } + + addTagPill(tag: string) { + if (!this.tagsContainer) return; + const normalized = tag.startsWith("#") ? tag : `#${tag}`; + // avoid duplicates + const exists = Array.from( + this.tagsContainer.querySelectorAll(".tag-pill"), + ).some((el: Element) => el.getAttribute("data-tag") === normalized); + if (exists) return; + const pill = this.tagsContainer.createDiv({ cls: "tag-pill" }); + pill.setAttr("data-tag", normalized); + pill.createSpan({ text: normalized }); + const removeBtn = pill.createEl("button", { text: "✕" }); + removeBtn.addEventListener("click", () => pill.remove()); + } + + handleAddTag(tag: string) { + if (!this.tagsContainer) return; + const t = tag.trim(); + if (!t) return; + this.addTagPill(t); + } + + onClose() { + const { contentEl } = this; + contentEl.empty(); + } } diff --git a/src/modals/MergeBoardsModal.ts b/src/modals/MergeBoardsModal.ts index c4692819..c4861648 100644 --- a/src/modals/MergeBoardsModal.ts +++ b/src/modals/MergeBoardsModal.ts @@ -1,6 +1,6 @@ import { App, Modal, Setting, Notice, TFile, setIcon } from "obsidian"; import TaskBoard from "../../main.js"; -import { Board } from "../interfaces/BoardConfigs.js"; +import { AdvancedFilter, Board } from "../interfaces/BoardConfigs.js"; import TaskBoardFileManager from "../managers/TaskBoardFileManager.js"; import { MultiSuggest } from "../services/MultiSuggest.js"; import { generateRandomStringId } from "../utils/TaskItemUtils.js"; @@ -141,9 +141,11 @@ export class MergeBoardsModal extends Modal { const actions = modalContent.createDiv({ cls: "mergeBoardsModalActions", }); - this.mergeButton = actions.createEl("button", { text: "Merge Boards" }); + this.mergeButton = actions.createEl("button", { + text: t("merge-boards"), + }); this.mergeButton.addEventListener("click", () => { - this.handleMerge(); + void this.handleMerge(); }); // Cancel Button @@ -227,7 +229,7 @@ export class MergeBoardsModal extends Modal { // Re-enable button if (this.mergeButton) { this.mergeButton.disabled = false; - this.mergeButton.textContent = "Merge Boards"; + this.mergeButton.textContent = t("merge-boards"); } } } @@ -236,18 +238,6 @@ export class MergeBoardsModal extends Modal { // Generate new ID const newId = generateRandomStringId("board"); - // Combine filterConfig - const combinedFilterConfig = { - enableSavedFilters: - board1.filterConfig?.enableSavedFilters || - board2.filterConfig?.enableSavedFilters || - false, - savedConfigs: [ - ...(board1.filterConfig?.savedConfigs || []), - ...(board2.filterConfig?.savedConfigs || []), - ], - }; - // Combine views const combinedViews = [...board1.views, ...board2.views]; @@ -257,7 +247,6 @@ export class MergeBoardsModal extends Modal { revision: board1?.revision ?? CURRENT_REVISION, name: this.newBoardName, description: board1.description, - filterConfig: combinedFilterConfig, views: combinedViews, // lastViewId: board1.lastViewId, // Use from first board lastViewIndex: 0, diff --git a/src/modals/ModifiedFilesModal.ts b/src/modals/ModifiedFilesModal.ts index 5c3de16b..0fcf4c21 100644 --- a/src/modals/ModifiedFilesModal.ts +++ b/src/modals/ModifiedFilesModal.ts @@ -109,6 +109,7 @@ export class ModifiedFilesModal extends Modal { cls: "deletedFilesSectionHeader", }); sectionContainer.createEl("p", { + // eslint-disable-next-line obsidianmd/ui/sentence-case text: "NOTE : Renamed files are little difficult to find. So if you see a lot of files here, it will be good idea to run the vault scan to re-build the cache.", }); diff --git a/src/modals/ScanFilterModal.ts b/src/modals/ScanFilterModal.ts index 84feebd3..3938bc3f 100644 --- a/src/modals/ScanFilterModal.ts +++ b/src/modals/ScanFilterModal.ts @@ -115,7 +115,7 @@ export class ScanFilterModal extends Modal { } this.selectedValues.forEach((value) => { - const itemEl = listEl!.createDiv("scan-filter-item"); + const itemEl = listEl.createDiv("scan-filter-item"); itemEl.setText(value); const removeBtn = itemEl.createEl("button", { text: "Remove", diff --git a/src/modals/ScanVaultModal.tsx b/src/modals/ScanVaultModal.tsx index 8884037b..e859abb6 100644 --- a/src/modals/ScanVaultModal.tsx +++ b/src/modals/ScanVaultModal.tsx @@ -21,7 +21,7 @@ export const findMaxIdCounterAndUpdateSettings = (plugin: TaskBoard) => { // Check Pending tasks Object.values(plugin.vaultScanner.tasksCache.Pending).forEach((tasks) => { tasks.forEach((task) => { - const taskIdNum = task.legacyId ? parseInt(task.legacyId as unknown as string, 10) : 0; + const taskIdNum = task.legacyId ? parseInt(task.legacyId, 10) : 0; if (!isNaN(taskIdNum) && taskIdNum > maxId) { maxId = taskIdNum; } @@ -31,7 +31,7 @@ export const findMaxIdCounterAndUpdateSettings = (plugin: TaskBoard) => { // Check Completed tasks Object.values(plugin.vaultScanner.tasksCache.Completed).forEach((tasks) => { tasks.forEach((task) => { - const taskIdNum = task.legacyId ? parseInt(task.legacyId as unknown as string, 10) : 0; + const taskIdNum = task.legacyId ? parseInt(task.legacyId, 10) : 0; if (!isNaN(taskIdNum) && taskIdNum > maxId) { maxId = taskIdNum; } @@ -40,7 +40,7 @@ export const findMaxIdCounterAndUpdateSettings = (plugin: TaskBoard) => { // Update the uniqueIdCounter in settings to be one more than the max found ID plugin.settings.data.uniqueIdCounter = maxId + 1; - plugin.saveSettings(); + void plugin.saveSettings(); } const ScanVaultModalContent: React.FC<{ app: App, plugin: TaskBoard, vaultScanner: VaultScanner }> = ({ app, plugin, vaultScanner }) => { @@ -97,8 +97,8 @@ const ScanVaultModalContent: React.FC<{ app: App, plugin: TaskBoard, vaultScanne findMaxIdCounterAndUpdateSettings(plugin); // If mandatory scan, close all existing Task Board views to force re-opening with the newly scanned data. Else, just emit REFRESH_BOARD event to update the existing board(s) with the newly scanned data. - if (localStorage.getItem(MANDATORY_SCAN_KEY) === "true") { - localStorage.setItem(MANDATORY_SCAN_KEY, "false"); + if (plugin.app.loadLocalStorage(MANDATORY_SCAN_KEY) === "true") { + plugin.app.saveLocalStorage(MANDATORY_SCAN_KEY, "false"); plugin.app.workspace.getLeavesOfType(VIEW_TYPE_TASKBOARD).forEach((leaf) => { leaf.detach(); }); @@ -115,7 +115,8 @@ const ScanVaultModalContent: React.FC<{ app: App, plugin: TaskBoard, vaultScanne const componentRef = useRef(null); useEffect(() => { // Initialize Obsidian Component on mount - componentRef.current = plugin.view; + // componentRef.current = plugin.view; + componentRef.current = new Component(); }, []); const taskRendererRef = useRef<{ [key: string]: HTMLDivElement | null }>({}); @@ -135,7 +136,7 @@ const ScanVaultModalContent: React.FC<{ app: App, plugin: TaskBoard, vaultScanne priority: task.priority, }; - getFormattedTaskContent(newTaskContent).then((formatedContent) => { + void getFormattedTaskContent(newTaskContent).then((formatedContent) => { const uniqueKey = `${filePath}-task-${taskIndex}`; const descElement = taskRendererRef.current[uniqueKey]; @@ -143,7 +144,7 @@ const ScanVaultModalContent: React.FC<{ app: App, plugin: TaskBoard, vaultScanne if (descElement && formatedContent !== "") { descElement.empty(); // Render task description using MarkdownUIRenderer - MarkdownUIRenderer.renderTaskDisc( + void MarkdownUIRenderer.strictRender( app, formatedContent, descElement, @@ -160,7 +161,7 @@ const ScanVaultModalContent: React.FC<{ app: App, plugin: TaskBoard, vaultScanne return (

{t("vault-scanner")}

- {localStorage.getItem(MANDATORY_SCAN_KEY) === "true" ? + {plugin.app.loadLocalStorage(MANDATORY_SCAN_KEY) === "true" ? (<>
{t("scan-vault-from-the-vault-upgrade-message-1")} {CURRENT_PLUGIN_VERSION}
{t("scan-vault-from-the-vault-upgrade-message-2")}
@@ -178,7 +179,7 @@ const ScanVaultModalContent: React.FC<{ app: App, plugin: TaskBoard, vaultScanne
-
@@ -200,7 +201,7 @@ const ScanVaultModalContent: React.FC<{ app: App, plugin: TaskBoard, vaultScanne

{filePath}

- {collectedTasks.Pending[filePath].map((task: any, taskIndex: number) => { + {collectedTasks.Pending[filePath].map((task: unknown, taskIndex: number) => { const uniqueKey = `${filePath}-task-${taskIndex}`; return (
diff --git a/src/modals/SwimlanesConfigModal.tsx b/src/modals/SwimlanesConfigModal.tsx index beae984a..2f651f1d 100644 --- a/src/modals/SwimlanesConfigModal.tsx +++ b/src/modals/SwimlanesConfigModal.tsx @@ -32,7 +32,7 @@ export class SwimlanesConfigModal extends Modal { private customSortOrder: { value: string; index: number }[]; private maxHeight: string; private groupAllRest: boolean; - private headerUIType: string; + private headerUIType: HeaderUITypeOptions; private sortableInstance: Sortable | null = null; private sortableListEl: HTMLElement | null = null; @@ -498,7 +498,7 @@ export class SwimlanesConfigModal extends Modal { .addToggle((toggle) => toggle .setValue(this.hideEmptySwimlanes) - .onChange(async (value) => { + .onChange((value) => { this.hideEmptySwimlanes = value; this.edited = true; }),) @@ -519,9 +519,9 @@ export class SwimlanesConfigModal extends Modal { }); dropdown .setValue(this.headerUIType) - .onChange(async (value) => { + .onChange((value) => { this.headerUIType = - value; + value as HeaderUITypeOptions; this.edited = true; }) }); diff --git a/src/modals/TaskBoardActionsModal.ts b/src/modals/TaskBoardActionsModal.ts index 19b073ff..3b692b69 100644 --- a/src/modals/TaskBoardActionsModal.ts +++ b/src/modals/TaskBoardActionsModal.ts @@ -18,7 +18,7 @@ export class TaskBoardActionsModal extends Modal { super(plugin.app); this.plugin = plugin; this.columns = columns; - this.setTitle("Task Board Actions"); + this.setTitle("Task board actions"); } onOpen() { @@ -30,7 +30,7 @@ export class TaskBoardActionsModal extends Modal { // Header const header = contentEl.createDiv({ cls: "taskboard-actions-header" }); - header.createEl("h2", { text: "Active Actions" }); + header.createEl("h2", { text: "Active actions" }); const addBtn = header.createEl("button", { text: "Add action", @@ -44,7 +44,7 @@ export class TaskBoardActionsModal extends Modal { targetColumn: this.columns[0].name || "", }; this.plugin.settings.data.actions.push(newAction); - this.plugin.saveSettings(); + void this.plugin.saveSettings(); this.refresh(); }; @@ -54,7 +54,7 @@ export class TaskBoardActionsModal extends Modal { }); const inactiveSection = contentEl.createDiv(); inactiveSection.createEl("h2", { - text: "Inactive Actions", + text: "Inactive actions", cls: "taskboard-inactive-actions-section", }); @@ -62,8 +62,7 @@ export class TaskBoardActionsModal extends Modal { cls: "taskboard-actions-modal-list", }); - const actions: TaskBoardAction[] = - this.plugin.settings.data.actions; + const actions: TaskBoardAction[] = this.plugin.settings.data.actions; for (let i = 0; i < actions.length; i++) { const action = actions[i]; @@ -79,7 +78,7 @@ export class TaskBoardActionsModal extends Modal { toggle.setValue(action.enabled); toggle.onChange((val) => { action.enabled = val; - this.plugin.saveSettings(); + void this.plugin.saveSettings(); this.refresh(); }); @@ -90,7 +89,7 @@ export class TaskBoardActionsModal extends Modal { .setValue(action.trigger) .onChange((val) => { action.trigger = val as "Complete" | "Incomplete"; - this.plugin.saveSettings(); + void this.plugin.saveSettings(); }); new DropdownComponent(container) @@ -98,7 +97,7 @@ export class TaskBoardActionsModal extends Modal { .setValue(action.type) .onChange((val) => { action.type = val as "move" | "copy"; - this.plugin.saveSettings(); + void this.plugin.saveSettings(); }); container.createSpan({ text: "the card to" }); @@ -106,24 +105,21 @@ export class TaskBoardActionsModal extends Modal { new DropdownComponent(container) .addOptions( Object.fromEntries( - this.columns.map((c) => [c.index, c.name || ""]) - ) + this.columns.map((c) => [c.index, c.name || ""]), + ), ) .setValue(action.targetColumn) .onChange((val) => { action.targetColumn = val; - this.plugin.saveSettings(); + void this.plugin.saveSettings(); }); new ButtonComponent(container) .setIcon("trash") - .setTooltip("Delete Action") + .setTooltip("Delete action") .onClick(() => { - this.plugin.settings.data.actions.splice( - i, - 1 - ); - this.plugin.saveSettings(); + this.plugin.settings.data.actions.splice(i, 1); + void this.plugin.saveSettings(); this.refresh(); }); } diff --git a/src/modals/TaskEditorModal.tsx b/src/modals/TaskEditorModal.tsx index 34709d41..44ae2fa1 100644 --- a/src/modals/TaskEditorModal.tsx +++ b/src/modals/TaskEditorModal.tsx @@ -25,13 +25,13 @@ export class TaskEditorModal extends Modal { isEdited: boolean; isTaskNote: boolean; activeNote: boolean; - saveTask: (updatedTask: taskItem, quickAddPluginChoice: string, updatedNoteContent?: string) => void; + saveTask: (updatedTask: taskItem, quickAddPluginChoice: string, updatedNoteContent?: string) => Promise; public waitForClose: Promise; private resolvePromise: (input: string) => void = (input: string) => { }; private rejectPromise: (reason?: unknown) => void = (reason?: unknown) => { }; - constructor(plugin: TaskBoard, saveTask: (updatedTask: taskItem, quickAddPluginChoice: string, updatedNoteContent?: string) => void, isTaskNote: boolean, activeNote: boolean, taskExists: boolean, task?: taskItem, filePath?: string) { + constructor(plugin: TaskBoard, saveTask: (updatedTask: taskItem, quickAddPluginChoice: string, updatedNoteContent?: string) => Promise, isTaskNote: boolean, activeNote: boolean, taskExists: boolean, task?: taskItem, filePath?: string) { super(plugin.app); this.plugin = plugin; this.filePath = filePath ? filePath : ""; @@ -106,7 +106,7 @@ export class TaskEditorModal extends Modal { this.isEdited = false; const formattedContent = await getFormattedTaskContent(updatedTask); this.resolvePromise(formattedContent); - this.saveTask(updatedTask, quickAddPluginChoice, updatedNoteContent); + void this.saveTask(updatedTask, quickAddPluginChoice, updatedNoteContent); this.close(); }} onClose={() => this.close()} @@ -142,7 +142,6 @@ export class TaskEditorModal extends Modal { this.handleCloseAttempt(); } else { this.modalEl.addClass("slide-out"); - sleep(300); this.resolvePromise(""); this.onClose(); super.close(); diff --git a/src/modals/TextInputModal.ts b/src/modals/TextInputModal.ts index d8edc03d..81041024 100644 --- a/src/modals/TextInputModal.ts +++ b/src/modals/TextInputModal.ts @@ -47,7 +47,7 @@ export class TextInputModal extends Modal { }); // Focus the input - setTimeout(() => { + window.setTimeout(() => { this.inputEl.focus(); this.inputEl.select(); }, 100); diff --git a/src/modals/date_picker/DatePickerComponent.ts b/src/modals/date_picker/DatePickerComponent.ts index 1649ae4c..93d5092e 100644 --- a/src/modals/date_picker/DatePickerComponent.ts +++ b/src/modals/date_picker/DatePickerComponent.ts @@ -221,7 +221,7 @@ export class DatePickerComponent extends Component { }); // Month/Year display - const monthYear = header.createDiv({ + header.createDiv({ cls: "calendar-month-year", text: format(currentDate, "MMMM yyyy"), }); diff --git a/src/obsidian_views/Archived_TaskBoardView_TextFileView.tsx b/src/obsidian_views/Archived_TaskBoardView_TextFileView.tsx deleted file mode 100644 index 933cb12d..00000000 --- a/src/obsidian_views/Archived_TaskBoardView_TextFileView.tsx +++ /dev/null @@ -1,421 +0,0 @@ -// src/views/TaskBoardView.tsx - -import { TextFileView, Platform, WorkspaceLeaf, ViewStateResult, Notice, Menu } from "obsidian"; -import { Root, createRoot } from "react-dom/client"; -import { StrictMode } from "react"; -import { t } from "i18next"; -import TaskBoard from "../../main.js"; -import TaskBoardViewContainer from "../components/TaskBoardViewContainer.js"; -import { Board, DEFAULT_BOARD } from "../interfaces/BoardConfigs.js"; -import { VIEW_TYPE_TASKBOARD, PENDING_SCAN_FILE_STACK, MANDATORY_SCAN_KEY } from "../interfaces/Constants.js"; -import { TaskBoardIcon, funnelIcon, ScanVaultIcon, RefreshIcon } from "../interfaces/Icons.js"; -import { bugReporterManagerInsatance } from "../managers/BugReporter.js"; -import { eventEmitter } from "../services/EventEmitter.js"; -import { openScanVaultModal, openBoardsExplorerModal } from "../services/OpenModals.js"; -import { generateRandomStringId } from "../utils/TaskItemUtils.js"; - -export class TaskBoardView extends TextFileView { - plugin: TaskBoard; - leaf: WorkspaceLeaf; - root: Root | null = null; - private currentFilePath: string = ""; - boardName: string = "No Board Loaded"; - private currentBoard: Board | null = null; - - constructor(plugin: TaskBoard, leaf: WorkspaceLeaf) { - super(leaf); - this.leaf = leaf; - this.app = plugin.app; - this.plugin = plugin; - // this.boards = []; - this.icon = TaskBoardIcon; - } - - getViewType() { - return VIEW_TYPE_TASKBOARD; - } - - getDisplayText() { - return this.boardName; - } - - setViewData(data: string): void { - try { - const board = JSON.parse(data) as Board; - this.boardName = board.name || "Unnamed Board"; - this.currentBoard = board; - if (this.file) { - this.currentFilePath = this.file.path; - } - this.renderBoard(board); - } catch (error) { - bugReporterManagerInsatance.showNotice( - 183, - `Failed to load board from file: ${this.file?.path}`, - String(error), - "TaskBoardView.tsx/setViewData" - ); - this.renderNoBoard(); - } - } - - getViewData(): string { - return this.currentBoard ? JSON.stringify(this.currentBoard, null, "\t") : "{}"; - } - - clear(): void { - this.root?.unmount(); - this.root = null; - const container = this.containerEl.children[1] as HTMLElement; - if (container) { - container.empty(); - } - this.currentBoard = null; - this.boardName = "No Board Loaded"; - } - - onPaneMenu(menu: Menu, source: "more-options" | "tab-header" | string): void { - if (source === "more-options") { - menu.addItem((item) => { - item.setTitle(t("quick-actions")); - item.setIsLabel(true); - }); - menu.addItem((item) => { - item.setTitle(t("refresh-the-board")); - item.setIcon("rotate-cw"); - item.onClick(async () => { - // refreshBoardButton(); - }); - }); - menu.addItem((item) => { - item.setTitle(t("show-hide-properties")); - item.setIcon("list"); - item.onClick(async () => { - // handlePropertiesBtnClick(event); - }); - }); - menu.addItem((item) => { - item.setTitle(t("open-board-filters-modal")); - item.setIcon(funnelIcon); - item.onClick(async () => { - // handleFilterButtonClick(event); - }); - }); - menu.addItem((item) => { - item.setTitle(t("open-board-configuration-modal")); - item.setIcon("settings"); - item.onClick(async () => { - // openBoardConfigModal(plugin, currentBoardData, (updatedBoard: Board) => { - // // handleUpdateBoards(plugin, updatedBoards, setCurrentBoardData) - // // setCurrentBoardData(updatedBoard); - // this.plugin.taskBoardFileManager.saveBoard(updatedBoard); - // }) - }); - }); - menu.addItem((item) => { - item.setTitle(t("scan-vault-modal")); - item.setIcon(ScanVaultIcon); - item.onClick(async () => { - openScanVaultModal(this.plugin); - }); - }); - - - // DEPRECATED : As we are moving the view switching options to the ribbon menu, we will remove these options from the pane menu to avoid confusion for users. - // menu.addItem((item) => { - // item.setTitle(t("view-type")); - // item.setIsLabel(true); - // }); - // menu.addItem((item) => { - // item.setTitle(t("kanban-view")); - // item.setIcon("square-kanban"); - // item.onClick(async () => { - // eventEmitter.emit("SWITCH_VIEW", 'kanban'); - // }); - // }); - // menu.addItem((item) => { - // item.setTitle(t("map-view")); - // item.setIcon("network"); - // item.onClick(async () => { - // eventEmitter.emit("SWITCH_VIEW", 'map'); - // }); - // }); - } - } - - /** - * This is called by Obsidian when Obsidian is about to save its state/layout. - * - * @returns The state data to be stored inside the workspace.json file inside the - * Obsidian's config folder. (.obsidian/workspace.json) - */ - getState() { - // console.log("Value of currentFilePath : ", this.currentFilePath); - // Save the current filePath to the workspace state - return { - ...super.getState(), - filePath: this.currentFilePath ?? "", - }; - } - - /** - * This setState is always called after the onOpen function has finished its work by Obsidian. - * If the leaf was saved by Obsidian in its workspace.json file previosly, - * we can get the data in this function through the state param. - * @param state - * @param result - */ - async setState(state: any, result: ViewStateResult): Promise { - const { filePath } = state; - - // Check if a specific .taskboard file was clicked from File Navigator - const clickedFilePath = (this.leaf as any).taskboardFilePath as string | undefined; - if (clickedFilePath && typeof clickedFilePath === 'string' && clickedFilePath.endsWith('.taskboard')) { - // Load and render the clicked file - const clickedFileData = await this.plugin.taskBoardFileManager.loadBoardUsingPath(clickedFilePath); - if (clickedFileData) { - this.currentFilePath = clickedFilePath; - state = { - ...state, - filePath: this.currentFilePath - }; - this.renderBoard(clickedFileData); - } else { - bugReporterManagerInsatance.showNotice(183, `There was an issue with opening the task board file : ${clickedFilePath}`, "clickedFileData is undefined", "TaskBoardView.tsx/onOpen"); - } - } else { - if (filePath && typeof filePath === "string") { - // This will run when an in-active (deffered) leaf will come in focus. Specially happen when Obsidian is opened again and the Task Board leaf was brought in focus. - const boardData = await this.plugin.taskBoardFileManager.loadBoardUsingPath( - filePath - ); - if (boardData) { - this.currentFilePath = filePath; - state = { - ...state, - filePath: this.currentFilePath - }; - this.renderBoard(boardData); - } else { - bugReporterManagerInsatance.showNotice( - 183, - `There was an issue with opening the task board file : ${filePath}`, - "boardData is undefined", - "TaskBoardView.tsx/setState" - ); - } - } - else { - // This will run when user has clicked on the Ribbon icon or running the "Open task board" command. - // We need to get the last opened board. - const lastViewedBoardData = await this.plugin.taskBoardFileManager.getLastOpenedBoard(); - if (lastViewedBoardData) { - // Get the filePath from the registry - const taskBoardFilesRegistry = this.plugin.settings.data.taskBoardFilesRegistry || {}; - const registryEntries = Object.values(taskBoardFilesRegistry); - - if (registryEntries.length > 0) { - const firstItemFromRegistry = registryEntries[0]; - if (firstItemFromRegistry.filePath) { - this.currentFilePath = firstItemFromRegistry.filePath; - state = { - ...state, - filePath: this.currentFilePath, - }; - } - } - - this.renderBoard(lastViewedBoardData); - } else { - this.renderNoBoard(); - bugReporterManagerInsatance.addToLogs(185, "There was an issue with opening the last viewed board. lastViewedBoardData is undefined", "TaskBoardView.tsx/onOpen"); - } - - } - } - - await super.setState(state, result); - } - - /** - * This function is ran by Obsidian, whenever a new leaf is created or - * an in-active leaf is brough to life. - */ - async onOpen() { - if (Platform.isMobile) { - this.addAction(RefreshIcon, t("refresh-board-button"), async () => { - const fileStackString = localStorage.getItem(PENDING_SCAN_FILE_STACK); - const fileStack = fileStackString ? JSON.parse(fileStackString) : null; - - if (fileStack && fileStack.length > 0) { - await this.plugin.realTimeScanner.processAllUpdatedFiles(); - } - eventEmitter.emit("REFRESH_BOARD"); - }).addClass("taskboardRefreshBtn"); - } - - const mandatoryScanSignal = localStorage.getItem(MANDATORY_SCAN_KEY) === "true"; - - if (!Platform.isMobile || mandatoryScanSignal) { - this.addAction(ScanVaultIcon, t("scan-vault-modal"), () => { - openScanVaultModal(this.plugin); - }).addClass("taskboardScanVaultBtn"); - } - - if (mandatoryScanSignal) this.highlighgtScanvaultIcon(); - } - - async highlighgtScanvaultIcon() { - const scanVaultIcon = this.containerEl.querySelector( - ".taskboardScanVaultBtn" - ) as HTMLElement; - if (scanVaultIcon) { - scanVaultIcon.classList.add("highlight"); - setInterval(() => { - scanVaultIcon.classList.toggle("highlight"); - }, 800); // Toggle highlight class every 500ms for blinking effect - } - } - - // private async loadBoards() { - // try { - // this.boards = await loadBoardsData(this.plugin); - // } catch (err) { - // bugReporterManagerInsatance.showNotice( - // 89, - // "Failed to load board configurations from data.json", - // String(err), - // "TaskBoardView.tsx/loadBoards" - // ); - // } - // } - - private renderBoard(currentBoardData: Board) { - this.boardName = currentBoardData.name || "Unnamed Board"; - this.currentBoard = currentBoardData; - this.root = createRoot(this.containerEl.children[1]); - this.root.render( - - - - ); - - // Signal the workspace to save the updated layout state - this.app.workspace.requestSaveLayout(); - } - - /** - * This function will render a message to the user when there is no board to show in the view. This can happen when user opens the view for the first time and there is no board created, or when there is an issue with loading the board data. - * Will show a button to create a new board and to scan the vault when there is no board found. - **/ - private renderNoBoard() { - const container = this.containerEl.children[1] as HTMLElement; - container.empty(); // Clear any previous content - - const wrapper = container.createDiv({ cls: "taskboard-no-board-wrapper" }); - - const content = wrapper.createDiv({ cls: "taskboard-no-board-container" }); - - // Main message - content.createEl("h2", { - text: t("no-board-found"), - cls: "taskboard-no-board-title", - }); - - // Detailed description - const description = content.createEl("p", { - cls: "taskboard-no-board-description", - text: t("no-boards-found-description") - }); - - // Action buttons - const buttonContainer = content.createDiv({ - cls: "taskboard-no-board-buttons", - }); - - // Create template board button - const createBtn = buttonContainer.createEl("button", { - text: t("create-template-board") || "Create Template Board", - cls: "taskboard-no-board-create-btn", - attr: { - "aria-label": t("create-template-board"), - }, - }); - createBtn.addEventListener("click", () => { - this.handleCreateTemplateBoard(); - }); - - // Scan vault button - const scanBtn = buttonContainer.createEl("button", { - text: t("scan-vault-modal") || "Scan Vault", - cls: "taskboard-no-board-scan-btn", - attr: { - "aria-label": t("scan-vault-modal"), - }, - }); - scanBtn.addEventListener("click", () => { - openBoardsExplorerModal(this.plugin); - }); - } - - /** - * Creates a new template board from DEFAULT_BOARD, saves it to disk, and renders it in the current view. - * Generates a unique ID and file path for the new board. - */ - private async handleCreateTemplateBoard() { - try { - // Generate unique ID and filename for the new template board - const boardId = generateRandomStringId('board'); - const timestamp = new Date().getTime(); - const filePath = `TaskBoard-Template-${timestamp}.taskboard`; - - // Create a deep copy of DEFAULT_BOARD and update its properties - const newBoard: Board = JSON.parse(JSON.stringify(DEFAULT_BOARD)); - newBoard.id = boardId; - - // Save the board to disk - const saveSuccess = await this.plugin.taskBoardFileManager.createNewBoardFile( - filePath, - newBoard, - ); - - if (!saveSuccess) { - bugReporterManagerInsatance.showNotice( - 187, - "Failed to create template board", - "saveBoardToDisk returned false", - "TaskBoardView.tsx/handleCreateTemplateBoard", - ); - return; - } - - // Update current file path - this.currentFilePath = filePath; - - // Show success notice - new Notice(t("board-created-successfully")); - - // Render the newly created board - this.renderBoard(newBoard); - - } catch (error) { - bugReporterManagerInsatance.showNotice( - 187, - "Error creating template board", - String(error), - "TaskBoardView.tsx/handleCreateTemplateBoard", - ); - } - } - - async onClose() { - // Clean up when view is closed - this.root?.unmount(); - this.plugin.leafIsActive = false; - // onUnloadSave(this.plugin); - } -} diff --git a/src/obsidian_views/TaskBoardView.tsx b/src/obsidian_views/TaskBoardView.tsx index b4a6afde..ac9dcb82 100644 --- a/src/obsidian_views/TaskBoardView.tsx +++ b/src/obsidian_views/TaskBoardView.tsx @@ -40,7 +40,7 @@ export class TaskBoardView extends ItemView { return this.boardName; } - onPaneMenu(menu: Menu, source: "more-options" | "tab-header" | string): void { + onPaneMenu(menu: Menu, source: "more-options" | "tab-header"): void { if (source === "more-options") { menu.addItem((item) => { item.setTitle(t("quick-actions")); @@ -130,18 +130,18 @@ export class TaskBoardView extends ItemView { * @param state * @param result */ - async setState(state: any, result: ViewStateResult): Promise { - const { filePath } = state; + async setState(state: unknown, result: ViewStateResult): Promise { + const filePath = (state as Record).filePath as string | undefined; // Check if a specific .taskboard file was clicked from File Navigator - const clickedFilePath = (this.leaf as any).taskboardFilePath as string | undefined; + const clickedFilePath = this.leaf.taskboardFilePath as string | undefined; if (clickedFilePath && typeof clickedFilePath === 'string' && clickedFilePath.endsWith('.taskboard')) { // Load and render the clicked file const clickedFileData = await this.plugin.taskBoardFileManager.loadBoardUsingPath(clickedFilePath); if (clickedFileData) { this.currentFilePath = clickedFilePath; state = { - ...state, + ...(state as Record), filePath: this.currentFilePath }; this.renderBoard(clickedFileData); @@ -157,7 +157,7 @@ export class TaskBoardView extends ItemView { if (boardData) { this.currentFilePath = filePath; state = { - ...state, + ...(state as Record), filePath: this.currentFilePath }; this.renderBoard(boardData); @@ -184,7 +184,7 @@ export class TaskBoardView extends ItemView { if (firstItemFromRegistry.filePath) { this.currentFilePath = firstItemFromRegistry.filePath; state = { - ...state, + ...(state as Record), filePath: this.currentFilePath, }; } @@ -209,8 +209,9 @@ export class TaskBoardView extends ItemView { async onOpen() { if (Platform.isMobile) { this.addAction(RefreshIcon, t("refresh-board-button"), async () => { - const fileStackString = localStorage.getItem(PENDING_SCAN_FILE_STACK); - const fileStack = fileStackString ? JSON.parse(fileStackString) : null; + const fileStackString = this.app.loadLocalStorage(PENDING_SCAN_FILE_STACK); + const parsedData: unknown = JSON.parse(fileStackString as string); + const fileStack = fileStackString ? parsedData as string[] : null; if (fileStack && fileStack.length > 0) { await this.plugin.realTimeScanner.processAllUpdatedFiles(); @@ -219,7 +220,7 @@ export class TaskBoardView extends ItemView { }).addClass("taskboardRefreshBtn"); } - const mandatoryScanSignal = localStorage.getItem(MANDATORY_SCAN_KEY) === "true"; + const mandatoryScanSignal = this.app.loadLocalStorage(MANDATORY_SCAN_KEY) === "true"; if (!Platform.isMobile || mandatoryScanSignal) { this.addAction(ScanVaultIcon, t("scan-vault-modal"), () => { @@ -227,7 +228,7 @@ export class TaskBoardView extends ItemView { }).addClass("taskboardScanVaultBtn"); } - if (mandatoryScanSignal) this.highlighgtScanvaultIcon(); + if (mandatoryScanSignal) void this.highlighgtScanvaultIcon(); } async highlighgtScanvaultIcon() { @@ -236,7 +237,7 @@ export class TaskBoardView extends ItemView { ) as HTMLElement; if (scanVaultIcon) { scanVaultIcon.classList.add("highlight"); - setInterval(() => { + window.setInterval(() => { scanVaultIcon.classList.toggle("highlight"); }, 800); // Toggle highlight class every 500ms for blinking effect } @@ -264,6 +265,7 @@ export class TaskBoardView extends ItemView { plugin={this.plugin} currentBoardData={currentBoardData} currentLeaf={this.leaf} + viewId="" /> ); @@ -306,26 +308,29 @@ export class TaskBoardView extends ItemView { // Create path elements let currentPath = ''; - // Add folder parts - folderParts.forEach((part, index) => { - if (part) { - currentPath += part; - const folderPath = currentPath; - const folderSpan = titleContainer.createSpan({ text: part, cls: 'taskboard-path-folder' }); - folderSpan.addEventListener('click', () => { - const folder = this.app.vault.getAbstractFileByPath(folderPath); - if (folder instanceof TFolder) { - revealFileFolderInExplorer(this.plugin, folder); - } - }); + // Add folder parts - Only show if the screen width is sufficiently large + // Else will only show the fileName + if (this.leaf.width > 500) { + folderParts.forEach((part, index) => { + if (part) { + currentPath += part; + const folderPath = currentPath; + const folderSpan = titleContainer.createSpan({ text: part, cls: 'taskboard-path-folder' }); + folderSpan.addEventListener('click', () => { + const folder = this.app.vault.getAbstractFileByPath(folderPath); + if (folder instanceof TFolder) { + revealFileFolderInExplorer(this.plugin, folder); + } + }); - // Add separator - if (index < folderParts.length - 1 || fileName) { - titleContainer.createSpan({ text: '/', cls: 'taskboard-path-separator' }); + // Add separator + if (index < folderParts.length - 1 || fileName) { + titleContainer.createSpan({ text: '/', cls: 'taskboard-path-separator' }); + } + currentPath += '/'; } - currentPath += '/'; - } - }); + }); + } // Add file name if (fileName) { @@ -381,7 +386,7 @@ export class TaskBoardView extends ItemView { }, }); createBtn.addEventListener("click", () => { - this.handleCreateTemplateBoard(); + void this.handleCreateTemplateBoard(); }); // Scan vault button @@ -409,7 +414,7 @@ export class TaskBoardView extends ItemView { const filePath = `TaskBoard-Template-${timestamp}.taskboard`; // Create a deep copy of DEFAULT_BOARD and update its properties - const newBoard: Board = JSON.parse(JSON.stringify(DEFAULT_BOARD)); + let newBoard: Board = DEFAULT_BOARD; newBoard.id = boardId; // Save the board to disk @@ -447,6 +452,10 @@ export class TaskBoardView extends ItemView { } } + onResize(): void { + this.renderCustomViewHeader(); + } + async onClose() { // Clean up when view is closed this.root?.unmount(); diff --git a/src/obsidian_views/TaskEditorView.tsx b/src/obsidian_views/TaskEditorView.tsx index 4c1ae452..69c46def 100644 --- a/src/obsidian_views/TaskEditorView.tsx +++ b/src/obsidian_views/TaskEditorView.tsx @@ -13,7 +13,6 @@ import { allowedFileExtensionsRegEx } from "../regularExpressions/MiscelleneousR import { getCurrentLocalDateTimeString } from "../utils/DateTimeCalculations.js"; import { readDataOfVaultFile } from "../utils/MarkdownFileOperations.js"; import { generateTaskId } from "../utils/TaskItemUtils.js"; -import { getFormattedTaskContent } from "../utils/taskLine/TaskContentFormatter.js"; export class TaskEditorView extends ItemView { @@ -26,13 +25,13 @@ export class TaskEditorView extends ItemView { isEdited: boolean; isTaskNote: boolean; activeNote: boolean; - saveTask: (updatedTask: taskItem, quickAddPluginChoice: string, updatedNoteContent?: string) => void; + saveTask: (updatedTask: taskItem, quickAddPluginChoice: string, updatedNoteContent?: string) => Promise; constructor( plugin: TaskBoard, leaf: WorkspaceLeaf, viewTypeId: string, - saveTask: (updatedTask: taskItem, quickAddPluginChoice: string, updatedNoteContent?: string) => void, + saveTask: (updatedTask: taskItem, quickAddPluginChoice: string, updatedNoteContent?: string) => Promise, isTaskNote: boolean, activeNote: boolean, taskExists: boolean, @@ -78,7 +77,7 @@ export class TaskEditorView extends ItemView { if (this.taskExists) { const data = await readDataOfVaultFile(this.plugin, this.filePath, true); - if (data == null) this.onClose(); + if (data == null) await this.onClose(); else noteContent = data; if (!this.task.title) this.task.title = this.filePath.split('/').pop()?.replace(allowedFileExtensionsRegEx, "") ?? ""; @@ -112,8 +111,8 @@ export class TaskEditorView extends ItemView { filePath={this.filePath} onSave={async (updatedTask: taskItem, quickAddPluginChoice: string, updatedNoteContent?: string) => { this.isEdited = false; - const formattedContent = await getFormattedTaskContent(updatedTask); - this.saveTask(updatedTask, quickAddPluginChoice, updatedNoteContent); + // const formattedContent = await getFormattedTaskContent(updatedTask); + await this.saveTask(updatedTask, quickAddPluginChoice, updatedNoteContent); // Close the view leaf this.app.workspace.detachLeavesOfType(this.viewTypeId); }} diff --git a/src/services/CommunityPlugins.ts b/src/services/CommunityPlugins.ts index e152fa81..7a6d79a4 100644 --- a/src/services/CommunityPlugins.ts +++ b/src/services/CommunityPlugins.ts @@ -1,7 +1,17 @@ -import { TFile, TFolder } from "obsidian"; +import { App, Plugin, TFile, TFolder } from "obsidian"; import { TaskBoardSubmodule } from "./subModules.js"; import type TaskBoard from "../../main.js"; +export interface QuickAddPlugin extends Plugin { + app: App; + api: { + executeChoice( + choiceName: string, + variables?: { [key: string]: unknown }, + ): Promise; + }; +} + export class CommunityPlugins extends TaskBoardSubmodule { get fileExplorerPlugin() { return this.app.internalPlugins.getEnabledPluginById("file-explorer"); @@ -19,8 +29,8 @@ export class CommunityPlugins extends TaskBoardSubmodule { return !!this.reminderPlugin; } - get quickAddPlugin() { - return this.app.plugins.plugins["quickadd"] ?? null; + get quickAddPlugin(): QuickAddPlugin { + return (this.app.plugins.plugins.quickAdd as QuickAddPlugin) ?? null; } isQuickAddPluginEnabled() { @@ -48,10 +58,13 @@ export function isReminderPluginInstalled(plugin: TaskBoard) { reminderPlugin.isReminderPluginEnabled(); } -export function revealFileFolderInExplorer(plugin: TaskBoard, abstractFile: TFile | TFolder) { +export function revealFileFolderInExplorer( + plugin: TaskBoard, + abstractFile: TFile | TFolder, +) { const communityPlugins = new CommunityPlugins(plugin); - if(communityPlugins.isFileExplorerPluginEnabled()) { + if (communityPlugins.isFileExplorerPluginEnabled()) { communityPlugins.fileExplorerPlugin?.revealInFolder(abstractFile); } } diff --git a/src/services/EventEmitter.ts b/src/services/EventEmitter.ts index 9041d865..39fe2e0f 100644 --- a/src/services/EventEmitter.ts +++ b/src/services/EventEmitter.ts @@ -1,15 +1,19 @@ // /src/services/ EventEmitter.ts +type EventListener = (data: T) => void; + class EventEmitter { - private events: { [key: string]: Function[] } = Object.create(null); + private events: Record>> = {}; private readonly blockedEvents = new Set([ "__proto__", "constructor", "prototype", ]); - // Add an event listener - on(event: string, listener: Function) { + // Keep the listener payload generic at the call site, but store the handler + // internally as `unknown` so the event bus remains compatible with the + // existing callback signatures used across the plugin. + on(event: string, listener: EventListener) { if (this.blockedEvents.has(event)) { console.warn( "[Task Board] [Event Emitter] : Blocked event is registered : ", @@ -20,11 +24,11 @@ class EventEmitter { if (!this.events[event]) { this.events[event] = []; } - this.events[event].push(listener); + this.events[event].push(listener as EventListener); } // Emit an event, calling all listeners - emit(event: string, data?: any) { + emit(event: string, data?: T) { if (this.blockedEvents.has(event)) { console.warn( "[Task Board] [Event Emitter] : Blocked event is emitted : ", @@ -38,7 +42,7 @@ class EventEmitter { } // Remove an event listener - off(event: string, listener: Function) { + off(event: string, listener: EventListener) { if (this.blockedEvents.has(event)) { console.warn( "[Task Board] [Event Emitter] : Blocked event is un-registered : ", @@ -48,7 +52,8 @@ class EventEmitter { } if (this.events[event]) { this.events[event] = this.events[event].filter( - (registeredListener) => registeredListener !== listener, + (registeredListener) => + registeredListener !== (listener as EventListener), ); } } diff --git a/src/services/FileSystem.ts b/src/services/FileSystem.ts index c83fdf52..f66774f3 100644 --- a/src/services/FileSystem.ts +++ b/src/services/FileSystem.ts @@ -1,13 +1,14 @@ +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ +/* eslint-disable import/no-nodejs-modules */ // import { BlobReader, configure, Reader, ZipReader } from '@zip.js/zip.js'; import type * as NodeFS from "node:fs"; import type * as NodeOS from "node:os"; import type * as NodePath from "node:path"; import type * as NodeUrl from "node:url"; import type * as NodeZlib from "node:zlib"; -import { App, Platform } from "obsidian"; +import { App, Platform, TFile } from "obsidian"; +import { Buffer } from "node:buffer"; - -import TaskBoard from "../../main.js"; import { bugReporterManagerInsatance } from "../managers/BugReporter.js"; // import { configureWebWorker } from './z-worker-inline'; @@ -73,7 +74,7 @@ export function nodeBufferToArrayBuffer( } export class NodePickedFile implements PickedFile { - readonly type: "file" = "file"; + readonly type = "file"; readonly filepath: string; readonly fullpath: string; @@ -124,7 +125,7 @@ export class NodePickedFile implements PickedFile { } export class NodePickedFolder implements PickedFolder { - readonly type: "folder" = "folder"; + readonly type = "folder"; readonly filepath: string; readonly name: string; @@ -162,7 +163,7 @@ export class NodePickedFolder implements PickedFolder { } export class WebPickedFile implements PickedFile { - readonly type: "file" = "file"; + readonly type = "file"; readonly file: File; readonly fullpath: string; @@ -215,8 +216,9 @@ export class WebPickedFile implements PickedFile { // return callback(new ZipReader(new BlobReader(this.file))); // } + // Use file.name instead of this.file.toString() to avoid [object Object] default stringification toString(): string { - return this.file.toString(); + return this.file.name; } } @@ -302,7 +304,7 @@ export function splitext(name: string) { /** * Checks and creates folders if they are not present. * Recursively checks each child folder if it exists of not. - * + * * @param plugin - The plugin's app instance * @param folderPath - Only the folder path. (Filename should not be present) * @returns - Void @@ -328,7 +330,7 @@ export async function createFolderRecursively( // If a file exists where a folder is expected, report and abort // (this is unlikely but safer to surface) // existing.type may not be available in all builds, so just check truthiness and skip create - if ((existing as any).path && !(existing as any).children) { + if (existing.path && existing instanceof TFile) { bugReporterManagerInsatance.showNotice( 58, `A file exists where a folder is expected: ${currentPath}`, diff --git a/src/services/FrontmatterRenderer.ts b/src/services/FrontmatterRenderer.ts index 4ee4b111..0df2fb30 100644 --- a/src/services/FrontmatterRenderer.ts +++ b/src/services/FrontmatterRenderer.ts @@ -10,7 +10,7 @@ * In the current version, this FrontmatterRenderer is using a simple custom styling method to render the properties. */ -import { App, Component, Notice, TFile } from "obsidian"; +import { App, Component, Notice, setIcon, TFile } from "obsidian"; import TaskBoard from "../../main.js"; import { extractFrontmatterFromContent } from "../utils/taskNote/FrontmatterOperations.js"; @@ -148,9 +148,17 @@ export class FrontmatterRenderer { const collapseIcon = header.createSpan({ cls: "taskboard-frontmatter-collapse-icon", }); - collapseIcon.replaceChildren(); - const collapseIconSVG = ``; - collapseIcon.insertAdjacentHTML("beforeend", collapseIconSVG); + // collapseIcon.replaceChildren(); + // const collapseIconSVG = ``; + // // collapseIcon.insertAdjacentHTML("beforeend", collapseIconSVG); + // collapseIcon.appendChild( + // createSvg("svg", { + // cls: "taskboard-frontmatter-collapse-icon", + + // }), + // ); + + setIcon(collapseIcon, "chevron-down"); // or 'chevron-up' for expanded state // Add "Properties" text header.createSpan({ @@ -174,8 +182,10 @@ export class FrontmatterRenderer { if (this.isFrontmatterContainerCollapsed) { propertiesContainer.hide(); collapseIcon.replaceChildren(); - const collapseIconSVG = ``; - collapseIcon.insertAdjacentHTML("beforeend", collapseIconSVG); + // const collapseIconSVG = ``; + // // collapseIcon.insertAdjacentHTML("beforeend", collapseIconSVG); + // collapseIcon.textContent = collapseIconSVG; + setIcon(collapseIcon, "chevron-right"); } // Render each property using PropertyWidget @@ -188,14 +198,18 @@ export class FrontmatterRenderer { if (this.isFrontmatterContainerCollapsed) { propertiesContainer.hide(); - collapseIcon.replaceChildren(); - const collapseIconSVG = ``; - collapseIcon.insertAdjacentHTML("beforeend", collapseIconSVG); + // collapseIcon.replaceChildren(); + // const collapseIconSVG = ``; + // // collapseIcon.insertAdjacentHTML("beforeend", collapseIconSVG); + // collapseIcon.textContent = collapseIconSVG; + setIcon(collapseIcon, "chevron-down"); } else { propertiesContainer.show(); - collapseIcon.replaceChildren(); - const collapseIconSVG = ``; - collapseIcon.insertAdjacentHTML("beforeend", collapseIconSVG); + // collapseIcon.replaceChildren(); + // const collapseIconSVG = ``; + // // collapseIcon.insertAdjacentHTML("beforeend", collapseIconSVG); + // collapseIcon.textContent = collapseIconSVG; + setIcon(collapseIcon, "chevron-down"); } frontmatterSection.toggleClass( @@ -218,7 +232,7 @@ export class FrontmatterRenderer { */ private renderProperties( containerEl: HTMLElement, - frontmatter: Record, + frontmatter: Record, file?: TFile, ): void { this.renderPropertiesSimple(containerEl, frontmatter); @@ -336,7 +350,7 @@ export class FrontmatterRenderer { */ private renderPropertiesSimple( containerEl: HTMLElement, - frontmatter: Record, + frontmatter: Record, ): void { const propertiesToRender = Object.entries(frontmatter).filter( ([key]) => key !== "position", @@ -373,7 +387,7 @@ export class FrontmatterRenderer { */ private renderPropertyValueSimple( containerEl: HTMLElement, - value: any, + value: unknown, ): void { if (Array.isArray(value)) { const list = containerEl.createEl("ul", { diff --git a/src/services/MarkdownEditor.ts b/src/services/MarkdownEditor.ts index 877303e6..00594686 100644 --- a/src/services/MarkdownEditor.ts +++ b/src/services/MarkdownEditor.ts @@ -8,14 +8,15 @@ import { App, Editor, + MarkdownFileInfo, MarkdownScrollableEditView, Scope, TFile, - WidgetEditorView, + Workspace, WorkspaceLeaf, } from "obsidian"; -import { EditorSelection, Prec } from "@codemirror/state"; +import { EditorSelection, Prec, Range } from "@codemirror/state"; import { EditorView, keymap, @@ -40,7 +41,7 @@ import { FrontmatterRenderer } from "./FrontmatterRenderer.js"; export function createEmbeddableMarkdownEditor( plugin: TaskBoard, container: HTMLElement, - options: Partial + options: Partial, ): EmbeddableMarkdownEditor { // Get the editor class const EditorClass = resolveEditorPrototype(plugin.app); @@ -51,32 +52,35 @@ export function createEmbeddableMarkdownEditor( plugin.app, EditorClass, container, - options + options, ); } /** * Resolves the markdown editor prototype from the app */ -function resolveEditorPrototype(app: App): any { +function resolveEditorPrototype(app: App): unknown { // Create a temporary editor to resolve the prototype of ScrollableMarkdownEditor const widgetEditorView = app.embedRegistry.embedByExtension.md( { app, containerEl: createDiv() }, + // eslint-disable-next-line obsidianmd/no-tfile-tfolder-cast -- null TFile needed for API null as unknown as TFile, - "" - ) as WidgetEditorView; + "", + ); // Mark as editable to instantiate the editor widgetEditorView.editable = true; widgetEditorView.showEditor(); + // Navigate prototype chain to resolve the ScrollableMarkdownEditor constructor + const editMode = widgetEditorView.editMode as object; const MarkdownEditor = Object.getPrototypeOf( - Object.getPrototypeOf(widgetEditorView.editMode!) - ); + Object.getPrototypeOf(editMode), + ) as { constructor: unknown }; // Unload to remove the temporary editor widgetEditorView.unload(); - // Return the constructor, using 'any' type to bypass the abstract class check + // Return the constructor, bypassing the abstract class check return MarkdownEditor.constructor; } @@ -87,13 +91,14 @@ interface MarkdownEditorProps { placeholder?: string; enableFrontmatterUI?: boolean; // Enable enhanced frontmatter UI file?: TFile; // Optional file for context in property rendering + singleLine?: boolean; onEnter: ( editor: EmbeddableMarkdownEditor, mod: boolean, - shift: boolean + shift: boolean, ) => boolean; - // onEscape: (editor: EmbeddableMarkdownEditor) => void; + onEscape?: (editor: EmbeddableMarkdownEditor) => void; onSubmit: (editor: EmbeddableMarkdownEditor) => void; onBlur: (editor: EmbeddableMarkdownEditor) => void; onPaste: (e: ClipboardEvent, editor: EmbeddableMarkdownEditor) => void; @@ -145,7 +150,7 @@ export class EmbeddableMarkdownEditor { get app(): App { return this.editor.app; } - get owner(): any { + get owner(): unknown { return this.editor.owner; } get _loaded(): boolean { @@ -162,13 +167,135 @@ export class EmbeddableMarkdownEditor { constructor( plugin: TaskBoard, app: App, - EditorClass: any, + EditorClass: unknown, container: HTMLElement, - options: Partial + options: Partial, ) { this.plugin = plugin; + + // Store user options first + this.options = { ...defaultProperties, ...options }; + this.initial_value = this.options.value!; + this.scope = new Scope(app.scope); + + // Prevent Mod+Enter default behavior + this.scope.register(["Mod"], "Enter", () => true); + + // Store reference to self for the patched method BEFORE using it + // eslint-disable-next-line @typescript-eslint/no-this-alias -- needed for monkey-patching context + const self = this; + + // Use monkey-around to safely patch the method + const uninstaller = around( + (EditorClass as { prototype: object }).prototype, + { + buildLocalExtensions: (originalMethod: unknown) => + function (this: object) { + const extensions = ( + originalMethod as (this: object) => unknown[] + ).call(this); + + // Only add our custom extensions if this is our editor instance + if (this === self.editor) { + // Add placeholder if configured + if (self.options.placeholder) { + extensions.push( + placeholder(self.options.placeholder), + ); + } + + // Add paste, blur, and focus event handlers + extensions.push( + EditorView.domEventHandlers({ + paste: (event) => { + self.options.onPaste(event, self); + }, + blur: () => { + // Always trigger blur callback and let it handle the logic + app.keymap.popScope(self.scope); + if (self.options.onBlur) { + self.options.onBlur(self); + } + }, + focusin: () => { + app.keymap.pushScope(self.scope); + app.workspace.activeEditor = + self.owner as MarkdownFileInfo | null; + }, + }), + ); + + // Add keyboard handlers + const keyBindings = [ + { + key: "Enter", + run: () => { + return self.options.onEnter( + self, + false, + false, + ); + }, + shift: () => + self.options.onEnter(self, false, true), + }, + { + key: "Mod-Enter", + run: () => + self.options.onEnter(self, true, false), + shift: () => + self.options.onEnter(self, true, true), + }, + { + key: "Escape", + run: () => { + self.options.onEscape?.(self); + return true; + }, + preventDefault: true, + }, + ]; + + // For single line mode, prevent Enter key from creating new lines + if (self.options.singleLine) { + keyBindings[0] = { + key: "Enter", + run: () => { + // In single line mode, Enter should trigger onEnter + return self.options.onEnter( + self, + false, + false, + ); + }, + shift: () => { + // Even with shift, still call onEnter in single line mode + return self.options.onEnter( + self, + false, + true, + ); + }, + }; + } + + extensions.push( + Prec.highest(keymap.of(keyBindings)), + ); + } + + return extensions; + }, + }, + ); + plugin.register(uninstaller); + // Create the editor with the app instance - this.editor = new EditorClass(app, container, { + this.editor = new (EditorClass as new ( + app: App, + container: HTMLElement, + opts: Record, + ) => MarkdownScrollableEditView)(app, container, { app, // This mocks the MarkdownView functions, required for proper scrolling onMarkdownScroll: () => {}, @@ -177,18 +304,14 @@ export class EmbeddableMarkdownEditor { this.frontmatterRenderer = new FrontmatterRenderer(plugin, this.editor); - // Store user options - this.options = { ...defaultProperties, ...options }; - this.initial_value = this.options.value!; - this.scope = new Scope(app.scope); - // Prevent Mod+Enter default behavior this.scope.register(["Mod"], "Enter", () => true); // Set up the editor relationship for commands to work - if (this.owner) { - this.owner.editMode = this; - this.owner.editor = this.editor.editor; + const ownerRecord = this.owner as Record | undefined; + if (ownerRecord) { + ownerRecord.editMode = this; + ownerRecord.editor = this.editor.editor; } // Set initial content @@ -198,32 +321,48 @@ export class EmbeddableMarkdownEditor { this.register( around(app.workspace, { setActiveLeaf: - (oldMethod: any) => - (leaf: WorkspaceLeaf, ...args: any[]) => { + (oldMethod: unknown) => + (leaf: WorkspaceLeaf, ...args: unknown[]) => { if (!this.activeCM?.hasFocus) { - oldMethod.call(app.workspace, leaf, ...args); + ( + oldMethod as ( + this: Workspace, + leaf: WorkspaceLeaf, + ...args: unknown[] + ) => void + ).call(app.workspace, leaf, ...args); } }, - }) + }), ); // Set up blur event handler + const contentDOM = ( + this.editor.editor as unknown as Record + )?.cm as Record | undefined; if ( this.options.onBlur !== defaultProperties.onBlur && - this.editor.editor?.cm?.contentDOM + contentDOM?.contentDOM ) { - this.editor.editor.cm.contentDOM.addEventListener("blur", () => { - app.keymap.popScope(this.scope); - if (this._loaded) this.options.onBlur(this); - }); + (contentDOM.contentDOM as HTMLElement).addEventListener( + "blur", + () => { + app.keymap.popScope(this.scope); + if (this._loaded) this.options.onBlur(this); + }, + ); } // Set up focus event handler - if (this.editor.editor?.cm?.contentDOM) { - this.editor.editor.cm.contentDOM.addEventListener("focusin", () => { - app.keymap.pushScope(this.scope); - app.workspace.activeEditor = this.owner; - }); + if (contentDOM?.contentDOM) { + (contentDOM.contentDOM as HTMLElement).addEventListener( + "focusin", + () => { + app.keymap.pushScope(this.scope); + app.workspace.activeEditor = this + .owner as MarkdownFileInfo | null; + }, + ); } // Apply custom class if provided @@ -236,7 +375,7 @@ export class EmbeddableMarkdownEditor { this.editor.editor.cm.dispatch({ selection: EditorSelection.range( options.cursorLocation.anchor, - options.cursorLocation.head + options.cursorLocation.head, ), }); } @@ -261,7 +400,7 @@ export class EmbeddableMarkdownEditor { paste: (event) => { this.options.onPaste(event, this); }, - }) + }), ); // Add keyboard handlers @@ -287,8 +426,8 @@ export class EmbeddableMarkdownEditor { // }, // preventDefault: true, // }, - ]) - ) + ]), + ), ); return extensions; @@ -311,15 +450,16 @@ export class EmbeddableMarkdownEditor { */ private createFrontmatterHidingExtension() { // Helper function to build decorations for frontmatter - const buildDecorations = (state: any): DecorationSet => { - const decorations: any[] = []; - const doc = state.doc; + const buildDecorations = (state: unknown): DecorationSet => { + const decorations: Range[] = []; + const editorState = state as { doc: { toString: () => string } }; + const doc = editorState.doc; const text = doc.toString(); // Regular expression to match frontmatter blocks // Matches content that starts with '---' and ends with '---' const frontmatterRegex = /^---\s*\n([\s\S]*?)\n---\s*$/gm; - let match; + let match: RegExpExecArray | null; while ((match = frontmatterRegex.exec(text)) !== null) { const start = match.index; @@ -332,7 +472,7 @@ export class EmbeddableMarkdownEditor { attributes: { style: "display: none;", }, - }).range(start, end) + }).range(start, end), ); } @@ -368,7 +508,7 @@ export class EmbeddableMarkdownEditor { const contentWithoutFrontmatter = this.frontmatterRenderer.extractContentWithoutFrontmatter( content, - frontmatterContent + frontmatterContent, ); this.editor.set(contentWithoutFrontmatter, focus); @@ -399,7 +539,7 @@ export class EmbeddableMarkdownEditor { // Find or create a wrapper in the container // We'll prepend the frontmatter UI to the container const wrapper = this.containerEl.querySelector( - ".taskboard-frontmatter-wrapper" + ".taskboard-frontmatter-wrapper", ) as HTMLElement; if (!wrapper) { @@ -411,7 +551,7 @@ export class EmbeddableMarkdownEditor { // Insert at the beginning of the container this.containerEl.insertBefore( this.frontmatterUIContainer, - this.containerEl.firstChild + this.containerEl.firstChild, ); } else { this.frontmatterUIContainer = wrapper; @@ -422,7 +562,7 @@ export class EmbeddableMarkdownEditor { const result = this.frontmatterRenderer.renderCollapsibleFrontmatter( this.frontmatterUIContainer, content, - this.options.file + this.options.file, ); // If no frontmatter was rendered, remove the container @@ -433,8 +573,8 @@ export class EmbeddableMarkdownEditor { } // Register cleanup callback - register(cb: any): void { - this.editor.register(cb); + register(cb: unknown): void { + this.editor.register(cb as () => unknown); } // Clean up method that ensures proper destruction diff --git a/src/services/MarkdownHoverPreview.ts b/src/services/MarkdownHoverPreview.ts index 2cbbf33f..a4b7aad5 100644 --- a/src/services/MarkdownHoverPreview.ts +++ b/src/services/MarkdownHoverPreview.ts @@ -8,7 +8,7 @@ export function hookMarkdownLinkMouseEventHandlers( plugin: TaskBoard, containerEl: HTMLElement, sourcePath: string, - filePath: string + filePath: string, ) { containerEl .querySelectorAll("a.internal-link") @@ -18,10 +18,10 @@ export function hookMarkdownLinkMouseEventHandlers( evt.preventDefault(); const linktext = el.getAttribute("href"); if (linktext) { - app.workspace.openLinkText( + void app.workspace.openLinkText( linktext, sourcePath, - Keymap.isModEvent(evt) + Keymap.isModEvent(evt), ); } }); @@ -47,7 +47,7 @@ export function hookMarkdownLinkMouseEventHandlers( export function markdownButtonHoverPreviewEvent( app: App, event: React.MouseEvent, - filePath: string + filePath: string, ) { app.workspace.trigger("hover-link", { event, diff --git a/src/services/MarkdownUIRenderer.ts b/src/services/MarkdownUIRenderer.ts index 0a1610fe..a526c8bb 100644 --- a/src/services/MarkdownUIRenderer.ts +++ b/src/services/MarkdownUIRenderer.ts @@ -33,13 +33,12 @@ export function createAndAppendElement( // so had to convert all of these to the equivalent but more elaborate document.createElement() and // appendChild() calls. - const el: HTMLElementTagNameMap[K] = document.createElement(tagName); + const el: HTMLElementTagNameMap[K] = activeDocument.createElement(tagName); parentElement.appendChild(el); return el; } export class MarkdownUIRenderer { - private readonly textRenderer: TextRenderer; private readonly obsidianComponent: Component | null; private readonly parentUlElement: HTMLElement; // private readonly taskLayoutOptions: TaskLayoutOptions; @@ -55,6 +54,7 @@ export class MarkdownUIRenderer { if (!obsidianComponent) { return; } + await MarkdownRenderer.render( app, text, @@ -76,7 +76,6 @@ export class MarkdownUIRenderer { * @param parentUlElement HTML element where the task shall be rendered. */ constructor({ - textRenderer = MarkdownUIRenderer.obsidianMarkdownRenderer, obsidianComponent, parentUlElement, }: { @@ -84,12 +83,11 @@ export class MarkdownUIRenderer { obsidianComponent: Component | null; parentUlElement: HTMLElement; }) { - this.textRenderer = textRenderer; this.obsidianComponent = obsidianComponent; this.parentUlElement = parentUlElement; } - public static async renderTaskDisc( + public static async strictRender( app: App, taskDescText: string, element: HTMLDivElement, @@ -111,7 +109,7 @@ export class MarkdownUIRenderer { ); } - static async renderSubtaskText( + public static async safeRender( app: App, subtaskText: string, el: HTMLElement, @@ -119,11 +117,11 @@ export class MarkdownUIRenderer { taskItemComponent: Component | null, ) { try { - // console.log("renderSubtaskText : Received following text : ", subtaskText); + // console.log("safeRender : Received following text : ", subtaskText); let componentEl = taskItemComponent ?? new Component(); - if (!componentEl) { - return; - } + // if (!componentEl) { + // return; + // } el.replaceChildren(); @@ -139,7 +137,7 @@ export class MarkdownUIRenderer { bugReporterManagerInsatance.addToLogs( 102, String(error), - "MarkdownUIRenderer.ts/MarkdownUIRenderer.renderSubtaskText", + "MarkdownUIRenderer.ts/MarkdownUIRenderer.safeRender", ); } } diff --git a/src/services/MultiSuggest.ts b/src/services/MultiSuggest.ts index 708a67ed..31a448de 100644 --- a/src/services/MultiSuggest.ts +++ b/src/services/MultiSuggest.ts @@ -85,9 +85,15 @@ export function getTagSuggestions(app: App): string[] { return tagsArray; } +// Define expected shape of QuickAdd plugin choices for type safety +interface QuickAddChoice { + type: string; + name: string; +} + export function getQuickAddPluginChoices( app: App, - quickAddPluginObj: any, + quickAddPluginObj: unknown, ): string[] { try { if (!quickAddPluginObj) { @@ -96,7 +102,8 @@ export function getQuickAddPluginChoices( const quickAddPlugin = app.plugins.getPlugin("quickadd"); if (!quickAddPlugin) return []; - const choices = quickAddPluginObj.settings.choices; + const settings = (quickAddPluginObj as Record).settings as Record; + const choices = settings.choices as Record; return Object.keys(choices) .filter((key) => choices[key].type === "Capture") @@ -141,18 +148,15 @@ export function getYAMLPropertySuggestions(app: App): string[] { const metadata = app.metadataCache.getFileCache(file); if (metadata && metadata.frontmatter) { // console.log("Frontmatter:", metadata.frontmatter, "\nFile:", file.path); - Object.keys(metadata.frontmatter).forEach((key) => { - const value = metadata.frontmatter - ? metadata.frontmatter[key] - : null; - if (Array.isArray(value)) { - value.forEach((val) => { - yamlPropertiesSet.add(`["${key}": ${val}]`); - }); - } else { - yamlPropertiesSet.add(`["${key}": ${value}]`); - } + // metadata.frontmatter values are unknown; cast safely for template literals + Object.keys(metadata.frontmatter).forEach((key) => { + // Cast frontmatter to avoid any-typed access from Obsidian's API + const raw = (metadata.frontmatter as Record)?.[key]; + const values = Array.isArray(raw) ? raw : [raw]; + values.forEach((val) => { + yamlPropertiesSet.add(`["${key}": ${String(val)}]`); }); + }); } } }); diff --git a/src/services/ObsidianDebugInfo.ts b/src/services/ObsidianDebugInfo.ts index a9f71cc0..1b8e82f6 100644 --- a/src/services/ObsidianDebugInfo.ts +++ b/src/services/ObsidianDebugInfo.ts @@ -16,8 +16,8 @@ export async function getObsidianDebugInfo(app: App) { // This is an empty string if it's the default theme const themeName = app.customCss.theme; const themeManifest = app.customCss.themes[themeName]; - const numSnippets = app.customCss.snippets.filter((snippet: any) => - app.customCss.enabledSnippets.has(snippet) + const numSnippets = app.customCss.snippets.filter((snippet: unknown) => + app.customCss.enabledSnippets.has(String(snippet)), ).length; const plugins = app.plugins.plugins; @@ -27,7 +27,7 @@ export async function getObsidianDebugInfo(app: App) { "Use [[Wikilinks]]": !app.vault.getConfig("useMarkdownLinks"), // This entry is not in Obsidian's built-in "Show debug info" command. // It replaces the "Base theme" entry for identifying the color scheme even if it's set to be "adapt to system" - "Base color scheme": document.body.hasClass("theme-dark") + "Base color scheme": activeDocument.body.hasClass("theme-dark") ? "dark" : "light", // This entry is not in Obsidian's built-in "Show debug info" command @@ -38,39 +38,66 @@ export async function getObsidianDebugInfo(app: App) { "Snippets enabled": numSnippets, "Plugins installed": Object.keys(app.plugins.manifests).length, "Plugins enabled": Object.values(plugins).map( - (plugin) => `${plugin!.manifest.name} v${plugin!.manifest.version}` + (plugin) => `${plugin.manifest.name} v${plugin.manifest.version}`, ), }; } -export async function getWebViewVersion() { +// Capacitor plugins return untyped results; use a concise interface for the shape we access +interface CapacitorDeviceInfo { + webViewVersion?: string; + platform?: string; + osVersion?: string; + manufacturer?: string; + model?: string; +} +interface CapacitorAppInfo { + version?: string; + build?: string; +} + +export async function getWebViewVersion(): Promise { if (!Platform.isMobileApp) { return null; } - const deviceInfo = await window.Capacitor.Plugins.Device.getInfo(); - return deviceInfo.webViewVersion; + const plugins = (window.Capacitor as Record) + .Plugins as Record; + const deviceInfo = await ( + plugins.Device as { getInfo: () => Promise } + ).getInfo(); + return deviceInfo.webViewVersion ?? null; } /** * Get the information about the Obsidian app and the system. * This is the same as the "SYSTEM INFO" section in the result of the "Show debug info" command. */ -export async function getSystemInfo(): Promise { +export async function getSystemInfo(): Promise> { if (window.electron) { - // eslint-disable-next-line @typescript-eslint/no-require-imports + // eslint-disable-next-line @typescript-eslint/no-require-imports, import/no-nodejs-modules, no-undef const os = require("os") as typeof import("os"); + const electron = window.electron; + const ipcRenderer = electron.ipcRenderer as { + sendSync: (channel: string) => string; + }; + const remote = electron.remote as { app: { getVersion: () => string } }; return { - "Obsidian version": window.electron.ipcRenderer.sendSync("version"), - // @ts-ignore - "Installer version": window.electron.remote.app.getVersion(), + "Obsidian version": ipcRenderer.sendSync("version"), + "Installer version": remote.app.getVersion(), "Operating system": os.version() + " " + os.release(), }; } - const appInfo = await window.Capacitor.Plugins.App.getInfo(); - const deviceInfo = await window.Capacitor.Plugins.Device.getInfo(); - const info: any = { + const apps = (window.Capacitor as Record) + .Plugins as Record; + const appInfo = await ( + apps.App as { getInfo: () => Promise } + ).getInfo(); + const deviceInfo = await ( + apps.Device as { getInfo: () => Promise } + ).getInfo(); + const info: Record = { "Obsidian version": `${appInfo.version} (${appInfo.build})`, "API version": apiVersion, "Operating system": `${deviceInfo.platform} ${deviceInfo.osVersion} (${deviceInfo.manufacturer} ${deviceInfo.model})`, diff --git a/src/services/OpenModals.ts b/src/services/OpenModals.ts index 2a1d658a..6b301273 100644 --- a/src/services/OpenModals.ts +++ b/src/services/OpenModals.ts @@ -71,16 +71,22 @@ export const openAddNewTaskInCurrentFileModal = ( app: App, plugin: TaskBoard, activeFile: TFile, - cursorPosition?: { line: number; ch: number } | undefined, + cursorPosition?: { line: number; ch: number }, ) => { const AddTaskModal = new TaskEditorModal( plugin, - (newTask: taskItem, quickAddPluginChoice: string) => { - addTaskInNote(plugin, newTask, true, cursorPosition).then( + async (newTask: taskItem, quickAddPluginChoice: string) => { + void addTaskInNote(plugin, newTask, true, cursorPosition).then( (newId) => { - plugin.realTimeScanner.processAllUpdatedFiles( - newTask.filePath, - ); + plugin.realTimeScanner + .processAllUpdatedFiles(newTask.filePath) + .catch((error) => { + bugReporterManagerInsatance.addToLogs( + 217, + String(error), + "OpenModals.ts/openAddNewTaskInCurrentFileModal", + ); + }); }, ); @@ -96,7 +102,6 @@ export const openAddNewTaskInCurrentFileModal = ( // @todo - I dont think I need to emit this here, since the processAllUpdatedFiles function will refresh the file and then it will emit this same signal anyways. And, until that, we dont have to refresh the view at all. eventEmitter.emit("SOFT_REFRESH"); cursorPosition = undefined; - return true; }, false, true, @@ -120,11 +125,13 @@ export const openAddNewTaskModal = (plugin: TaskBoard, activeFile?: TFile) => { if (communityPlugins.isQuickAddPluginIntegrationEnabled()) { // Call the API of QuickAdd plugin and pass the formatted content. let completeTask = await getFormattedTaskContent(newTask); - const { formattedTaskContent, newId } = - await addIdToTaskContent(plugin, completeTask); + const { formattedTaskContent } = await addIdToTaskContent( + plugin, + completeTask, + ); completeTask = formattedTaskContent; - (communityPlugins.quickAddPlugin as any)?.api.executeChoice( + await communityPlugins.quickAddPlugin?.api.executeChoice( quickAddPluginChoice, { value: completeTask + "\n", @@ -132,9 +139,15 @@ export const openAddNewTaskModal = (plugin: TaskBoard, activeFile?: TFile) => { ); } else { await addTaskInNote(plugin, newTask, false).then((newId) => { - plugin.realTimeScanner.processAllUpdatedFiles( - newTask.filePath, - ); + plugin.realTimeScanner + .processAllUpdatedFiles(newTask.filePath) + .catch((error) => { + bugReporterManagerInsatance.addToLogs( + 217, + String(error), + "OpenModals.ts/openAddNewTaskModal", + ); + }); }); } @@ -167,7 +180,7 @@ export const openAddNewTaskNoteModal = (app: App, plugin: TaskBoard) => { async ( newTask: taskItem, quickAddPluginChoice: string, - noteContent: string | undefined, + noteContent?: string, ) => { if (!noteContent) { bugReporterManagerInsatance.showNotice( @@ -201,7 +214,7 @@ export const openAddNewTaskNoteModal = (app: App, plugin: TaskBoard) => { } // Required for Obsidian to create the folder and index it. - sleep(200); + await sleep(200); } await plugin.app.vault @@ -211,10 +224,18 @@ export const openAddNewTaskNoteModal = (app: App, plugin: TaskBoard) => { plugin.realTimeScanner.onFileModified( newTask.filePath, ); - sleep(1000).then(() => { + window.setTimeout(() => { // TODO : Is 1 seconds really required ? - plugin.realTimeScanner.processAllUpdatedFiles(); - }); + plugin.realTimeScanner + .processAllUpdatedFiles() + .catch((error) => { + bugReporterManagerInsatance.addToLogs( + 217, + String(error), + "OpenModals.ts/openAddNewTaskNoteModal", + ); + }); + }, 1000); }); } else { const newName = getCurrentLocalDateTimeString(); @@ -238,9 +259,18 @@ export const openAddNewTaskNoteModal = (app: App, plugin: TaskBoard) => { plugin.realTimeScanner.onFileModified( `Copy-${newTask.filePath}`, ); - sleep(1000).then(() => { - plugin.realTimeScanner.processAllUpdatedFiles(); - }); + + window.setTimeout(() => { + plugin.realTimeScanner + .processAllUpdatedFiles() + .catch((error) => { + bugReporterManagerInsatance.addToLogs( + 217, + String(error), + "OpenModals.ts/openAddNewTaskNoteModal", + ); + }); + }, 1000); }); } } catch (error) { @@ -250,7 +280,6 @@ export const openAddNewTaskNoteModal = (app: App, plugin: TaskBoard) => { "OpenModals.ts/openAddNewTaskNoteModal/callback()", ); new Notice(t("error-creating-task-note"), 5000); - return false; } } @@ -266,13 +295,13 @@ export const openAddNewTaskNoteModal = (app: App, plugin: TaskBoard) => { AddTaskModal.open(); }; -export const openEditTaskModal = async ( +export const openEditTaskModal = ( plugin: TaskBoard, existingTask: taskItem, ) => { const EditTaskModal = new TaskEditorModal( plugin, - (updatedTask: taskItem) => { + async (updatedTask: taskItem) => { let eventData: UpdateTaskEventData = { taskID: existingTask.id, state: true, @@ -281,14 +310,24 @@ export const openEditTaskModal = async ( updatedTask.filePath = existingTask.filePath; // Update the task in the file and JSON - updateTaskInFile(plugin, updatedTask, existingTask).then( - (newId) => { - plugin.realTimeScanner.processAllUpdatedFiles( - updatedTask.filePath, - existingTask.id, - ); - }, - ); + updateTaskInFile(plugin, updatedTask, existingTask) + .then((newId) => { + plugin.realTimeScanner + .processAllUpdatedFiles( + updatedTask.filePath, + existingTask.id, + ) + .catch((error) => { + bugReporterManagerInsatance.addToLogs( + 217, + String(error), + "OpenModals.ts/openEditTaskModal", + ); + }); + }) + .catch((error) => { + // No need to handle here, the function already handles this. + }); // DEPRECATED : See notes from //src/utils/TaskItemCacheOperations.ts file // updateTaskInJson(plugin, updatedTask); @@ -332,29 +371,45 @@ export const openEditTaskNoteModal = ( plugin, updatedTask, ).then(() => { - sleep(1000).then(() => { + window.setTimeout(() => { // TODO : Is 1 sec really required ? // This is required to rescan the updated file and refresh the board. - plugin.realTimeScanner.processAllUpdatedFiles( - updatedTask.filePath, - existingTask.id, - ); - }); + plugin.realTimeScanner + .processAllUpdatedFiles( + updatedTask.filePath, + existingTask.id, + ) + .catch((error) => { + bugReporterManagerInsatance.addToLogs( + 217, + String(error), + "OpenModals.ts/openEditTaskNoteModal", + ); + }); + }, 1000); }); } else { - writeDataToVaultFile( + await writeDataToVaultFile( plugin, updatedTask.filePath, newTaskContent, ).then(() => { - sleep(1000).then(() => { + window.setTimeout(() => { // TODO : Is 1 sec really required ? // This is required to rescan the updated file and refresh the board. - plugin.realTimeScanner.processAllUpdatedFiles( - updatedTask.filePath, - existingTask.id, - ); - }); + plugin.realTimeScanner + .processAllUpdatedFiles( + updatedTask.filePath, + existingTask.id, + ) + .catch((error) => { + bugReporterManagerInsatance.addToLogs( + 217, + String(error), + "OpenModals.ts/openEditTaskNoteModal", + ); + }); + }, 1000); }); } @@ -561,7 +616,7 @@ export const openScanFiltersModal = ( filterType: keyof ScanFilters, onSave: (scanFilters: string[]) => void, ) => { - new ScanFilterModal(plugin, filterType, async (newValues) => { + new ScanFilterModal(plugin, filterType, (newValues) => { onSave(newValues); }).open(); }; @@ -654,7 +709,7 @@ export const openEditTaskView = async ( matchLeaf.setEphemeralState({ viewTaskId: taskId }); // Reveal the leaf - workspace.revealLeaf(matchLeaf); + await workspace.revealLeaf(matchLeaf); return leaves[0]; } else { @@ -695,14 +750,22 @@ export const openEditTaskView = async ( ) => { if (!isTaskNote) { // Update the task in the file and JSON - updateTaskInFile( + await updateTaskInFile( plugin, updatedTask, task, ).then((newId) => { - plugin.realTimeScanner.processAllUpdatedFiles( - updatedTask.filePath, - ); + plugin.realTimeScanner + .processAllUpdatedFiles( + updatedTask.filePath, + ) + .catch((error) => { + bugReporterManagerInsatance.addToLogs( + 217, + String(error), + "OpenModals.ts/openEditTaskView", + ); + }); }); } else { if (!updatedNoteContent) { @@ -711,28 +774,43 @@ export const openEditTaskView = async ( plugin, updatedTask, ).then(() => { - // This is required to rescan the updated file and refresh the board. - sleep(1000).then(() => { + window.setTimeout(() => { // TODO : Is 1 sec really required ? // This is required to rescan the updated file and refresh the board. - plugin.realTimeScanner.processAllUpdatedFiles( - updatedTask.filePath, - ); - }); + plugin.realTimeScanner + .processAllUpdatedFiles( + updatedTask.filePath, + ) + .catch((error) => { + bugReporterManagerInsatance.addToLogs( + 217, + String(error), + "OpenModals.ts/openEditTaskView", + ); + }); + }, 1000); }); } else { - writeDataToVaultFile( + await writeDataToVaultFile( plugin, updatedTask.filePath, updatedNoteContent, ).then(() => { - sleep(1000).then(() => { + window.setTimeout(() => { // TODO : Is 1 sec really required ? // This is required to rescan the updated file and refresh the board. - plugin.realTimeScanner.processAllUpdatedFiles( - updatedTask.filePath, - ); - }); + plugin.realTimeScanner + .processAllUpdatedFiles( + updatedTask.filePath, + ) + .catch((error) => { + bugReporterManagerInsatance.addToLogs( + 217, + String(error), + "OpenModals.ts/openEditTaskView", + ); + }); + }, 1000); }); } } @@ -756,13 +834,23 @@ export const openEditTaskView = async ( ) => { if (!isTaskNote) { // Update the task in the file and JSON - updateTaskInFile(plugin, updatedTask, task).then( - (newId) => { - plugin.realTimeScanner.processAllUpdatedFiles( + void updateTaskInFile( + plugin, + updatedTask, + task, + ).then((newId) => { + plugin.realTimeScanner + .processAllUpdatedFiles( updatedTask.filePath, - ); - }, - ); + ) + .catch((error) => { + bugReporterManagerInsatance.addToLogs( + 217, + String(error), + "OpenModals.ts/openEditTaskView", + ); + }); + }); } else { if (!updatedNoteContent) { // Update frontmatter with task properties @@ -770,27 +858,41 @@ export const openEditTaskView = async ( plugin, updatedTask, ).then(() => { - // This is required to rescan the updated file and refresh the board. - sleep(1000).then(() => { - // This is required to rescan the updated file and refresh the board. - plugin.realTimeScanner.processAllUpdatedFiles( - updatedTask.filePath, - ); - }); + window.setTimeout(() => { + // This delay is required to rescan the updated file and refresh the board. + plugin.realTimeScanner + .processAllUpdatedFiles( + updatedTask.filePath, + ) + .catch((error) => { + bugReporterManagerInsatance.addToLogs( + 217, + String(error), + "OpenModals.ts/openEditTaskView", + ); + }); + }, 1000); }); } else { - writeDataToVaultFile( + void writeDataToVaultFile( plugin, updatedTask.filePath, updatedNoteContent, ).then(() => { - sleep(1000).then(() => { - // TODO : Is 1 sec really required ? - // This is required to rescan the updated file and refresh the board. - plugin.realTimeScanner.processAllUpdatedFiles( - updatedTask.filePath, - ); - }); + window.setTimeout(() => { + // This delay is required to rescan the updated file and refresh the board. + plugin.realTimeScanner + .processAllUpdatedFiles( + updatedTask.filePath, + ) + .catch((error) => { + bugReporterManagerInsatance.addToLogs( + 217, + String(error), + "OpenModals.ts/openEditTaskView", + ); + }); + }, 1000); }); } } @@ -818,7 +920,7 @@ export const openEditTaskView = async ( }); // Reveal the leaf - workspace.revealLeaf(leaf); + await workspace.revealLeaf(leaf); } return leaf; } @@ -831,7 +933,7 @@ export const openDateInputModal = async ( initialValue?: string, ) => { const datePicker = new DatePickerModal(plugin, dateName, initialValue); - datePicker.onDateSelected = async (date: string) => { + datePicker.onDateSelected = (date: string) => { // newTask[dateType] = date; // updateTaskItemDate(plugin, newTask, dateType, date); onSelect(date); diff --git a/src/services/TaskSelectorWithCreateModal.ts b/src/services/TaskSelectorWithCreateModal.ts index bbb4778a..e171fb52 100644 --- a/src/services/TaskSelectorWithCreateModal.ts +++ b/src/services/TaskSelectorWithCreateModal.ts @@ -212,7 +212,7 @@ export class TaskSelectorWithCreateModal extends SuggestModal { taskData["id"] = generateRandomStringId(); let completeTask: taskItem = taskData as taskItem; // Create the task - const result = await createNewInlineTask(this.plugin, completeTask); + await createNewInlineTask(this.plugin, completeTask); new Notice( t("A new task created successfully : ", { diff --git a/src/services/editor_extensions/GutterMarker.ts b/src/services/editor_extensions/GutterMarker.ts index 4b93aee3..6a2a07d1 100644 --- a/src/services/editor_extensions/GutterMarker.ts +++ b/src/services/editor_extensions/GutterMarker.ts @@ -10,6 +10,7 @@ import { syntaxTree, tokenClassNodeProp } from "@codemirror/language"; import TaskBoard from "../../../main.js"; import { TaskEditorModal } from "../../modals/TaskEditorModal.js"; import { isTaskLine } from "../../utils/CheckBoxUtils.js"; +import { t } from "i18next"; // Task icon marker class TaskGutterMarker extends GutterMarker { @@ -22,7 +23,7 @@ class TaskGutterMarker extends GutterMarker { text: string, lineNum: number, view: EditorView, - plugin: TaskBoard + plugin: TaskBoard, ) { super(); this.text = text; @@ -37,12 +38,12 @@ class TaskGutterMarker extends GutterMarker { // ".cm-scroller" // ) as HTMLDivElement; const scroller = this.view.scrollDOM; - if (scroller) scroller.style.paddingLeft = "0"; + if (scroller) scroller.addClass("tb_padding_left_0"); const markerEl = createEl("div"); const button = new ExtraButtonComponent(markerEl) .setIcon("edit") - .setTooltip("Edit Task") + .setTooltip(t("edit-task")) .onClick(() => { const lineText = this.view.state.doc.line(this.lineNum).text; const file = this.plugin.app.workspace.getActiveFile(); @@ -52,7 +53,7 @@ class TaskGutterMarker extends GutterMarker { // Check if the line is in a codeblock or frontmatter const line = this.view.state.doc.line(this.lineNum); const syntaxNode = syntaxTree(this.view.state).resolveInner( - line.from + 1 + line.from + 1, ); const nodeProps = syntaxNode.type.prop(tokenClassNodeProp); @@ -67,9 +68,9 @@ class TaskGutterMarker extends GutterMarker { } // Create a save callback for the modal - const saveTask = ( - updatedTask: any, - quickAddPluginChoice: string + const saveTask = async ( + updatedTask: unknown, + quickAddPluginChoice: string, ) => { // This will be handled by the existing task saving logic // The modal will update the file content automatically @@ -84,7 +85,7 @@ class TaskGutterMarker extends GutterMarker { true, // activeNote true, // taskExists (we're editing an existing task) undefined, // task - let the modal parse it from the line - file.path // filePath + file.path, // filePath ); modal.open(); return true; @@ -114,7 +115,7 @@ export function taskGutterExtension(app: App, plugin: TaskBoard): Extension { // Check if the line is in a codeblock or frontmatter const syntaxNode = syntaxTree(view.state).resolveInner( - line.from + 1 + line.from + 1, ); const nodeProps = syntaxNode.type.prop(tokenClassNodeProp); diff --git a/src/services/editor_extensions/PropertiesHiding.ts b/src/services/editor_extensions/PropertiesHiding.ts index 018e3494..eaeaf852 100644 --- a/src/services/editor_extensions/PropertiesHiding.ts +++ b/src/services/editor_extensions/PropertiesHiding.ts @@ -32,7 +32,7 @@ class HiddenPropertyWidget extends WidgetType { } toDOM() { - const span = document.createElement("span"); + const span = activeDocument.createElement("span"); span.className = "taskboard-hidden-property-placeholder"; span.textContent = "..."; span.title = `Hidden: ${this.content}`; @@ -101,7 +101,7 @@ export function getTaskPropertyRegexPatterns( .TaskFormatRegularExpWithGlobal.dependsOnRegex; case taskPropertiesNames.Reminder: - return /\(\@(\d{4}-\d{2}-\d{2}( \d{2}:\d{2})?)\)/g; + return /\(@(\d{4}-\d{2}-\d{2}( \d{2}:\d{2})?)\)/g; default: return /(?:)/g; diff --git a/src/services/subModules.ts b/src/services/subModules.ts index 3b553edb..c29b1711 100644 --- a/src/services/subModules.ts +++ b/src/services/subModules.ts @@ -14,4 +14,6 @@ export class TaskBoardSubmodule { get settings(): PluginDataJson { return this.plugin.settings; } + + api: unknown; } diff --git a/src/services/tasks-plugin/helpers.ts b/src/services/tasks-plugin/helpers.ts index 3cad0c40..2f7a92f5 100644 --- a/src/services/tasks-plugin/helpers.ts +++ b/src/services/tasks-plugin/helpers.ts @@ -4,11 +4,21 @@ import TaskBoard from "../../../main.js"; import { CustomStatus } from "../../interfaces/GlobalSettings.js"; import { taskItem } from "../../interfaces/TaskItem.js"; import { bugReporterManagerInsatance } from "../../managers/BugReporter.js"; -import { getFormattedTaskContent, addIdToTaskContent } from "../../utils/taskLine/TaskContentFormatter.js"; +import { + getFormattedTaskContent, + addIdToTaskContent, +} from "../../utils/taskLine/TaskContentFormatter.js"; import { replaceOldTaskWithNewTask } from "../../utils/taskLine/TaskLineUtils.js"; import { eventEmitter } from "../EventEmitter.js"; import { TasksPluginApi } from "./api.js"; +type TasksPluginStatusSettings = { + statusSettings?: { + coreStatuses?: CustomStatus[]; + customStatuses?: CustomStatus[]; + }; +}; + export async function isTasksPluginEnabled(plugin: TaskBoard) { try { const tasksPluginO = new TasksPluginApi(plugin); @@ -37,17 +47,21 @@ export async function fetchTasksPluginCustomStatuses( // Read the file content const data: string = await plugin.app.vault.adapter.read(path); - const parsedData = JSON.parse(data); + // Treat the plugin data as an explicit structure instead of relying on + // the implicit `any` that JSON.parse() introduces. + const parsedData = JSON.parse(data) as unknown; + const statusSettings = (parsedData as Partial) + .statusSettings; // Extract coreStatuses from the JSON const coreStatuses: CustomStatus[] = - parsedData?.statusSettings?.coreStatuses || []; + statusSettings?.coreStatuses ?? []; // Extract customStatuses from the JSON const customStatuses: CustomStatus[] = - parsedData?.statusSettings?.customStatuses || []; + statusSettings?.customStatuses ?? []; - const statusMap = new Map(); + const statusMap = new Map(); coreStatuses.forEach((status: CustomStatus) => statusMap.set(status.symbol, status), ); @@ -99,7 +113,7 @@ export async function openTasksPluginEditModal( // Prepare the updated task block const completeOldTaskContent = await getFormattedTaskContent(oldTask); if (completeOldTaskContent === "") - throw "getSanitizedTaskContent returned empty string"; + throw new Error("getSanitizedTaskContent returned empty string"); if (tasksPlugin.isTasksPluginEnabled()) { const tasksPluginApiOutput = await tasksPlugin.editTaskLineModal( @@ -135,7 +149,7 @@ export async function openTasksPluginEditModal( completeOldTaskContent, newContent, ); - } else if ((twoTaskTitles.length = 1)) { + } else if (twoTaskTitles.length === 1) { const { formattedTaskContent, newId } = await addIdToTaskContent(plugin, tasksPluginApiOutput); const tasksPluginApiOutputWithId = formattedTaskContent; @@ -150,7 +164,7 @@ export async function openTasksPluginEditModal( completeOldTaskContent, newContent, ); - } else if ((twoTaskTitles.length = 2)) { + } else if (twoTaskTitles.length === 2) { newContent = `${twoTaskTitles[0]}${ oldTask.body.length > 0 ? `\n${oldTask.body.join("\n")}` @@ -177,8 +191,16 @@ export async function openTasksPluginEditModal( return; } - plugin.realTimeScanner.processAllUpdatedFiles(oldTask.filePath); - setTimeout(() => { + plugin.realTimeScanner + .processAllUpdatedFiles(oldTask.filePath) + .catch((error) => { + bugReporterManagerInsatance.addToLogs( + 217, + String(error), + "helpers.ts/openTasksPluginEditModal", + ); + }); + window.setTimeout(() => { // This event emmitter will stop any loading animation of ongoing task-card. eventEmitter.emit("UPDATE_TASK", { taskID: oldTask.id, diff --git a/src/settings/2_x_x_Migrations/LegacyInterfacesAndTypings.ts b/src/settings/2_x_x_Migrations/LegacyInterfacesAndTypings.ts index be6b87f8..9d988f19 100644 --- a/src/settings/2_x_x_Migrations/LegacyInterfacesAndTypings.ts +++ b/src/settings/2_x_x_Migrations/LegacyInterfacesAndTypings.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-deprecated */ import { ColumnData, Filter, @@ -12,6 +13,14 @@ import { mapViewArrowDirection, mapViewScrollAction, mapViewEdgeType, + RibbonIconActions, + taskPropertyFormatOptions, + scanModeOptions, + taskCardStyleNames, + mapViewBackgrounVariantTypes, + mapViewNodeMapOrientation, + NotificationService, + UniversalDateOptions, } from "../../interfaces/Enums.js"; import { ScanFilters, @@ -19,6 +28,7 @@ import { CustomStatus, FrontmatterFormattingInterface, TaskBoardAction, + taskBoardFilesRegistryType, } from "../../interfaces/GlobalSettings.js"; export type BoardLegacy = { @@ -49,7 +59,7 @@ export interface globalSettingsDataLegacy { scanFilters: ScanFilters; firstDayOfWeek?: string; ignoreFileNameDates: boolean; - taskPropertyFormat: string; + taskPropertyFormat: taskPropertyFormatOptions; dateFormat: string; dateTimeFormat: string; dailyNotesPluginComp: boolean; @@ -62,16 +72,16 @@ export interface globalSettingsDataLegacy { autoAddCancelledDate: boolean; // scanVaultAtStartup: boolean; -- @deprecated v1.9.0 - A better approach has been used using showModifiedFilesNotice feature. showModifiedFilesNotice: boolean; - scanMode: string; + scanMode: scanModeOptions; columnWidth: string; visiblePropertiesList: string[]; - taskCardStyle: string; + taskCardStyle: taskCardStyleNames; showVerticalScroll: boolean; dragAutoScrollEdgePercent: number; tagColors: TagColor[]; editButtonAction: EditButtonMode; doubleClickCardToEdit: EditButtonMode; - universalDate: string; + universalDate: UniversalDateOptions; customStatuses: CustomStatus[]; showTaskWithoutMetadata: boolean; tagColorsType: TagColorType; @@ -91,7 +101,7 @@ export interface globalSettingsDataLegacy { frontmatterFormatting: FrontmatterFormattingInterface[]; showFrontmatterTagsOnCards: boolean; tasksCacheFilePath: string; - notificationService: string; + notificationService: NotificationService; actions: TaskBoardAction[]; searchQuery?: string; hiddenTaskProperties: taskPropertiesNames[]; @@ -100,15 +110,17 @@ export interface globalSettingsDataLegacy { experimentalFeatures: boolean; safeGuardFeature: boolean; lastViewHistory: { - viewedType: string; - boardIndex: number; + viewedType?: string; + boardIndex?: number; settingTab: number; taskId?: string; + // New setting introduced in 2.x.x series + boardFilePath: string; }; boundTaskCompletionToChildTasks: boolean; mapView: { - background: string; - mapOrientation: string; + background: mapViewBackgrounVariantTypes; + mapOrientation: mapViewNodeMapOrientation; optimizedRender: boolean; arrowDirection: mapViewArrowDirection; animatedEdges: boolean; @@ -117,6 +129,12 @@ export interface globalSettingsDataLegacy { renderVisibleNodes: boolean; edgeType: mapViewEdgeType; }; + + // These are new settings introduced in v2.x.x, hence adding here for the setting synchronization utility to work properly. + taskBoardFilesRegistry: taskBoardFilesRegistryType; + enableDragnDropTouch: boolean; + filtersWarehouse: Filter[]; + ribbonIconAction: RibbonIconActions; } export interface PluginDataJsonLegacy { diff --git a/src/settings/2_x_x_Migrations/MigrationModal.tsx b/src/settings/2_x_x_Migrations/MigrationModal.tsx index cf529809..67a04c10 100644 --- a/src/settings/2_x_x_Migrations/MigrationModal.tsx +++ b/src/settings/2_x_x_Migrations/MigrationModal.tsx @@ -40,7 +40,7 @@ const MigrationModalContent: React.FC<{ setLogs((prev) => [...prev, logEntry]); // Auto-scroll to bottom - setTimeout(() => { + window.setTimeout(() => { if (terminalRef.current) { terminalRef.current.scrollTop = terminalRef.current.scrollHeight; } @@ -130,7 +130,7 @@ const MigrationModalContent: React.FC<{ } // Show Notice to reload Obsidian and make the "Reload Obsidian" button visible - showReloadObsidianNotice(plugin); + void showReloadObsidianNotice(plugin); // Capture all logs after migration completes and all logs are added result.logs = [...logsRef.current]; @@ -213,7 +213,7 @@ const MigrationModalContent: React.FC<{