fixed incorrect link behaviour

This commit is contained in:
poanse 2026-05-03 22:46:32 +03:00
parent c4036d0ae2
commit f1ad3fe0cd
8 changed files with 72 additions and 44 deletions

View file

@ -1,7 +1,7 @@
{
"id": "taskmap",
"name": "Taskmap",
"version": "0.1.4",
"version": "0.1.5",
"minAppVersion": "1.12.4",
"description": "Plan projects via interactive GUI task trees with automatic layout.",
"author": "poanse",

2
package-lock.json generated
View file

@ -1,6 +1,6 @@
{
"name": "obsidian-taskmap",
"version": "0.1.4",
"version": "0.1.5",
"lockfileVersion": 3,
"requires": true,
"packages": {

View file

@ -1,6 +1,6 @@
{
"name": "obsidian-taskmap",
"version": "0.1.4",
"version": "0.1.5",
"description": "Taskmap plugin for Obsidian (https://obsidian.md)",
"main": "main.js",
"scripts": {

View file

@ -22,7 +22,12 @@ import {
V2,
} from "./NodePositionsCalculator";
import { DraggingManager } from "./DraggingManager.svelte";
import { delink, LinkManager, tasknameFromFilePath } from "./LinkManager";
import {
delink,
LinkManager,
taskNameFromFile,
taskPathFromFile,
} from "./LinkManager";
import { HistoryManager } from "./data/HistoryManager.svelte";
import type { ProjectData } from "./data/ProjectData.svelte.js";
import { VersionedData } from "./data/VersionedData";
@ -508,8 +513,8 @@ export class Context {
);
this.versionedData.setName(
taskId,
tasknameFromFilePath(this.versionedData.getTask(taskId).name),
filepath,
taskNameFromFile(tfile),
taskPathFromFile(tfile),
);
this.save();
await this.app.workspace

View file

@ -1,7 +1,12 @@
import { type App, type Plugin, TAbstractFile, TFile, TFolder } from "obsidian";
import { TASKMAP_VIEW_TYPE, TaskmapView } from "./TaskmapView";
import { loadProjectData, updateFile } from "./SaveManager";
import { delink, generateMarkdownLink, pathIsUnderFolder } from "./LinkManager";
import {
delink,
pathIsUnderFolder,
taskNameFromFile,
taskPathFromFile,
} from "./LinkManager";
import type { ProjectData } from "./data/ProjectData.svelte";
/** In-memory index of `.taskmap` files; `TFile.path` stays in sync with vault renames. */
@ -113,9 +118,9 @@ export class FileWatcherWithCache {
if (!projectData) {
continue;
}
const affectedTasks = projectData.getTasks().filter(
(t) => t.path && deletedFile.path === t.path,
);
const affectedTasks = projectData
.getTasks()
.filter((t) => t.path && deletedFile.path === t.path);
for (const t of affectedTasks) {
t.name = delink(t.name);
t.path = undefined;
@ -153,12 +158,12 @@ export class FileWatcherWithCache {
if (!projectData) {
continue;
}
const affectedTasks = projectData.getTasks().filter(
(t) => t.path && t.path === oldPath,
);
const affectedTasks = projectData
.getTasks()
.filter((t) => t.path && t.path === oldPath);
for (const t of affectedTasks) {
t.path = changedMdFile.path;
t.name = generateMarkdownLink(app, changedMdFile);
t.path = taskPathFromFile(changedMdFile);
t.name = taskNameFromFile(changedMdFile);
}
if (affectedTasks) {
console.debug(`${taskmapFile.path} changed`);

View file

@ -1,6 +1,7 @@
import type { App, TFile } from "obsidian";
import type { TaskData } from "./types";
export function tasknameFromFilePath(path: string) {
export function linkFromFilePath(path: string) {
if (path.endsWith(".md")) {
path = path.slice(0, path.length - 3);
}
@ -15,14 +16,20 @@ export function delink(s: string) {
return isLink(s) ? s.slice(2, s.length - 2) : s;
}
/** relativePath to TFile */
export function getFromRelativePath(app: App, path: string) {
return app.vault.getFileByPath(path);
export function nameFromLink(app: App, s: string) {
if (!isLink(s)) {
return s;
}
const delinked = delink(s);
const file = app.vault.getFileByPath(delinked);
if (!file) {
return delinked;
}
return file.basename;
}
/** TFile to wikilink */
export function generateMarkdownLink(app: App, file: TFile) {
return app.fileManager.generateMarkdownLink(file, "");
export function generateMarkdownLinkFromTask(task: TaskData) {
return `[${task.name}](${task.path})`;
}
/** True if `path` is a file inside `folderPath` (not a false `startsWith` on the folder segment). */
@ -30,6 +37,13 @@ export function pathIsUnderFolder(path: string, folderPath: string): boolean {
return path.startsWith(folderPath + "/");
}
export function taskPathFromFile(tfile: TFile) {
return tfile.path;
}
export function taskNameFromFile(tfile: TFile) {
return tfile.basename;
}
export class LinkManager {
private app: App;

View file

@ -3,7 +3,13 @@
import {Component, MarkdownRenderer, Notice} from "obsidian";
import {LinkSuggest} from "../helpers/LinkSuggest";
import type {Context} from "../Context.svelte.js";
import {delink, getFromRelativePath, isLink} from "../LinkManager";
import {
generateMarkdownLinkFromTask,
isLink,
linkFromFilePath,
nameFromLink, taskNameFromFile,
taskPathFromFile
} from "../LinkManager";
import {NoTaskId} from "../NodePositionsCalculator";
let {
@ -62,7 +68,7 @@
const target = e.target as HTMLElement;
const link = target.closest(".internal-link");
if (link && taskData.path) {
const file = getFromRelativePath(context.app, taskData.path);
const file = context.app.vault.getFileByPath(taskData.path);
if (file) {
e.preventDefault(); // TODO: is this line needed?
e.stopPropagation();
@ -96,7 +102,7 @@
return;
}
textPreviewEl.empty(); // Clear previous render
const content = taskData.name;
const content = taskData.path ? generateMarkdownLinkFromTask(taskData) : taskData.name;
await MarkdownRenderer.render(
context.app,
content,
@ -159,20 +165,27 @@
return;
}
let path;
let newName = textEditEl.value;
if (isLink(newName)) {
const file = context.linkManager.getFromLink(newName);
let name;
let maybeLink = textEditEl.value;
if (textEditEl.value == taskData.name) {
// no changes
return;
}
if (isLink(maybeLink)) {
const file = context.linkManager.getFromLink(maybeLink);
if (file !== null) {
path = file.path;
path = taskPathFromFile(file);
name = taskNameFromFile(file);
} else {
new Notice(`Link ${newName} points to a nonexistent file`);
new Notice(`Link ${maybeLink} points to a nonexistent file`);
path = undefined;
newName = delink(newName);
name = nameFromLink(context.app, maybeLink);
}
} else {
path = undefined;
name = maybeLink;
}
context.versionedData.setName(taskId, newName, path);
context.versionedData.setName(taskId, name, path);
context.save();
}
</script>
@ -193,7 +206,7 @@
onblur={handleBlur}
onkeydown={handleKeydown}
oninput={handleInput}
>{taskData.name}</textarea>
>{taskData.path ? linkFromFilePath(taskData.path) : taskData.name}</textarea>
{:else}
<div
class="text-preview tasktext"

View file

@ -1,4 +1,5 @@
import { AbstractInputSuggest, App, prepareFuzzySearch, TFile } from "obsidian";
import { linkFromFilePath } from "../LinkManager";
export class LinkSuggest extends AbstractInputSuggest<TFile> {
textInputEl: HTMLTextAreaElement;
@ -58,21 +59,11 @@ export class LinkSuggest extends AbstractInputSuggest<TFile> {
if (!match) return;
const linkStart = lineStart + match.index!;
const beforeLink = text.substring(0, linkStart);
const afterCursor = text.substring(cursor);
const newLinkText = `[[${file.basename}]]`;
this.textInputEl.value = beforeLink + newLinkText + afterCursor;
this.textInputEl.value = linkFromFilePath(file.path);
// Trigger Svelte update
this.textInputEl.dispatchEvent(new Event("input"));
// Reset cursor
const newCursor = beforeLink.length + newLinkText.length;
this.textInputEl.setSelectionRange(newCursor, newCursor);
this.textInputEl.focus();
this.textInputEl.blur();
this.close();