@atlastodo/shared 0.0.0-stage → 0.1.11-rc.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +661 -0
- package/dist/activity.d.ts +23 -0
- package/dist/avatarColor.d.ts +15 -0
- package/dist/boardMove.d.ts +14 -0
- package/dist/bugReport.d.ts +47 -0
- package/dist/calendar.d.ts +23 -0
- package/dist/comments.d.ts +14 -0
- package/dist/countdown.d.ts +14 -0
- package/dist/cursor.d.ts +6 -0
- package/dist/dataTransfer.d.ts +19 -0
- package/dist/duplicateProject.d.ts +19 -0
- package/dist/duplicateSection.d.ts +15 -0
- package/dist/filterQuery.d.ts +64 -0
- package/dist/focus.d.ts +17 -0
- package/dist/format.d.ts +30 -0
- package/dist/fuzzy.d.ts +14 -0
- package/dist/grouping.d.ts +24 -0
- package/dist/habitGroups.d.ts +46 -0
- package/dist/habitSchedule.d.ts +4 -0
- package/dist/habits.d.ts +115 -0
- package/dist/hotkeys.d.ts +50 -0
- package/dist/i18n.d.ts +14796 -0
- package/dist/ids.d.ts +7 -0
- package/dist/importTicktick.d.ts +34 -0
- package/dist/importTodoist.d.ts +60 -0
- package/dist/index.d.ts +62 -0
- package/dist/index.js +21446 -0
- package/dist/members.d.ts +26 -0
- package/dist/planDay.d.ts +31 -0
- package/dist/pomodoro.d.ts +49 -0
- package/dist/priority.d.ts +2 -0
- package/dist/projectMapper.d.ts +15 -0
- package/dist/projectStyle.d.ts +14 -0
- package/dist/projectTree.d.ts +40 -0
- package/dist/quickAddLexicons.d.ts +64 -0
- package/dist/quickAddLexiconsWire.d.ts +11 -0
- package/dist/quickAddParse.d.ts +44 -0
- package/dist/quickSchedule.d.ts +9 -0
- package/dist/rank.d.ts +9 -0
- package/dist/recurrence.d.ts +60 -0
- package/dist/regions.d.ts +6 -0
- package/dist/reminderSchedule.d.ts +42 -0
- package/dist/reminders.d.ts +28 -0
- package/dist/reorder.d.ts +21 -0
- package/dist/sectionMapper.d.ts +8 -0
- package/dist/sectionOps.d.ts +5 -0
- package/dist/serverUrl.d.ts +12 -0
- package/dist/smartLists.d.ts +11 -0
- package/dist/stats.d.ts +28 -0
- package/dist/swipe.d.ts +29 -0
- package/dist/taskClipboard.d.ts +19 -0
- package/dist/taskListTree.d.ts +33 -0
- package/dist/taskMapper.d.ts +3 -0
- package/dist/taskOps.d.ts +49 -0
- package/dist/taskSearch.d.ts +11 -0
- package/dist/taskTree.d.ts +53 -0
- package/dist/theme.d.ts +14 -0
- package/dist/toast.d.ts +26 -0
- package/dist/trash.d.ts +24 -0
- package/dist/views.d.ts +8 -0
- package/dist/zonedTime.d.ts +30 -0
- package/package.json +55 -4
- package/test-vectors/filter_vectors.json +1613 -0
- package/test-vectors/quick_add_vectors.json +1656 -0
- package/test-vectors/recurrence_vectors.json +779 -0
- package/test-vectors/task_rules_vectors.json +729 -0
- package/README.md +0 -3
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { type CreateTaskInput, type LocalStore, type Task } from "@atlastodo/client-core";
|
|
2
|
+
/**
|
|
3
|
+
* The task rules as pure functions over a {@link LocalStore}, with no React.
|
|
4
|
+
*
|
|
5
|
+
* - `now` is injected; `timeZone` defaults to the runtime zone.
|
|
6
|
+
* - Ids come from `store.newEntityId()`, never `crypto.randomUUID()`: Hermes has no global
|
|
7
|
+
* `crypto`, and a non-UUID entity id 422s the whole sync push batch.
|
|
8
|
+
* - Deletes are soft (`deleted_at`); a real tombstone is only issued by an explicit purge.
|
|
9
|
+
* - A locked task is read-only: every write refuses one this device cannot decrypt.
|
|
10
|
+
*/
|
|
11
|
+
export declare function isTaskLocked(store: LocalStore, id: string): boolean;
|
|
12
|
+
export interface TaskMove {
|
|
13
|
+
section_id?: string | null;
|
|
14
|
+
sort_order?: number;
|
|
15
|
+
due_at?: number | null;
|
|
16
|
+
parent_id?: string | null;
|
|
17
|
+
}
|
|
18
|
+
export declare function allTasks(store: LocalStore): Task[];
|
|
19
|
+
export declare function visibleTasks(store: LocalStore, tasks?: Task[]): Task[];
|
|
20
|
+
export declare function archivedTasks(store: LocalStore, tasks?: Task[]): Task[];
|
|
21
|
+
export declare function createTask(store: LocalStore, input: CreateTaskInput, timeZone?: string): string;
|
|
22
|
+
/**
|
|
23
|
+
* Complete or reopen a task. Completing a recurring task with a due date closes this instance and
|
|
24
|
+
* spawns the next occurrence. `start_at` and absolute reminders move by the same calendar days as
|
|
25
|
+
* the due date; due-relative reminders carry over with the fire stamp reset. Reopening never spawns.
|
|
26
|
+
* Returns the spawned id (the UI needs it to undo), or `null`.
|
|
27
|
+
*/
|
|
28
|
+
export declare function toggleTask(store: LocalStore, task: Task, actorId?: string, now?: number, timeZone?: string): string | null;
|
|
29
|
+
export declare function updateTask(store: LocalStore, task: Task, patch: Partial<Task>, actorId?: string, now?: number, timeZone?: string): boolean;
|
|
30
|
+
export type RecurEditScope = "this_occurrence" | "all_occurrences";
|
|
31
|
+
export declare function updateRecurringTask(store: LocalStore, task: Task, patch: Partial<Task>, scope: RecurEditScope, actorId?: string, now?: number, timeZone?: string): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Advance a recurring task to its next occurrence without completing it. The due date only moves
|
|
34
|
+
* forward: an after-completion rule landing before it is a no-op. Returns whether it advanced.
|
|
35
|
+
*/
|
|
36
|
+
export declare function skipTask(store: LocalStore, task: Task, actorId?: string, now?: number, timeZone?: string): boolean;
|
|
37
|
+
export declare function moveTask(store: LocalStore, id: string, patch: TaskMove, now?: number): boolean;
|
|
38
|
+
export declare function reparentTask(store: LocalStore, allTasks: Task[], id: string, parentId: string | null, sortOrder: number, now?: number): boolean;
|
|
39
|
+
export interface TaskProjectMove {
|
|
40
|
+
project_id: string | null;
|
|
41
|
+
section_id: string | null;
|
|
42
|
+
sort_order?: number;
|
|
43
|
+
}
|
|
44
|
+
export declare function moveTaskToProject(store: LocalStore, id: string, target: TaskProjectMove, now?: number): boolean;
|
|
45
|
+
export declare function softDeleteTask(store: LocalStore, task: Task, now?: number): boolean;
|
|
46
|
+
export declare function restoreTask(store: LocalStore, task: Task): boolean;
|
|
47
|
+
export declare function discardTask(store: LocalStore, id: string): boolean;
|
|
48
|
+
export declare function duplicateTask(store: LocalStore, task: Task, timeZone?: string): string | null;
|
|
49
|
+
export declare function setTaskArchived(store: LocalStore, task: Task, archived: boolean, now?: number): boolean;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Task text search: case-insensitive substring match on title or notes, ranked with `scoreMatch`
|
|
3
|
+
* to stay consistent with the command palette.
|
|
4
|
+
*/
|
|
5
|
+
import type { Task } from "@atlastodo/client-core";
|
|
6
|
+
export interface TaskSearchHit {
|
|
7
|
+
task: Task;
|
|
8
|
+
field: "title" | "notes";
|
|
9
|
+
}
|
|
10
|
+
export declare const TASK_SEARCH_LIMIT = 20;
|
|
11
|
+
export declare function searchTasks(tasks: Task[], query: string, limit?: number): TaskSearchHit[];
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { Task } from "@atlastodo/client-core";
|
|
2
|
+
import { type RankWrite } from "./reorder.js";
|
|
3
|
+
/**
|
|
4
|
+
* The subtask tree: a flat `Task[]` as a nested render list, plus the maths for reparenting by drag.
|
|
5
|
+
*
|
|
6
|
+
* Siblings default to `sort_order` then `created_at`; a custom order must be injected as a
|
|
7
|
+
* {@link TaskTreeOrder}, since the build re-orders every level. Orphans (`parent_id` null or
|
|
8
|
+
* outside the set) render at depth 0 instead of vanishing.
|
|
9
|
+
*/
|
|
10
|
+
export interface TaskTreeNode {
|
|
11
|
+
task: Task;
|
|
12
|
+
depth: number;
|
|
13
|
+
children: TaskTreeNode[];
|
|
14
|
+
}
|
|
15
|
+
export interface FlatTaskRow {
|
|
16
|
+
task: Task;
|
|
17
|
+
depth: number;
|
|
18
|
+
hasChildren: boolean;
|
|
19
|
+
childCount: number;
|
|
20
|
+
completedChildCount: number;
|
|
21
|
+
}
|
|
22
|
+
export interface TaskTreeOrder {
|
|
23
|
+
compareSiblings?: (a: Task, b: Task) => number;
|
|
24
|
+
compareRoots?: (a: Task, b: Task) => number;
|
|
25
|
+
}
|
|
26
|
+
export declare function buildTaskTree(tasks: Task[], order?: TaskTreeOrder): TaskTreeNode[];
|
|
27
|
+
export declare function flattenTree(tasks: Task[], collapsed?: ReadonlySet<string>, order?: TaskTreeOrder): FlatTaskRow[];
|
|
28
|
+
export declare function rankAfterChildren(tasks: Task[], parentId: string, movedId?: string): number;
|
|
29
|
+
export declare function indentTarget(tasks: Task[], id: string, allTasks?: Task[]): {
|
|
30
|
+
parent_id: string | null;
|
|
31
|
+
sort_order: number;
|
|
32
|
+
writes: RankWrite[];
|
|
33
|
+
} | null;
|
|
34
|
+
export declare function outdentTarget(tasks: Task[], id: string): {
|
|
35
|
+
parent_id: string | null;
|
|
36
|
+
sort_order: number;
|
|
37
|
+
writes: RankWrite[];
|
|
38
|
+
} | null;
|
|
39
|
+
export declare function subtreeProgress(node: TaskTreeNode): {
|
|
40
|
+
done: number;
|
|
41
|
+
total: number;
|
|
42
|
+
};
|
|
43
|
+
export declare function wouldCycle(tasks: Task[], movingId: string, candidateParentId: string | null): boolean;
|
|
44
|
+
/**
|
|
45
|
+
* Resolve a drag over the flattened list into a reparent target. The moved row's depth is
|
|
46
|
+
* `max(aboveDepth, belowDepth)`, at most one deeper than the row above. `null` for a no-op or a cycle.
|
|
47
|
+
*/
|
|
48
|
+
export declare function resolveIndentTarget(rows: FlatTaskRow[], from: number, to: number): {
|
|
49
|
+
id: string;
|
|
50
|
+
parent_id: string | null;
|
|
51
|
+
sort_order: number;
|
|
52
|
+
writes: RankWrite[];
|
|
53
|
+
} | null;
|
package/dist/theme.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type ThemePref = "light" | "dark" | "system";
|
|
2
|
+
export type ResolvedTheme = "light" | "dark";
|
|
3
|
+
export declare const DEFAULT_THEME: ThemePref;
|
|
4
|
+
export declare function isThemePref(v: unknown): v is ThemePref;
|
|
5
|
+
export declare function resolveTheme(pref: ThemePref, systemDark: boolean): ResolvedTheme;
|
|
6
|
+
export type AccentName = "indigo" | "blue" | "violet" | "emerald" | "amber" | "rose";
|
|
7
|
+
export type AccentScale = Record<50 | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 900 | 950, string>;
|
|
8
|
+
export declare const ACCENTS: Record<AccentName, AccentScale>;
|
|
9
|
+
export declare const ACCENT_NAMES: AccentName[];
|
|
10
|
+
export declare const ACCENT_LABEL_KEYS: Record<AccentName, string>;
|
|
11
|
+
export declare const DEFAULT_ACCENT: AccentName;
|
|
12
|
+
export declare function isAccentName(v: unknown): v is AccentName;
|
|
13
|
+
export declare function hexToRgbTriad(hex: string): string;
|
|
14
|
+
export declare function accentVars(name: AccentName): Record<string, string>;
|
package/dist/toast.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Toast state: a tiny pure reducer for add/dismiss/replace, testable without React or timers. The
|
|
3
|
+
* provider owns the auto-dismiss timeout.
|
|
4
|
+
*/
|
|
5
|
+
export interface ToastAction {
|
|
6
|
+
label: string;
|
|
7
|
+
run: () => void;
|
|
8
|
+
}
|
|
9
|
+
export interface Toast {
|
|
10
|
+
id: string;
|
|
11
|
+
message: string;
|
|
12
|
+
action?: ToastAction;
|
|
13
|
+
}
|
|
14
|
+
export type ToastState = Toast[];
|
|
15
|
+
export type ToastEvent = {
|
|
16
|
+
type: "add";
|
|
17
|
+
toast: Toast;
|
|
18
|
+
} | {
|
|
19
|
+
type: "dismiss";
|
|
20
|
+
id: string;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Newest toast last; adding an id that already exists replaces it (so re-showing does not stack).
|
|
24
|
+
*/
|
|
25
|
+
export declare function toastReducer(state: ToastState, event: ToastEvent): ToastState;
|
|
26
|
+
export declare const TOAST_TTL_MS = 6000;
|
package/dist/trash.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trash. Deleting sets a synced `deleted_at`, recoverable for 30 days. "Delete permanently" and
|
|
3
|
+
* the sweep issue the real tombstone (`store.remove`). A trashed container's children are hidden
|
|
4
|
+
* by a read-time cascade and hard-purged with it.
|
|
5
|
+
*/
|
|
6
|
+
import { type EntityKind, type LocalStore } from "@atlastodo/client-core";
|
|
7
|
+
export declare const TRASH_KINDS: EntityKind[];
|
|
8
|
+
export declare const TRASH_RETENTION_MS: number;
|
|
9
|
+
export declare function daysUntilPurge(deletedAt: number, now: number): number;
|
|
10
|
+
export declare function isTrashed(fields: Record<string, unknown>): boolean;
|
|
11
|
+
export declare function restoreEntity(store: LocalStore, kind: EntityKind, id: string): void;
|
|
12
|
+
export declare function softDelete(store: LocalStore, kick: () => void, kind: EntityKind, id: string): () => void;
|
|
13
|
+
export declare function restoreFromTrash(store: LocalStore, kick: () => void, kind: EntityKind, id: string): void;
|
|
14
|
+
export declare function purge(store: LocalStore, kick: () => void, kind: EntityKind, id: string): void;
|
|
15
|
+
export interface TrashItem {
|
|
16
|
+
kind: EntityKind;
|
|
17
|
+
id: string;
|
|
18
|
+
label: string;
|
|
19
|
+
deletedAt: number;
|
|
20
|
+
daysLeft: number;
|
|
21
|
+
}
|
|
22
|
+
export declare function listTrash(store: LocalStore, now: number): TrashItem[];
|
|
23
|
+
export declare function sweepExpiredTrash(store: LocalStore, kick: () => void, now: number): number;
|
|
24
|
+
export declare function purgeAllTrash(store: LocalStore, kick: () => void, now: number): number;
|
package/dist/views.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The app's smart lists: the always-present views that are not a project, filter or feature. Shared
|
|
3
|
+
* because the stored `default_view` preference is validated against it on every client; routing
|
|
4
|
+
* stays per app.
|
|
5
|
+
*/
|
|
6
|
+
export type SmartView = "today" | "upcoming" | "inbox" | "all" | "assigned" | "completed";
|
|
7
|
+
export declare const SMART_VIEWS: SmartView[];
|
|
8
|
+
export declare function isSmartView(value: unknown): value is SmartView;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Timezone-aware day-boundary math on `Intl.DateTimeFormat`, the single source of the instant to
|
|
3
|
+
* calendar-day mapping for smart lists, calendar and quick-add. `timeZone` defaults to the runtime's.
|
|
4
|
+
*/
|
|
5
|
+
export declare function isValidTimeZone(timeZone: string): boolean;
|
|
6
|
+
export declare function resolveTimeZone(timeZone?: string): string;
|
|
7
|
+
export interface ZonedParts {
|
|
8
|
+
year: number;
|
|
9
|
+
month: number;
|
|
10
|
+
day: number;
|
|
11
|
+
hour: number;
|
|
12
|
+
minute: number;
|
|
13
|
+
second: number;
|
|
14
|
+
weekday: number;
|
|
15
|
+
}
|
|
16
|
+
export declare function zonedParts(ms: number, timeZone?: string): ZonedParts;
|
|
17
|
+
/**
|
|
18
|
+
* The inverse of {@link zonedParts}; an offset probe with one correction keeps it stable across
|
|
19
|
+
* DST. A wall time skipped by a DST gap resolves forward by the gap's length, as RFC 5545 does
|
|
20
|
+
* (02:30 on a spring-forward night is 03:30).
|
|
21
|
+
*/
|
|
22
|
+
export declare function makeInstant(year: number, month: number, day: number, hour: number, minute: number, second: number, timeZone?: string): number;
|
|
23
|
+
export declare function startOfDay(ms: number, timeZone?: string): number;
|
|
24
|
+
export declare const DEFAULT_DUE_HOUR = 23;
|
|
25
|
+
export declare const DEFAULT_DUE_MINUTE = 59;
|
|
26
|
+
export declare function endOfDay(ms: number, timeZone?: string): number;
|
|
27
|
+
export declare function dayKey(ms: number, timeZone?: string): string;
|
|
28
|
+
export declare function dayOffset(due: number, now: number, timeZone?: string): number;
|
|
29
|
+
export declare function addDays(ms: number, n: number, timeZone?: string): number;
|
|
30
|
+
export declare function shiftWallClockToZone(ms: number, fromZone: string, toZone: string): number;
|
package/package.json
CHANGED
|
@@ -1,6 +1,57 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlastodo/shared",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.11-rc.5",
|
|
4
|
+
"description": "Atlas shared logic: the portable task rules, recurrence, quick add, filters, i18n and theme used by every Atlas client",
|
|
5
|
+
"license": "AGPL-3.0-only",
|
|
6
|
+
"homepage": "https://github.com/atlastodo/atlas-todo/tree/main/packages/shared",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/atlastodo/atlas-todo.git",
|
|
10
|
+
"directory": "packages/shared"
|
|
11
|
+
},
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/atlastodo/atlas-todo/issues"
|
|
14
|
+
},
|
|
15
|
+
"type": "module",
|
|
16
|
+
"main": "./dist/index.js",
|
|
17
|
+
"types": "./dist/index.d.ts",
|
|
18
|
+
"exports": {
|
|
19
|
+
".": {
|
|
20
|
+
"@atlastodo/source": "./src/index.ts",
|
|
21
|
+
"types": "./dist/index.d.ts",
|
|
22
|
+
"default": "./dist/index.js"
|
|
23
|
+
},
|
|
24
|
+
"./test-vectors/*": "./test-vectors/*",
|
|
25
|
+
"./package.json": "./package.json"
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"dist",
|
|
29
|
+
"test-vectors"
|
|
30
|
+
],
|
|
31
|
+
"publishConfig": {
|
|
32
|
+
"access": "public"
|
|
33
|
+
},
|
|
34
|
+
"atlas": {
|
|
35
|
+
"testVectors": [
|
|
36
|
+
"filter_vectors.json",
|
|
37
|
+
"quick_add_vectors.json",
|
|
38
|
+
"recurrence_vectors.json",
|
|
39
|
+
"task_rules_vectors.json"
|
|
40
|
+
]
|
|
41
|
+
},
|
|
42
|
+
"scripts": {
|
|
43
|
+
"build": "bun ../../scripts/build-core-package.mjs",
|
|
44
|
+
"typecheck": "tsc --noEmit",
|
|
45
|
+
"test": "vitest run"
|
|
46
|
+
},
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"@atlastodo/client-core": "0.1.11-rc.5",
|
|
49
|
+
"@noble/hashes": "^2.4.0",
|
|
50
|
+
"fflate": "^0.8.3"
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@types/node": "^26.6.5",
|
|
54
|
+
"typescript": "^7.0.2",
|
|
55
|
+
"vitest": "^5.0.3"
|
|
56
|
+
}
|
|
57
|
+
}
|