mirror of
https://github.com/taskgenius/taskgenius-plugin.git
synced 2026-07-22 06:40:25 +00:00
fix(file-source): remove project tag when deriving project from tags
When project metadata is derived from tags like #project/Alpha, the tag is now removed from the final tags array to prevent duplication. This only applies when the project comes from tag extraction, not from frontmatter properties. Also optimizes import to use type-only import for plugin reference.
This commit is contained in:
parent
66e1f6ad88
commit
4f0dc243ed
2 changed files with 63 additions and 15 deletions
|
|
@ -360,6 +360,26 @@ describe('FileSource', () => {
|
|||
|
||||
expect(task).toBeNull();
|
||||
});
|
||||
|
||||
it('should remove project tag when used to derive project metadata', async () => {
|
||||
const fileCache = {
|
||||
frontmatter: {
|
||||
tags: ['context/foo']
|
||||
},
|
||||
tags: [
|
||||
{ tag: '#project/Alpha' },
|
||||
{ tag: '#task' }
|
||||
]
|
||||
};
|
||||
mockApp.metadataCache.getFileCache.mockReturnValue(fileCache);
|
||||
|
||||
const task = await fileSource.createFileTask('test.md');
|
||||
|
||||
expect(task).toBeTruthy();
|
||||
expect(task!.metadata.project).toBe('Alpha');
|
||||
expect(task!.metadata.tags).toEqual(expect.arrayContaining(['#task', '#context/foo']));
|
||||
expect(task!.metadata.tags).not.toContain('#project/Alpha');
|
||||
});
|
||||
});
|
||||
|
||||
describe('statistics', () => {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import type { App, TFile, EventRef, CachedMetadata } from "obsidian";
|
||||
import TaskProgressBarPlugin from "@/index";
|
||||
import type TaskProgressBarPlugin from "@/index";
|
||||
import type { Task } from "@/types/task";
|
||||
import type {
|
||||
FileSourceConfiguration,
|
||||
|
|
@ -869,7 +869,8 @@ export class FileSource {
|
|||
// Extract project from tags as a fallback (only if not in frontmatter)
|
||||
// Note: In FileSource mode, context and area come ONLY from frontmatter,
|
||||
// not from tags like #context/xxx or #area/xxx
|
||||
const projectFromTags = this.extractProjectFromTags(allTags);
|
||||
const projectTagExtraction = this.extractProjectFromTags(allTags);
|
||||
const projectFromTags = projectTagExtraction.project;
|
||||
|
||||
// Priority order for project:
|
||||
// 1. Direct frontmatter field (project: xxx) - highest priority
|
||||
|
|
@ -877,6 +878,16 @@ export class FileSource {
|
|||
// 3. Tag extraction (#project/xxx) - lowest priority, only if frontmatter has nothing
|
||||
const projectValue = resolvedFrontmatter.project ?? projectFromTags;
|
||||
|
||||
const shouldStripProjectTags =
|
||||
!resolvedFrontmatter.project &&
|
||||
Boolean(projectFromTags) &&
|
||||
projectTagExtraction.matchedTags.length > 0;
|
||||
const tagsForMetadata = shouldStripProjectTags
|
||||
? allTags.filter(
|
||||
(tag) => !projectTagExtraction.matchedTags.includes(tag)
|
||||
)
|
||||
: allTags;
|
||||
|
||||
// Extract standard task metadata
|
||||
const metadata: Partial<FileSourceTaskMetadata> = {
|
||||
dueDate: this.parseDate(
|
||||
|
|
@ -903,7 +914,7 @@ export class FileSource {
|
|||
// Context and area: ONLY from frontmatter (direct or mapped via metadataMappings)
|
||||
context: resolvedFrontmatter.context,
|
||||
area: resolvedFrontmatter.area,
|
||||
tags: allTags,
|
||||
tags: tagsForMetadata,
|
||||
status: status,
|
||||
children: [],
|
||||
};
|
||||
|
|
@ -917,9 +928,20 @@ export class FileSource {
|
|||
* Note: context and area are NOT extracted from tags in FileSource mode,
|
||||
* they should be defined directly in frontmatter (context: xxx, area: xxx)
|
||||
*/
|
||||
private extractProjectFromTags(tags: string[]): string | undefined {
|
||||
private extractProjectFromTags(tags: string[]): {
|
||||
project?: string;
|
||||
matchedTags: string[];
|
||||
} {
|
||||
// Get configurable project prefix from plugin settings, with fallback default
|
||||
const projectPrefix = "project";
|
||||
const configuredPrefix =
|
||||
this.plugin?.settings?.projectTagPrefix?.["tasks"];
|
||||
const projectPrefix =
|
||||
typeof configuredPrefix === "string" && configuredPrefix.trim()
|
||||
? configuredPrefix.trim()
|
||||
: "project";
|
||||
|
||||
const matchedTags: string[] = [];
|
||||
let projectValue: string | undefined;
|
||||
|
||||
for (const tag of tags) {
|
||||
if (!tag || typeof tag !== "string") continue;
|
||||
|
|
@ -933,21 +955,27 @@ export class FileSource {
|
|||
.toLowerCase()
|
||||
.startsWith(`${projectPrefix.toLowerCase()}/`)
|
||||
) {
|
||||
// Extract the value using the actual prefix length (preserving case in the value)
|
||||
const slashIndex = tagWithoutHash.indexOf("/");
|
||||
if (slashIndex !== -1) {
|
||||
const value = tagWithoutHash.substring(slashIndex + 1);
|
||||
if (value) {
|
||||
console.log(
|
||||
`[FileSource] Extracted project from tag: ${value}`
|
||||
);
|
||||
return value;
|
||||
matchedTags.push(tag);
|
||||
if (!projectValue) {
|
||||
// Extract the value using the actual prefix length (preserving case in the value)
|
||||
const slashIndex = tagWithoutHash.indexOf("/");
|
||||
if (slashIndex !== -1) {
|
||||
const value = tagWithoutHash.substring(slashIndex + 1);
|
||||
if (value) {
|
||||
projectValue = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
if (projectValue) {
|
||||
console.log(
|
||||
`[FileSource] Extracted project from tag: ${projectValue}`
|
||||
);
|
||||
}
|
||||
|
||||
return { project: projectValue, matchedTags };
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Reference in a new issue