Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion i18n.manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -586,7 +586,7 @@
"settings.general.taskIdentification.taskProperty.name": "fb00b60c99acd4529404e3ef8bd77017d9332da6",
"settings.general.taskIdentification.taskProperty.description": "5ba9a14eff27529d0bbb5eb8c793ca728759623b",
"settings.general.taskIdentification.taskPropertyValue.name": "258b4a08e80f7b9bec1e0ac3c2c5c3008676297d",
"settings.general.taskIdentification.taskPropertyValue.description": "062539e47430864ee4bf36d0b134ad3d7968f7a8",
"settings.general.taskIdentification.taskPropertyValue.description": "7ca741bd29f5fda2fafb7500ce9d8c15f8b208cc",
"settings.general.folderManagement.header": "679a99627ec2eac7d867141c7d18b2d71804736c",
"settings.general.folderManagement.excludedFolders.name": "956d3039f8ba3fd08869f0fffc6038f0a106fe9c",
"settings.general.folderManagement.excludedFolders.description": "bef8a14860dc486cf46aad464c4c57652fc676d3",
Expand Down
32 changes: 16 additions & 16 deletions i18n.state.json
Original file line number Diff line number Diff line change
Expand Up @@ -2349,8 +2349,8 @@
"translation": "fe54cfd0b371c0db30b573a782f874ee2ec83e29"
},
"settings.general.taskIdentification.taskPropertyValue.description": {
"source": "062539e47430864ee4bf36d0b134ad3d7968f7a8",
"translation": "fc00d383ed55b0705e1037b37fc96e92289ee44c"
"source": "7ca741bd29f5fda2fafb7500ce9d8c15f8b208cc",
"translation": "5057a36f03e94fde00663a85df93eb5ddecc8902"
},
"settings.general.folderManagement.header": {
"source": "679a99627ec2eac7d867141c7d18b2d71804736c",
Expand Down Expand Up @@ -11215,8 +11215,8 @@
"translation": "9f5e7fff0b1991a28c067bb16b69a0773050a887"
},
"settings.general.taskIdentification.taskPropertyValue.description": {
"source": "062539e47430864ee4bf36d0b134ad3d7968f7a8",
"translation": "90db7e8f935b4220989250c0eaaf44845cbe8cd2"
"source": "7ca741bd29f5fda2fafb7500ce9d8c15f8b208cc",
"translation": "587bf866d614114162dae920446297defaa3d672"
},
"settings.general.folderManagement.header": {
"source": "679a99627ec2eac7d867141c7d18b2d71804736c",
Expand Down Expand Up @@ -20081,8 +20081,8 @@
"translation": "a63ba99c5cff48c5527661c7bd7e759fef4abc0f"
},
"settings.general.taskIdentification.taskPropertyValue.description": {
"source": "062539e47430864ee4bf36d0b134ad3d7968f7a8",
"translation": "9c493294d3f02303ed2668bbfb126797915a3f9a"
"source": "7ca741bd29f5fda2fafb7500ce9d8c15f8b208cc",
"translation": "cb626729d872a1884710acaad8793e7b3660fd7d"
},
"settings.general.folderManagement.header": {
"source": "679a99627ec2eac7d867141c7d18b2d71804736c",
Expand Down Expand Up @@ -28947,8 +28947,8 @@
"translation": "961d481dd7fd53b281a8da5c4bdb69744c71ef1e"
},
"settings.general.taskIdentification.taskPropertyValue.description": {
"source": "062539e47430864ee4bf36d0b134ad3d7968f7a8",
"translation": "991fd53856fb12c49f5ce1e1ecfcac319cb143a4"
"source": "7ca741bd29f5fda2fafb7500ce9d8c15f8b208cc",
"translation": "27104de141028d65c140a1e71658058938755090"
},
"settings.general.folderManagement.header": {
"source": "679a99627ec2eac7d867141c7d18b2d71804736c",
Expand Down Expand Up @@ -37813,8 +37813,8 @@
"translation": "d9ab65c516df3680d7cb4cae79fce0b40377aaf6"
},
"settings.general.taskIdentification.taskPropertyValue.description": {
"source": "062539e47430864ee4bf36d0b134ad3d7968f7a8",
"translation": "ebd248aa2aaf679effff2cc85508ffc3e1a3f151"
"source": "7ca741bd29f5fda2fafb7500ce9d8c15f8b208cc",
"translation": "fe2fd597474dbc3938a9fa1f850a7844d26265c8"
},
"settings.general.folderManagement.header": {
"source": "679a99627ec2eac7d867141c7d18b2d71804736c",
Expand Down Expand Up @@ -46679,8 +46679,8 @@
"translation": "9c8ae4810200c854572f20371f939318f3fe422c"
},
"settings.general.taskIdentification.taskPropertyValue.description": {
"source": "062539e47430864ee4bf36d0b134ad3d7968f7a8",
"translation": "17a81e641866202e2de4097f59de2b8d21ef7fd6"
"source": "7ca741bd29f5fda2fafb7500ce9d8c15f8b208cc",
"translation": "381eeb846e39311762463001511b5351587fc0e5"
},
"settings.general.folderManagement.header": {
"source": "679a99627ec2eac7d867141c7d18b2d71804736c",
Expand Down Expand Up @@ -55545,8 +55545,8 @@
"translation": "a3fd42826f8635e08120459dc7ae119f273e42f3"
},
"settings.general.taskIdentification.taskPropertyValue.description": {
"source": "062539e47430864ee4bf36d0b134ad3d7968f7a8",
"translation": "d4961905d4456b7e71409da645f2b39d24fb2424"
"source": "7ca741bd29f5fda2fafb7500ce9d8c15f8b208cc",
"translation": "20b42216a409309bf498e5aacb3a580f914849d8"
},
"settings.general.folderManagement.header": {
"source": "679a99627ec2eac7d867141c7d18b2d71804736c",
Expand Down Expand Up @@ -64411,8 +64411,8 @@
"translation": "64f4d0f6328db55b4c5e9eccb304f972bb2de5a8"
},
"settings.general.taskIdentification.taskPropertyValue.description": {
"source": "062539e47430864ee4bf36d0b134ad3d7968f7a8",
"translation": "793d654a8377ba074d1297aa6517d53d4db1631f"
"source": "7ca741bd29f5fda2fafb7500ce9d8c15f8b208cc",
"translation": "ab3af390f38d944a2c1ba5a0c7bee73bf7f429f2"
},
"settings.general.folderManagement.header": {
"source": "679a99627ec2eac7d867141c7d18b2d71804736c",
Expand Down
2 changes: 1 addition & 1 deletion src/i18n/resources/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -915,7 +915,7 @@ export const de: TranslationTree = {
},
taskPropertyValue: {
name: "Aufgabeneigenschaftswert",
description: "Der Wert, der eine Notiz als Aufgabe identifiziert (z.B. \"task\")"
description: "Der Wert, der eine Notiz als Aufgabe identifiziert (z.B. \"task\"). Leer lassen, um jeden Wert zu akzeptieren, sodass eine Notiz eine Aufgabe ist, sobald die Eigenschaft vorhanden ist."
}
},
folderManagement: {
Expand Down
3 changes: 2 additions & 1 deletion src/i18n/resources/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -960,7 +960,8 @@ export const en: TranslationTree = {
},
taskPropertyValue: {
name: "Task property value",
description: 'The value that identifies a note as a task (e.g., "task")',
description:
'The value that identifies a note as a task (e.g., "task"). Leave empty to match any value, so a note is a task whenever the property is present.',
},
},
folderManagement: {
Expand Down
2 changes: 1 addition & 1 deletion src/i18n/resources/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -915,7 +915,7 @@ export const es: TranslationTree = {
},
taskPropertyValue: {
name: "Valor de propiedad de tarea",
description: "El valor que identifica una nota como tarea (ej. \"tarea\")"
description: "El valor que identifica una nota como tarea (ej. \"tarea\"). Déjelo vacío para que coincida con cualquier valor, de modo que una nota sea una tarea siempre que la propiedad esté presente."
}
},
folderManagement: {
Expand Down
2 changes: 1 addition & 1 deletion src/i18n/resources/fr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -915,7 +915,7 @@ export const fr: TranslationTree = {
},
taskPropertyValue: {
name: "Valeur de la propriété de tâche",
description: "La valeur qui identifie une note comme une tâche (ex. \"task\")"
description: "La valeur qui identifie une note comme une tâche (ex. \"task\"). Laissez vide pour correspondre à n'importe quelle valeur, de sorte qu'une note soit une tâche dès que la propriété est présente."
}
},
folderManagement: {
Expand Down
2 changes: 1 addition & 1 deletion src/i18n/resources/ja.ts
Original file line number Diff line number Diff line change
Expand Up @@ -915,7 +915,7 @@ export const ja: TranslationTree = {
},
taskPropertyValue: {
name: "タスクプロパティ値",
description: "ノートをタスクとして識別する値(例:\"task\")"
description: "ノートをタスクとして識別する値(例:\"task\")。空欄にすると任意の値に一致し、プロパティが存在すればノートはタスクになります。"
}
},
folderManagement: {
Expand Down
2 changes: 1 addition & 1 deletion src/i18n/resources/ko.ts
Original file line number Diff line number Diff line change
Expand Up @@ -915,7 +915,7 @@ export const ko: TranslationTree = {
},
taskPropertyValue: {
name: "작업 속성 값",
description: "노트를 작업으로 식별하는 값 (예: \"task\")"
description: "노트를 작업으로 식별하는 값 (예: \"task\"). 비워 두면 모든 값과 일치하여 속성이 존재하기만 하면 노트가 작업이 됩니다."
}
},
folderManagement: {
Expand Down
2 changes: 1 addition & 1 deletion src/i18n/resources/pt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -917,7 +917,7 @@ export const pt: TranslationTree = {
},
taskPropertyValue: {
name: "Valor da propriedade da tarefa",
description: "O valor que identifica uma nota como uma tarefa (ex: \"tarefa\")"
description: "O valor que identifica uma nota como uma tarefa (ex: \"tarefa\"). Deixe em branco para corresponder a qualquer valor, tornando a nota uma tarefa sempre que a propriedade estiver presente."
}
},
folderManagement: {
Expand Down
2 changes: 1 addition & 1 deletion src/i18n/resources/ru.ts
Original file line number Diff line number Diff line change
Expand Up @@ -915,7 +915,7 @@ export const ru: TranslationTree = {
},
taskPropertyValue: {
name: "Значение свойства задачи",
description: "Значение, которое идентифицирует заметку как задачу (например, \"задача\")"
description: "Значение, которое идентифицирует заметку как задачу (например, \"задача\"). Оставьте пустым, чтобы соответствовать любому значению — заметка становится задачей, как только это свойство присутствует."
}
},
folderManagement: {
Expand Down
2 changes: 1 addition & 1 deletion src/i18n/resources/zh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -915,7 +915,7 @@ export const zh: TranslationTree = {
},
taskPropertyValue: {
name: "任务属性值",
description: "识别笔记为任务的值(例如,\"task\")"
description: "识别笔记为任务的值(例如,\"task\")。留空可匹配任意值,只要存在该属性,笔记即视为任务。"
}
},
folderManagement: {
Expand Down
27 changes: 26 additions & 1 deletion src/utils/taskIdentification.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,23 @@ function isFrontmatterRecord(frontmatter: unknown): frontmatter is Record<string
return Boolean(frontmatter) && typeof frontmatter === "object" && !Array.isArray(frontmatter);
}

// A frontmatter value counts as "present" only when it holds a real value:
// non-empty/non-whitespace string, non-empty array, or any non-null scalar.
// Mirrors the Bases filter (`prop != "" && prop != null`) so an empty `status:`
// is not treated as a task when identifying by key-existence alone.
function hasNonEmptyValue(value: unknown): boolean {
if (value === undefined || value === null) {
return false;
}
if (typeof value === "string") {
return value.trim().length > 0;
}
if (Array.isArray(value)) {
return value.some((item) => hasNonEmptyValue(item));
}
return true;
}

export function compareTaskPropertyIdentifierValue(
frontmatterValue: unknown,
settingValue: string
Expand All @@ -37,9 +54,17 @@ export function isTaskFrontmatter(
if (settings.taskIdentificationMethod === "property") {
const propName = settings.taskPropertyName;
const propValue = settings.taskPropertyValue;
if (!propName || !propValue) return false;
if (!propName) return false;

const frontmatterValue = frontmatter[propName];

// Empty configured value means "match any value": the note is a task as
// long as the property key exists with a non-empty value, regardless of
// what that value is (e.g. identify by `status` alone).
if (propValue.trim().length === 0) {
return hasNonEmptyValue(frontmatterValue);
}

if (frontmatterValue === undefined) return false;

if (Array.isArray(frontmatterValue)) {
Expand Down
60 changes: 60 additions & 0 deletions tests/unit/utils/TaskManager.isTaskFile.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -227,4 +227,64 @@ describe('TaskManager.isTaskFile - tag hash prefix handling', () => {
expect(isTaskFile({ flags: ['note', true] }, settings)).toBe(true);
});
});

describe('Property-based identification with empty value (match any value)', () => {
const anyValueSettings: IsTaskFileSettings = {
taskIdentificationMethod: 'property',
taskPropertyName: 'status',
taskPropertyValue: '',
taskTag: 'task',
};

it('should identify a task when the property exists with any value', () => {
expect(isTaskFile({ status: 'open' }, anyValueSettings)).toBe(true);
expect(isTaskFile({ status: 'done' }, anyValueSettings)).toBe(true);
expect(isTaskFile({ status: 'anything-at-all' }, anyValueSettings)).toBe(true);
});

it('should treat whitespace-only configured value as match-any', () => {
const settings: IsTaskFileSettings = { ...anyValueSettings, taskPropertyValue: ' ' };
expect(isTaskFile({ status: 'open' }, settings)).toBe(true);
});

it('should identify a task for non-string scalar values', () => {
expect(isTaskFile({ status: 3 }, anyValueSettings)).toBe(true);
expect(isTaskFile({ status: true }, anyValueSettings)).toBe(true);
expect(isTaskFile({ status: false }, anyValueSettings)).toBe(true);
});

it('should identify a task when the property is a non-empty array', () => {
expect(isTaskFile({ status: ['open'] }, anyValueSettings)).toBe(true);
expect(isTaskFile({ status: ['', 'open'] }, anyValueSettings)).toBe(true);
});

it('should NOT identify a task when the property is absent', () => {
expect(isTaskFile({ type: 'note' }, anyValueSettings)).toBe(false);
expect(isTaskFile({}, anyValueSettings)).toBe(false);
});

it('should NOT identify a task when the property is present but empty', () => {
expect(isTaskFile({ status: '' }, anyValueSettings)).toBe(false);
expect(isTaskFile({ status: ' ' }, anyValueSettings)).toBe(false);
expect(isTaskFile({ status: null }, anyValueSettings)).toBe(false);
expect(isTaskFile({ status: [] }, anyValueSettings)).toBe(false);
expect(isTaskFile({ status: ['', ' '] }, anyValueSettings)).toBe(false);
});

it('should still exact-match when a value is configured', () => {
const settings: IsTaskFileSettings = { ...anyValueSettings, taskPropertyValue: 'done' };
expect(isTaskFile({ status: 'done' }, settings)).toBe(true);
expect(isTaskFile({ status: 'open' }, settings)).toBe(false);
});

it('should return false when no property name is configured, even with match-any value', () => {
const settings: IsTaskFileSettings = {
taskIdentificationMethod: 'property',
taskPropertyName: '',
taskPropertyValue: '',
taskTag: 'task',
};
expect(isTaskFile({ status: 'open' }, settings)).toBe(false);
});
});
});