Merge pull request #6053 from steindvart/task-settings

Move task-related settings to a dedicated Tasks Settings tab and fix technical issues
This commit is contained in:
Johannes Millan 2026-01-19 16:46:44 +01:00 committed by GitHub
commit 8734b7ef4c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
68 changed files with 1258 additions and 473 deletions

View file

@ -98,16 +98,17 @@ export function migrateState(
/**
* Migrate a single operation from its version to targetVersion.
* Returns null if the operation should be dropped (e.g., removed feature).
* Returns an array if the operation should be split into multiple operations.
* Pure function - no side effects.
*
* @param op - The operation to migrate
* @param targetVersion - Target version (defaults to CURRENT_SCHEMA_VERSION)
* @returns Migration result with transformed operation, null, or error
* @returns Migration result with transformed operation(s), null, or error
*/
export function migrateOperation(
op: OperationLike,
targetVersion: number = CURRENT_SCHEMA_VERSION,
): MigrationResult<OperationLike | null> {
): MigrationResult<OperationLike | OperationLike[] | null> {
const sourceVersion = op.schemaVersion ?? 1;
// Validate source version
@ -123,11 +124,12 @@ export function migrateOperation(
return { success: true, data: op };
}
let currentOp: OperationLike | null = { ...op };
// Start with an array containing the single operation
let currentOps: OperationLike[] = [{ ...op }];
let version = sourceVersion;
// Apply migrations sequentially
while (version < targetVersion && currentOp !== null) {
while (version < targetVersion && currentOps.length > 0) {
const migration = findMigration(version);
if (!migration) {
return {
@ -139,18 +141,30 @@ export function migrateOperation(
}
try {
if (migration.migrateOperation) {
currentOp = migration.migrateOperation(currentOp);
if (currentOp !== null) {
// Update version on the migrated operation
currentOp = { ...currentOp, schemaVersion: migration.toVersion };
const nextOps: OperationLike[] = [];
for (const currentOp of currentOps) {
if (migration.migrateOperation) {
const result = migration.migrateOperation(currentOp);
if (result === null) {
// Operation dropped, don't add to nextOps
continue;
} else if (Array.isArray(result)) {
// Operation split into multiple
for (const r of result) {
nextOps.push({ ...r, schemaVersion: migration.toVersion });
}
} else {
// Single operation returned
nextOps.push({ ...result, schemaVersion: migration.toVersion });
}
} else {
// No operation migration defined - just update version
nextOps.push({ ...currentOp, schemaVersion: migration.toVersion });
}
} else {
// No operation migration defined - just update version
currentOp = { ...currentOp, schemaVersion: migration.toVersion };
}
// Track version even if operation was dropped (null)
// This ensures migratedToVersion reflects where we actually stopped
currentOps = nextOps;
version = migration.toVersion;
} catch (err) {
const errorMessage = err instanceof Error ? err.message : String(err);
@ -163,17 +177,35 @@ export function migrateOperation(
}
}
return {
success: true,
data: currentOp,
migratedFromVersion: sourceVersion,
migratedToVersion: version,
};
// Return based on the number of resulting operations
if (currentOps.length === 0) {
return {
success: true,
data: null,
migratedFromVersion: sourceVersion,
migratedToVersion: version,
};
} else if (currentOps.length === 1) {
return {
success: true,
data: currentOps[0],
migratedFromVersion: sourceVersion,
migratedToVersion: version,
};
} else {
return {
success: true,
data: currentOps,
migratedFromVersion: sourceVersion,
migratedToVersion: version,
};
}
}
/**
* Migrate an array of operations.
* Drops operations that return null from migration.
* Handles operations that are split into multiple operations.
*
* @param ops - Array of operations to migrate
* @param targetVersion - Target version (defaults to CURRENT_SCHEMA_VERSION)
@ -195,10 +227,14 @@ export function migrateOperations(
};
}
if (result.data !== null && result.data !== undefined) {
migrated.push(result.data);
} else {
if (result.data === null || result.data === undefined) {
droppedCount++;
} else if (Array.isArray(result.data)) {
// Operation was split into multiple operations
migrated.push(...result.data);
} else {
// Single operation
migrated.push(result.data);
}
}

View file

@ -50,9 +50,11 @@ export interface SchemaMigration {
/**
* Transform an individual operation payload.
* Return null to drop the operation entirely (e.g., for removed features).
* Return an array to split one operation into multiple (e.g., when moving
* settings from one config section to another).
* Only required for non-additive changes (renames, removals, type changes).
*/
migrateOperation?: (op: OperationLike) => OperationLike | null;
migrateOperation?: (op: OperationLike) => OperationLike | OperationLike[] | null;
/**
* Explicit declaration that forces migration authors to think about

View file

@ -1,4 +1,5 @@
import type { SchemaMigration } from '../migration.types';
import { MiscToTasksSettingsMigration_v1v2 } from './misc-to-tasks-settings-migration-v1-to-v2';
/**
* Registry of all schema migrations.
@ -25,6 +26,4 @@ import type { SchemaMigration } from '../migration.types';
* }
* ```
*/
export const MIGRATIONS: SchemaMigration[] = [
// No migrations yet - schema version 1 is the initial version
];
export const MIGRATIONS: SchemaMigration[] = [MiscToTasksSettingsMigration_v1v2];

View file

@ -0,0 +1,163 @@
import { OperationLike, SchemaMigration } from '../migration.types';
/**
* Mapping from old misc field names to new tasks field names.
* Key: old field name in misc
* Value: new field name in tasks (or transform function)
*/
const FIELD_MAPPINGS: Record<string, string | ((value: unknown) => [string, unknown])> = {
isConfirmBeforeTaskDelete: 'isConfirmBeforeDelete',
isAutoAddWorkedOnToToday: 'isAutoAddWorkedOnToToday',
isAutMarkParentAsDone: 'isAutoMarkParentAsDone', // Fixed typo
isTrayShowCurrentTask: 'isTrayShowCurrent',
isTurnOffMarkdown: (value) => ['isMarkdownFormattingInNotesEnabled', !value], // Inverted
defaultProjectId: 'defaultProjectId',
taskNotesTpl: 'notesTemplate',
};
const MIGRATED_FIELDS = Object.keys(FIELD_MAPPINGS);
export const MiscToTasksSettingsMigration_v1v2: SchemaMigration = {
fromVersion: 1,
toVersion: 2,
description: 'Move settings from MiscConfig to TasksConfig.',
migrateState: (state: any) => {
const misc = state.globalConfig?.misc;
if (!misc || !hasMigratedFields(misc)) {
return state;
}
const tasks = state.globalConfig?.tasks ?? {};
// Skip if already migrated (tasks has new fields)
if (tasks.isConfirmBeforeDelete !== undefined) {
return state;
}
return {
...state,
globalConfig: {
...state.globalConfig,
misc: removeMigratedFields(misc),
tasks: { ...tasks, ...transformMiscToTasks(misc) },
},
};
},
requiresOperationMigration: true,
migrateOperation: (op: OperationLike): OperationLike | OperationLike[] | null => {
if (op.entityType !== 'GLOBAL_CONFIG' || op.entityId !== 'misc') {
return op;
}
const payload = op.payload as Record<string, unknown> | undefined;
if (!payload || typeof payload !== 'object') {
return op;
}
// Extract sectionCfg from various payload formats
let sectionCfg: Record<string, unknown> | null = null;
let actionPayload: Record<string, unknown> | null = null;
if (isMultiEntityPayload(payload)) {
actionPayload = payload.actionPayload;
sectionCfg = extractSectionCfg(actionPayload);
} else if ('sectionCfg' in payload) {
actionPayload = payload;
sectionCfg = extractSectionCfg(payload);
} else {
sectionCfg = payload;
}
if (!sectionCfg || !hasMigratedFields(sectionCfg)) {
return op;
}
// Transform settings
const tasksCfg = transformMiscToTasks(sectionCfg);
const miscCfg = removeMigratedFields(sectionCfg);
// Build payload for the new operation
const buildPayload = (cfg: Record<string, unknown>, sectionKey: string): unknown => {
if (isMultiEntityPayload(payload)) {
return {
...payload,
actionPayload: { ...actionPayload, sectionKey, sectionCfg: cfg },
};
} else if (actionPayload && 'sectionCfg' in actionPayload) {
return { ...actionPayload, sectionKey, sectionCfg: cfg };
}
return cfg;
};
const result: OperationLike[] = [];
if (Object.keys(miscCfg).length > 0) {
result.push({ ...op, payload: buildPayload(miscCfg, 'misc') });
}
if (Object.keys(tasksCfg).length > 0) {
result.push({
...op,
id: `${op.id}_tasks_migrated`,
entityId: 'tasks',
payload: buildPayload(tasksCfg, 'tasks'),
});
}
return result.length === 0 ? null : result.length === 1 ? result[0] : result;
},
};
function transformMiscToTasks(miscCfg: Record<string, unknown>): Record<string, unknown> {
const tasksCfg: Record<string, unknown> = {};
for (const [oldKey, mapping] of Object.entries(FIELD_MAPPINGS)) {
if (oldKey in miscCfg) {
if (typeof mapping === 'function') {
const [newKey, newValue] = mapping(miscCfg[oldKey]);
tasksCfg[newKey] = newValue;
} else {
tasksCfg[mapping] = miscCfg[oldKey];
}
}
}
return tasksCfg;
}
function removeMigratedFields(miscCfg: Record<string, unknown>): Record<string, unknown> {
const result = { ...miscCfg };
for (const key of MIGRATED_FIELDS) {
delete result[key];
}
return result;
}
function hasMigratedFields(cfg: Record<string, unknown>): boolean {
return MIGRATED_FIELDS.some((key) => key in cfg);
}
interface MultiEntityPayload {
actionPayload: Record<string, unknown>;
entityChanges?: unknown[];
}
function isMultiEntityPayload(payload: unknown): payload is MultiEntityPayload {
return (
payload !== null &&
typeof payload === 'object' &&
'actionPayload' in payload &&
typeof (payload as MultiEntityPayload).actionPayload === 'object'
);
}
function extractSectionCfg(
actionPayload: Record<string, unknown>,
): Record<string, unknown> | null {
if ('sectionCfg' in actionPayload && typeof actionPayload['sectionCfg'] === 'object') {
return actionPayload['sectionCfg'] as Record<string, unknown>;
}
return null;
}

View file

@ -2,7 +2,7 @@
* Current schema version for all operations and state snapshots.
* Increment this BEFORE adding a new migration.
*/
export const CURRENT_SCHEMA_VERSION = 1;
export const CURRENT_SCHEMA_VERSION = 2;
/**
* Minimum schema version that this codebase can still handle.

View file

@ -182,13 +182,14 @@ describe('shared-schema migration functions', () => {
});
describe('validateMigrationRegistry', () => {
it('returns empty array when no migrations and version is 1', () => {
// Only valid if CURRENT_SCHEMA_VERSION is 1
if (CURRENT_SCHEMA_VERSION === 1) {
const errors = validateMigrationRegistry();
expect(errors).toEqual([]);
}
});
// @todo: How can we change this test to check this behavior and increase CURRENT_SCHEMA_VERSION?
// it('returns empty array when no migrations and version is 1', () => {
// // Only valid if CURRENT_SCHEMA_VERSION is 1
// if (CURRENT_SCHEMA_VERSION === 1) {
// const errors = validateMigrationRegistry();
// expect(errors).toEqual([]);
// }
// });
it('returns errors when CURRENT_SCHEMA_VERSION > 1 but no migrations', () => {
// This is a consistency check for when we add migrations

View file

@ -0,0 +1,366 @@
import { describe, it, expect } from 'vitest';
import { MIGRATIONS } from '../../src/migrations';
import { OperationLike } from '../../src/migration.types';
describe('Migrate MiscConfig to TasksConfig', () => {
const migration = MIGRATIONS.find((m) => m.fromVersion === 1 && m.toVersion === 2);
if (!migration) {
throw new Error('Migration for version 1 to 2 not found');
}
describe('migrateState', () => {
it('should migrate settings from misc to tasks', () => {
const initialState = {
globalConfig: {
misc: {
isConfirmBeforeTaskDelete: true,
isAutoAddWorkedOnToToday: true,
isAutMarkParentAsDone: false,
isTrayShowCurrentTask: true,
isTurnOffMarkdown: false,
defaultProjectId: 'project_1',
taskNotesTpl: 'Template',
},
tasks: {},
},
};
const migratedState = migration.migrateState(initialState) as {
globalConfig: {
misc: Record<string, unknown>;
tasks: Record<string, unknown>;
};
};
expect(migratedState.globalConfig.tasks).toEqual({
isConfirmBeforeDelete: true,
isAutoAddWorkedOnToToday: true,
isAutoMarkParentAsDone: false,
isTrayShowCurrent: true,
isMarkdownFormattingInNotesEnabled: true,
defaultProjectId: 'project_1',
notesTemplate: 'Template',
});
expect(migratedState.globalConfig.misc).toEqual({});
});
it('should not modify state if misc is empty', () => {
const initialState = {
globalConfig: {
misc: {},
tasks: {},
},
};
const migratedState = migration.migrateState(initialState) as {
globalConfig: {
misc: Record<string, unknown>;
tasks: Record<string, unknown>;
};
};
expect(migratedState).toEqual(initialState);
});
it('should not modify state if tasks already migrated', () => {
const initialState = {
globalConfig: {
misc: {
isConfirmBeforeTaskDelete: true,
},
tasks: {
isConfirmBeforeDelete: true,
},
},
};
const migratedState = migration.migrateState(initialState) as {
globalConfig: {
misc: Record<string, unknown>;
tasks: Record<string, unknown>;
};
};
expect(migratedState).toEqual(initialState);
});
});
describe('migrateOperation', () => {
it('should split misc operation into misc and tasks operations', () => {
const op: OperationLike = {
id: 'op_1',
opType: 'UPD',
entityType: 'GLOBAL_CONFIG',
entityId: 'misc',
payload: {
isConfirmBeforeTaskDelete: true,
isTurnOffMarkdown: true,
isMinimizeToTray: false,
},
schemaVersion: 1,
};
const result = migration.migrateOperation!(op);
expect(Array.isArray(result)).toBe(true);
const resultArray = result as OperationLike[];
expect(resultArray.length).toBe(2);
// First operation should be misc with non-migrated settings
const miscOp = resultArray.find((r) => r.entityId === 'misc');
expect(miscOp).toBeDefined();
expect(miscOp!.payload).toEqual({
isMinimizeToTray: false,
});
// Second operation should be tasks with migrated settings
const tasksOp = resultArray.find((r) => r.entityId === 'tasks');
expect(tasksOp).toBeDefined();
expect(tasksOp!.id).toBe('op_1_tasks_migrated');
expect(tasksOp!.payload).toEqual({
isConfirmBeforeDelete: true,
isMarkdownFormattingInNotesEnabled: false, // Inverted from isTurnOffMarkdown: true
});
});
it('should return only tasks operation when misc payload contains only migrated settings', () => {
const op: OperationLike = {
id: 'op_2',
opType: 'UPD',
entityType: 'GLOBAL_CONFIG',
entityId: 'misc',
payload: {
isAutoAddWorkedOnToToday: true,
defaultProjectId: 'project_123',
},
schemaVersion: 1,
};
const result = migration.migrateOperation!(op);
// Should return single tasks operation
expect(Array.isArray(result)).toBe(false);
const singleOp = result as OperationLike;
expect(singleOp.entityId).toBe('tasks');
expect(singleOp.payload).toEqual({
isAutoAddWorkedOnToToday: true,
defaultProjectId: 'project_123',
});
});
it('should not modify non-GLOBAL_CONFIG operations', () => {
const op: OperationLike = {
id: 'op_3',
opType: 'UPD',
entityType: 'TASK',
entityId: 'task_1',
payload: { title: 'Test task' },
schemaVersion: 1,
};
const result = migration.migrateOperation!(op);
expect(result).toEqual(op);
});
it('should not modify GLOBAL_CONFIG operations with non-misc entityId', () => {
const op: OperationLike = {
id: 'op_4',
opType: 'UPD',
entityType: 'GLOBAL_CONFIG',
entityId: 'keyboard',
payload: { someKey: 'value' },
schemaVersion: 1,
};
const result = migration.migrateOperation!(op);
expect(result).toEqual(op);
});
it('should not modify misc operations without migrated settings', () => {
const op: OperationLike = {
id: 'op_5',
opType: 'UPD',
entityType: 'GLOBAL_CONFIG',
entityId: 'misc',
payload: {
isMinimizeToTray: true,
isConfirmBeforeExit: false,
},
schemaVersion: 1,
};
const result = migration.migrateOperation!(op);
expect(result).toEqual(op);
});
it('should correctly invert isTurnOffMarkdown to isMarkdownFormattingInNotesEnabled', () => {
const opWithMarkdownOff: OperationLike = {
id: 'op_6',
opType: 'UPD',
entityType: 'GLOBAL_CONFIG',
entityId: 'misc',
payload: { isTurnOffMarkdown: true },
schemaVersion: 1,
};
const result = migration.migrateOperation!(opWithMarkdownOff) as OperationLike;
expect(result.entityId).toBe('tasks');
expect(
(result.payload as Record<string, unknown>)['isMarkdownFormattingInNotesEnabled'],
).toBe(false);
const opWithMarkdownOn: OperationLike = {
id: 'op_7',
opType: 'UPD',
entityType: 'GLOBAL_CONFIG',
entityId: 'misc',
payload: { isTurnOffMarkdown: false },
schemaVersion: 1,
};
const result2 = migration.migrateOperation!(opWithMarkdownOn) as OperationLike;
expect(result2.entityId).toBe('tasks');
expect(
(result2.payload as Record<string, unknown>)[
'isMarkdownFormattingInNotesEnabled'
],
).toBe(true);
});
it('should rename isAutMarkParentAsDone to isAutoMarkParentAsDone', () => {
const op: OperationLike = {
id: 'op_8',
opType: 'UPD',
entityType: 'GLOBAL_CONFIG',
entityId: 'misc',
payload: { isAutMarkParentAsDone: true },
schemaVersion: 1,
};
const result = migration.migrateOperation!(op) as OperationLike;
expect(result.entityId).toBe('tasks');
expect((result.payload as Record<string, unknown>)['isAutoMarkParentAsDone']).toBe(
true,
);
expect(
(result.payload as Record<string, unknown>)['isAutMarkParentAsDone'],
).toBeUndefined();
});
describe('MultiEntityPayload format', () => {
it('should handle MultiEntityPayload with sectionCfg containing migrated settings', () => {
const op: OperationLike = {
id: 'op_multi_1',
opType: 'UPD',
entityType: 'GLOBAL_CONFIG',
entityId: 'misc',
payload: {
actionPayload: {
sectionKey: 'misc',
sectionCfg: {
isConfirmBeforeTaskDelete: true,
isTurnOffMarkdown: true,
isMinimizeToTray: false,
},
},
entityChanges: [],
},
schemaVersion: 1,
};
const result = migration.migrateOperation!(op);
expect(Array.isArray(result)).toBe(true);
const resultArray = result as OperationLike[];
expect(resultArray.length).toBe(2);
// Misc operation
const miscOp = resultArray.find((r) => r.entityId === 'misc');
expect(miscOp).toBeDefined();
const miscPayload = miscOp!.payload as {
actionPayload: { sectionKey: string; sectionCfg: Record<string, unknown> };
};
expect(miscPayload.actionPayload.sectionKey).toBe('misc');
expect(miscPayload.actionPayload.sectionCfg).toEqual({
isMinimizeToTray: false,
});
// Tasks operation
const tasksOp = resultArray.find((r) => r.entityId === 'tasks');
expect(tasksOp).toBeDefined();
const tasksPayload = tasksOp!.payload as {
actionPayload: { sectionKey: string; sectionCfg: Record<string, unknown> };
};
expect(tasksPayload.actionPayload.sectionKey).toBe('tasks');
expect(tasksPayload.actionPayload.sectionCfg).toEqual({
isConfirmBeforeDelete: true,
isMarkdownFormattingInNotesEnabled: false,
});
});
it('should handle MultiEntityPayload with only migrated settings', () => {
const op: OperationLike = {
id: 'op_multi_2',
opType: 'UPD',
entityType: 'GLOBAL_CONFIG',
entityId: 'misc',
payload: {
actionPayload: {
sectionKey: 'misc',
sectionCfg: {
defaultProjectId: 'project_abc',
taskNotesTpl: 'My template',
},
},
entityChanges: [],
},
schemaVersion: 1,
};
const result = migration.migrateOperation!(op);
// Should return single tasks operation
expect(Array.isArray(result)).toBe(false);
const singleOp = result as OperationLike;
expect(singleOp.entityId).toBe('tasks');
const payload = singleOp.payload as {
actionPayload: { sectionKey: string; sectionCfg: Record<string, unknown> };
};
expect(payload.actionPayload.sectionKey).toBe('tasks');
expect(payload.actionPayload.sectionCfg).toEqual({
defaultProjectId: 'project_abc',
notesTemplate: 'My template',
});
});
it('should not modify MultiEntityPayload without migrated settings', () => {
const op: OperationLike = {
id: 'op_multi_3',
opType: 'UPD',
entityType: 'GLOBAL_CONFIG',
entityId: 'misc',
payload: {
actionPayload: {
sectionKey: 'misc',
sectionCfg: {
isMinimizeToTray: true,
isConfirmBeforeExit: false,
},
},
entityChanges: [],
},
schemaVersion: 1,
};
const result = migration.migrateOperation!(op);
expect(result).toEqual(op);
});
});
});
});

View file

@ -537,6 +537,15 @@ export class SnapshotService {
let payload = row.payload;
const opSchemaVersion = row.schemaVersion ?? 1;
// Prepare list of operations to process (may be expanded by migration)
let opsToProcess: Array<{
opType: string;
entityType: string;
entityId: string | null;
payload: unknown;
}> = [{ opType, entityType, entityId, payload }];
if (opSchemaVersion < CURRENT_SCHEMA_VERSION) {
const opLike: OperationLike = {
id: row.id,
@ -554,71 +563,109 @@ export class SnapshotService {
const migratedOp = migrationResult.data;
if (!migratedOp) continue;
opType = migratedOp.opType as Operation['opType'];
entityType = migratedOp.entityType;
entityId = migratedOp.entityId ?? null;
payload = migratedOp.payload as any;
}
// Handle full-state operations BEFORE entity type check
// These operations replace the entire state and don't use a specific entity type
if (opType === 'SYNC_IMPORT' || opType === 'BACKUP_IMPORT' || opType === 'REPAIR') {
if (payload && typeof payload === 'object' && 'appDataComplete' in payload) {
Object.assign(state, (payload as { appDataComplete: unknown }).appDataComplete);
// Handle array result (operation was split into multiple)
if (Array.isArray(migratedOp)) {
opsToProcess = migratedOp.map((op) => ({
opType: op.opType,
entityType: op.entityType,
entityId: op.entityId ?? null,
payload: op.payload,
}));
} else {
Object.assign(state, payload);
opsToProcess = [
{
opType: migratedOp.opType,
entityType: migratedOp.entityType,
entityId: migratedOp.entityId ?? null,
payload: migratedOp.payload,
},
];
}
continue;
}
if (!ALLOWED_ENTITY_TYPES.has(entityType)) continue;
// Process all operations (original or migrated)
for (const opToProcess of opsToProcess) {
const {
opType: processOpType,
entityType: processEntityType,
entityId: processEntityId,
payload: processPayload,
} = opToProcess;
if (!state[entityType]) {
state[entityType] = {};
}
// Handle full-state operations BEFORE entity type check
// These operations replace the entire state and don't use a specific entity type
if (
processOpType === 'SYNC_IMPORT' ||
processOpType === 'BACKUP_IMPORT' ||
processOpType === 'REPAIR'
) {
if (
processPayload &&
typeof processPayload === 'object' &&
'appDataComplete' in processPayload
) {
Object.assign(
state,
(processPayload as { appDataComplete: unknown }).appDataComplete,
);
} else {
Object.assign(state, processPayload);
}
continue;
}
switch (opType) {
case 'CRT':
case 'UPD':
if (entityId) {
state[entityType][entityId] = {
...(state[entityType][entityId] as Record<string, unknown>),
...(payload as Record<string, unknown>),
};
}
break;
case 'DEL':
if (entityId) {
delete state[entityType][entityId];
}
break;
case 'MOV':
if (entityId && payload) {
state[entityType][entityId] = {
...(state[entityType][entityId] as Record<string, unknown>),
...(payload as Record<string, unknown>),
};
}
break;
case 'BATCH':
if (payload && typeof payload === 'object') {
const batchPayload = payload as Record<string, unknown>;
if (batchPayload.entities && typeof batchPayload.entities === 'object') {
const entities = batchPayload.entities as Record<string, unknown>;
for (const [id, entity] of Object.entries(entities)) {
state[entityType][id] = {
...(state[entityType][id] as Record<string, unknown>),
...(entity as Record<string, unknown>),
};
}
} else if (entityId) {
state[entityType][entityId] = {
...(state[entityType][entityId] as Record<string, unknown>),
...batchPayload,
if (!ALLOWED_ENTITY_TYPES.has(processEntityType)) continue;
if (!state[processEntityType]) {
state[processEntityType] = {};
}
switch (processOpType) {
case 'CRT':
case 'UPD':
if (processEntityId) {
state[processEntityType][processEntityId] = {
...(state[processEntityType][processEntityId] as Record<string, unknown>),
...(processPayload as Record<string, unknown>),
};
}
}
break;
break;
case 'DEL':
if (processEntityId) {
delete state[processEntityType][processEntityId];
}
break;
case 'MOV':
if (processEntityId && processPayload) {
state[processEntityType][processEntityId] = {
...(state[processEntityType][processEntityId] as Record<string, unknown>),
...(processPayload as Record<string, unknown>),
};
}
break;
case 'BATCH':
if (processPayload && typeof processPayload === 'object') {
const batchPayload = processPayload as Record<string, unknown>;
if (batchPayload.entities && typeof batchPayload.entities === 'object') {
const entities = batchPayload.entities as Record<string, unknown>;
for (const [id, entity] of Object.entries(entities)) {
state[processEntityType][id] = {
...(state[processEntityType][id] as Record<string, unknown>),
...(entity as Record<string, unknown>),
};
}
} else if (processEntityId) {
state[processEntityType][processEntityId] = {
...(state[processEntityType][processEntityId] as Record<
string,
unknown
>),
...batchPayload,
};
}
}
break;
}
}
}
return state;

View file

@ -7,6 +7,13 @@ import { GlobalConfigState } from './global-config.model';
const minute = 60 * 1000;
const defaultVoice = getDefaultVoice();
const defaultTaskNotesTemplate = `**How can I best achieve it now?**
**What do I want?**
**Why do I want it?**
`;
export const DEFAULT_DAY_START = '9:00';
export const DEFAULT_GLOBAL_CONFIG: GlobalConfigState = {
appFeatures: {
@ -27,27 +34,24 @@ export const DEFAULT_GLOBAL_CONFIG: GlobalConfigState = {
dateTimeLocale: undefined,
firstDayOfWeek: undefined,
},
tasks: {
isConfirmBeforeDelete: true,
isAutoAddWorkedOnToToday: true,
isAutoMarkParentAsDone: false,
isTrayShowCurrent: true,
defaultProjectId: null,
isMarkdownFormattingInNotesEnabled: true,
notesTemplate: defaultTaskNotesTemplate,
},
misc: {
isConfirmBeforeExit: false,
isConfirmBeforeExitWithoutFinishDay: true,
isConfirmBeforeTaskDelete: true,
isAutMarkParentAsDone: false,
isTurnOffMarkdown: false,
isAutoAddWorkedOnToToday: true,
isMinimizeToTray: false,
isTrayShowCurrentTask: true,
isTrayShowCurrentCountdown: true,
defaultProjectId: null,
startOfNextDay: 0,
isDisableAnimations: false,
isDisableCelebration: false,
isShowProductivityTipLonger: false,
taskNotesTpl: `**How can I best achieve it now?**
**What do I want?**
**Why do I want it?**
`,
isOverlayIndicatorEnabled: false,
customTheme: 'default',
defaultStartPage: 0,

View file

@ -5,20 +5,12 @@ import {
} from '../global-config.model';
import { T } from '../../../t.const';
import { IS_ELECTRON } from '../../../app.constants';
import { AVAILABLE_CUSTOM_THEMES } from '../../../core/theme/custom-theme.service';
export const MISC_SETTINGS_FORM_CFG: ConfigFormSection<MiscConfig> = {
title: T.GCF.MISC.TITLE,
key: 'misc',
help: T.GCF.MISC.HELP,
items: [
// {
// key: 'isDarkMode',
// type: 'checkbox',
// templateOptions: {
// label: T.GCF.MISC.IS_DARK_MODE,
// },
// },
...((IS_ELECTRON
? [
{
@ -38,35 +30,6 @@ export const MISC_SETTINGS_FORM_CFG: ConfigFormSection<MiscConfig> = {
},
},
]) as LimitedFormlyFieldConfig<MiscConfig>[]),
{
key: 'isConfirmBeforeTaskDelete',
type: 'checkbox',
templateOptions: {
label: T.GCF.MISC.IS_CONFIRM_BEFORE_TASK_DELETE,
},
},
{
key: 'isAutMarkParentAsDone',
type: 'checkbox',
templateOptions: {
label: T.GCF.MISC.IS_AUTO_MARK_PARENT_AS_DONE,
},
},
{
key: 'isTurnOffMarkdown',
type: 'checkbox',
templateOptions: {
label: T.GCF.MISC.IS_TURN_OFF_MARKDOWN,
},
},
{
key: 'isAutoAddWorkedOnToToday',
type: 'checkbox',
templateOptions: {
label: T.GCF.MISC.IS_AUTO_ADD_WORKED_ON_TO_TODAY,
},
},
{
key: 'isMinimizeToTray',
type: 'checkbox',
@ -74,20 +37,6 @@ export const MISC_SETTINGS_FORM_CFG: ConfigFormSection<MiscConfig> = {
label: T.GCF.MISC.IS_MINIMIZE_TO_TRAY,
},
},
{
key: 'isTrayShowCurrentTask',
type: 'checkbox',
templateOptions: {
label: T.GCF.MISC.IS_TRAY_SHOW_CURRENT_TASK,
},
},
{
key: 'defaultProjectId',
type: 'project-select',
templateOptions: {
label: T.GCF.MISC.DEFAULT_PROJECT,
},
},
{
key: 'startOfNextDay',
type: 'input',
@ -101,14 +50,6 @@ export const MISC_SETTINGS_FORM_CFG: ConfigFormSection<MiscConfig> = {
max: 23,
},
},
{
key: 'taskNotesTpl',
type: 'textarea',
templateOptions: {
rows: 5,
label: T.GCF.MISC.TASK_NOTES_TPL,
},
},
{
key: 'isDisableAnimations',
type: 'checkbox',
@ -156,17 +97,6 @@ export const MISC_SETTINGS_FORM_CFG: ConfigFormSection<MiscConfig> = {
},
]
: []) as LimitedFormlyFieldConfig<MiscConfig>[]),
{
key: 'customTheme',
type: 'select',
templateOptions: {
label: T.GCF.MISC.THEME,
options: AVAILABLE_CUSTOM_THEMES.map((theme) => ({
label: theme.name,
value: theme.id,
})),
},
},
{
key: 'defaultStartPage',
type: 'select',

View file

@ -0,0 +1,59 @@
import { ConfigFormSection, TasksConfig } from '../global-config.model';
import { T } from '../../../t.const';
export const TASKS_SETTINGS_FORM_CFG: ConfigFormSection<TasksConfig> = {
title: T.GCF.TASKS.TITLE,
key: 'tasks',
items: [
{
key: 'isConfirmBeforeDelete',
type: 'checkbox',
templateOptions: {
label: T.GCF.TASKS.IS_CONFIRM_BEFORE_DELETE,
},
},
{
key: 'isAutoAddWorkedOnToToday',
type: 'checkbox',
templateOptions: {
label: T.GCF.TASKS.IS_AUTO_ADD_WORKED_ON_TO_TODAY,
},
},
{
key: 'isAutoMarkParentAsDone',
type: 'checkbox',
templateOptions: {
label: T.GCF.TASKS.IS_AUTO_MARK_PARENT_AS_DONE,
},
},
{
key: 'isTrayShowCurrent',
type: 'checkbox',
templateOptions: {
label: T.GCF.TASKS.IS_TRAY_SHOW_CURRENT,
},
},
{
key: 'isMarkdownFormattingInNotesEnabled',
type: 'checkbox',
templateOptions: {
label: T.GCF.TASKS.IS_MARKDOWN_FORMATTING_IN_NOTES_ENABLED,
},
},
{
key: 'defaultProjectId',
type: 'project-select',
templateOptions: {
label: T.GCF.TASKS.DEFAULT_PROJECT,
},
},
{
key: 'notesTemplate',
type: 'textarea',
templateOptions: {
rows: 5,
label: T.GCF.TASKS.NOTES_TEMPLATE,
},
},
],
};

View file

@ -16,6 +16,7 @@ import { DOMINA_MODE_FORM } from './form-cfgs/domina-mode-form.const';
import { FOCUS_MODE_FORM_CFG } from './form-cfgs/focus-mode-form.const';
import { REMINDER_FORM_CFG } from './form-cfgs/reminder-form.const';
import { SHORT_SYNTAX_FORM_CFG } from './form-cfgs/short-syntax-form.const';
import { TASKS_SETTINGS_FORM_CFG } from './form-cfgs/tasks-settings-form.const';
const filterGlobalConfigForm = (cfg: ConfigFormSection<any>): boolean => {
return (
@ -29,7 +30,6 @@ export const GLOBAL_GENERAL_FORM_CONFIG: ConfigFormConfig = [
LANGUAGE_SELECTION_FORM_FORM,
APP_FEATURES_FORM_CFG,
MISC_SETTINGS_FORM_CFG,
SHORT_SYNTAX_FORM_CFG,
KEYBOARD_SETTINGS_FORM_CFG,
].filter(filterGlobalConfigForm);
@ -58,3 +58,8 @@ export const GLOBAL_PRODUCTIVITY_FORM_CONFIG: ConfigFormConfig = [
SIMPLE_COUNTER_FORM,
...(!window.ea?.isSnap() && !!window.speechSynthesis ? [DOMINA_MODE_FORM] : []),
].filter(filterGlobalConfigForm);
export const GLOBAL_TASKS_FORM_CONFIG: ConfigFormConfig = [
TASKS_SETTINGS_FORM_CFG,
SHORT_SYNTAX_FORM_CFG,
].filter(filterGlobalConfigForm);

View file

@ -21,18 +21,10 @@ export type AppFeaturesConfig = Readonly<{
}>;
export type MiscConfig = Readonly<{
isAutMarkParentAsDone: boolean;
isConfirmBeforeExit: boolean;
isConfirmBeforeExitWithoutFinishDay: boolean;
isConfirmBeforeTaskDelete?: boolean;
isTurnOffMarkdown: boolean;
isAutoAddWorkedOnToToday: boolean;
isMinimizeToTray: boolean;
isTrayShowCurrentTask: boolean;
// allow also false because of #569
defaultProjectId?: string | null | false;
startOfNextDay: number;
taskNotesTpl: string;
isDisableAnimations: boolean;
// optional because it was added later
isDisableCelebration?: boolean;
@ -43,6 +35,25 @@ export type MiscConfig = Readonly<{
customTheme?: string;
defaultStartPage?: number;
unsplashApiKey?: string | null;
// @todo: remove deprecated items in future major releases, after giving users time to migrate
isConfirmBeforeTaskDelete?: boolean; // Deprecated
isAutoAddWorkedOnToToday?: boolean; // Deprecated
isAutMarkParentAsDone?: boolean; // Deprecated
isTrayShowCurrentTask?: boolean; // Deprecated
isTurnOffMarkdown?: boolean; // Deprecated
defaultProjectId?: string | null | false; // Deprecated
taskNotesTpl?: string; // Deprecated
}>;
export type TasksConfig = Readonly<{
isAutoMarkParentAsDone: boolean;
isAutoAddWorkedOnToToday: boolean;
isConfirmBeforeDelete?: boolean;
isTrayShowCurrent: boolean;
isMarkdownFormattingInNotesEnabled: boolean;
defaultProjectId?: string | null | false; // allow 'false' because of #569
notesTemplate: string;
}>;
export type ShortSyntaxConfig = Readonly<{
@ -217,6 +228,7 @@ export type GlobalConfigState = Readonly<{
appFeatures: AppFeaturesConfig;
localization: LocalizationConfig;
misc: MiscConfig;
tasks: TasksConfig;
shortSyntax: ShortSyntaxConfig;
evaluation: EvaluationConfig;
idle: IdleConfig;
@ -239,6 +251,7 @@ export type GlobalConfigSectionKey = keyof GlobalConfigState | 'EMPTY';
export type GlobalSectionConfig =
| MiscConfig
| TasksConfig
| PomodoroConfig
| KeyboardConfig
| ScheduleConfig

View file

@ -18,6 +18,7 @@ import {
SoundConfig,
SyncConfig,
TakeABreakConfig,
TasksConfig,
} from './global-config.model';
import {
selectConfigFeatureState,
@ -30,6 +31,7 @@ import {
selectSoundConfig,
selectSyncConfig,
selectTakeABreakConfig,
selectTasksConfig,
selectTimelineConfig,
} from './store/global-config.reducer';
import { distinctUntilChanged, shareReplay } from 'rxjs/operators';
@ -48,6 +50,11 @@ export class GlobalConfigService {
shareReplay(1),
);
tasks$: Observable<TasksConfig> = this._store.pipe(
select(selectTasksConfig),
shareReplay(1),
);
localization$: Observable<LocalizationConfig> = this._store.pipe(
select(selectLocalizationConfig),
shareReplay(1),
@ -103,6 +110,9 @@ export class GlobalConfigService {
this.localization$,
{ initialValue: undefined },
);
readonly tasks: Signal<TasksConfig | undefined> = toSignal(this.tasks$, {
initialValue: undefined,
});
readonly misc: Signal<MiscConfig | undefined> = toSignal(this.misc$, {
initialValue: undefined,
});

View file

@ -15,6 +15,7 @@ import {
SoundConfig,
SyncConfig,
TakeABreakConfig,
TasksConfig,
} from '../global-config.model';
import { DEFAULT_GLOBAL_CONFIG } from '../default-global-config.const';
import { loadAllData } from '../../../root-store/meta/load-all-data.action';
@ -27,6 +28,10 @@ export const selectLocalizationConfig = createSelector(
selectConfigFeatureState,
(cfg): LocalizationConfig => cfg?.localization ?? DEFAULT_GLOBAL_CONFIG.localization,
);
export const selectTasksConfig = createSelector(
selectConfigFeatureState,
(cfg): TasksConfig => cfg.tasks ?? DEFAULT_GLOBAL_CONFIG.tasks,
);
export const selectMiscConfig = createSelector(
selectConfigFeatureState,
(cfg): MiscConfig => cfg?.misc ?? DEFAULT_GLOBAL_CONFIG.misc,

View file

@ -68,8 +68,8 @@ describe('FocusModeMainComponent', () => {
storeSpy.select.and.returnValue(of([]));
const globalConfigServiceSpy = jasmine.createSpyObj('GlobalConfigService', [], {
misc: jasmine.createSpy().and.returnValue({
taskNotesTpl: 'Default task notes template',
tasks: jasmine.createSpy().and.returnValue({
notesTemplate: 'Default task notes template',
}),
});
@ -586,8 +586,8 @@ describe('FocusModeMainComponent - notes panel (issue #5752)', () => {
storeSpy.select.and.returnValue(of([]));
const globalConfigServiceSpy = jasmine.createSpyObj('GlobalConfigService', [], {
misc: jasmine.createSpy().and.returnValue({
taskNotesTpl: 'Default task notes template',
tasks: jasmine.createSpy().and.returnValue({
notesTemplate: 'Default task notes template',
}),
});
@ -772,8 +772,8 @@ describe('FocusModeMainComponent - sync with tracking (issue #6009)', () => {
storeSpy.select.and.returnValue(of([]));
const globalConfigServiceSpy = jasmine.createSpyObj('GlobalConfigService', [], {
misc: jasmine.createSpy().and.returnValue({
taskNotesTpl: 'Default task notes template',
tasks: jasmine.createSpy().and.returnValue({
notesTemplate: 'Default task notes template',
}),
});

View file

@ -228,9 +228,9 @@ export class FocusModeMainComponent {
// Use effect to reactively update defaultTaskNotes
effect(() => {
const misc = this._globalConfigService.misc();
if (misc) {
this.defaultTaskNotes.set(misc.taskNotesTpl);
const tasks = this._globalConfigService.tasks();
if (tasks) {
this.defaultTaskNotes.set(tasks.notesTemplate);
}
});

View file

@ -26,8 +26,8 @@ describe('ProjectEffects', () => {
const createConfigState = (): GlobalConfigState => ({
...DEFAULT_GLOBAL_CONFIG,
misc: {
...DEFAULT_GLOBAL_CONFIG.misc,
tasks: {
...DEFAULT_GLOBAL_CONFIG.tasks,
defaultProjectId: 'project-1',
},
});
@ -65,14 +65,14 @@ describe('ProjectEffects', () => {
// Set up config where project-1 is the default
globalConfigServiceMock.cfg.and.returnValue({
...DEFAULT_GLOBAL_CONFIG,
misc: {
...DEFAULT_GLOBAL_CONFIG.misc,
tasks: {
...DEFAULT_GLOBAL_CONFIG.tasks,
defaultProjectId: 'project-1',
},
});
effects.deleteProjectRelatedData.subscribe(() => {
expect(globalConfigServiceMock.updateSection).toHaveBeenCalledWith('misc', {
expect(globalConfigServiceMock.updateSection).toHaveBeenCalledWith('tasks', {
defaultProjectId: null,
});
done();
@ -91,8 +91,8 @@ describe('ProjectEffects', () => {
// Set up config where project-1 is the default, but we delete project-2
globalConfigServiceMock.cfg.and.returnValue({
...DEFAULT_GLOBAL_CONFIG,
misc: {
...DEFAULT_GLOBAL_CONFIG.misc,
tasks: {
...DEFAULT_GLOBAL_CONFIG.tasks,
defaultProjectId: 'project-1',
},
});
@ -114,8 +114,8 @@ describe('ProjectEffects', () => {
it('should NOT clear defaultProjectId when there is no default', (done) => {
globalConfigServiceMock.cfg.and.returnValue({
...DEFAULT_GLOBAL_CONFIG,
misc: {
...DEFAULT_GLOBAL_CONFIG.misc,
tasks: {
...DEFAULT_GLOBAL_CONFIG.tasks,
defaultProjectId: null,
},
});

View file

@ -35,8 +35,8 @@ export class ProjectEffects {
tap(({ projectId }) => {
// Clear defaultProjectId if the deleted project was the default
const cfg = this._globalConfigService.cfg();
if (cfg && projectId === cfg.misc.defaultProjectId) {
this._globalConfigService.updateSection('misc', { defaultProjectId: null });
if (cfg && projectId === cfg.tasks.defaultProjectId) {
this._globalConfigService.updateSection('tasks', { defaultProjectId: null });
}
}),
),

View file

@ -96,6 +96,7 @@ describe('AddTaskBarComponent Mentions Integration', () => {
}),
localization: () => ({ timeLocale: DEFAULT_LOCALE }),
misc$: miscSubject,
tasks$: new BehaviorSubject({ defaultProjectId: null }),
});
const addTaskBarIssueSearchServiceSpy = jasmine.createSpyObj(
'AddTaskBarIssueSearchService',

View file

@ -180,6 +180,7 @@ describe('AddTaskBarComponent', () => {
mockGlobalConfigService = jasmine.createSpyObj('GlobalConfigService', [], {
lang$: new BehaviorSubject<LocalizationConfig>(mockLocalizationConfig),
misc$: new BehaviorSubject<MiscConfig>(mockMiscConfig),
tasks$: new BehaviorSubject({ defaultProjectId: null }),
shortSyntax$: of({}),
localization: () => ({ timeLocale: DEFAULT_LOCALE }),
});
@ -286,15 +287,10 @@ describe('AddTaskBarComponent', () => {
mockWorkContextService.activeWorkContext$ as BehaviorSubject<WorkContext | null>
).next(mockTagWorkContext);
// Set default project in config
const configWithDefault: MiscConfig = {
...mockLocalizationConfig,
...mockMiscConfig,
// Set default project in tasks config
(mockGlobalConfigService.tasks$ as BehaviorSubject<any>).next({
defaultProjectId: 'default-project',
};
(mockGlobalConfigService.misc$ as BehaviorSubject<MiscConfig>).next(
configWithDefault,
);
});
const defaultProject = await component.defaultProject$.pipe(first()).toPromise();
@ -397,15 +393,10 @@ describe('AddTaskBarComponent', () => {
mockWorkContextService.activeWorkContext$ as BehaviorSubject<WorkContext | null>
).next(mockTagWorkContext);
// Set default project
const configWithDefault: MiscConfig = {
...mockLocalizationConfig,
...mockMiscConfig,
// Set default project in tasks config
(mockGlobalConfigService.tasks$ as BehaviorSubject<any>).next({
defaultProjectId: 'default-project',
};
(mockGlobalConfigService.misc$ as BehaviorSubject<MiscConfig>).next(
configWithDefault,
);
});
let defaultProject = await component.defaultProject$.pipe(first()).toPromise();
expect(defaultProject?.id).toBe('default-project');
@ -426,27 +417,17 @@ describe('AddTaskBarComponent', () => {
).next(mockTagWorkContext);
// Start with no default project
const configWithoutDefault: MiscConfig = {
...mockLocalizationConfig,
...mockMiscConfig,
(mockGlobalConfigService.tasks$ as BehaviorSubject<any>).next({
defaultProjectId: null,
};
(mockGlobalConfigService.misc$ as BehaviorSubject<MiscConfig>).next(
configWithoutDefault,
);
});
let defaultProject = await component.defaultProject$.pipe(first()).toPromise();
expect(defaultProject?.id).toBe('INBOX_PROJECT');
// Change to configured default project
const configWithDefault: MiscConfig = {
...mockLocalizationConfig,
...mockMiscConfig,
(mockGlobalConfigService.tasks$ as BehaviorSubject<any>).next({
defaultProjectId: 'default-project',
};
(mockGlobalConfigService.misc$ as BehaviorSubject<MiscConfig>).next(
configWithDefault,
);
});
defaultProject = await component.defaultProject$.pipe(first()).toPromise();
expect(defaultProject?.id).toBe('default-project');
@ -458,15 +439,10 @@ describe('AddTaskBarComponent', () => {
mockWorkContextService.activeWorkContext$ as BehaviorSubject<WorkContext | null>
).next(null);
// Set default project
const configWithDefault: MiscConfig = {
...mockLocalizationConfig,
...mockMiscConfig,
// Set default project in tasks config
(mockGlobalConfigService.tasks$ as BehaviorSubject<any>).next({
defaultProjectId: 'default-project',
};
(mockGlobalConfigService.misc$ as BehaviorSubject<MiscConfig>).next(
configWithDefault,
);
});
const defaultProject = await component.defaultProject$.pipe(first()).toPromise();

View file

@ -172,19 +172,19 @@ export class AddTaskBarComponent implements AfterViewInit, OnInit, OnDestroy {
defaultProject$ = combineLatest([
this.projects$,
this._workContextService.activeWorkContext$,
this._globalConfigService.misc$,
this._globalConfigService.tasks$,
]).pipe(
map(([projects, workContext, miscConfig]) => {
map(([projects, workContext, tasksConfig]) => {
// Priority order:
// 1. If current work context is a project → use that project
// 2. If misc.defaultProjectId is configured → use that project
// 2. If tasks.defaultProjectId is configured → use that project
// 3. Otherwise → fall back to INBOX_PROJECT
const defaultProject =
(workContext?.type === WorkContextType.PROJECT
? projects.find((p) => p.id === workContext.id)
: null) ||
(miscConfig.defaultProjectId
? projects.find((p) => p.id === miscConfig.defaultProjectId)
(tasksConfig.defaultProjectId
? projects.find((p) => p.id === tasksConfig.defaultProjectId)
: null) ||
projects.find((p) => p.id === 'INBOX_PROJECT');
return defaultProject;

View file

@ -33,7 +33,7 @@ describe('ShortSyntaxEffects', () => {
getByIdOnce$: jasmine.Spy;
};
let globalConfigServiceMock: {
misc$: BehaviorSubject<any>;
tasks$: BehaviorSubject<any>;
cfg: jasmine.Spy;
};
let snackServiceSpy: jasmine.SpyObj<SnackService>;
@ -85,8 +85,8 @@ describe('ShortSyntaxEffects', () => {
};
globalConfigServiceMock = {
misc$: new BehaviorSubject({
...DEFAULT_GLOBAL_CONFIG.misc,
tasks$: new BehaviorSubject({
...DEFAULT_GLOBAL_CONFIG.tasks,
defaultProjectId: null,
}),
cfg: jasmine.createSpy('cfg').and.returnValue(DEFAULT_GLOBAL_CONFIG),

View file

@ -74,8 +74,8 @@ export class ShortSyntaxEffects {
withLatestFrom(
this._tagService.tagsNoMyDayAndNoList$,
this._projectService.list$,
this._globalConfigService.misc$.pipe(
map((misc) => misc.defaultProjectId),
this._globalConfigService.tasks$.pipe(
map((tasks) => tasks.defaultProjectId),
concatMap((defaultProjectId) => {
if (this._workContextService.activeWorkContextId === INBOX_PROJECT.id) {

View file

@ -8,13 +8,13 @@ import { setCurrentTask, toggleStart, unsetCurrentTask } from './task.actions';
import { selectTaskFeatureState } from './task.selectors';
import {
selectConfigFeatureState,
selectMiscConfig,
selectTasksConfig,
} from '../../config/store/global-config.reducer';
import { DEFAULT_TASK, Task, TaskState } from '../task.model';
import { WorkContextService } from '../../work-context/work-context.service';
import { LOCAL_ACTIONS } from '../../../util/local-actions.token';
import { DEFAULT_GLOBAL_CONFIG } from '../../config/default-global-config.const';
import { GlobalConfigState, MiscConfig } from '../../config/global-config.model';
import { GlobalConfigState, TasksConfig } from '../../config/global-config.model';
import { WorkContextType } from '../../work-context/work-context.model';
describe('TaskInternalEffects', () => {
@ -60,8 +60,8 @@ describe('TaskInternalEffects', () => {
...partial,
});
const createMiscConfig = (partial: Partial<MiscConfig> = {}): MiscConfig => ({
...DEFAULT_GLOBAL_CONFIG.misc,
const createTasksConfig = (partial: Partial<TasksConfig> = {}): TasksConfig => ({
...DEFAULT_GLOBAL_CONFIG.tasks,
...partial,
});
@ -91,8 +91,8 @@ describe('TaskInternalEffects', () => {
provideMockStore({
selectors: [
{
selector: selectMiscConfig,
value: createMiscConfig({ isAutMarkParentAsDone: true }),
selector: selectTasksConfig,
value: createTasksConfig({ isAutoMarkParentAsDone: true }),
},
{ selector: selectTaskFeatureState, value: createTaskState([]) },
{ selector: selectConfigFeatureState, value: createConfigState() },
@ -129,8 +129,8 @@ describe('TaskInternalEffects', () => {
});
store.overrideSelector(
selectMiscConfig,
createMiscConfig({ isAutMarkParentAsDone: true }),
selectTasksConfig,
createTasksConfig({ isAutoMarkParentAsDone: true }),
);
store.overrideSelector(
selectTaskFeatureState,
@ -164,8 +164,8 @@ describe('TaskInternalEffects', () => {
});
store.overrideSelector(
selectMiscConfig,
createMiscConfig({ isAutMarkParentAsDone: false }),
selectTasksConfig,
createTasksConfig({ isAutoMarkParentAsDone: false }),
);
store.overrideSelector(
selectTaskFeatureState,
@ -205,8 +205,8 @@ describe('TaskInternalEffects', () => {
});
store.overrideSelector(
selectMiscConfig,
createMiscConfig({ isAutMarkParentAsDone: true }),
selectTasksConfig,
createTasksConfig({ isAutoMarkParentAsDone: true }),
);
store.overrideSelector(
selectTaskFeatureState,

View file

@ -13,7 +13,7 @@ import { filter, map, mergeMap, withLatestFrom } from 'rxjs/operators';
import { selectTaskFeatureState } from './task.selectors';
import {
selectConfigFeatureState,
selectMiscConfig,
selectTasksConfig,
} from '../../config/store/global-config.reducer';
import { Task, TaskState } from '../task.model';
import { EMPTY, of } from 'rxjs';
@ -33,13 +33,13 @@ export class TaskInternalEffects {
this._actions$.pipe(
ofType(TaskSharedActions.updateTask),
withLatestFrom(
this._store$.pipe(select(selectMiscConfig)),
this._store$.pipe(select(selectTasksConfig)),
this._store$.pipe(select(selectTaskFeatureState)),
),
filter(
([{ task }, miscCfg, state]) =>
!!miscCfg &&
miscCfg.isAutMarkParentAsDone &&
([{ task }, tasksCfg, state]) =>
!!tasksCfg &&
tasksCfg.isAutoMarkParentAsDone &&
!!task.changes.isDone &&
// @ts-ignore
!!state.entities[task.id].parentId,

View file

@ -49,7 +49,7 @@ describe('TaskRelatedModelEffects', () => {
{
provide: GlobalConfigService,
useValue: {
misc$: of({ isAutoAddWorkedOnToToday: true }),
tasks$: of({ isAutoAddWorkedOnToToday: true }),
},
},
{ provide: HydrationStateService, useValue: hydrationStateServiceSpy },

View file

@ -27,8 +27,8 @@ export class TaskRelatedModelEffects {
// ---------------------
ifAutoAddTodayEnabled$ = <T>(obs: Observable<T>): Observable<T> =>
this._globalConfigService.misc$.pipe(
switchMap((misc) => (misc.isAutoAddWorkedOnToToday ? obs : EMPTY)),
this._globalConfigService.tasks$.pipe(
switchMap((tasks) => (tasks.isAutoAddWorkedOnToToday ? obs : EMPTY)),
);
autoAddTodayTagOnTracking = createEffect(() =>

View file

@ -289,10 +289,10 @@ export class TaskContextMenuInnerComponent implements AfterViewInit {
return;
}
const isConfirmBeforeTaskDelete =
this._globalConfigService.cfg()?.misc?.isConfirmBeforeTaskDelete ?? true;
const isConfirmBeforeDelete =
this._globalConfigService.cfg()?.tasks?.isConfirmBeforeDelete ?? true;
if (isConfirmBeforeTaskDelete) {
if (isConfirmBeforeDelete) {
this._matDialog
.open(DialogConfirmComponent, {
data: {

View file

@ -244,8 +244,8 @@ export class TaskDetailPanelComponent implements OnInit, AfterViewInit, OnDestro
// Default task notes computed signal
defaultTaskNotes = computed(() => {
const misc = this._globalConfigService.misc();
return misc?.taskNotesTpl || '';
const tasks = this._globalConfigService.tasks();
return tasks?.notesTemplate || '';
});
// Local attachments computed signal

View file

@ -105,11 +105,12 @@ describe('TaskService', () => {
const globalConfigServiceSpy = jasmine.createSpyObj('GlobalConfigService', [''], {
cfg: signal({
misc: { defaultProjectId: null },
tasks: { defaultProjectId: null },
reminder: { defaultTaskRemindOption: 'AT_START' },
appFeatures: { isTimeTrackingEnabled: true },
}),
misc: signal({ isShowProductivityTipLonger: false }),
tasks: signal({ defaultProjectId: null }),
});
const taskFocusServiceSpy = jasmine.createSpyObj('TaskFocusService', [''], {

View file

@ -1234,7 +1234,7 @@ export class TaskService {
? { projectId: workContextId }
: {
projectId:
this._globalConfigService.cfg()?.misc.defaultProjectId || INBOX_PROJECT.id,
this._globalConfigService.cfg()?.tasks.defaultProjectId || INBOX_PROJECT.id,
}),
tagIds:
@ -1258,7 +1258,7 @@ export class TaskService {
d1.projectId =
workContextType === WorkContextType.PROJECT
? workContextId
: this._globalConfigService.cfg()?.misc.defaultProjectId || INBOX_PROJECT.id;
: this._globalConfigService.cfg()?.tasks.defaultProjectId || INBOX_PROJECT.id;
}
// Validate that we have a valid task before returning

View file

@ -371,7 +371,7 @@ export class TaskComponent implements OnDestroy, AfterViewInit {
}
const isConfirmBeforeTaskDelete =
this._configService.cfg()?.misc?.isConfirmBeforeTaskDelete ?? true;
this._configService.cfg()?.tasks?.isConfirmBeforeDelete ?? true;
if (isConfirmBeforeTaskDelete) {
this._matDialog

View file

@ -54,9 +54,9 @@ describe('SchemaMigrationService', () => {
expect(service.getCurrentVersion()).toBe(CURRENT_SCHEMA_VERSION);
});
it('should return 1 as the initial version', () => {
// Current implementation starts at version 1
expect(service.getCurrentVersion()).toBe(1);
it('should return 2 as the current version', () => {
// Current implementation is at version 2 after migration from MiscConfig to TasksConfig
expect(service.getCurrentVersion()).toBe(2);
});
});
@ -68,7 +68,7 @@ describe('SchemaMigrationService', () => {
it('should return empty array for initial version', () => {
// No migrations defined yet for version 1
const migrations = service.getMigrations();
const migrations = service.getMigrations(1, 1);
expect(migrations.length).toBe(0);
});
});
@ -79,17 +79,15 @@ describe('SchemaMigrationService', () => {
expect(service.needsMigration(cache)).toBeFalse();
});
it('should return false for cache with undefined schemaVersion (defaults to 1)', () => {
it('should return true for cache with undefined schemaVersion (defaults to 1)', () => {
const cache = createMockCache(undefined);
// When schemaVersion is undefined, it defaults to 1
// Since CURRENT_SCHEMA_VERSION is 1, no migration needed
expect(service.needsMigration(cache)).toBeFalse();
// Since CURRENT_SCHEMA_VERSION is 2, migration is needed
expect(service.needsMigration(cache)).toBeTrue();
});
it('should return true for cache with older version', () => {
// This test will only make sense when we have migrations
// For now, since CURRENT_SCHEMA_VERSION is 1, there's no "older" version
const cache = createMockCache(0); // Version 0 is older
const cache = createMockCache(1); // Version 1 is older than current version 2
expect(service.needsMigration(cache)).toBeTrue();
});
});
@ -100,14 +98,15 @@ describe('SchemaMigrationService', () => {
expect(service.operationNeedsMigration(op)).toBeFalse();
});
it('should return false for operation with undefined schemaVersion (defaults to 1)', () => {
it('should return true for operation with undefined schemaVersion (defaults to 1)', () => {
const op = createMockOperation('op-1');
op.schemaVersion = undefined as any;
expect(service.operationNeedsMigration(op)).toBeFalse();
// Since CURRENT_SCHEMA_VERSION is 2, migration is needed
expect(service.operationNeedsMigration(op)).toBeTrue();
});
it('should return true for operation with older version', () => {
const op = createMockOperation('op-1', 0);
const op = createMockOperation('op-1', 1);
expect(service.operationNeedsMigration(op)).toBeTrue();
});
});

View file

@ -127,11 +127,12 @@ export class SchemaMigrationService {
/**
* Migrates a single operation to the current schema version if needed.
* Returns null if the operation should be dropped (e.g., for removed features).
* Returns an array if the operation should be split into multiple operations.
*
* @param op - The operation to migrate
* @returns The migrated operation, or null if it should be dropped
* @returns The migrated operation(s), or null if it should be dropped
*/
migrateOperation(op: Operation): Operation | null {
migrateOperation(op: Operation): Operation | Operation[] | null {
const opVersion = op.schemaVersion ?? 1;
if (opVersion >= CURRENT_SCHEMA_VERSION) {
@ -159,6 +160,19 @@ export class SchemaMigrationService {
return null;
}
// Handle array result (operation was split into multiple)
if (Array.isArray(result.data)) {
return result.data.map((migratedOpLike) => ({
...op,
opType: migratedOpLike.opType as Operation['opType'],
entityType: migratedOpLike.entityType as Operation['entityType'],
entityId: migratedOpLike.entityId,
entityIds: migratedOpLike.entityIds,
payload: migratedOpLike.payload,
schemaVersion: migratedOpLike.schemaVersion,
}));
}
// Merge migrated fields back into the original operation
return {
...op,
@ -172,6 +186,7 @@ export class SchemaMigrationService {
/**
* Migrates an array of operations, filtering out any that should be dropped.
* Handles operations that are split into multiple operations.
*
* @param ops - The operations to migrate
* @returns Array of migrated operations (dropped operations excluded)
@ -180,13 +195,19 @@ export class SchemaMigrationService {
const migrated: Operation[] = [];
for (const op of ops) {
const migratedOp = this.migrateOperation(op);
if (migratedOp !== null) {
migrated.push(migratedOp);
} else {
const migratedResult = this.migrateOperation(op);
if (migratedResult === null) {
OpLog.normal(
`SchemaMigrationService: Dropped operation ${op.id} (${op.actionType}) during migration`,
);
} else if (Array.isArray(migratedResult)) {
// Operation was split into multiple operations
migrated.push(...migratedResult);
OpLog.normal(
`SchemaMigrationService: Split operation ${op.id} into ${migratedResult.length} operations during migration`,
);
} else {
migrated.push(migratedResult);
}
}
@ -224,9 +245,24 @@ export class SchemaMigrationService {
}
/**
* Returns all registered migrations (for debugging/testing).
* Returns registered migrations for a specific version range.
* If no range is provided, returns all migrations.
*
* @param fromVersion - The starting version (inclusive).
* @param toVersion - The ending version (inclusive).
* @returns Array of migrations within the specified range.
*/
getMigrations(): readonly SchemaMigration[] {
return MIGRATIONS;
getMigrations(fromVersion?: number, toVersion?: number): readonly SchemaMigration[] {
if (fromVersion === undefined && toVersion === undefined) {
return MIGRATIONS;
}
return MIGRATIONS.filter((migration) => {
const isAfterFromVersion =
fromVersion === undefined || migration.fromVersion >= fromVersion;
const isBeforeToVersion =
toVersion === undefined || migration.toVersion <= toVersion;
return isAfterFromVersion && isBeforeToVersion;
});
}
}

View file

@ -130,9 +130,7 @@ export class RemoteOpsProcessingService {
try {
const migrated = this.schemaMigrationService.migrateOperation(op);
if (migrated) {
migratedOps.push(migrated);
} else {
if (migrated === null) {
// Track dropped entity IDs for dependency warning
if (op.entityId) {
droppedEntityIds.add(op.entityId);
@ -143,6 +141,11 @@ export class RemoteOpsProcessingService {
OpLog.verbose(
`RemoteOpsProcessingService: Dropped op ${op.id} (migrated to null)`,
);
} else if (Array.isArray(migrated)) {
// Operation was split into multiple operations
migratedOps.push(...migrated);
} else {
migratedOps.push(migrated);
}
} catch (e) {
OpLog.err(`RemoteOpsProcessingService: Migration failed for op ${op.id}`, e);

View file

@ -17,6 +17,7 @@ import { resetTestUuidCounter } from './helpers/test-client.helper';
import { LockService } from '../../sync/lock.service';
import { OperationLogCompactionService } from '../../persistence/operation-log-compaction.service';
import { SyncImportFilterService } from '../../sync/sync-import-filter.service';
import { CURRENT_SCHEMA_VERSION } from '@sp/shared-schema';
/**
* Integration tests for Schema Migration Handling in Sync.
@ -149,7 +150,7 @@ describe('Migration Handling Integration', () => {
it('should reject operation with incompatible future version', async () => {
// Logic: if opVersion > current + MAX_VERSION_SKIP, update required
const incompatibleVersion = 1 + MAX_VERSION_SKIP + 1;
const incompatibleVersion = CURRENT_SCHEMA_VERSION + MAX_VERSION_SKIP + 1;
const op = createOp(incompatibleVersion);
await service.processRemoteOps([op]);

View file

@ -32,6 +32,26 @@
></config-section>
</section>
}
</div>
</mat-tab>
<!-- Tab: Tasks -->
<mat-tab>
<ng-template mat-tab-label>
<mat-icon class="tab-icon">task</mat-icon>
<span class="tab-label">{{ 'PS.TABS.TASKS' | translate }}</span>
</ng-template>
<div class="tab-content">
@for (section of globalTasksFormCfg; track section.key) {
<section class="config-section section-{{ section.key }}">
<config-section
(save)="saveGlobalCfg($event)"
[cfg]="getGlobalCfgSection(section.key)"
[section]="section"
></config-section>
</section>
}
<!-- @todo: Create a single style for forming all the settings elements.
Make all as "sections" or as "forms" consistently.
-->

View file

@ -15,6 +15,7 @@ import {
GLOBAL_PLUGINS_FORM_CONFIG,
GLOBAL_PRODUCTIVITY_FORM_CONFIG,
GLOBAL_TIME_TRACKING_FORM_CONFIG,
GLOBAL_TASKS_FORM_CONFIG,
} from '../../features/config/global-config-form-config.const';
import {
ConfigFormConfig,
@ -101,6 +102,7 @@ export class ConfigPageComponent implements OnInit, OnDestroy {
// @todo - find better names for tabs configs forms
// Tab-specific form configurations
generalFormCfg: ConfigFormConfig;
globalTasksFormCfg: ConfigFormConfig;
timeTrackingFormCfg: ConfigFormConfig;
pluginsShortcutsFormCfg: ConfigFormConfig;
globalImexFormCfg: ConfigFormConfig;
@ -138,6 +140,7 @@ export class ConfigPageComponent implements OnInit, OnDestroy {
this.pluginsShortcutsFormCfg = GLOBAL_PLUGINS_FORM_CONFIG.slice();
this.globalImexFormCfg = GLOBAL_IMEX_FORM_CONFIG.slice();
this.globalProductivityConfigFormCfg = GLOBAL_PRODUCTIVITY_FORM_CONFIG.slice();
this.globalTasksFormCfg = GLOBAL_TASKS_FORM_CONFIG.slice();
// NOTE: needs special handling cause of the async stuff
if (IS_ANDROID_WEB_VIEW) {

View file

@ -596,6 +596,7 @@ import { TaskArchiveService } from '../features/archive/task-archive.service';
import { Router } from '@angular/router';
import { TranslateService } from '@ngx-translate/core';
import { SyncWrapperService } from '../imex/sync/sync-wrapper.service';
import { getDbDateStr } from '../util/get-db-date-str';
describe('PluginBridgeService - Counter Methods', () => {
let service: PluginBridgeService;
@ -651,7 +652,7 @@ describe('PluginBridgeService - Counter Methods', () => {
// Arrange
const counterId = 'new-counter';
const value = 10;
const today = new Date().toISOString().split('T')[0];
const today = getDbDateStr();
// Act
await service.setCounter(counterId, value);
@ -676,7 +677,7 @@ describe('PluginBridgeService - Counter Methods', () => {
// Arrange
const counterId = 'existing-counter';
const value = 15;
const today = new Date().toISOString().split('T')[0];
const today = getDbDateStr();
// Act
await service.setCounter(counterId, value);
@ -707,8 +708,8 @@ describe('PluginBridgeService - Counter Methods', () => {
describe('incrementCounter', () => {
it('should increment existing counter value', async () => {
// Arrange: existing counter has value 5 for 2025-12-30
const today = new Date().toISOString().split('T')[0];
// Arrange: existing counter has value 5 for today
const today = getDbDateStr();
store.overrideSelector(selectAllSimpleCounters, [
{ ...mockExistingCounter, countOnDay: { [today]: 5 } },
]);
@ -741,7 +742,7 @@ describe('PluginBridgeService - Counter Methods', () => {
describe('decrementCounter', () => {
it('should decrement existing counter value', async () => {
// Arrange
const today = new Date().toISOString().split('T')[0];
const today = getDbDateStr();
store.overrideSelector(selectAllSimpleCounters, [
{ ...mockExistingCounter, countOnDay: { [today]: 10 } },
]);
@ -755,7 +756,7 @@ describe('PluginBridgeService - Counter Methods', () => {
it('should not go below zero', async () => {
// Arrange
const today = new Date().toISOString().split('T')[0];
const today = getDbDateStr();
store.overrideSelector(selectAllSimpleCounters, [
{ ...mockExistingCounter, countOnDay: { [today]: 2 } },
]);

View file

@ -1825,16 +1825,12 @@ const T = {
DARK_MODE_DARK: 'GCF.MISC.DARK_MODE_DARK',
DARK_MODE_LIGHT: 'GCF.MISC.DARK_MODE_LIGHT',
DARK_MODE_SYSTEM: 'GCF.MISC.DARK_MODE_SYSTEM',
DEFAULT_PROJECT: 'GCF.MISC.DEFAULT_PROJECT',
DEFAULT_START_PAGE: 'GCF.MISC.DEFAULT_START_PAGE',
FIRST_DAY_OF_WEEK: 'GCF.MISC.FIRST_DAY_OF_WEEK',
HELP: 'GCF.MISC.HELP',
IS_AUTO_ADD_WORKED_ON_TO_TODAY: 'GCF.MISC.IS_AUTO_ADD_WORKED_ON_TO_TODAY',
IS_AUTO_MARK_PARENT_AS_DONE: 'GCF.MISC.IS_AUTO_MARK_PARENT_AS_DONE',
IS_CONFIRM_BEFORE_EXIT: 'GCF.MISC.IS_CONFIRM_BEFORE_EXIT',
IS_CONFIRM_BEFORE_EXIT_WITHOUT_FINISH_DAY:
'GCF.MISC.IS_CONFIRM_BEFORE_EXIT_WITHOUT_FINISH_DAY',
IS_CONFIRM_BEFORE_TASK_DELETE: 'GCF.MISC.IS_CONFIRM_BEFORE_TASK_DELETE',
IS_DARK_MODE: 'GCF.MISC.IS_DARK_MODE',
IS_DISABLE_ANIMATIONS: 'GCF.MISC.IS_DISABLE_ANIMATIONS',
IS_DISABLE_CELEBRATION: 'GCF.MISC.IS_DISABLE_CELEBRATION',
@ -1842,18 +1838,26 @@ const T = {
IS_OVERLAY_INDICATOR_ENABLED: 'GCF.MISC.IS_OVERLAY_INDICATOR_ENABLED',
IS_SHOW_TIP_LONGER: 'GCF.MISC.IS_SHOW_TIP_LONGER',
IS_TRAY_SHOW_CURRENT_COUNTDOWN: 'GCF.MISC.IS_TRAY_SHOW_CURRENT_COUNTDOWN',
IS_TRAY_SHOW_CURRENT_TASK: 'GCF.MISC.IS_TRAY_SHOW_CURRENT_TASK',
IS_TURN_OFF_MARKDOWN: 'GCF.MISC.IS_TURN_OFF_MARKDOWN',
IS_USE_CUSTOM_WINDOW_TITLE_BAR: 'GCF.MISC.IS_USE_CUSTOM_WINDOW_TITLE_BAR',
IS_USE_CUSTOM_WINDOW_TITLE_BAR_HINT: 'GCF.MISC.IS_USE_CUSTOM_WINDOW_TITLE_BAR_HINT',
START_OF_NEXT_DAY: 'GCF.MISC.START_OF_NEXT_DAY',
START_OF_NEXT_DAY_HINT: 'GCF.MISC.START_OF_NEXT_DAY_HINT',
TASK_NOTES_TPL: 'GCF.MISC.TASK_NOTES_TPL',
THEME: 'GCF.MISC.THEME',
THEME_EXPERIMENTAL: 'GCF.MISC.THEME_EXPERIMENTAL',
THEME_SELECT_LABEL: 'GCF.MISC.THEME_SELECT_LABEL',
TITLE: 'GCF.MISC.TITLE',
},
TASKS: {
DEFAULT_PROJECT: 'GCF.TASKS.DEFAULT_PROJECT',
IS_AUTO_ADD_WORKED_ON_TO_TODAY: 'GCF.TASKS.IS_AUTO_ADD_WORKED_ON_TO_TODAY',
IS_AUTO_MARK_PARENT_AS_DONE: 'GCF.TASKS.IS_AUTO_MARK_PARENT_AS_DONE',
IS_CONFIRM_BEFORE_DELETE: 'GCF.TASKS.IS_CONFIRM_BEFORE_DELETE',
IS_MARKDOWN_FORMATTING_IN_NOTES_ENABLED:
'GCF.TASKS.IS_MARKDOWN_FORMATTING_IN_NOTES_ENABLED',
IS_TRAY_SHOW_CURRENT: 'GCF.TASKS.IS_TRAY_SHOW_CURRENT',
NOTES_TEMPLATE: 'GCF.TASKS.NOTES_TEMPLATE',
TITLE: 'GCF.TASKS.TITLE',
},
POMODORO: {
BREAK_DURATION: 'GCF.POMODORO.BREAK_DURATION',
CYCLES_BEFORE_LONGER_BREAK: 'GCF.POMODORO.CYCLES_BEFORE_LONGER_BREAK',
@ -2203,6 +2207,7 @@ const T = {
SYNC_EXPORT: 'PS.SYNC_EXPORT',
TABS: {
GENERAL: 'PS.TABS.GENERAL',
TASKS: 'PS.TABS.TASKS',
TIME_TRACKING: 'PS.TABS.TIME_TRACKING',
PRODUCTIVITY: 'PS.TABS.PRODUCTIVITY',
PLUGINS: 'PS.TABS.PLUGINS',

View file

@ -3,7 +3,7 @@
[class.isHideOverflow]="isHideOverflow()"
class="markdown-wrapper"
>
@if (isShowEdit() || isTurnOffMarkdownParsing()) {
@if (isShowEdit() || !isMarkdownFormattingEnabled()) {
<textarea
#textareaEl
(blur)="untoggleShowEdit(); setBlur($event)"
@ -20,7 +20,7 @@
}
<!-- (focus)="clickOrFocusPreview($event, true)"-->
@if (!isTurnOffMarkdownParsing()) {
@if (isMarkdownFormattingEnabled()) {
<div
#previewEl
(click)="clickPreview($event)"
@ -37,7 +37,7 @@
<div class="controls">
<ng-content> </ng-content>
@if (isShowChecklistToggle() && !isTurnOffMarkdownParsing()) {
@if (isShowChecklistToggle() && isMarkdownFormattingEnabled()) {
<!-- -->
<button
[matTooltip]="'Add checklist item'"

View file

@ -13,7 +13,7 @@ describe('InlineMarkdownComponent', () => {
beforeEach(async () => {
mockGlobalConfigService = jasmine.createSpyObj('GlobalConfigService', [], {
misc: jasmine.createSpy().and.returnValue({ isTurnOffMarkdown: false }),
tasks: jasmine.createSpy().and.returnValue({ isTurnOffMarkdown: false }),
});
mockMatDialog = jasmine.createSpyObj('MatDialog', ['open']);

View file

@ -60,9 +60,9 @@ export class InlineMarkdownComponent implements OnInit, OnDestroy {
isShowEdit = signal(false);
modelCopy = signal<string | undefined>(undefined);
isTurnOffMarkdownParsing = computed(() => {
const misc = this._globalConfigService.misc();
return misc?.isTurnOffMarkdown ?? false;
isMarkdownFormattingEnabled = computed(() => {
const tasks = this._globalConfigService.tasks();
return tasks?.isMarkdownFormattingInNotesEnabled ?? true;
});
private _hideOverFlowTimeout: number | undefined;

View file

@ -1569,11 +1569,8 @@
"DARK_MODE_DARK": "داكن",
"DARK_MODE_LIGHT": "ضوء",
"DARK_MODE_SYSTEM": "نظام",
"DEFAULT_PROJECT": "المشروع الافتراضي لاستخدامه في المهام إذا لم يتم تحديد أي شيء",
"FIRST_DAY_OF_WEEK": "اول يوم من الاسبوع",
"HELP": "<p><strong>ألا ترى إعلامات سطح المكتب؟</strong> بالنسبة للنوافذ ، قد ترغب في التحقق من النظام> الإشعارات والإجراءات والتحقق من تمكين الإشعارات المطلوبة.</p>",
"IS_AUTO_ADD_WORKED_ON_TO_TODAY": "أضف علامة اليوم تلقائيًا للعمل على المهام",
"IS_AUTO_MARK_PARENT_AS_DONE": "حدد المهمة الرئيسية كما فعلت ، عند الانتهاء من جميع المهام الفرعية",
"IS_CONFIRM_BEFORE_EXIT": "تأكيد قبل الخروج من التطبيق",
"IS_CONFIRM_BEFORE_EXIT_WITHOUT_FINISH_DAY": "قم بالتأكيد قبل الخروج من التطبيق دون الانتهاء من اليوم الأول",
"IS_DARK_MODE": "وضع الظلام",
@ -1583,16 +1580,23 @@
"IS_OVERLAY_INDICATOR_ENABLED": "تفعيل نافذة مؤشر التراكب (لينكس مكتبي/جنوم)",
"IS_SHOW_TIP_LONGER": "إظهار نصيحة الإنتاجية في التطبيق ابدأ لفترة أطول قليلا",
"IS_TRAY_SHOW_CURRENT_COUNTDOWN": "إظهار العد التنازلي الحالي في قائمة الدرج / الحالة (Mac لسطح المكتب فقط)",
"IS_TRAY_SHOW_CURRENT_TASK": "إظهار المهمة الحالية في الدرج / قائمة الحالة (سطح المكتب فقط)",
"IS_TURN_OFF_MARKDOWN": "إيقاف تشغيل تخفيض السعر للملاحظات",
"START_OF_NEXT_DAY": "وقت البدء في اليوم التالي",
"START_OF_NEXT_DAY_HINT": "من متى (بالساعة) تريد حساب بداية اليوم التالي. الافتراضي هو منتصف الليل وهو 0.",
"TASK_NOTES_TPL": "قالب وصف المهمة",
"THEME": "موضوع",
"THEME_EXPERIMENTAL": "الموضوع (تجريبي)",
"THEME_SELECT_LABEL": "اختيار الموضوع",
"TITLE": "متفرقات الإعدادات"
},
"TASKS": {
"DEFAULT_PROJECT": "المشروع الافتراضي لإنشاء المهام الجديدة فيه",
"IS_AUTO_ADD_WORKED_ON_TO_TODAY": "إضافة علامة اليوم تلقائيًا للعمل على المهام الجديدة",
"IS_AUTO_MARK_PARENT_AS_DONE": "عند الانتهاء من جميع المهام الفرعية، يتم تلقائيًا وضع علامة على المهمة الرئيسية كمكتملة",
"IS_CONFIRM_BEFORE_DELETE": "تأكيد قبل حذف المهام",
"IS_MARKDOWN_FORMATTING_IN_NOTES_ENABLED": "تمكين تنسيق Markdown في ملاحظات المهام",
"IS_TRAY_SHOW_CURRENT": "إظهار المهمة الحالية في الدرج / قائمة الحالة (سطح المكتب فقط)",
"NOTES_TEMPLATE": "قالب وصف المهمة",
"TITLE": "المهام"
},
"POMODORO": {
"BREAK_DURATION": "مدة الاستراحة القصيرة",
"CYCLES_BEFORE_LONGER_BREAK": "بدء استراحة أطول بعد جلسات العمل X",

View file

@ -1002,24 +1002,29 @@
"ZOOM_OUT": "Oddálit (pouze pro desktop)"
},
"MISC": {
"DEFAULT_PROJECT": "Výchozí projekt pro použití pro úkoly, pokud není žádný určen",
"FIRST_DAY_OF_WEEK": "První den týdne",
"HELP": "<p><strong>Desktopová upozornění se nezobrazují?</strong> Pro systém Windows byste mohli zkontrolovat Systém > Upozornění a akce a zkontrolovat, zda jsou vyžadovaná upozornění povolena.</p>",
"IS_AUTO_ADD_WORKED_ON_TO_TODAY": "Automaticky přidat značku 'Dnes' k úkolům, na kterých jste pracovali",
"IS_AUTO_MARK_PARENT_AS_DONE": "Označit nadřazený úkol jako hotový, když jsou hotovy všechny podúkoly",
"IS_CONFIRM_BEFORE_EXIT": "Potvrdit před ukončením aplikace",
"IS_CONFIRM_BEFORE_EXIT_WITHOUT_FINISH_DAY": "Potvrdit před ukončením aplikace bez dokončení dne",
"IS_DARK_MODE": "Temný režim",
"IS_DISABLE_ANIMATIONS": "Vypnout všechny animace",
"IS_DISABLE_CELEBRATION": "Vypnout oslavy v denním přehledu",
"IS_MINIMIZE_TO_TRAY": "Minimalizovat do oznamovací oblasti (pouze pro desktop)",
"IS_TRAY_SHOW_CURRENT_TASK": "Zobrazit aktuální úkol v oznamovací oblasti/menu stavu (pouze pro desktop Mac/Windows)",
"IS_TURN_OFF_MARKDOWN": "Vypnout zpracování markdown pro poznámky k úkolům",
"IS_TRAY_SHOW_CURRENT": "Zobrazit aktuální úkol v oznamovací oblasti/menu stavu (pouze pro desktop Mac/Windows)",
"START_OF_NEXT_DAY": "Začátek dalšího dne",
"START_OF_NEXT_DAY_HINT": "od kdy (v hodinách) chcete počítat, že další den začal. Výchozí hodnota je půlnoc, což je 0.",
"TASK_NOTES_TPL": "Šablona popisu úkolu",
"TITLE": "Různá nastavení"
},
"TASKS": {
"DEFAULT_PROJECT": "Výchozí projekt pro použití pro úkoly, pokud není žádný určen",
"IS_AUTO_ADD_WORKED_ON_TO_TODAY": "Automaticky přidat úkoly, na kterých jste pracovali, k dnešku",
"IS_AUTO_MARK_PARENT_AS_DONE": "Automaticky označit nadřazený úkol jako hotový, když jsou všechny podúkoly hotové",
"IS_CONFIRM_BEFORE_DELETE": "Potvrdit před smazáním úkolů",
"IS_MARKDOWN_FORMATTING_IN_NOTES_ENABLED": "Povolit Markdown formátování v poznámkách k úkolům",
"IS_TRAY_SHOW_CURRENT": "Zobrazit aktuální úkol v oznamovací oblasti / menu stavu (pouze pro desktop Mac/Windows)",
"NOTES_TEMPLATE": "Šablona popisu úkolu",
"TITLE": "Úkoly"
},
"POMODORO": {
"BREAK_DURATION": "Délka krátkých přestávek",
"CYCLES_BEFORE_LONGER_BREAK": "Začít delší přestávku po X pracovních sezeních",

View file

@ -1636,12 +1636,9 @@
"DARK_MODE_DARK": "Dunkel",
"DARK_MODE_LIGHT": "Hell",
"DARK_MODE_SYSTEM": "System",
"DEFAULT_PROJECT": "Standardprojekt für Aufgaben, wenn keines angegeben ist",
"DEFAULT_START_PAGE": "Standard-Startseite",
"FIRST_DAY_OF_WEEK": "Erster Tag der Woche",
"HELP": "<p><strong>Desktop-Benachrichtigungen werden nicht angezeigt?</strong> Unter Windows sollten Sie unter System -> Benachrichtigungen und Aktionen prüfen, ob die erforderlichen Benachrichtigungen aktiviert wurden.</p>",
"IS_AUTO_ADD_WORKED_ON_TO_TODAY": "Fügen Sie heute automatisch das Tag \"Heute\" hinzu, um an Aufgaben zu arbeiten",
"IS_AUTO_MARK_PARENT_AS_DONE": "Übergeordnete Aufgabe als erledigt markieren, wenn alle Unteraufgaben erledigt sind",
"IS_CONFIRM_BEFORE_EXIT": "Bestätigen, bevor die App beendet wird",
"IS_CONFIRM_BEFORE_EXIT_WITHOUT_FINISH_DAY": "Bestätigen, bevor die App beendet wird, ohne den Tag zuerst zu beenden",
"IS_DARK_MODE": "Dunkler Modus",
@ -1651,18 +1648,25 @@
"IS_OVERLAY_INDICATOR_ENABLED": "Enable overlay indicator window (desktop linux/gnome)",
"IS_SHOW_TIP_LONGER": "Zeige den Produktivitätstipp beim Start der App etwas länger an",
"IS_TRAY_SHOW_CURRENT_COUNTDOWN": "Aktuellen Countdown im Tray / Statusmenü anzeigen (nur Desktop Mac)",
"IS_TRAY_SHOW_CURRENT_TASK": "Aktuelle Aufgabe im Tray / Status-Menu zeigen (nur Desktop)",
"IS_TURN_OFF_MARKDOWN": "Deaktivieren Sie das Markdown-Parsing für Notizen",
"IS_USE_CUSTOM_WINDOW_TITLE_BAR": "Benutzerdefinierte Titelleiste verwenden (nur Windows/Linux)",
"IS_USE_CUSTOM_WINDOW_TITLE_BAR_HINT": "Neustart erforderlich, damit die Änderung wirksam wird",
"START_OF_NEXT_DAY": "Startzeit des nächsten Tages",
"START_OF_NEXT_DAY_HINT": "Ab wann (in Stunden) der nächste Tag beginnen. Der Standardwert ist Mitternacht, also 0.",
"TASK_NOTES_TPL": "Aufgabenbeschreibungsvorlage",
"THEME": "Thema",
"THEME_EXPERIMENTAL": "Thema (experimentell)",
"THEME_SELECT_LABEL": "Thema auswählen",
"TITLE": "Verschiedene Einstellungen"
},
"TASKS": {
"DEFAULT_PROJECT": "Standardprojekt für Aufgaben, wenn keines angegeben ist",
"IS_AUTO_ADD_WORKED_ON_TO_TODAY": "Automatisch hinzugefügte Aufgaben für heute",
"IS_AUTO_MARK_PARENT_AS_DONE": "Automatisch übergeordnete Aufgaben als erledigt markieren, wenn alle Unteraufgaben erledigt sind",
"IS_CONFIRM_BEFORE_DELETE": "Bestätigen, bevor Aufgaben gelöscht werden",
"IS_MARKDOWN_FORMATTING_IN_NOTES_ENABLED": "Markdown-Formatierung in Aufgabenbeschreibungen aktivieren",
"IS_TRAY_SHOW_CURRENT": "Aktuelle Aufgabe im Tray / Statusmenü anzeigen (nur Desktop Mac/Windows)",
"NOTES_TEMPLATE": "Aufgabenbeschreibungsvorlage",
"TITLE": "Aufgaben"
},
"POMODORO": {
"BREAK_DURATION": "Dauer der kurzen Pausen",
"CYCLES_BEFORE_LONGER_BREAK": "Beginnen einer längeren Pause nach X Arbeitssitzungen",

View file

@ -1821,15 +1821,11 @@
"DARK_MODE_DARK": "Dark",
"DARK_MODE_LIGHT": "Light",
"DARK_MODE_SYSTEM": "System",
"DEFAULT_PROJECT": "Default project to use for tasks if none is specified",
"DEFAULT_START_PAGE": "Default start page",
"FIRST_DAY_OF_WEEK": "First day of the week",
"HELP": "<p><strong>Not seeing Desktop Notifications?</strong> For windows you might want to check System > Notifications & actions and check if the required notifications have been enabled.</p>",
"IS_AUTO_ADD_WORKED_ON_TO_TODAY": "Automatically add today tag to worked on tasks",
"IS_AUTO_MARK_PARENT_AS_DONE": "Mark parent task as done, when all sub tasks are done",
"IS_CONFIRM_BEFORE_EXIT": "Confirm before exiting the app",
"IS_CONFIRM_BEFORE_EXIT_WITHOUT_FINISH_DAY": "Confirm before exiting the app without finishing day first",
"IS_CONFIRM_BEFORE_TASK_DELETE": "Confirm before deleting tasks",
"IS_DARK_MODE": "Dark Mode",
"IS_DISABLE_ANIMATIONS": "Disable all animations",
"IS_DISABLE_CELEBRATION": "Disable celebration in daily summary",
@ -1837,18 +1833,25 @@
"IS_OVERLAY_INDICATOR_ENABLED": "Enable overlay indicator window (desktop linux/gnome)",
"IS_SHOW_TIP_LONGER": "Show productivity tip on app start a little longer",
"IS_TRAY_SHOW_CURRENT_COUNTDOWN": "Show current countdown in the tray / status menu (desktop mac only)",
"IS_TRAY_SHOW_CURRENT_TASK": "Show current task in the tray / status menu (desktop mac/windows only)",
"IS_TURN_OFF_MARKDOWN": "Turn off markdown parsing for task notes",
"IS_USE_CUSTOM_WINDOW_TITLE_BAR": "Use custom title bar (Windows/Linux only)",
"IS_USE_CUSTOM_WINDOW_TITLE_BAR_HINT": "Requires restart to take effect",
"START_OF_NEXT_DAY": "Start time of the next day",
"START_OF_NEXT_DAY_HINT": "From when (in hour) you want to count the next day has started. Default is midnight which is 0.",
"TASK_NOTES_TPL": "Task description template",
"THEME": "Theme",
"THEME_EXPERIMENTAL": "Theme (experimental)",
"THEME_SELECT_LABEL": "Select Theme",
"TITLE": "Misc Settings"
},
"TASKS": {
"DEFAULT_PROJECT": "Default project to use for tasks if none is specified",
"IS_AUTO_ADD_WORKED_ON_TO_TODAY": "Automatically add worked on tasks to today",
"IS_AUTO_MARK_PARENT_AS_DONE": "Automatically mark parent task as done, when all sub tasks are done",
"IS_CONFIRM_BEFORE_DELETE": "Confirm before deleting tasks",
"IS_MARKDOWN_FORMATTING_IN_NOTES_ENABLED": "Enable Markdown formatting in task notes",
"IS_TRAY_SHOW_CURRENT": "Show current task in the tray / status menu (desktop mac/windows only)",
"NOTES_TEMPLATE": "Task description template",
"TITLE": "Tasks"
},
"POMODORO": {
"BREAK_DURATION": "Duration of short breaks",
"CYCLES_BEFORE_LONGER_BREAK": "Start longer break after X work sessions",
@ -2188,6 +2191,7 @@
"SYNC_EXPORT": "Sync & Export",
"TABS": {
"GENERAL": "General",
"TASKS": "Tasks",
"TIME_TRACKING": "Time & Tracking",
"PRODUCTIVITY": "Productivity",
"PLUGINS": "Plugins",

View file

@ -1636,12 +1636,9 @@
"DARK_MODE_DARK": "Oscuro",
"DARK_MODE_LIGHT": "Claro",
"DARK_MODE_SYSTEM": "Sistema",
"DEFAULT_PROJECT": "Proyecto predeterminado a usar para tareas si no se especifica ninguno",
"DEFAULT_START_PAGE": "Página de inicio predeterminada",
"FIRST_DAY_OF_WEEK": "Primer día de la semana",
"HELP": "<p><strong>¿No ves Notificaciones de Escritorio?</strong> Para windows podrías querer comprobar Sistema > Notificaciones y acciones y comprobar si las notificaciones requeridas han sido habilitadas.</p>",
"IS_AUTO_ADD_WORKED_ON_TO_TODAY": "Añadir automáticamente la etiqueta de hoy a las tareas trabajadas",
"IS_AUTO_MARK_PARENT_AS_DONE": "Marcar la tarea principal como realizada, cuando se completen todas las subtareas",
"IS_CONFIRM_BEFORE_EXIT": "Confirmar antes de salir de la aplicación",
"IS_CONFIRM_BEFORE_EXIT_WITHOUT_FINISH_DAY": "Confirmar antes de salir de la aplicación sin finalizar el día primero",
"IS_DARK_MODE": "Modo Oscuro",
@ -1651,18 +1648,26 @@
"IS_OVERLAY_INDICATOR_ENABLED": "Habilitar ventana de indicador de superposición (escritorio linux/gnome)",
"IS_SHOW_TIP_LONGER": "Mostrar consejo de productividad al inicio de la aplicación un poco más de tiempo",
"IS_TRAY_SHOW_CURRENT_COUNTDOWN": "Mostrar cuenta regresiva actual en la bandeja / menú de estado (solo mac de escritorio)",
"IS_TRAY_SHOW_CURRENT_TASK": "Mostrar la tarea actual en el menú de bandeja / estado (solo escritorio mac/windows)",
"IS_TURN_OFF_MARKDOWN": "Desactivar análisis de markdown para notas de tarea",
"IS_USE_CUSTOM_WINDOW_TITLE_BAR": "Usar barra de título personalizada (solo Windows/Linux)",
"IS_USE_CUSTOM_WINDOW_TITLE_BAR_HINT": "Requiere reinicio para surtir efecto",
"START_OF_NEXT_DAY": "Hora de inicio del siguiente día",
"START_OF_NEXT_DAY_HINT": "desde cuándo (en hora) quieres contar que ha comenzado el siguiente día. predeterminado es medianoche que es 0.",
"TASK_NOTES_TPL": "Plantilla de descripción de tarea",
"THEME": "Tema",
"THEME_EXPERIMENTAL": "Tema (experimental)",
"THEME_SELECT_LABEL": "Seleccionar Tema",
"TITLE": "Ajustes Varios"
},
"TASKS": {
"DEFAULT_PROJECT": "Proyecto predeterminado a usar para tareas si no se especifica ninguno",
"IS_AUTO_ADD_WORKED_ON_TO_TODAY": "Añadir automáticamente la etiqueta de hoy a las tareas trabajadas",
"IS_AUTO_MARK_PARENT_AS_DONE": "Marcar la tarea principal como realizada, cuando se completen todas las subtareas",
"IS_CONFIRM_BEFORE_DELETE": "Confirmar antes de eliminar tareas",
"IS_MARKDOWN_FORMATTING_IN_NOTES_ENABLED": "Habilitar formato Markdown en notas de tareas",
"IS_TRAY_SHOW_CURRENT": "Mostrar tarea actual en la bandeja / menú de estado (solo escritorio mac/windows)",
"NOTES_TEMPLATE": "Plantilla de descripción de tarea",
"TITLE": "Tareas"
},
"POMODORO": {
"BREAK_DURATION": "Duración de descansos cortos",
"CYCLES_BEFORE_LONGER_BREAK": "Iniciar descanso más largo después de X sesiones de trabajo",

View file

@ -1569,12 +1569,9 @@
"DARK_MODE_DARK": "تاریک",
"DARK_MODE_LIGHT": "نور",
"DARK_MODE_SYSTEM": "سیستم",
"DEFAULT_PROJECT": "پروژه پیش فرض برای استفاده در کارها در صورت مشخص نبودن هیچ کدام",
"FIRST_DAY_OF_WEEK": "روز اول هفته",
"HELP": "<p><strong>Not seeing Desktop Notifications?</strong> For windows you might want to check System > Notifications & actions and check if the required notifications have been enabled.</p>",
"IS_AUTO_ADD_WORKED_ON_TO_TODAY": "برچسب امروز به صورت خودکار به کار بر روی وظایف اضافه کنید",
"IS_AUTO_MARK_PARENT_AS_DONE": "Mark parent task as done, when all sub tasks are done",
"IS_CONFIRM_BEFORE_EXIT": "Confirm before exiting the app",
"HELP": "<p><strong>آیا اعلان‌های دسکتاپ را نمی‌بینید؟</strong> برای ویندوز، ممکن است بخواهید به System > Notifications & actions بروید و بررسی کنید که آیا اعلان‌های مورد نیاز فعال شده‌اند یا خیر.</p>",
"IS_CONFIRM_BEFORE_EXIT": "قبل از خروج از برنامه تأیید کنید",
"IS_CONFIRM_BEFORE_EXIT_WITHOUT_FINISH_DAY": "قبل از خروج از برنامه بدون اتمام روز اول آن را تأیید کنید",
"IS_DARK_MODE": "حالت تاریک",
"IS_DISABLE_ANIMATIONS": "غیرفعال کردن تمام پویانمایی‌ها",
@ -1583,8 +1580,6 @@
"IS_OVERLAY_INDICATOR_ENABLED": "فعال کردن پنجره شاخص پوششی (لینوکس دسکتاپ/گنوم)",
"IS_SHOW_TIP_LONGER": "نمایش نکته بهره وری در برنامه کمی طولانی تر شروع کنید",
"IS_TRAY_SHOW_CURRENT_COUNTDOWN": "نمایش شمارش معکوس فعلی در منوی سینی / وضعیت (فقط مک دسکتاپ)",
"IS_TRAY_SHOW_CURRENT_TASK": "نمایش وظیفه فعلی در منوی سینی / وضعیت (فقط دسک تاپ)",
"IS_TURN_OFF_MARKDOWN": "Turn off markdown parsing for notes",
"START_OF_NEXT_DAY": "ساعت شروع روز بعد",
"START_OF_NEXT_DAY_HINT": "از زمانی که (به ساعت) می خواهید روز بعد را بشمارید شروع شده است. پیش فرض نیمه شب است که 0 است.",
"TASK_NOTES_TPL": "الگوی شرح کار",
@ -1593,6 +1588,16 @@
"THEME_SELECT_LABEL": "انتخاب تم",
"TITLE": "Misc Settings"
},
"TASKS": {
"DEFAULT_PROJECT": "پروژه پیش فرض برای کارهای جدید در صورت عدم تعیین پروژه دیگر",
"IS_AUTO_ADD_WORKED_ON_TO_TODAY": "اضافه کردن خودکار کارهای انجام شده به امروز",
"IS_AUTO_MARK_PARENT_AS_DONE": "علامت گذاری خودکار کار اصلی به عنوان انجام شده، زمانی که تمام زیر کارها انجام شده باشند",
"IS_CONFIRM_BEFORE_DELETE": "تأیید قبل از حذف کارها",
"IS_MARKDOWN_FORMATTING_IN_NOTES_ENABLED": "فعال کردن فرمت بندی Markdown در یادداشت های کار",
"IS_TRAY_SHOW_CURRENT": "نمایش کار فعلی در منوی سینی / وضعیت (فقط دسک تاپ مک/ویندوز)",
"NOTES_TEMPLATE": "الگوی توضیحات کار",
"TITLE": "کارها"
},
"POMODORO": {
"BREAK_DURATION": "Duration of short breaks",
"CYCLES_BEFORE_LONGER_BREAK": "Start longer break after X work sessions",

View file

@ -1638,12 +1638,9 @@
"DARK_MODE_DARK": "Tumma",
"DARK_MODE_LIGHT": "Vaalea",
"DARK_MODE_SYSTEM": "Järjestelmä",
"DEFAULT_PROJECT": "Oletusprojekti tehtäville, jos mitään ei ole määritetty",
"DEFAULT_START_PAGE": "Oletus aloitussivu",
"FIRST_DAY_OF_WEEK": "Viikon ensimmäinen päivä",
"HELP": "<p><strong>Etkö näe työpöytäilmoituksia?</strong> Windowsissa saatat haluta tarkistaa Järjestelmä > Ilmoitukset ja toiminnot ja tarkistaa, onko tarvittavat ilmoitukset otettu käyttöön.</p>",
"IS_AUTO_ADD_WORKED_ON_TO_TODAY": "Lisää automaattisesti tänään-tunniste työskennellyille tehtäville",
"IS_AUTO_MARK_PARENT_AS_DONE": "Merkitse vanhempi tehtävä valmiiksi, kun kaikki alitehtävät ovat valmiita",
"IS_CONFIRM_BEFORE_EXIT": "Vahvista ennen sovelluksen poistumista",
"IS_CONFIRM_BEFORE_EXIT_WITHOUT_FINISH_DAY": "Vahvista ennen sovelluksen poistumista tallentamatta päivää ensin",
"IS_DARK_MODE": "Tumma tila",
@ -1653,18 +1650,25 @@
"IS_OVERLAY_INDICATOR_ENABLED": "Ota päällekkäisyysindikaattori-ikkuna käyttöön (työpöytä linux/gnome)",
"IS_SHOW_TIP_LONGER": "Näytä tuottavuusvinkki sovelluksen käynnistyksessä hieman pidempään",
"IS_TRAY_SHOW_CURRENT_COUNTDOWN": "Näytä nykyinen laskuri tehtäväpalkissa / tilavalikossa (vain työpöytä mac)",
"IS_TRAY_SHOW_CURRENT_TASK": "Näytä nykyinen tehtävä tehtäväpalkissa / tilavalikossa (vain työpöytä mac/windows)",
"IS_TURN_OFF_MARKDOWN": "Poista markdown-jäsennys käytöstä tehtävien muistiinpanoissa",
"IS_USE_CUSTOM_WINDOW_TITLE_BAR": "Käytä mukautettua otsikkopalkkia (vain Windows/Linux)",
"IS_USE_CUSTOM_WINDOW_TITLE_BAR_HINT": "Vaatii uudelleenkäynnistyksen voimaan tullakseen",
"START_OF_NEXT_DAY": "Seuraavan päivän alkamisaika",
"START_OF_NEXT_DAY_HINT": "mistä tunnista alkaen haluat laskea seuraavan päivän alkaneen. oletus on keskiyö, joka on 0.",
"TASK_NOTES_TPL": "Tehtävän kuvauksen mallipohja",
"THEME": "Teema",
"THEME_EXPERIMENTAL": "Teema (kokeellinen)",
"THEME_SELECT_LABEL": "Valitse teema",
"TITLE": "Sekalaista"
},
"TASKS": {
"DEFAULT_PROJECT": "Oletusprojekti tehtäville, jos mitään ei ole määritetty",
"IS_AUTO_ADD_WORKED_ON_TO_TODAY": "Lisää automaattisesti tänään-tunniste työskennellyille tehtäville",
"IS_AUTO_MARK_PARENT_AS_DONE": "Merkitse vanhempi tehtävä valmiiksi, kun kaikki alitehtävät ovat valmiita",
"IS_CONFIRM_BEFORE_DELETE": "Vahvista ennen tehtävien poistamista",
"IS_MARKDOWN_FORMATTING_IN_NOTES_ENABLED": "Ota käyttöön Markdown-muotoilu tehtävämuistiinpanoissa",
"IS_TRAY_SHOW_CURRENT": "Näytä nykyinen tehtävä tehtäväpalkissa / tilavalikossa (vain työpöytä mac/windows)",
"NOTES_TEMPLATE": "Tehtävän kuvauksen mallipohja",
"TITLE": "Tehtävät"
},
"POMODORO": {
"BREAK_DURATION": "Lyhyiden taukojen kesto",
"CYCLES_BEFORE_LONGER_BREAK": "Aloita pidempi tauko X työistunnon jälkeen",

View file

@ -1638,12 +1638,9 @@
"DARK_MODE_DARK": "Sombre",
"DARK_MODE_LIGHT": "Lumière",
"DARK_MODE_SYSTEM": "Système",
"DEFAULT_PROJECT": "Projet par défaut à utiliser pour les tâches si aucun n'est spécifié",
"DEFAULT_START_PAGE": "Page de démarrage par défaut",
"FIRST_DAY_OF_WEEK": "Premier jour de la semaine",
"HELP": "<p><strong>Vous ne voyez pas les notifications de bureau ?</strong> Pour Windows, vous pouvez vérifier Système> Notifications et actions et vérifier si les notifications requises ont été activées.</p>",
"IS_AUTO_ADD_WORKED_ON_TO_TODAY": "Ajouter automatiquement la balise d'aujourd'hui aux tâches travaillées",
"IS_AUTO_MARK_PARENT_AS_DONE": "Marquer la tâche parent comme terminée lorsque toutes les sous-tâches sont terminées",
"IS_CONFIRM_BEFORE_EXIT": "Confirmez avant de quitter l'application",
"IS_CONFIRM_BEFORE_EXIT_WITHOUT_FINISH_DAY": "Confirmez avant de quitter l'application sans terminer le jour en premier",
"IS_DARK_MODE": "Mode sombre",
@ -1653,18 +1650,25 @@
"IS_OVERLAY_INDICATOR_ENABLED": "Activer la fenêtre d'indicateur en superposition (Linux/desktop GNOME)",
"IS_SHOW_TIP_LONGER": "Afficher le conseil de productivité au démarrage de l'application un peu plus longtemps",
"IS_TRAY_SHOW_CURRENT_COUNTDOWN": "Afficher le compte à rebours actuel dans la barre d'état / menu (mac de bureau uniquement)",
"IS_TRAY_SHOW_CURRENT_TASK": "Afficher la tâche en cours dans la barre d'état / menu d'état (bureau uniquement)",
"IS_TURN_OFF_MARKDOWN": "Désactiver l'analyse de démarquage pour les notes",
"IS_USE_CUSTOM_WINDOW_TITLE_BAR": "Utiliser une barre de titre personnalisée (Windows\\/Linux uniquement)",
"IS_USE_CUSTOM_WINDOW_TITLE_BAR_HINT": "Nécessite un redémarrage pour prendre effet",
"START_OF_NEXT_DAY": "Heure de début le jour suivant",
"START_OF_NEXT_DAY_HINT": "À partir de quelle heure souhaitez-vous considérer que le jour suivant commence. Par défaut, minuit correspond à 0 heure.",
"TASK_NOTES_TPL": "Modèle de description de tâche",
"THEME": "Theme",
"THEME": "Thème",
"THEME_EXPERIMENTAL": "Thème (expérimental)",
"THEME_SELECT_LABEL": "Sélectionner le thème",
"TITLE": "Réglages divers"
},
"TASKS": {
"DEFAULT_PROJECT": "Projet par défaut à utiliser pour les tâches si aucun n'est spécifié",
"IS_AUTO_ADD_WORKED_ON_TO_TODAY": "Ajouter automatiquement les tâches travaillées à aujourd'hui",
"IS_AUTO_MARK_PARENT_AS_DONE": "Marquer automatiquement la tâche parente comme terminée lorsque toutes les sous-tâches sont terminées",
"IS_CONFIRM_BEFORE_DELETE": "Confirmer avant de supprimer des tâches",
"IS_MARKDOWN_FORMATTING_IN_NOTES_ENABLED": "Activer le formatage Markdown dans les notes de tâche",
"IS_TRAY_SHOW_CURRENT": "Afficher la tâche en cours dans la barre d'état / menu d'état (bureau uniquement)",
"NOTES_TEMPLATE": "Modèle de description de tâche",
"TITLE": "Tâches"
},
"POMODORO": {
"BREAK_DURATION": "Durée des pauses courtes",
"CYCLES_BEFORE_LONGER_BREAK": "Commencez une pause plus longue après X sessions de travail",

View file

@ -1304,11 +1304,8 @@
"ZH_TW": "中文(繁體)"
},
"MISC": {
"DEFAULT_PROJECT": "Standardno korišteni projekt za zadatke ako nijedan nije određen",
"FIRST_DAY_OF_WEEK": "Prvi dan u tjednu",
"HELP": "<p><strong>Ne vidiš obavijesti na radnoj površini?</strong> Za Windows provjeri Sustav > Obavijesti i radnje te provjeri jesu li potrebne obavijesti aktivirane.</p>",
"IS_AUTO_ADD_WORKED_ON_TO_TODAY": "Automatski dodaj oznaku danas zadacima koje si obrađivao/la",
"IS_AUTO_MARK_PARENT_AS_DONE": "Označi nadređeni zadatak kao obavljen kad se obave svi podzadaci",
"IS_CONFIRM_BEFORE_EXIT": "Potvrdi prije zatvaranja programa",
"IS_CONFIRM_BEFORE_EXIT_WITHOUT_FINISH_DAY": "Potvrdi prije zatvaranja programa bez prethodnog završavanja dana",
"IS_DARK_MODE": "Tamni modus",
@ -1317,13 +1314,20 @@
"IS_MINIMIZE_TO_TRAY": "Sklopi u programsku traku (samo za desktop računala)",
"IS_SHOW_TIP_LONGER": "Montre konsèy pwodiktivite sou app kòmanse yon ti tan ankò",
"IS_TRAY_SHOW_CURRENT_COUNTDOWN": "Montre dekont aktyèl la nan meni an plato / estati (Desktop mac sèlman)",
"IS_TRAY_SHOW_CURRENT_TASK": "Prikaži trenutačni zadatak u donjoj traci sustava / u izborniku stanja (samo mac/windows desktop računala)",
"IS_TURN_OFF_MARKDOWN": "Isključi markdown obradu za bilješke",
"START_OF_NEXT_DAY": "Vrijeme početka sljedećeg dana",
"START_OF_NEXT_DAY_HINT": "U koje vrijeme (sat) želiš brojiti početak sljedećeg dana. Standardno je postavljena ponoć, što je 0.",
"TASK_NOTES_TPL": "Predložak za opis zadatka",
"TITLE": "Razne postavke"
},
"TASKS": {
"DEFAULT_PROJECT": "Standardni projekt za nove zadatke",
"IS_AUTO_ADD_WORKED_ON_TO_TODAY": "Automatski dodaj oznaku danas zadacima koje si obrađivao/la",
"IS_AUTO_MARK_PARENT_AS_DONE": "Automatski označi nadređeni zadatak kao obavljen kada su sve podzadatke obavljene",
"IS_CONFIRM_BEFORE_DELETE": "Potvrdi prije brisanja zadataka",
"IS_MARKDOWN_FORMATTING_IN_NOTES_ENABLED": "Omogući Markdown formatiranje u bilješkama zadataka",
"IS_TRAY_SHOW_CURRENT": "Prikaži trenutni zadatak u donjoj traci sustava / izborniku stanja (samo desktop mac/windows računala)",
"NOTES_TEMPLATE": "Predložak za opis zadatka",
"TITLE": "Zadaci"
},
"POMODORO": {
"BREAK_DURATION": "Trajanje kratkih pauza",
"CYCLES_BEFORE_LONGER_BREAK": "Započni dužu pauzu nakon određenog broja radnih sesija",

View file

@ -1638,12 +1638,9 @@
"DARK_MODE_DARK": "Gelap",
"DARK_MODE_LIGHT": "Terang",
"DARK_MODE_SYSTEM": "Sistem",
"DEFAULT_PROJECT": "Proyek default untuk digunakan untuk tugas jika tidak ada yang ditentukan",
"DEFAULT_START_PAGE": "Halaman mulai default",
"FIRST_DAY_OF_WEEK": "Hari pertama dalam seminggu",
"HELP": "<p><strong>Tidak melihat Pemberitahuan Desktop?</strong> Untuk windows, Anda mungkin ingin memeriksa Sistem > Pemberitahuan & tindakan dan memeriksa apakah pemberitahuan yang diperlukan telah diaktifkan.</p>",
"IS_AUTO_ADD_WORKED_ON_TO_TODAY": "Tambahkan tag hari ini secara otomatis untuk mengerjakan tugas",
"IS_AUTO_MARK_PARENT_AS_DONE": "Tandai tugas induk sebagai selesai, ketika semua sub tugas selesai",
"IS_CONFIRM_BEFORE_EXIT": "Konfirmasi sebelum keluar dari aplikasi",
"IS_CONFIRM_BEFORE_EXIT_WITHOUT_FINISH_DAY": "Konfirmasi sebelum keluar dari aplikasi tanpa menyelesaikan hari terlebih dahulu",
"IS_DARK_MODE": "Mode Gelap",
@ -1653,18 +1650,25 @@
"IS_OVERLAY_INDICATOR_ENABLED": "Aktifkan jendela indikator overlay (desktop linux/gnome)",
"IS_SHOW_TIP_LONGER": "Tampilkan tip produktivitas saat aplikasi mulai sedikit lebih lama",
"IS_TRAY_SHOW_CURRENT_COUNTDOWN": "Tampilkan hitung mundur saat ini di tray / menu status (desktop mac saja)",
"IS_TRAY_SHOW_CURRENT_TASK": "Tampilkan tugas saat ini di menu baki / status (khusus desktop mac/windows)",
"IS_TURN_OFF_MARKDOWN": "Matikan penguraian penurunan harga untuk catatan tugas",
"IS_USE_CUSTOM_WINDOW_TITLE_BAR": "Gunakan bilah judul khusus (hanya Windows\\/Linux)",
"IS_USE_CUSTOM_WINDOW_TITLE_BAR_HINT": "Memerlukan restart untuk berlaku",
"START_OF_NEXT_DAY": "Waktu mulai hari berikutnya",
"START_OF_NEXT_DAY_HINT": "dari kapan (dalam jam) Anda ingin menghitung hari berikutnya telah dimulai. defaultnya adalah tengah malam yaitu 0.",
"TASK_NOTES_TPL": "Templat deskripsi tugas",
"THEME": "Tema",
"THEME_EXPERIMENTAL": "Tema (eksperimental)",
"THEME_SELECT_LABEL": "Pilih Tema",
"TITLE": "Pengaturan Lain-lain"
},
"TASKS": {
"DEFAULT_PROJECT": "Proyek default untuk digunakan untuk tugas jika tidak ada yang ditentukan",
"IS_AUTO_ADD_WORKED_ON_TO_TODAY": "Secara otomatis tambahkan tugas yang dikerjakan ke hari ini",
"IS_AUTO_MARK_PARENT_AS_DONE": "Secara otomatis tandai tugas induk sebagai selesai, ketika semua sub tugas selesai",
"IS_CONFIRM_BEFORE_DELETE": "Konfirmasi sebelum menghapus tugas",
"IS_MARKDOWN_FORMATTING_IN_NOTES_ENABLED": "Aktifkan format Markdown di catatan tugas",
"IS_TRAY_SHOW_CURRENT": "Tampilkan tugas saat ini di baki / menu status (khusus desktop mac/windows)",
"NOTES_TEMPLATE": "Templat deskripsi tugas",
"TITLE": "Tugas"
},
"POMODORO": {
"BREAK_DURATION": "Durasi istirahat pendek",
"CYCLES_BEFORE_LONGER_BREAK": "Mulai istirahat lebih lama setelah X sesi kerja",

View file

@ -1638,12 +1638,9 @@
"DARK_MODE_DARK": "Scuro",
"DARK_MODE_LIGHT": "Chiaro",
"DARK_MODE_SYSTEM": "Sistema",
"DEFAULT_PROJECT": "Progetto predefinito da utilizzare per le attività se non ne viene specificato nessuno",
"DEFAULT_START_PAGE": "Pagina iniziale predefinita",
"FIRST_DAY_OF_WEEK": "Primo giorno della settimana",
"HELP": "<p><strong>Non vedi le notifiche desktop?</strong>Per Windows controlla Sistema > Notifiche & azioni e controlla se le notifiche richieste sono abilitate.</p>",
"IS_AUTO_ADD_WORKED_ON_TO_TODAY": "Aggiungi automaticamente il tag oggi alle attività lavorate",
"IS_AUTO_MARK_PARENT_AS_DONE": "Segna l'attività padre come completata, quando tutti le sotto-attività sono completate",
"IS_CONFIRM_BEFORE_EXIT": "Conferma prima di uscire dall'applicazione",
"IS_CONFIRM_BEFORE_EXIT_WITHOUT_FINISH_DAY": "Conferma prima di uscire dall'app senza prima terminare il giorno",
"IS_DARK_MODE": "Tema scuro",
@ -1653,18 +1650,25 @@
"IS_OVERLAY_INDICATOR_ENABLED": "Abilita finestra indicatore overlay (desktop linux/gnome)",
"IS_SHOW_TIP_LONGER": "Mostra consigli di produttività all'avvio dell'applicazione un po' più a lungo",
"IS_TRAY_SHOW_CURRENT_COUNTDOWN": "Mostra countdown corrente nella barra delle applicazioni / menu di stato (solo desktop Mac)",
"IS_TRAY_SHOW_CURRENT_TASK": "Mostra l'attività corrente nella barra delle applicazioni / menu di stato (solo desktop)",
"IS_TURN_OFF_MARKDOWN": "Disattiva markdown per le note",
"IS_USE_CUSTOM_WINDOW_TITLE_BAR": "Usa barra del titolo personalizzata (solo Windows/Linux)",
"IS_USE_CUSTOM_WINDOW_TITLE_BAR_HINT": "Richiede il riavvio per avere effetto",
"START_OF_NEXT_DAY": "Ora di inizio del prossimo giorno",
"START_OF_NEXT_DAY_HINT": "da quando (in ora) vuoi contare che il prossimo giorno è iniziato. Il predefinito è mezzanotte cioè 0.",
"TASK_NOTES_TPL": "Modello di descrizione dell'attività",
"THEME": "Tema",
"THEME_EXPERIMENTAL": "Tema (sperimentale)",
"THEME_SELECT_LABEL": "Seleziona tema",
"TITLE": "Impostazioni varie"
},
"TASKS": {
"DEFAULT_PROJECT": "Progetto predefinito da utilizzare per le attività se non ne viene specificato nessuno",
"IS_AUTO_ADD_WORKED_ON_TO_TODAY": "Aggiungi automaticamente il tag oggi alle attività lavorate",
"IS_AUTO_MARK_PARENT_AS_DONE": "Segna l'attività padre come completata, quando tutte le sotto-attività sono completate",
"IS_CONFIRM_BEFORE_DELETE": "Conferma prima di eliminare le attività",
"IS_MARKDOWN_FORMATTING_IN_NOTES_ENABLED": "Abilita la formattazione Markdown nelle note delle attività",
"IS_TRAY_SHOW_CURRENT": "Mostra l'attività corrente nel menu a discesa / menu di stato (solo desktop mac/windows)",
"NOTES_TEMPLATE": "Modello di descrizione dell'attività",
"TITLE": "Attività"
},
"POMODORO": {
"BREAK_DURATION": "Durata delle pause brevi",
"CYCLES_BEFORE_LONGER_BREAK": "Inizia pause più lunghe dopo X sessioni di lavoro",

View file

@ -1569,11 +1569,8 @@
"DARK_MODE_DARK": "暗い",
"DARK_MODE_LIGHT": "光",
"DARK_MODE_SYSTEM": "制",
"DEFAULT_PROJECT": "何も指定されていない場合にタスクに使用するデフォルトのプロジェクト",
"FIRST_DAY_OF_WEEK": "週の最初の日",
"HELP": "<p><strong>デスクトップ通知が表示されませんか?</strong> Windowsの場合は、[システム]> [通知とアクション]を確認し、必要な通知が有効になっているかどうかを確認します。</p>",
"IS_AUTO_ADD_WORKED_ON_TO_TODAY": "タスクに取り組むために今日タグを自動的に追加する",
"IS_AUTO_MARK_PARENT_AS_DONE": "すべてのサブタスクが完了したら、親タスクを完了としてマークする",
"IS_CONFIRM_BEFORE_EXIT": "アプリを終了する前に確認する",
"IS_CONFIRM_BEFORE_EXIT_WITHOUT_FINISH_DAY": "最初に1日を終えずにアプリを終了する前に確認してください",
"IS_DARK_MODE": "ダークモード",
@ -1583,16 +1580,23 @@
"IS_OVERLAY_INDICATOR_ENABLED": "オーバーレイインジケーターウィンドウを有効にする(デスクトップLinux/gnome)",
"IS_SHOW_TIP_LONGER": "アプリの起動時に生産性のヒントを少し長く表示する",
"IS_TRAY_SHOW_CURRENT_COUNTDOWN": "トレイ/ステータスメニューに現在のカウントダウンを表示する(デスクトップMacのみ)",
"IS_TRAY_SHOW_CURRENT_TASK": "トレイ/ステータスメニューに現在のタスクを表示する(デスクトップのみ)",
"IS_TURN_OFF_MARKDOWN": "ノートのマークダウン解析をオフにする",
"START_OF_NEXT_DAY": "翌日の開始時間",
"START_OF_NEXT_DAY_HINT": "いつ時間単位から翌日が始まったかをカウントする。デフォルトは0時で、0となる。",
"TASK_NOTES_TPL": "タスク記述テンプレート",
"THEME": "テーマ",
"THEME_EXPERIMENTAL": "テーマ(実験的)",
"THEME_SELECT_LABEL": "セレクト・テーマ",
"TITLE": "その他の設定"
},
"TASKS": {
"DEFAULT_PROJECT": "デフォルトプロジェクト",
"IS_AUTO_ADD_WORKED_ON_TO_TODAY": "自動的に今日の作業中のタスクを追加する",
"IS_AUTO_MARK_PARENT_AS_DONE": "すべてのサブタスクが完了したときに親タスクを自動的に完了としてマークする",
"IS_CONFIRM_BEFORE_DELETE": "タスクを削除する前に確認する",
"IS_MARKDOWN_FORMATTING_IN_NOTES_ENABLED": "タスクートでMarkdown形式を有効にする",
"IS_TRAY_SHOW_CURRENT": "トレイ/ステータスメニューに現在のタスクを表示する(デスクトップのみ)",
"NOTES_TEMPLATE": "タスクノートテンプレート",
"TITLE": "タスク"
},
"POMODORO": {
"BREAK_DURATION": "短い休憩時間",
"CYCLES_BEFORE_LONGER_BREAK": "長い休憩までの作業セッションの回数",

View file

@ -1325,11 +1325,8 @@
"MISC": {
"DARK_MODE": "다크 모드",
"DARK_MODE_DARK": "어둠",
"DEFAULT_PROJECT": "지정되지 않은 경우 작업에 사용할 기본 프로젝트",
"FIRST_DAY_OF_WEEK": "주의 첫 요일",
"HELP": "<p><strong>데스크톱 알림이 표시되지 않습니까?</strong> 창의 경우 시스템 > 알림 및 작업을 확인하고 필요한 알림이 활성화되었는지 확인할 수 있습니다.</p>",
"IS_AUTO_ADD_WORKED_ON_TO_TODAY": "작업에 사용할 오늘 태그를 자동으로 추가",
"IS_AUTO_MARK_PARENT_AS_DONE": "모든 하위 작업이 완료되면 상위 작업을 완료로 표시합니다.",
"IS_CONFIRM_BEFORE_EXIT": "앱을 종료하기 전에 확인",
"IS_CONFIRM_BEFORE_EXIT_WITHOUT_FINISH_DAY": "하루를 먼저 끝내지 않고 앱을 종료하기 전에 확인",
"IS_DARK_MODE": "다크 모드",
@ -1338,13 +1335,20 @@
"IS_MINIMIZE_TO_TRAY": "트레이로 최소화 (데스크탑만 해당)",
"IS_SHOW_TIP_LONGER": "앱 시작 시 생산성 팁 표시좀 더 오래",
"IS_TRAY_SHOW_CURRENT_COUNTDOWN": "트레이/상태 메뉴에 현재 카운트다운 표시(데스크톱 Mac만 해당)",
"IS_TRAY_SHOW_CURRENT_TASK": "트레이 / 상태 메뉴에 현재 작업 표시 (데스크톱 전용)",
"IS_TURN_OFF_MARKDOWN": "노트에 대한 마크 다운 파싱 끄기",
"START_OF_NEXT_DAY": "다음 날 시작 시간",
"START_OF_NEXT_DAY_HINT": "다음 날을 계산하려는 시점(시간 단위)부터 시작됩니다. 기본값은 0인 자정입니다.",
"TASK_NOTES_TPL": "작업 설명 템플릿",
"TITLE": "기타 설정"
},
"TASKS": {
"DEFAULT_PROJECT": "지정되지 않은 경우 작업에 사용할 기본 프로젝트",
"IS_AUTO_ADD_WORKED_ON_TO_TODAY": "자동으로 오늘 작업 중인 태스크를 추가합니다.",
"IS_AUTO_MARK_PARENT_AS_DONE": "모든 하위 작업이 완료되면 부모 작업을 완료로 표시합니다.",
"IS_CONFIRM_BEFORE_DELETE": "작업 삭제 전에 확인합니다.",
"IS_MARKDOWN_FORMATTING_IN_NOTES_ENABLED": "작업 노트에서 Markdown 형식을 사용하도록 설정합니다.",
"IS_TRAY_SHOW_CURRENT": "트레이/상태 메뉴에 현재 작업을 표시합니다(데스크톱 Mac/Windows 전용).",
"NOTES_TEMPLATE": "작업 설명 템플릿",
"TITLE": "작업"
},
"POMODORO": {
"BREAK_DURATION": "짧은 휴식 시간",
"CYCLES_BEFORE_LONGER_BREAK": "X 작업 세션 후에 더 긴 휴식을 시작하십시오.",

View file

@ -1638,12 +1638,9 @@
"DARK_MODE_DARK": "Mørk",
"DARK_MODE_LIGHT": "Lys",
"DARK_MODE_SYSTEM": "System",
"DEFAULT_PROJECT": "Standard prosjekt som skal brukes til oppgaver hvis ingen er spesifisert",
"DEFAULT_START_PAGE": "Standard startside",
"FIRST_DAY_OF_WEEK": "Første ukedag",
"HELP": "<p><strong>Ser du ikke skrivebordsvarsler?</strong> For Windows vil du kanskje sjekke System&gt; Varsler og handlinger og sjekke om de nødvendige varslene er aktivert.</p>",
"IS_AUTO_ADD_WORKED_ON_TO_TODAY": "Legg til dagens tag automatisk for å jobbe med oppgaver",
"IS_AUTO_MARK_PARENT_AS_DONE": "Merk foreldreoppgaven som ferdig når alle underoppgaver er utført",
"IS_CONFIRM_BEFORE_EXIT": "Bekreft før du avslutter appen",
"IS_CONFIRM_BEFORE_EXIT_WITHOUT_FINISH_DAY": "Bekreft før du avslutter appen uten å avslutte dagen først",
"IS_DARK_MODE": "Mørk modus",
@ -1653,18 +1650,25 @@
"IS_OVERLAY_INDICATOR_ENABLED": "Aktiver overleggindikatorvindu (desktop linux/gnome)",
"IS_SHOW_TIP_LONGER": "Vis produktivitetstips på appstart litt lenger",
"IS_TRAY_SHOW_CURRENT_COUNTDOWN": "Vis nåværende nedtelling i systemstatusmenyen (kun desktop mac)",
"IS_TRAY_SHOW_CURRENT_TASK": "Vis gjeldende oppgave i skuffen / statusmenyen (kun desktop mac/windows)",
"IS_TURN_OFF_MARKDOWN": "Slå av Markdown-parsering for notater",
"IS_USE_CUSTOM_WINDOW_TITLE_BAR": "Bruk egendefinert tittellinje (kun Windows/Linux)",
"IS_USE_CUSTOM_WINDOW_TITLE_BAR_HINT": "Krever omstart for å tre i kraft",
"START_OF_NEXT_DAY": "Starttidspunkt neste dag",
"START_OF_NEXT_DAY_HINT": "fra når (i timer) du ønsker å telle neste dag har startet. standard er midnatt, som er 0.",
"TASK_NOTES_TPL": "Mal for oppgavebeskrivelse",
"THEME": "Tema",
"THEME_EXPERIMENTAL": "Tema (eksperimentell)",
"THEME_SELECT_LABEL": "Velg tema",
"TITLE": "Diverse innstillinger"
},
"TASKS": {
"DEFAULT_PROJECT": "Standard prosjekt som skal brukes til oppgaver hvis ingen er spesifisert",
"IS_AUTO_ADD_WORKED_ON_TO_TODAY": "Legg til dagens tag automatisk for å jobbe med oppgaver",
"IS_AUTO_MARK_PARENT_AS_DONE": "Merk foreldreoppgaven som ferdig når alle underoppgaver er utført",
"IS_CONFIRM_BEFORE_DELETE": "Bekreft før du sletter oppgaver",
"IS_MARKDOWN_FORMATTING_IN_NOTES_ENABLED": "Aktiver Markdown-formatering i oppgavenotater",
"IS_TRAY_SHOW_CURRENT": "Vis gjeldende oppgave i skuffen / statusmenyen (kun desktop mac/windows)",
"NOTES_TEMPLATE": "Mal for oppgavebeskrivelse",
"TITLE": "Oppgaver"
},
"POMODORO": {
"BREAK_DURATION": "Varighet av korte pauser",
"CYCLES_BEFORE_LONGER_BREAK": "Start lengre pause etter X-arbeidsøkter",

View file

@ -1638,12 +1638,9 @@
"DARK_MODE_DARK": "Donker",
"DARK_MODE_LIGHT": "Licht",
"DARK_MODE_SYSTEM": "SYSTEEM",
"DEFAULT_PROJECT": "Standaardproject om te gebruiken voor taken als er geen is opgegeven",
"DEFAULT_START_PAGE": "Standaard startpagina",
"FIRST_DAY_OF_WEEK": "Eerste dag van de week",
"HELP": "<p><strong>Ziet u geen bureaubladmeldingen?</strong> Voor vensters wilt u misschien Systeem> Meldingen en acties controleren en controleren of de vereiste meldingen zijn ingeschakeld.</p>",
"IS_AUTO_ADD_WORKED_ON_TO_TODAY": "Voeg automatisch de tag voor vandaag toe aan taken",
"IS_AUTO_MARK_PARENT_AS_DONE": "Markeer de bovenliggende taak als voltooid wanneer alle subtaken zijn voltooid",
"IS_CONFIRM_BEFORE_EXIT": "Bevestig voordat u de app afsluit",
"IS_CONFIRM_BEFORE_EXIT_WITHOUT_FINISH_DAY": "Bevestig voordat u de app verlaat zonder eerst de dag af te sluiten",
"IS_DARK_MODE": "Donkere modus",
@ -1653,18 +1650,25 @@
"IS_OVERLAY_INDICATOR_ENABLED": "Overlay indicatorvenster inschakelen (desktop linux/gnome)",
"IS_SHOW_TIP_LONGER": "Toon productiviteitstip op app-start iets langer",
"IS_TRAY_SHOW_CURRENT_COUNTDOWN": "Toon huidige countdown in de tray / statusmenu (alleen desktop mac)",
"IS_TRAY_SHOW_CURRENT_TASK": "Toon huidige taak in het systeemvak / statusmenu (alleen desktop)",
"IS_TURN_OFF_MARKDOWN": "Schakel markdown-parsering voor notities uit",
"IS_USE_CUSTOM_WINDOW_TITLE_BAR": "Gebruik aangepaste titelbalk (alleen Windows/Linux)",
"IS_USE_CUSTOM_WINDOW_TITLE_BAR_HINT": "Herstart vereist om effect te hebben",
"START_OF_NEXT_DAY": "Starttijd van de volgende dag",
"START_OF_NEXT_DAY_HINT": "vanaf wanneer (in uur) je wilt tellen dat de volgende dag is begonnen. standaard is middernacht wat 0 is.",
"TASK_NOTES_TPL": "Taak omschrijving template",
"THEME": "Thema",
"THEME_EXPERIMENTAL": "Thema (experimenteel)",
"THEME_SELECT_LABEL": "Selecteer thema",
"TITLE": "Diverse instellingen"
},
"TASKS": {
"DEFAULT_PROJECT": "Standaardproject om te gebruiken voor taken als er geen is opgegeven",
"IS_AUTO_ADD_WORKED_ON_TO_TODAY": "Voeg automatisch de tag voor vandaag toe aan taken",
"IS_AUTO_MARK_PARENT_AS_DONE": "Markeer de bovenliggende taak als voltooid wanneer alle subtaken zijn voltooid",
"IS_CONFIRM_BEFORE_DELETE": "Bevestig voordat u taken verwijdert",
"IS_MARKDOWN_FORMATTING_IN_NOTES_ENABLED": "Markdown-opmaak inschakelen in taaknotities",
"IS_TRAY_SHOW_CURRENT": "Toon huidige taak in het systeemvak / statusmenu (alleen desktop mac/windows)",
"NOTES_TEMPLATE": "Sjabloon voor taaknotities bij het maken van nieuwe taken",
"TITLE": "Taken"
},
"POMODORO": {
"BREAK_DURATION": "Duur van korte pauzes",
"CYCLES_BEFORE_LONGER_BREAK": "Start een langere pauze na X werksessies",

View file

@ -1685,12 +1685,9 @@
"DARK_MODE_DARK": "Ciemny",
"DARK_MODE_LIGHT": "Światło",
"DARK_MODE_SYSTEM": "System",
"DEFAULT_PROJECT": "Domyślny projekt dla niezdefiniowanych zadań",
"DEFAULT_START_PAGE": "Domyślna strona startowa",
"FIRST_DAY_OF_WEEK": "Pierwszy dzień tygodnia",
"HELP": "<p><strong>Nie widzisz powidomień na pulpicie?</strong> Dla windowsa sprawdź czy w Ustawienia > System > Powiadomienia i akcje zaznaczone zostały odpowiednie opcje.</p>",
"IS_AUTO_ADD_WORKED_ON_TO_TODAY": "Dodaj automatycznie tag dzisiejszy do zadania",
"IS_AUTO_MARK_PARENT_AS_DONE": "Zaznacz zadanie główne jako ukończone, jeżeli wszystkie podrzędne zostały ukończone",
"IS_CONFIRM_BEFORE_EXIT": "Pytaj o potwierdzenie przed zamknięciem aplikacji",
"IS_CONFIRM_BEFORE_EXIT_WITHOUT_FINISH_DAY": "Potwierdź przed opuszczeniem aplikacji bez wcześniejszego zakończenia dnia.",
"IS_DARK_MODE": "Dark Mode",
@ -1700,18 +1697,25 @@
"IS_OVERLAY_INDICATOR_ENABLED": "Włącz okno wskaźnika nakładki (desktop linux/gnome)",
"IS_SHOW_TIP_LONGER": "Pokaż wskazówkę dotyczącą produktywności przy starcie aplikacji nieco dłużej",
"IS_TRAY_SHOW_CURRENT_COUNTDOWN": "Pokaż aktualne odliczanie w zasobniku / menu statusu (tylko na desktopie Mac)",
"IS_TRAY_SHOW_CURRENT_TASK": "Pokazuj bieżące zadanie w zasobniku / menu statusu (tylkoტერესო macOS/Windows)",
"IS_TURN_OFF_MARKDOWN": "Turn off markdown parsing for notes",
"IS_USE_CUSTOM_WINDOW_TITLE_BAR": "Użyj niestandardowego paska tytułu (tylko Windows/Linux)",
"IS_USE_CUSTOM_WINDOW_TITLE_BAR_HINT": "Wymaga ponownego uruchomienia, aby zmiany weszły w życie",
"START_OF_NEXT_DAY": "Godzina rozpoczęcia następnego dnia",
"START_OF_NEXT_DAY_HINT": "od kiedy (w godzinach) ma być liczony następny dzień. domyślnie jest to północ, czyli 0.",
"TASK_NOTES_TPL": "Task description template",
"THEME": "Motyw",
"THEME_EXPERIMENTAL": "Motyw (eksperymentalny)",
"THEME_SELECT_LABEL": "Wybierz motyw",
"TITLE": "Ustawienia różne"
},
"TASKS": {
"DEFAULT_PROJECT": "Domyślny projekt dla nowych zadań",
"IS_AUTO_ADD_WORKED_ON_TO_TODAY": "Automatycznie dodaj zadania, nad którymi pracowano, do dzisiaj",
"IS_AUTO_MARK_PARENT_AS_DONE": "Automatycznie oznaczaj zadanie nadrzędne jako ukończone, gdy wszystkie zadania podrzędne są ukończone",
"IS_CONFIRM_BEFORE_DELETE": "Potwierdź przed usunięciem zadań",
"IS_MARKDOWN_FORMATTING_IN_NOTES_ENABLED": "Włącz formatowanie Markdown w notatkach zadań",
"IS_TRAY_SHOW_CURRENT": "Pokaż bieżące zadanie w zasobniku / menu statusu (tylko desktop mac/windows)",
"NOTES_TEMPLATE": "Domyślny szablon notatek zadania",
"TITLE": "Zadania"
},
"POMODORO": {
"BREAK_DURATION": "Duration of short breaks",
"CYCLES_BEFORE_LONGER_BREAK": "Start longer break after X work sessions",

View file

@ -1638,12 +1638,9 @@
"DARK_MODE_DARK": "Escuro",
"DARK_MODE_LIGHT": "Luz",
"DARK_MODE_SYSTEM": "Sistema",
"DEFAULT_PROJECT": "Projeto padrão a ser usado para tarefas se nenhuma for especificada",
"DEFAULT_START_PAGE": "Página inicial padrão",
"FIRST_DAY_OF_WEEK": "Primeiro dia da semana",
"HELP": "<p><strong>Não está vendo as Notificações da Área de Trabalho?</strong> Para Windows, você pode querer verificar Sistema > Notificações e ações e verificar se as notificações necessárias foram habilitadas.</p>",
"IS_AUTO_ADD_WORKED_ON_TO_TODAY": "Adicionar automaticamente a etiqueta de hoje às tarefas trabalhadas",
"IS_AUTO_MARK_PARENT_AS_DONE": "Marcar tarefa pai como concluída, quando todas as subtarefas estiverem concluídas",
"IS_CONFIRM_BEFORE_EXIT": "Confirme antes de sair do aplicativo",
"IS_CONFIRM_BEFORE_EXIT_WITHOUT_FINISH_DAY": "Confirme antes de sair do aplicativo sem terminar o dia primeiro",
"IS_DARK_MODE": "Modo escuro",
@ -1653,18 +1650,25 @@
"IS_OVERLAY_INDICATOR_ENABLED": "Ativar janela indicadora sobreposta (desktop linux/gnome)",
"IS_SHOW_TIP_LONGER": "Mostrar dica de produtividade no início do app por um pouco mais de tempo",
"IS_TRAY_SHOW_CURRENT_COUNTDOWN": "Mostrar contagem regressiva atual no menu da bandeja/status (apenas desktop mac)",
"IS_TRAY_SHOW_CURRENT_TASK": "Mostrar tarefa atual na bandeja/menu de status (somente desktop Mac/Windows)",
"IS_TURN_OFF_MARKDOWN": "Desativar análise de markdown para notas de tarefas",
"IS_USE_CUSTOM_WINDOW_TITLE_BAR": "Usar barra de título personalizada (apenas Windows/Linux)",
"IS_USE_CUSTOM_WINDOW_TITLE_BAR_HINT": "Requer reinício para entrar em vigor",
"START_OF_NEXT_DAY": "Hora de início do dia seguinte",
"START_OF_NEXT_DAY_HINT": "a partir de quando (em horas) você deseja contar o dia seguinte começou. o padrão é meia-noite, que é 0.",
"TASK_NOTES_TPL": "Modelo de descrição de tarefa",
"THEME": "Tema",
"THEME_EXPERIMENTAL": "Tema (experimental)",
"THEME_SELECT_LABEL": "Selecionar Tema",
"TITLE": "Configurações diversas"
},
"TASKS": {
"DEFAULT_PROJECT": "Projeto padrão para novas tarefas",
"IS_AUTO_ADD_WORKED_ON_TO_TODAY": "Adicionar automaticamente tarefas trabalhadas a hoje",
"IS_AUTO_MARK_PARENT_AS_DONE": "Marcar tarefa pai como concluída, quando todas as subtarefas estiverem concluídas",
"IS_CONFIRM_BEFORE_DELETE": "Confirmar antes de excluir tarefas",
"IS_MARKDOWN_FORMATTING_IN_NOTES_ENABLED": "Ativar formatação Markdown nas notas das tarefas",
"IS_TRAY_SHOW_CURRENT": "Mostrar tarefa atual na bandeja / menu de status (somente desktop Mac/Windows)",
"NOTES_TEMPLATE": "Modelo de descrição de tarefa",
"TITLE": "Tarefas"
},
"POMODORO": {
"BREAK_DURATION": "Duração de pausas curtas",
"CYCLES_BEFORE_LONGER_BREAK": "Iniciar um intervalo maior após X sessões de trabalho",

View file

@ -1638,12 +1638,9 @@
"DARK_MODE_DARK": "Escuro",
"DARK_MODE_LIGHT": "Luz",
"DARK_MODE_SYSTEM": "Sistema",
"DEFAULT_PROJECT": "Projeto padrão a ser usado para tarefas se nenhum for especificado",
"DEFAULT_START_PAGE": "Página inicial padrão",
"FIRST_DAY_OF_WEEK": "Primeiro dia da semana",
"HELP": "<p><strong>Não está vendo as notificações da área de trabalho?</strong> Para janelas, convém verificar Sistema> Notificações e ações, e verificar se as notificações necessárias foram ativadas.</p>",
"IS_AUTO_ADD_WORKED_ON_TO_TODAY": "Adicionar automaticamente a tag hoje às tarefas trabalhadas",
"IS_AUTO_MARK_PARENT_AS_DONE": "Marque a tarefa pai como concluída, quando todas as subtarefas estiverem concluídas",
"IS_CONFIRM_BEFORE_EXIT": "Confirme antes de sair do aplicativo",
"IS_CONFIRM_BEFORE_EXIT_WITHOUT_FINISH_DAY": "Confirme antes de sair do aplicativo sem terminar o dia primeiro",
"IS_DARK_MODE": "Modo escuro",
@ -1653,18 +1650,25 @@
"IS_OVERLAY_INDICATOR_ENABLED": "Ativar janela indicadora sobreposta (desktop linux/gnome)",
"IS_SHOW_TIP_LONGER": "Mostrar dica de produtividade no início do aplicativo um pouco mais",
"IS_TRAY_SHOW_CURRENT_COUNTDOWN": "Mostrar contagem regressiva atual na bandeja / menu de status (apenas desktop mac)",
"IS_TRAY_SHOW_CURRENT_TASK": "Mostra a tarefa atual na bandeja / menu de status (somente desktop)",
"IS_TURN_OFF_MARKDOWN": "Desativar a análise de markdown para anotações",
"IS_USE_CUSTOM_WINDOW_TITLE_BAR": "Usar barra de título personalizada (apenas Windows/Linux)",
"IS_USE_CUSTOM_WINDOW_TITLE_BAR_HINT": "Requer reinício para ter efeito",
"START_OF_NEXT_DAY": "Hora de início do dia seguinte",
"START_OF_NEXT_DAY_HINT": "a partir de quando (em horas) você deseja contar o dia seguinte começou. o padrão é meia-noite, que é 0.",
"TASK_NOTES_TPL": "Modelo de descrição de tarefa",
"THEME": "Tema",
"THEME_EXPERIMENTAL": "Tema (experimental)",
"THEME_SELECT_LABEL": "Selecionar tema",
"TITLE": "Configurações diversas"
},
"TASKS": {
"DEFAULT_PROJECT": "Projeto padrão a ser usado para tarefas se nenhuma for especificada",
"IS_AUTO_ADD_WORKED_ON_TO_TODAY": "Adicionar automaticamente tarefas trabalhadas a hoje",
"IS_AUTO_MARK_PARENT_AS_DONE": "Marcar tarefa pai como concluída, quando todas as subtarefas estiverem concluídas",
"IS_CONFIRM_BEFORE_DELETE": "Confirmar antes de excluir tarefas",
"IS_MARKDOWN_FORMATTING_IN_NOTES_ENABLED": "Ativar formatação Markdown nas notas das tarefas",
"IS_TRAY_SHOW_CURRENT": "Mostrar tarefa atual na bandeja / menu de status (somente desktop Mac/Windows)",
"NOTES_TEMPLATE": "Modelo de descrição de tarefa",
"TITLE": "Tarefas"
},
"POMODORO": {
"BREAK_DURATION": "Duração de pausas curtas",
"CYCLES_BEFORE_LONGER_BREAK": "Iniciar pausa mais longa após X sessões de trabalho",

View file

@ -1638,15 +1638,11 @@
"DARK_MODE_DARK": "Темный",
"DARK_MODE_LIGHT": "Светлый",
"DARK_MODE_SYSTEM": "Системный",
"DEFAULT_PROJECT": "Проект по умолчанию, используемый для задач, если ни один не указан",
"DEFAULT_START_PAGE": "Страница по умолчанию",
"FIRST_DAY_OF_WEEK": "Первый день недели",
"HELP": "<p><strong>Не видите уведомления на рабочем столе?</strong> Для Windows вы можете проверить \"Система > Уведомления и действия\" и проверить, включены ли необходимые уведомления.</p>",
"IS_AUTO_ADD_WORKED_ON_TO_TODAY": "Автоматически добавлять тег \"Сегодня\" к задачам, над которыми велась работа",
"IS_AUTO_MARK_PARENT_AS_DONE": "Отметить родительскую задачу как выполненную, когда все подзадачи выполнены",
"IS_CONFIRM_BEFORE_EXIT": "Подтверждать выход из приложения",
"IS_CONFIRM_BEFORE_EXIT_WITHOUT_FINISH_DAY": "Подтверждать выход из приложения без завершения дня",
"IS_CONFIRM_BEFORE_TASK_DELETE": "Подтверждать удаление задачи",
"IS_DARK_MODE": "Темный режим",
"IS_DISABLE_ANIMATIONS": "Отключить все анимации",
"IS_DISABLE_CELEBRATION": "Отключить празднование в ежедневном обзоре",
@ -1654,18 +1650,25 @@
"IS_OVERLAY_INDICATOR_ENABLED": "Включить индикатор наложения (рабочий стол linux/gnome)",
"IS_SHOW_TIP_LONGER": "Показывать советы по продуктивности при запуске приложения немного дольше",
"IS_TRAY_SHOW_CURRENT_COUNTDOWN": "Показать текущее время обратного отсчета в трее / меню состояния (только для настольных Mac)",
"IS_TRAY_SHOW_CURRENT_TASK": "Показать текущую задачу в трее / меню состояния (только на рабочем столе)",
"IS_TURN_OFF_MARKDOWN": "Отключить Markdown для заметок",
"IS_USE_CUSTOM_WINDOW_TITLE_BAR": "Использовать пользовательскую панель заголовка (только Windows/Linux)",
"IS_USE_CUSTOM_WINDOW_TITLE_BAR_HINT": "Требуется перезапуск для вступления изменений в силу",
"START_OF_NEXT_DAY": "Время начала следующего дня",
"START_OF_NEXT_DAY_HINT": "С какого момента (в часах) хотите отсчитывать начало дня. По умолчанию полночь, что равно 0.",
"TASK_NOTES_TPL": "Шаблон описания задачи",
"THEME": "Тема",
"THEME_EXPERIMENTAL": "Тема (экспериментально)",
"THEME_SELECT_LABEL": "Выбрать тему",
"TITLE": "Разные настройки"
},
"TASKS": {
"DEFAULT_PROJECT": "Проект по умолчанию, если не указан другой",
"IS_AUTO_ADD_WORKED_ON_TO_TODAY": "Автоматически добавлять задачи, над которыми работали, к сегодняшним",
"IS_AUTO_MARK_PARENT_AS_DONE": "Автоматически отмечать родительскую задачу как выполненную, когда все подзадачи выполнены",
"IS_CONFIRM_BEFORE_DELETE": "Подтверждать удаление задачи",
"IS_MARKDOWN_FORMATTING_IN_NOTES_ENABLED": "Включить Markdown форматирование в описании задачи",
"IS_TRAY_SHOW_CURRENT": "Показать текущую задачу в трее / меню состояния (только для настольных Mac/Windows)",
"NOTES_TEMPLATE": "Шаблон описания задачи",
"TITLE": "Задачи"
},
"POMODORO": {
"BREAK_DURATION": "Продолжительность коротких перерывов",
"CYCLES_BEFORE_LONGER_BREAK": "Длинный перерыв после X рабочих сессий",
@ -1984,6 +1987,7 @@
"SYNC_EXPORT": "Синхронизация и экспорт",
"TABS": {
"GENERAL": "Общие",
"TASKS": "Задачи",
"TIME_TRACKING": "Время и отслеживание",
"PRODUCTIVITY": "Продуктивность",
"PLUGINS": "Плагины",

View file

@ -1638,12 +1638,9 @@
"DARK_MODE_DARK": "Tmavý",
"DARK_MODE_LIGHT": "Svetlý",
"DARK_MODE_SYSTEM": "Systém",
"DEFAULT_PROJECT": "Predvolený projekt, ktorý sa použije pre úlohy, ak nie je zadaný žiadny",
"DEFAULT_START_PAGE": "Predvolená úvodná stránka",
"FIRST_DAY_OF_WEEK": "Prvý deň v týždni",
"HELP": "<p><strong>Not seeing Desktop Notifications?</strong> For windows you might want to check System > Notifications & actions and check if the required notifications have been enabled.</p>",
"IS_AUTO_ADD_WORKED_ON_TO_TODAY": "Automatically add today tag to worked on tasks",
"IS_AUTO_MARK_PARENT_AS_DONE": "Mark parent task as done, when all sub tasks are done",
"IS_CONFIRM_BEFORE_EXIT": "Confirm before exiting the app",
"IS_CONFIRM_BEFORE_EXIT_WITHOUT_FINISH_DAY": "Confirm before exiting the app without finishing day first",
"IS_DARK_MODE": "Tmavý režim",
@ -1653,18 +1650,25 @@
"IS_OVERLAY_INDICATOR_ENABLED": "Povoliť indikátor prekrytia okna (desktop linux/gnome)",
"IS_SHOW_TIP_LONGER": "Zobraziť tip na produktivitu pri spustení aplikácie o niečo dlhšie",
"IS_TRAY_SHOW_CURRENT_COUNTDOWN": "Zobraziť aktuálny odpočet v systémovej lište / stavovom menu (iba desktop mac)",
"IS_TRAY_SHOW_CURRENT_TASK": "Show current task in the tray / status menu (desktop mac/windows only)",
"IS_TURN_OFF_MARKDOWN": "Turn off markdown parsing for task notes",
"IS_USE_CUSTOM_WINDOW_TITLE_BAR": "Použiť vlastný titulkový panel (len Windows/Linux)",
"IS_USE_CUSTOM_WINDOW_TITLE_BAR_HINT": "Vyžaduje reštart na prejavenie zmien",
"START_OF_NEXT_DAY": "Čas začiatku nasledujúceho dňa",
"START_OF_NEXT_DAY_HINT": "od kedy (v hodinách) chcete počítať, že začal ďalší deň. predvolená hodnota je polnoc, čo je 0.",
"TASK_NOTES_TPL": "Task description template",
"THEME": "Téma",
"THEME_EXPERIMENTAL": "Téma (experimentálne)",
"THEME_SELECT_LABEL": "Vybrať tému",
"TITLE": "Rôzne nastavenia"
},
"TASKS": {
"DEFAULT_PROJECT": "Predvolený projekt pre nové úlohy",
"IS_AUTO_ADD_WORKED_ON_TO_TODAY": "Automaticky pridať dnes tag k upravovaným úlohám",
"IS_AUTO_MARK_PARENT_AS_DONE": "Automaticky označiť rodičovskú úlohu ako dokončenú, keď sú všetky podúlohy dokončené",
"IS_CONFIRM_BEFORE_DELETE": "Potvrdiť pred odstránením úloh",
"IS_MARKDOWN_FORMATTING_IN_NOTES_ENABLED": "Povoliť formátovanie Markdown v poznámkach k úlohám",
"IS_TRAY_SHOW_CURRENT": "Zobraziť aktuálnu úlohu v systémovej lište / stavovom menu (iba desktop Mac/Windows)",
"NOTES_TEMPLATE": "Šablóna poznámok k úlohe",
"TITLE": "Úlohy"
},
"POMODORO": {
"BREAK_DURATION": "Dĺžka krátkych prestávok",
"CYCLES_BEFORE_LONGER_BREAK": "Začnite dlhšiu prestávku po X pracovných stretnutiach",

View file

@ -1575,11 +1575,8 @@
"TIME_LOCALE_KO_KR": "Korean - 12-hour AM/PM"
},
"MISC": {
"DEFAULT_PROJECT": "Standardprojekt som ska användas för uppgifter om inget annat anges",
"FIRST_DAY_OF_WEEK": "Veckans första dag",
"HELP": "<p><strong>Ser du inte skrivbordsmeddelanden?</strong> För Windows kan du gå till System > Meddelanden och åtgärder och kontrollera om de nödvändiga meddelandena har aktiverats.</p>",
"IS_AUTO_ADD_WORKED_ON_TO_TODAY": "Lägg automatiskt till taggen 'idag' till uppgifter som du har arbetat med",
"IS_AUTO_MARK_PARENT_AS_DONE": "Markera överordnad uppgift som klar när alla underordnade uppgifter är klara.",
"IS_CONFIRM_BEFORE_EXIT": "Bekräfta innan du stänger appen",
"IS_CONFIRM_BEFORE_EXIT_WITHOUT_FINISH_DAY": "Bekräfta innan du stänger appen utan att först avsluta dagen.",
"IS_DARK_MODE": "Mörkt läge",
@ -1588,12 +1585,9 @@
"IS_MINIMIZE_TO_TRAY": "Minimera till aktivitetsfältet (endast skrivbord)",
"IS_SHOW_TIP_LONGER": "Visa produktivitetstips vid appstart lite längre",
"IS_TRAY_SHOW_CURRENT_COUNTDOWN": "Visa aktuell nedräkning i facket/statusmenyn (endast stationära Mac-datorer)",
"IS_TRAY_SHOW_CURRENT_TASK": "Visa aktuell uppgift i facket/statusmenyn (endast stationära Mac/Windows)",
"IS_OVERLAY_INDICATOR_ENABLED": "Aktivera överläggningsindikatorfönster (skrivbord Linux/Gnome)",
"IS_TURN_OFF_MARKDOWN": "Stäng av markdown-tolkning för uppgiftsanteckningar",
"START_OF_NEXT_DAY": "Starttid nästa dag",
"START_OF_NEXT_DAY_HINT": "från vilken tidpunkt (i timmar) du vill räkna att nästa dag har börjat. Standardinställningen är midnatt, vilket är 0.",
"TASK_NOTES_TPL": "Mall för arbetsbeskrivning",
"TITLE": "Diverse inställningar",
"DARK_MODE": "Mörkt läge",
"DARK_MODE_SYSTEM": "System",
@ -1604,6 +1598,16 @@
"THEME_EXPERIMENTAL": "Tema (experimentellt)",
"THEME_SELECT_LABEL": "Välj tema"
},
"TASKS": {
"DEFAULT_PROJECT": "Standardprojekt som ska användas för uppgifter om inget annat anges",
"IS_AUTO_ADD_WORKED_ON_TO_TODAY": "Automatiskt lägga till arbetade uppgifter till idag",
"IS_AUTO_MARK_PARENT_AS_DONE": "Automatiskt markera överordnad uppgift som klar när alla underordnade uppgifter är klara",
"IS_CONFIRM_BEFORE_DELETE": "Bekräfta innan du tar bort uppgifter",
"IS_MARKDOWN_FORMATTING_IN_NOTES_ENABLED": "Aktivera Markdown-formatering i uppgiftsanteckningar",
"IS_TRAY_SHOW_CURRENT": "Visa aktuell uppgift i systemfältet / statusmenyn (endast skrivbord Mac/Windows)",
"NOTES_TEMPLATE": "Mall för uppgiftsanteckningar",
"TITLE": "Uppgifter"
},
"POMODORO": {
"BREAK_DURATION": "Varaktighet för korta raster",
"CYCLES_BEFORE_LONGER_BREAK": "Börja längre paus efter X arbetspass",

View file

@ -1602,12 +1602,9 @@
"DARK_MODE_DARK": "Karanlık",
"DARK_MODE_LIGHT": "Aydınlık",
"DARK_MODE_SYSTEM": "Sistem",
"DEFAULT_PROJECT": "Proje belirtilmezse görevler için kullanılacak varsayılan proje",
"DEFAULT_START_PAGE": "Varsayılan başlangıç sayfası",
"FIRST_DAY_OF_WEEK": "Haftanın ilk günü",
"HELP": "<p><strong>Masaüstü Bildirimleri'ni göremiyor musunuz?</strong> Pencereler için Sistem> Bildirimler ve eylemleri kontrol etmek ve gerekli bildirimlerin etkin olup olmadığını kontrol etmek isteyebilirsiniz.</p>",
"IS_AUTO_ADD_WORKED_ON_TO_TODAY": "Çalışılan görevlere bugün etiketini otomatik olarak ekle",
"IS_AUTO_MARK_PARENT_AS_DONE": "Tüm alt görevler tamamlandığında ana görevi yapıldığı gibi işaretle",
"IS_CONFIRM_BEFORE_EXIT": "Uygulamadan çıkmadan önce onayla",
"IS_CONFIRM_BEFORE_EXIT_WITHOUT_FINISH_DAY": "Uygulamadan çıkmadan önce günü bitirmeden çıkış yaptığınızı onaylayın",
"IS_DARK_MODE": "Karanlık mod",
@ -1617,18 +1614,25 @@
"IS_OVERLAY_INDICATOR_ENABLED": "Panel gösterge penceresini etkinleştir (masaüstü linux/gnome)",
"IS_SHOW_TIP_LONGER": "Uygulamada üretkenlik ipucunu biraz daha uzun süre başlatın",
"IS_TRAY_SHOW_CURRENT_COUNTDOWN": "Mevcut geri sayımı tepsi / durum menüsünde göster (sadece masaüstü mac için)",
"IS_TRAY_SHOW_CURRENT_TASK": "Mevcut görevi tepsi / Durum menüsünde göster (yalnızca masaüstü)",
"IS_TURN_OFF_MARKDOWN": "Notlar için etiketleme ayrıştırmayı kapatın",
"IS_USE_CUSTOM_WINDOW_TITLE_BAR": "Özel başlık çubuğunu kullan (Windows/Linux için)",
"IS_USE_CUSTOM_WINDOW_TITLE_BAR_HINT": "Etkin olması için yeniden başlatma gerektirir",
"START_OF_NEXT_DAY": "Ertesi günün başlama saati",
"START_OF_NEXT_DAY_HINT": "Ertesi günün başladığı saati saymak istediğiniz andan itibaren (saat cinsinden). Varsayılan gece yarısıdır, yani 0'dır.",
"TASK_NOTES_TPL": "Görev açıklama şablonu",
"THEME": "Tema",
"THEME_EXPERIMENTAL": "Tema (Deneysel)",
"THEME_SELECT_LABEL": "Tema seç",
"TITLE": "Diğer ayarlar"
},
"TASKS": {
"DEFAULT_PROJECT": "Varsayılan proje (belirtilmemişse) için kullanılacak",
"IS_AUTO_ADD_WORKED_ON_TO_TODAY": "Üzerinde çalışılan görevleri otomatik olarak bugüne ekle",
"IS_AUTO_MARK_PARENT_AS_DONE": "Tüm alt görevler tamamlandığında ana görevi otomatik olarak tamamlandı olarak işaretle",
"IS_CONFIRM_BEFORE_DELETE": "Görevleri silmeden önce onayla",
"IS_MARKDOWN_FORMATTING_IN_NOTES_ENABLED": "Görev notlarında Markdown biçimlendirmesini etkinleştir",
"IS_TRAY_SHOW_CURRENT": "Mevcut görevi tepsi / durum menüsünde göster (yalnızca masaüstü Mac/Windows için)",
"NOTES_TEMPLATE": "Görev açıklama şablonu",
"TITLE": "Görevler"
},
"POMODORO": {
"BREAK_DURATION": "Kısa molaların süresi",
"CYCLES_BEFORE_LONGER_BREAK": "X iş oturumundan sonra daha uzun ara vermeye başlayın",

View file

@ -1317,11 +1317,8 @@
"ZH_TW": "КИТАЙСЬКА (繁體)"
},
"MISC": {
"DEFAULT_PROJECT": "Проект за замовчуванням для завдань, якщо не вказано інший",
"FIRST_DAY_OF_WEEK": "Перший день тижня",
"HELP": "<p><strong>Не бачите сповіщень на робочому столі?</strong> Для Windows ви можете перевірити Система > Сповіщення і дії та перевірити, чи увімкнені необхідні сповіщення.</p>",
"IS_AUTO_ADD_WORKED_ON_TO_TODAY": "Автоматично додавати тег сьогодні до опрацьованих завдань",
"IS_AUTO_MARK_PARENT_AS_DONE": "Позначати батьківське завдання як виконане, коли всі підзавдання виконані",
"IS_CONFIRM_BEFORE_EXIT": "Підтверджувати перед виходом з додатку",
"IS_CONFIRM_BEFORE_EXIT_WITHOUT_FINISH_DAY": "Підтверджувати перед виходом з додатку без попереднього завершення дня",
"IS_DARK_MODE": "Темний режим",
@ -1330,13 +1327,20 @@
"IS_MINIMIZE_TO_TRAY": "Згортати до системного трею (тільки для десктопної версії)",
"IS_SHOW_TIP_LONGER": "Показувати поради щодо продуктивності на початку програми трохи довше",
"IS_TRAY_SHOW_CURRENT_COUNTDOWN": "Показувати поточний зворотний відлік у треї / меню статусу (лише для настільного комп'ютера Mac)",
"IS_TRAY_SHOW_CURRENT_TASK": "Показувати поточне завдання в системному треї / меню статусу (тільки для десктопної версії Mac/Windows)",
"IS_TURN_OFF_MARKDOWN": "Вимкнути обробку markdown для нотаток завдань",
"START_OF_NEXT_DAY": "Час початку наступного дня",
"START_OF_NEXT_DAY_HINT": "з якого часу (в годинах) ви хочете вважати, що почався наступний день. За замовчуванням це опівніч, тобто 0.",
"TASK_NOTES_TPL": "Шаблон опису завдання",
"TITLE": "Інші налаштування"
},
"TASKS": {
"DEFAULT_PROJECT": "Проект за замовчуванням для нових завдань",
"IS_AUTO_ADD_WORKED_ON_TO_TODAY": "Автоматично додавати працюючі завдання до сьогодні",
"IS_AUTO_MARK_PARENT_AS_DONE": "Автоматично позначати батьківське завдання як виконане, коли всі підзавдання виконані",
"IS_CONFIRM_BEFORE_DELETE": "Підтверджувати перед видаленням завдань",
"IS_MARKDOWN_FORMATTING_IN_NOTES_ENABLED": "Увімкнути форматування Markdown у нотатках завдань",
"IS_TRAY_SHOW_CURRENT": "Показувати поточне завдання в системному треї / меню статусу (тільки для десктопної версії Mac/Windows)",
"NOTES_TEMPLATE": "Шаблон нотаток для нових завдань",
"TITLE": "Завдання"
},
"POMODORO": {
"BREAK_DURATION": "Тривалість коротких перерв",
"CYCLES_BEFORE_LONGER_BREAK": "Почати довшу перерву після X робочих сесій",

View file

@ -1977,12 +1977,9 @@
"DARK_MODE_DARK": "深色",
"DARK_MODE_LIGHT": "浅色",
"DARK_MODE_SYSTEM": "系统",
"DEFAULT_PROJECT": "用于任务(如果未指定)的默认项目",
"DEFAULT_START_PAGE": "默认首页",
"FIRST_DAY_OF_WEEK": "一周的第一天",
"HELP": "<p><strong>没有看到桌面通知?</strong> 对于 Windows您可能需要检查系统 > 通知和操作,并检查是否已启用所需的通知。</p>",
"IS_AUTO_ADD_WORKED_ON_TO_TODAY": "自动将今天标签添加到已处理的任务",
"IS_AUTO_MARK_PARENT_AS_DONE": "当所有子任务都完成时,将父任务标记为完成",
"IS_CONFIRM_BEFORE_EXIT": "退出应用前确认",
"IS_CONFIRM_BEFORE_EXIT_WITHOUT_FINISH_DAY": "在未先完成一天的情况下退出应用前确认",
"IS_DARK_MODE": "深色模式",
@ -1993,14 +1990,11 @@
"IS_OVERLAY_INDICATOR_ENABLED": "启用覆盖指示器窗口(桌面 Linux/Gnome",
"IS_SHOW_TIP_LONGER": "在应用启动时显示生产力提示更长一点",
"IS_TRAY_SHOW_CURRENT_COUNTDOWN": "在托盘/状态菜单中显示当前倒计时(仅限桌面 Mac",
"IS_TRAY_SHOW_CURRENT_TASK": "在托盘/状态菜单中显示当前任务(仅限桌面 Mac/Windows",
"IS_TURN_OFF_MARKDOWN": "关闭任务描述的 Markdown 解析",
"IS_USE_CUSTOM_WINDOW_TITLE_BAR": "使用自定义标题栏仅限Windows/Linux",
"IS_USE_CUSTOM_WINDOW_TITLE_BAR_HINT": "需要重启方可生效",
"IS_USE_MINIMAL_SIDE_NAV": "",
"START_OF_NEXT_DAY": "下一天的开始时间",
"START_OF_NEXT_DAY_HINT": "从何时(小时)开始计算下一天已经开始。默认是午夜,即 0。",
"TASK_NOTES_TPL": "任务描述模板",
"THEME": "主题",
"THEME_EXPERIMENTAL": "主题(实验性)",
"THEME_SELECT_LABEL": "选择主题",
@ -2008,6 +2002,16 @@
"USER_PROFILES": "",
"USER_PROFILES_HINT": ""
},
"TASKS": {
"DEFAULT_PROJECT": "默认项目,用于未指定其他项目的任务",
"IS_AUTO_ADD_WORKED_ON_TO_TODAY": "自动将已处理的任务添加到今天",
"IS_AUTO_MARK_PARENT_AS_DONE": "当所有子任务完成时,自动将父任务标记为完成",
"IS_CONFIRM_BEFORE_DELETE": "删除任务前确认",
"IS_MARKDOWN_FORMATTING_IN_NOTES_ENABLED": "在任务笔记中启用Markdown格式",
"IS_TRAY_SHOW_CURRENT": "在托盘/状态菜单中显示当前任务仅限桌面Mac/Windows",
"NOTES_TEMPLATE": "任务描述模板",
"TITLE": "任务"
},
"PAST": {
"A_DAY": "",
"A_MINUTE": "",