mirror of
https://github.com/poanse/obsidian-taskmap.git
synced 2026-07-22 06:05:58 +00:00
optimized for large number of tasks
This commit is contained in:
parent
33546c6c74
commit
af34f98d2c
13 changed files with 203 additions and 82 deletions
23
package-lock.json
generated
23
package-lock.json
generated
|
|
@ -6,7 +6,7 @@
|
|||
"packages": {
|
||||
"": {
|
||||
"name": "obsidian-taskmap",
|
||||
"version": "0.0.8",
|
||||
"version": "0.1.3",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@napolab/alpha-blend": "^2.0.0",
|
||||
|
|
@ -39,6 +39,7 @@
|
|||
"svelte-check": "^4.3.4",
|
||||
"svelte-preprocess": "^6.0.3",
|
||||
"tslib": "latest",
|
||||
"tsx": "^4.21.0",
|
||||
"typescript": "latest",
|
||||
"typescript-eslint": "^8.54.0"
|
||||
}
|
||||
|
|
@ -5990,6 +5991,26 @@
|
|||
"dev": true,
|
||||
"license": "0BSD"
|
||||
},
|
||||
"node_modules/tsx": {
|
||||
"version": "4.21.0",
|
||||
"resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz",
|
||||
"integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"esbuild": "~0.27.0",
|
||||
"get-tsconfig": "^4.7.5"
|
||||
},
|
||||
"bin": {
|
||||
"tsx": "dist/cli.mjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "~2.3.3"
|
||||
}
|
||||
},
|
||||
"node_modules/tunnel-agent": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
|
||||
|
|
|
|||
|
|
@ -7,7 +7,8 @@
|
|||
"dev": "node esbuild.config.mjs",
|
||||
"build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production",
|
||||
"version": "node version-bump.mjs && git add manifest.json versions.json",
|
||||
"svelte-check": "svelte-check --tsconfig tsconfig.json"
|
||||
"svelte-check": "svelte-check --tsconfig tsconfig.json",
|
||||
"perf:tasks": "tsx scripts/perf-tasks.ts"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
|
|
@ -33,6 +34,7 @@
|
|||
"svelte-check": "^4.3.4",
|
||||
"svelte-preprocess": "^6.0.3",
|
||||
"tslib": "latest",
|
||||
"tsx": "^4.21.0",
|
||||
"typescript": "latest",
|
||||
"typescript-eslint": "^8.54.0"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@ export class Context {
|
|||
this.pressedButtonCode = -1;
|
||||
|
||||
const sel = this.versionedData.getTaskOption(this.selectedTaskId);
|
||||
if (sel == null || sel.deleted) {
|
||||
if (sel === undefined || sel.deleted) {
|
||||
this.selectedTaskId = NoTaskId;
|
||||
this.toolbarStatus = DEFAULT_TOOLBAR_STATUS;
|
||||
} else {
|
||||
|
|
@ -110,12 +110,12 @@ export class Context {
|
|||
}
|
||||
|
||||
const focused = this.versionedData.getTaskOption(this.focusedTaskId);
|
||||
if (focused == null || focused.deleted) {
|
||||
if (focused === undefined || focused.deleted) {
|
||||
this.focusedTaskId = RootTaskId;
|
||||
}
|
||||
|
||||
const editing = this.versionedData.getTaskOption(this.editingTaskId);
|
||||
if (editing == null || editing.deleted) {
|
||||
if (editing === undefined || editing.deleted) {
|
||||
this.editingTaskId = NoTaskId;
|
||||
}
|
||||
|
||||
|
|
@ -274,10 +274,7 @@ export class Context {
|
|||
}
|
||||
|
||||
public isAncestorOfHidden(taskId: TaskId): boolean {
|
||||
return this.versionedData
|
||||
.getAncestors(this.focusedTaskId)
|
||||
.map((t) => t.taskId)
|
||||
.includes(taskId);
|
||||
return this.versionedData.getAncestorIds(this.focusedTaskId).includes(taskId);
|
||||
}
|
||||
|
||||
public updateTaskPositions(draggingOnly = false) {
|
||||
|
|
@ -471,9 +468,9 @@ export class Context {
|
|||
const task = this.versionedData.getTask(taskId);
|
||||
const ancestorTasks = this.versionedData.getAncestors(taskId);
|
||||
|
||||
const focusedAncestorIds = this.versionedData
|
||||
.getAncestors(this.focusedTaskId)
|
||||
.map((t) => t.taskId);
|
||||
const focusedAncestorIds = this.versionedData.getAncestorIds(
|
||||
this.focusedTaskId,
|
||||
);
|
||||
const focusedDescendantIds = this.versionedData.getDescendantIds(
|
||||
this.focusedTaskId,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ export class FileWatcherWithCache {
|
|||
if (!projectData) {
|
||||
continue;
|
||||
}
|
||||
const affectedTasks = projectData.tasks.filter(
|
||||
const affectedTasks = projectData.getTasks().filter(
|
||||
(t) => t.path && deletedFile.path === t.path,
|
||||
);
|
||||
for (const t of affectedTasks) {
|
||||
|
|
@ -153,7 +153,7 @@ export class FileWatcherWithCache {
|
|||
if (!projectData) {
|
||||
continue;
|
||||
}
|
||||
const affectedTasks = projectData.tasks.filter(
|
||||
const affectedTasks = projectData.getTasks().filter(
|
||||
(t) => t.path && t.path === oldPath,
|
||||
);
|
||||
for (const t of affectedTasks) {
|
||||
|
|
|
|||
|
|
@ -204,6 +204,11 @@ export class NodePositionsCalculator {
|
|||
Vector2
|
||||
>();
|
||||
parentIds.forEach((parentId) => {
|
||||
if (!subtreeSizeByNodeId.has(parentId)) {
|
||||
throw new Error(
|
||||
`Failed to calculate layout: taskId [${parentId}] not in subtreeSizeByNodeId`,
|
||||
);
|
||||
}
|
||||
parentAlignmentShift.set(
|
||||
parentId,
|
||||
V2.mult(
|
||||
|
|
@ -267,7 +272,10 @@ export class NodePositionsCalculator {
|
|||
depthOneTasksByRow.get(idx)!.push(t);
|
||||
});
|
||||
|
||||
const yShiftByRowId: Map<number, number> = new Map<number, number>();
|
||||
const yShiftByRowId: Map<number, number> = new Map<
|
||||
number,
|
||||
number
|
||||
>();
|
||||
[...depthOneTasksByRow.keys()].forEach((key) => {
|
||||
const rowIdx = Number(key);
|
||||
const elements = depthOneTasksByRow.get(rowIdx)!;
|
||||
|
|
@ -302,7 +310,10 @@ export class NodePositionsCalculator {
|
|||
// Sizes of subtrees by x. If 2 tasks are above each other, the larger one is used.
|
||||
this.subtreeWidthByHalfPriority = new Map<number, number>();
|
||||
|
||||
const rootChildrenByPriority: Map<number, TaskId> = new Map<number, TaskId>();
|
||||
const rootChildrenByPriority: Map<number, TaskId> = new Map<
|
||||
number,
|
||||
TaskId
|
||||
>();
|
||||
const rootChildren =
|
||||
childrenIdsByParentId.get(RootTaskId) ?? [];
|
||||
rootChildren.forEach((id, idx) => {
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ export function serializeProjectData(projectData: ProjectData) {
|
|||
return JSON.stringify(
|
||||
{
|
||||
schemaVersion: TASKMAP_FILE_SCHEMA_VERSION,
|
||||
tasks: projectData.tasks,
|
||||
tasks: [...projectData.tasks.values()],
|
||||
blockerPairs: projectData.blockerPairs,
|
||||
folderPath: projectData.folderPath,
|
||||
curTaskId: projectData.curTaskId,
|
||||
|
|
@ -56,3 +56,5 @@ export function deserializeProjectData(data: string) {
|
|||
const input = parseProjectFileJson(parsed);
|
||||
return new ProjectData(input);
|
||||
}
|
||||
|
||||
export const DEFAULT_DATA = serializeProjectData(ProjectData.getDefault());
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import { debounce, TextFileView, TFile, WorkspaceLeaf } from "obsidian";
|
||||
import { mount, unmount } from "svelte";
|
||||
import { DEFAULT_DATA, ProjectData } from "./data/ProjectData.svelte";
|
||||
import { ProjectData } from "./data/ProjectData.svelte";
|
||||
import { Context } from "./Context.svelte.js";
|
||||
import { NodePositionsCalculator } from "./NodePositionsCalculator";
|
||||
import TaskmapContainer from "./components/TaskmapContainer.svelte";
|
||||
import { deserializeProjectData, updateFile } from "./SaveManager";
|
||||
import { DEFAULT_DATA, deserializeProjectData, updateFile } from "./SaveManager";
|
||||
import type TaskmapPlugin from "./main";
|
||||
import { VersionedData } from "./data/VersionedData";
|
||||
import { HistoryManager } from "./data/HistoryManager.svelte";
|
||||
|
|
|
|||
|
|
@ -170,18 +170,20 @@
|
|||
</marker>
|
||||
</defs>
|
||||
<g class="svg-group">
|
||||
{#each (context.versionedData.getTasks()
|
||||
.filter(t => !context.isTaskHidden(t.taskId))
|
||||
.filter(t => t.taskId !== RootTaskId)
|
||||
.filter(t => !context.versionedData.isBranchHidden(t.taskId))
|
||||
) as task (task.taskId)}
|
||||
<Connection
|
||||
startTaskId={task.parentId}
|
||||
endTaskId={task.taskId}
|
||||
{context}
|
||||
isBlockerConnection={false}
|
||||
/>
|
||||
{/each}
|
||||
{#key context.versionedData.getTasksVersion()}
|
||||
{#each (context.versionedData.getTasks()
|
||||
.filter(t => !context.isTaskHidden(t.taskId))
|
||||
.filter(t => t.taskId !== RootTaskId)
|
||||
.filter(t => !context.versionedData.isBranchHidden(t.taskId))
|
||||
) as task (task.taskId)}
|
||||
<Connection
|
||||
startTaskId={task.parentId}
|
||||
endTaskId={task.taskId}
|
||||
{context}
|
||||
isBlockerConnection={false}
|
||||
/>
|
||||
{/each}
|
||||
{/key}
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
|
@ -189,9 +191,11 @@
|
|||
class="task-layer"
|
||||
role="presentation"
|
||||
>
|
||||
{#each context.versionedData.getTasks().filter(t => !context.isTaskHidden(t.taskId)) as task (task.taskId)}
|
||||
<Task taskId={task.taskId} {context} coords={context.getCurrentTaskPosition(task.taskId)}/>
|
||||
{/each}
|
||||
{#key context.versionedData.getTasksVersion()}
|
||||
{#each context.versionedData.getTasks().filter(t => !context.isTaskHidden(t.taskId)) as task (task.taskId)}
|
||||
<Task taskId={task.taskId} {context} coords={context.getCurrentTaskPosition(task.taskId)}/>
|
||||
{/each}
|
||||
{/key}
|
||||
</div>
|
||||
|
||||
<svg class="svg-layer" overflow="visible">
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ export class AddTaskAction implements Action {
|
|||
do(data: ProjectData): void {
|
||||
const childrenCount = data.getChildren(this.parentId).length;
|
||||
this.addedTaskId = data.curTaskId;
|
||||
data.tasks.push({
|
||||
const task = {
|
||||
taskId: this.addedTaskId,
|
||||
parentId: this.parentId,
|
||||
status: StatusCode.READY,
|
||||
|
|
@ -26,8 +26,8 @@ export class AddTaskAction implements Action {
|
|||
hidden: false,
|
||||
priority: childrenCount,
|
||||
depth: data.getTask(this.parentId).depth + 1,
|
||||
});
|
||||
data.curTaskId++;
|
||||
};
|
||||
data.addTask(task);
|
||||
data.recalcStatusRecursive(this.parentId);
|
||||
}
|
||||
|
||||
|
|
@ -38,8 +38,7 @@ export class AddTaskAction implements Action {
|
|||
if (data.curTaskId != this.addedTaskId + 1) {
|
||||
throw new Error();
|
||||
}
|
||||
data.tasks = data.tasks.filter((t) => t.taskId !== this.addedTaskId);
|
||||
data.curTaskId--;
|
||||
data.removeTask(this.addedTaskId);
|
||||
this.addedTaskId = undefined;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ export class HistoryManager {
|
|||
|
||||
execute(action: Action, data: ProjectData): void {
|
||||
action.do(data);
|
||||
data.markTasksUpdated();
|
||||
this.undoStack.push(action);
|
||||
this.redoStack = []; // Clear redo stack on new action
|
||||
}
|
||||
|
|
@ -16,6 +17,7 @@ export class HistoryManager {
|
|||
if (!action) return false;
|
||||
|
||||
action.undo(data);
|
||||
data.markTasksUpdated();
|
||||
this.redoStack.push(action);
|
||||
return true;
|
||||
}
|
||||
|
|
@ -25,6 +27,7 @@ export class HistoryManager {
|
|||
if (!action) return false;
|
||||
|
||||
action.do(data);
|
||||
data.markTasksUpdated();
|
||||
this.undoStack.push(action);
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,12 +4,16 @@
|
|||
type TaskData,
|
||||
type TaskId,
|
||||
} from "../types";
|
||||
import { SvelteMap } from "svelte/reactivity";
|
||||
import { NoTaskId, RootTaskId } from "../NodePositionsCalculator";
|
||||
import { serializeProjectData } from "../SaveManager";
|
||||
import type { ProjectFileParsed } from "./ProjectDataSchema";
|
||||
|
||||
export class ProjectData {
|
||||
tasks = $state(new Array<TaskData>());
|
||||
tasks = new SvelteMap<TaskId, TaskData>();
|
||||
childrenCache = new SvelteMap<TaskId, TaskId[]>();
|
||||
ancestorsCache = new SvelteMap<TaskId, TaskId[]>();
|
||||
descendantsCache = new SvelteMap<TaskId, TaskId[]>();
|
||||
tasksVersion = $state(0);
|
||||
blockerPairs = $state(new Array<BlockerPair>());
|
||||
folderPath: string | undefined;
|
||||
curTaskId = RootTaskId;
|
||||
|
|
@ -24,17 +28,96 @@ export class ProjectData {
|
|||
}
|
||||
|
||||
constructor(obj: ProjectFileParsed) {
|
||||
this.tasks = obj.tasks;
|
||||
this.tasks = new SvelteMap(
|
||||
obj.tasks.map((task) => [task.taskId, task]),
|
||||
);
|
||||
if (this.tasks.size == 0) {
|
||||
this.addRootTask();
|
||||
}
|
||||
this.rebuildCaches();
|
||||
this.blockerPairs = obj.blockerPairs ?? [];
|
||||
this.folderPath = obj.folderPath;
|
||||
this.curTaskId = obj.curTaskId;
|
||||
if (this.tasks.length == 0) {
|
||||
this.addRootTask();
|
||||
}
|
||||
|
||||
public markTasksUpdated() {
|
||||
this.tasksVersion += 1;
|
||||
}
|
||||
|
||||
private rebuildCaches() {
|
||||
// Order matters: descendants rely on ancestors, and ancestors rely on children.
|
||||
this.rebuildChildrenCache();
|
||||
this.rebuildAncestorsCache();
|
||||
this.rebuildDescendantsCache();
|
||||
}
|
||||
|
||||
private rebuildChildrenCache() {
|
||||
this.childrenCache = new SvelteMap<TaskId, TaskId[]>();
|
||||
for (const task of this.getTasks()) {
|
||||
const children = this.childrenCache.get(task.parentId) ?? [];
|
||||
children.push(task.taskId);
|
||||
this.childrenCache.set(task.parentId, children);
|
||||
}
|
||||
}
|
||||
|
||||
// Rebuild ancestors by traversing childrenCache top-down from root.
|
||||
private rebuildAncestorsCache() {
|
||||
this.ancestorsCache = new SvelteMap<TaskId, TaskId[]>();
|
||||
const queue: TaskId[] = [RootTaskId];
|
||||
|
||||
while (queue.length > 0) {
|
||||
const taskId = queue.shift();
|
||||
if (taskId === undefined) {
|
||||
continue;
|
||||
}
|
||||
const task = this.getTask(taskId);
|
||||
if (task.depth === 0) {
|
||||
this.ancestorsCache.set(taskId, []);
|
||||
} else {
|
||||
const parentAncestors =
|
||||
this.ancestorsCache.get(task.parentId) ?? [];
|
||||
this.ancestorsCache.set(taskId, [
|
||||
task.parentId,
|
||||
...parentAncestors,
|
||||
]);
|
||||
}
|
||||
queue.push(...(this.childrenCache.get(taskId) ?? []));
|
||||
}
|
||||
}
|
||||
|
||||
private rebuildDescendantsCache() {
|
||||
this.descendantsCache = new SvelteMap<TaskId, TaskId[]>();
|
||||
|
||||
for (const task of this.getTasks()) {
|
||||
this.descendantsCache.set(task.taskId, [task.taskId]);
|
||||
}
|
||||
|
||||
for (const taskId of this.tasks.keys()) {
|
||||
for (const ancestorId of this.getAncestorIds(taskId)) {
|
||||
const descendants = this.descendantsCache.get(ancestorId);
|
||||
if (descendants !== undefined) {
|
||||
descendants.push(taskId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public addTask(task: TaskData) {
|
||||
this.tasks.set(task.taskId, task);
|
||||
this.rebuildCaches();
|
||||
this.markTasksUpdated();
|
||||
this.curTaskId++;
|
||||
}
|
||||
|
||||
public removeTask(taskId: TaskId) {
|
||||
this.tasks.delete(taskId);
|
||||
this.rebuildCaches();
|
||||
this.markTasksUpdated();
|
||||
this.curTaskId--;
|
||||
}
|
||||
|
||||
public addRootTask() {
|
||||
this.tasks.push({
|
||||
const task = {
|
||||
taskId: this.curTaskId,
|
||||
parentId: NoTaskId,
|
||||
status: StatusCode.IN_PROGRESS,
|
||||
|
|
@ -43,38 +126,24 @@ export class ProjectData {
|
|||
depth: 0,
|
||||
deleted: false,
|
||||
hidden: false,
|
||||
});
|
||||
this.curTaskId++;
|
||||
};
|
||||
this.addTask(task);
|
||||
}
|
||||
|
||||
public getDescendantIds(taskId: number, includeDeleted: boolean = false) {
|
||||
const tasks = [taskId];
|
||||
const result: TaskId[] = [];
|
||||
while (tasks.length > 0) {
|
||||
const task = tasks.pop();
|
||||
if (task === undefined) {
|
||||
break;
|
||||
}
|
||||
result.push(task);
|
||||
tasks.push(...this.getChildren(task, includeDeleted));
|
||||
}
|
||||
return result;
|
||||
public getDescendantIds(taskId: number) {
|
||||
return this.descendantsCache.get(taskId) ?? [taskId];
|
||||
}
|
||||
|
||||
public getAncestors(taskId: number) {
|
||||
const res: TaskData[] = [];
|
||||
let task = this.getTask(taskId);
|
||||
while (task.depth != 0) {
|
||||
task = this.getTask(task.parentId);
|
||||
res.push(task);
|
||||
}
|
||||
return res;
|
||||
return this.getAncestorIds(taskId).map((id) => this.getTask(id));
|
||||
}
|
||||
|
||||
public getAncestorIds(taskId: number) {
|
||||
return [...(this.ancestorsCache.get(taskId) ?? [])];
|
||||
}
|
||||
|
||||
public isAncestorOf(taskId: TaskId, candidate: TaskId) {
|
||||
return this.getAncestors(taskId)
|
||||
.map((t) => t.taskId)
|
||||
.includes(candidate);
|
||||
return this.getAncestorIds(taskId).includes(candidate);
|
||||
}
|
||||
|
||||
public isDescendantOf(taskId: TaskId, candidate: TaskId) {
|
||||
|
|
@ -82,15 +151,15 @@ export class ProjectData {
|
|||
}
|
||||
|
||||
public getChildren(taskId: number, includeDeleted: boolean = false) {
|
||||
let res = this.tasks.filter((t) => t.parentId === taskId);
|
||||
if (!includeDeleted) {
|
||||
res = res.filter((t) => !t.deleted);
|
||||
const childIds = this.childrenCache.get(taskId) ?? [];
|
||||
if (includeDeleted) {
|
||||
return [...childIds];
|
||||
}
|
||||
return res.map((t) => t.taskId);
|
||||
return childIds.filter((id) => !this.getTask(id).deleted);
|
||||
}
|
||||
|
||||
public getTask(taskId: number) {
|
||||
const res = this.tasks.find((t) => t.taskId == taskId);
|
||||
const res = this.tasks.get(taskId);
|
||||
if (res) {
|
||||
return res;
|
||||
} else {
|
||||
|
|
@ -98,6 +167,12 @@ export class ProjectData {
|
|||
}
|
||||
}
|
||||
|
||||
public getTasks(includeDeleted: boolean = false) {
|
||||
return [...this.tasks.values()].filter(
|
||||
(t) => includeDeleted || !t.deleted,
|
||||
);
|
||||
}
|
||||
|
||||
public isTaskDeleted(taskId: number) {
|
||||
return this.getTask(taskId).deleted;
|
||||
}
|
||||
|
|
@ -106,9 +181,10 @@ export class ProjectData {
|
|||
const taskData = this.getTask(taskId);
|
||||
const oldParentId = taskData.parentId;
|
||||
taskData.parentId = newParentId;
|
||||
this.rebuildCaches();
|
||||
this.recalcStatusRecursive(oldParentId);
|
||||
this.recalcStatusRecursive(newParentId);
|
||||
[taskId, ...this.getDescendantIds(taskId)].forEach((taskId) => {
|
||||
this.getDescendantIds(taskId).forEach((taskId) => {
|
||||
const task = this.getTask(taskId);
|
||||
task.depth = this.getTask(task.parentId).depth + 1;
|
||||
});
|
||||
|
|
@ -212,5 +288,3 @@ export class ProjectData {
|
|||
this.folderPath = path === "" ? undefined : path;
|
||||
};
|
||||
}
|
||||
|
||||
export const DEFAULT_DATA = serializeProjectData(ProjectData.getDefault());
|
||||
|
|
|
|||
|
|
@ -133,7 +133,11 @@ export class VersionedData {
|
|||
};
|
||||
|
||||
public getTasks = (includeDeleted = false) => {
|
||||
return this.data.tasks.filter((t) => includeDeleted || !t.deleted);
|
||||
return this.data.getTasks(includeDeleted);
|
||||
};
|
||||
|
||||
public getTasksVersion = () => {
|
||||
return this.data.tasksVersion;
|
||||
};
|
||||
|
||||
public getTask = (taskId: TaskId) => {
|
||||
|
|
@ -141,7 +145,7 @@ export class VersionedData {
|
|||
};
|
||||
|
||||
public getTaskOption = (taskId: TaskId) => {
|
||||
return this.data.tasks.find((t) => t.taskId == taskId) ?? null;
|
||||
return this.data.tasks.get(taskId);
|
||||
};
|
||||
|
||||
public getChildren = (taskId: TaskId, includeDeleted?: boolean) => {
|
||||
|
|
@ -152,8 +156,12 @@ export class VersionedData {
|
|||
return this.data.getAncestors(taskId);
|
||||
};
|
||||
|
||||
public getDescendantIds = (taskId: TaskId, includeDeleted?: boolean) => {
|
||||
return this.data.getDescendantIds(taskId, includeDeleted);
|
||||
public getAncestorIds = (taskId: TaskId) => {
|
||||
return this.data.getAncestorIds(taskId);
|
||||
};
|
||||
|
||||
public getDescendantIds = (taskId: TaskId) => {
|
||||
return this.data.getDescendantIds(taskId);
|
||||
};
|
||||
|
||||
public isAncestorOf = (taskId: TaskId, candidate: TaskId) => {
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@ import { addIcon, Plugin } from "obsidian";
|
|||
import { TASKMAP_VIEW_TYPE, TaskmapView } from "./TaskmapView";
|
||||
import { DEFAULT_SETTINGS, type TaskmapSettings } from "./TaskmapSettings";
|
||||
import { TaskmapSettingTab } from "./TaskmapSettingTab";
|
||||
import { DEFAULT_DATA } from "./data/ProjectData.svelte";
|
||||
import { FileWatcherWithCache } from "./FileWatcherWithCache";
|
||||
import { LOGO_CONTENT, LOGO_NAME } from "./Constants";
|
||||
import { DEFAULT_DATA } from "./SaveManager";
|
||||
|
||||
export const FILE_EXTENSION = "taskmap";
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue