things-api 0.9.0 → 0.10.0
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/README.md +30 -11
- package/dist/audit/log.js +56 -2
- package/dist/audit/log.js.map +1 -1
- package/dist/audit/schema.d.ts +26 -8
- package/dist/audit/schema.js +42 -1
- package/dist/audit/schema.js.map +1 -1
- package/dist/cli/commands/area.d.ts +26 -3
- package/dist/cli/commands/area.js +125 -70
- package/dist/cli/commands/area.js.map +1 -1
- package/dist/cli/commands/doctor.d.ts +1 -1
- package/dist/cli/commands/doctor.js +44 -5
- package/dist/cli/commands/doctor.js.map +1 -1
- package/dist/cli/commands/mcp.js +23 -4
- package/dist/cli/commands/mcp.js.map +1 -1
- package/dist/cli/commands/project.d.ts +18 -1
- package/dist/cli/commands/project.js +69 -31
- package/dist/cli/commands/project.js.map +1 -1
- package/dist/cli/commands/reads.js +423 -157
- package/dist/cli/commands/reads.js.map +1 -1
- package/dist/cli/commands/repeat-flags.d.ts +20 -0
- package/dist/cli/commands/repeat-flags.js +76 -0
- package/dist/cli/commands/repeat-flags.js.map +1 -0
- package/dist/cli/commands/setup.js +2 -3
- package/dist/cli/commands/setup.js.map +1 -1
- package/dist/cli/commands/show.js +47 -24
- package/dist/cli/commands/show.js.map +1 -1
- package/dist/cli/commands/todo.d.ts +1 -1
- package/dist/cli/commands/todo.js +7 -6
- package/dist/cli/commands/todo.js.map +1 -1
- package/dist/cli/commands/writes.js +378 -148
- package/dist/cli/commands/writes.js.map +1 -1
- package/dist/cli/did-you-mean.d.ts +1 -1
- package/dist/cli/excess-args.d.ts +15 -0
- package/dist/cli/excess-args.js +51 -0
- package/dist/cli/excess-args.js.map +1 -0
- package/dist/cli/glyphs.d.ts +39 -2
- package/dist/cli/glyphs.js +91 -21
- package/dist/cli/glyphs.js.map +1 -1
- package/dist/cli/help.d.ts +54 -0
- package/dist/cli/help.js +355 -0
- package/dist/cli/help.js.map +1 -0
- package/dist/cli/main.js +32 -25
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/period.d.ts +7 -0
- package/dist/cli/period.js +12 -0
- package/dist/cli/period.js.map +1 -1
- package/dist/cli/read-driver.d.ts +26 -19
- package/dist/cli/read-driver.js +67 -33
- package/dist/cli/read-driver.js.map +1 -1
- package/dist/cli/render.d.ts +92 -28
- package/dist/cli/render.js +318 -97
- package/dist/cli/render.js.map +1 -1
- package/dist/cli/resolve-invocation.d.ts +42 -4
- package/dist/cli/resolve-invocation.js +97 -14
- package/dist/cli/resolve-invocation.js.map +1 -1
- package/dist/cli/tag-filters.d.ts +59 -0
- package/dist/cli/tag-filters.js +57 -0
- package/dist/cli/tag-filters.js.map +1 -0
- package/dist/cli/verb-hint.d.ts +25 -0
- package/dist/cli/verb-hint.js +138 -0
- package/dist/cli/verb-hint.js.map +1 -0
- package/dist/cli/width.d.ts +135 -0
- package/dist/cli/width.js +313 -0
- package/dist/cli/width.js.map +1 -0
- package/dist/client.d.ts +137 -24
- package/dist/client.js +105 -16
- package/dist/client.js.map +1 -1
- package/dist/config.d.ts +11 -0
- package/dist/config.js +3 -0
- package/dist/config.js.map +1 -1
- package/dist/contracts.d.ts +87 -17
- package/dist/contracts.js +44 -1
- package/dist/contracts.js.map +1 -1
- package/dist/db/fingerprint.d.ts +12 -0
- package/dist/db/fingerprint.js +15 -1
- package/dist/db/fingerprint.js.map +1 -1
- package/dist/db/locate.js +1 -1
- package/dist/db/locate.js.map +1 -1
- package/dist/diagnose.d.ts +63 -0
- package/dist/diagnose.js +38 -1
- package/dist/diagnose.js.map +1 -1
- package/dist/index.d.ts +30 -4
- package/dist/index.js +35 -2
- package/dist/index.js.map +1 -1
- package/dist/mcp/server.d.ts +9 -1
- package/dist/mcp/server.js +691 -133
- package/dist/mcp/server.js.map +1 -1
- package/dist/model/entities.d.ts +31 -7
- package/dist/model/entities.js.map +1 -1
- package/dist/model/mappers.d.ts +7 -0
- package/dist/model/mappers.js +7 -2
- package/dist/model/mappers.js.map +1 -1
- package/dist/model/serialize.d.ts +28 -0
- package/dist/model/serialize.js +80 -0
- package/dist/model/serialize.js.map +1 -0
- package/dist/model/when-sugar.d.ts +47 -0
- package/dist/model/when-sugar.js +45 -0
- package/dist/model/when-sugar.js.map +1 -0
- package/dist/read/area-view.d.ts +8 -1
- package/dist/read/area-view.js +49 -13
- package/dist/read/area-view.js.map +1 -1
- package/dist/read/filter-contract.d.ts +117 -0
- package/dist/read/filter-contract.js +78 -0
- package/dist/read/filter-contract.js.map +1 -0
- package/dist/read/predicates.d.ts +30 -0
- package/dist/read/predicates.js +30 -0
- package/dist/read/predicates.js.map +1 -1
- package/dist/read/project-view.d.ts +12 -1
- package/dist/read/project-view.js +39 -8
- package/dist/read/project-view.js.map +1 -1
- package/dist/read/queries.d.ts +149 -6
- package/dist/read/queries.js +250 -31
- package/dist/read/queries.js.map +1 -1
- package/dist/read/sections.d.ts +51 -0
- package/dist/read/sections.js +37 -0
- package/dist/read/sections.js.map +1 -0
- package/dist/read/sidebar-order.js +2 -1
- package/dist/read/sidebar-order.js.map +1 -1
- package/dist/read/tags.d.ts +27 -3
- package/dist/read/tags.js +83 -12
- package/dist/read/tags.js.map +1 -1
- package/dist/read/truncation.d.ts +71 -0
- package/dist/read/{pagination.js → truncation.js} +103 -94
- package/dist/read/truncation.js.map +1 -0
- package/dist/read/views.d.ts +140 -16
- package/dist/read/views.js +264 -67
- package/dist/read/views.js.map +1 -1
- package/dist/surface-copy.d.ts +19 -0
- package/dist/surface-copy.js +32 -0
- package/dist/surface-copy.js.map +1 -1
- package/dist/sync-health.d.ts +78 -0
- package/dist/sync-health.js +312 -0
- package/dist/sync-health.js.map +1 -0
- package/dist/write/accessibility-probe.d.ts +12 -0
- package/dist/write/accessibility-probe.js +63 -0
- package/dist/write/accessibility-probe.js.map +1 -0
- package/dist/write/automation-probe.d.ts +7 -0
- package/dist/write/automation-probe.js +8 -1
- package/dist/write/automation-probe.js.map +1 -1
- package/dist/write/batch.js +4 -2
- package/dist/write/batch.js.map +1 -1
- package/dist/write/capabilities.d.ts +8 -0
- package/dist/write/capabilities.js +13 -6
- package/dist/write/capabilities.js.map +1 -1
- package/dist/write/commands.d.ts +2 -0
- package/dist/write/commands.js +562 -48
- package/dist/write/commands.js.map +1 -1
- package/dist/write/edit-checklist.js +3 -2
- package/dist/write/edit-checklist.js.map +1 -1
- package/dist/write/guards.d.ts +2 -7
- package/dist/write/guards.js +64 -6
- package/dist/write/guards.js.map +1 -1
- package/dist/write/heading.js +2 -0
- package/dist/write/heading.js.map +1 -1
- package/dist/write/lock.d.ts +40 -2
- package/dist/write/lock.js +91 -14
- package/dist/write/lock.js.map +1 -1
- package/dist/write/make-repeating-project.d.ts +4 -0
- package/dist/write/make-repeating-project.js +253 -0
- package/dist/write/make-repeating-project.js.map +1 -0
- package/dist/write/operations.d.ts +145 -1
- package/dist/write/operations.js +48 -0
- package/dist/write/operations.js.map +1 -1
- package/dist/write/pipeline.d.ts +19 -0
- package/dist/write/pipeline.js +109 -26
- package/dist/write/pipeline.js.map +1 -1
- package/dist/write/pre-state.d.ts +68 -3
- package/dist/write/pre-state.js +102 -3
- package/dist/write/pre-state.js.map +1 -1
- package/dist/write/reopen.js +3 -2
- package/dist/write/reopen.js.map +1 -1
- package/dist/write/reorder.js +27 -17
- package/dist/write/reorder.js.map +1 -1
- package/dist/write/repeat-rule.d.ts +22 -0
- package/dist/write/repeat-rule.js +258 -0
- package/dist/write/repeat-rule.js.map +1 -0
- package/dist/write/reversibility.js +49 -0
- package/dist/write/reversibility.js.map +1 -1
- package/dist/write/tag-refs.d.ts +47 -0
- package/dist/write/tag-refs.js +126 -0
- package/dist/write/tag-refs.js.map +1 -0
- package/dist/write/undo.d.ts +74 -5
- package/dist/write/undo.js +493 -80
- package/dist/write/undo.js.map +1 -1
- package/dist/write/vectors/registry.d.ts +17 -1
- package/dist/write/vectors/registry.js +18 -2
- package/dist/write/vectors/registry.js.map +1 -1
- package/dist/write/vectors/types.d.ts +119 -3
- package/dist/write/vectors/ui-certification.d.ts +48 -0
- package/dist/write/vectors/ui-certification.js +46 -0
- package/dist/write/vectors/ui-certification.js.map +1 -0
- package/dist/write/vectors/ui-drag.d.ts +188 -0
- package/dist/write/vectors/ui-drag.js +1095 -0
- package/dist/write/vectors/ui-drag.js.map +1 -0
- package/dist/write/vectors/ui-recipes.d.ts +70 -0
- package/dist/write/vectors/ui-recipes.js +558 -0
- package/dist/write/vectors/ui-recipes.js.map +1 -0
- package/dist/write/vectors/ui.d.ts +137 -0
- package/dist/write/vectors/ui.js +656 -0
- package/dist/write/vectors/ui.js.map +1 -0
- package/dist/write/verify/delta.d.ts +31 -3
- package/dist/write/verify/delta.js +22 -6
- package/dist/write/verify/delta.js.map +1 -1
- package/dist/write/verify/poller.js +1 -0
- package/dist/write/verify/poller.js.map +1 -1
- package/package.json +1 -1
- package/dist/cli/exit-codes.d.ts +0 -26
- package/dist/cli/exit-codes.js +0 -26
- package/dist/cli/exit-codes.js.map +0 -1
- package/dist/cli/output.d.ts +0 -42
- package/dist/cli/output.js +0 -16
- package/dist/cli/output.js.map +0 -1
- package/dist/read/pagination.d.ts +0 -104
- package/dist/read/pagination.js.map +0 -1
package/dist/read/area-view.d.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import type { DatabaseSync } from "node:sqlite";
|
|
8
8
|
import type { Area, IsoDateGroup, Project, Todo } from "../model/entities.ts";
|
|
9
|
+
import { type ViewFilter } from "./views.ts";
|
|
9
10
|
export interface AreaView {
|
|
10
11
|
area: Area;
|
|
11
12
|
/** Open, unscheduled/current direct to-dos, by index. */
|
|
@@ -22,5 +23,11 @@ export interface AreaView {
|
|
|
22
23
|
logged: Todo[];
|
|
23
24
|
trashed: Todo[];
|
|
24
25
|
}
|
|
26
|
+
/** A someday project row: open, incubated, no start date. */
|
|
27
|
+
export declare const isSomedayProjectRow: (p: Project) => boolean;
|
|
28
|
+
/** A future-scheduled project row (start date strictly after `todayIso`). */
|
|
29
|
+
export declare const isScheduledProjectRow: (p: Project, todayIso: string) => boolean;
|
|
30
|
+
/** An active project row — everything that is neither someday nor future-scheduled. */
|
|
31
|
+
export declare const isActiveProjectRow: (p: Project, todayIso: string) => boolean;
|
|
25
32
|
/** Resolves by uuid or unique (case-insensitive) title; throws like the ref resolvers. */
|
|
26
|
-
export declare function areaView(db: DatabaseSync, ref: string, now?: Date): AreaView;
|
|
33
|
+
export declare function areaView(db: DatabaseSync, ref: string, now?: Date, filter?: ViewFilter): AreaView;
|
package/dist/read/area-view.js
CHANGED
|
@@ -2,9 +2,25 @@ import { encodePackedDate, localToday, decodePackedDate } from "../model/dates.j
|
|
|
2
2
|
import { mapProject, mapTodo } from "../model/mappers.js";
|
|
3
3
|
import { fetchTagsForTasks, fetchTaskRows, makeRefResolver, resolveAreaUuid } from "./queries.js";
|
|
4
4
|
import { logBoundary, markLogged } from "./log-boundary.js";
|
|
5
|
+
import { OVERDUE } from "./predicates.js";
|
|
5
6
|
import { areaTags } from "./tags.js";
|
|
7
|
+
import { tagFilter } from "./views.js";
|
|
8
|
+
// The area card splits its open projects into three display buckets by their
|
|
9
|
+
// own schedule (the card mirrors the GUI: active projects as rows, scheduled
|
|
10
|
+
// ones under Upcoming, someday ones under Someday). Closed-but-unswept projects
|
|
11
|
+
// stay "active" (checked in place) — start/startDate only classify OPEN rows.
|
|
12
|
+
// Shared by the bounding layer (capAreaSections keeps scheduled/someday whole
|
|
13
|
+
// while capping the active rows) and the human renderer, so the split is
|
|
14
|
+
// defined ONCE and the two never drift.
|
|
15
|
+
/** A someday project row: open, incubated, no start date. */
|
|
16
|
+
export const isSomedayProjectRow = (p) => p.status === "open" && p.start === "someday" && p.startDate === null;
|
|
17
|
+
/** A future-scheduled project row (start date strictly after `todayIso`). */
|
|
18
|
+
export const isScheduledProjectRow = (p, todayIso) => p.status === "open" && p.startDate !== null && p.startDate > todayIso;
|
|
19
|
+
/** An active project row — everything that is neither someday nor future-scheduled. */
|
|
20
|
+
export const isActiveProjectRow = (p, todayIso) => !isSomedayProjectRow(p) && !isScheduledProjectRow(p, todayIso);
|
|
6
21
|
/** Resolves by uuid or unique (case-insensitive) title; throws like the ref resolvers. */
|
|
7
|
-
export function areaView(db, ref, now) {
|
|
22
|
+
export function areaView(db, ref, now, filter = {}) {
|
|
23
|
+
const overdue = filter.overdue === true;
|
|
8
24
|
const uuid = resolveAreaUuid(db, ref);
|
|
9
25
|
const row = db
|
|
10
26
|
.prepare(`SELECT uuid, title, visible, "index" FROM TMArea WHERE uuid = ?`)
|
|
@@ -15,18 +31,38 @@ export function areaView(db, ref, now) {
|
|
|
15
31
|
uuid: row.uuid,
|
|
16
32
|
title: row.title ?? "",
|
|
17
33
|
visible: row.visible !== 0,
|
|
18
|
-
tags
|
|
34
|
+
// Area tags by NAME only (tag uuids are internal).
|
|
35
|
+
tags: areaTags(db, uuid).map((t) => ({ title: t.title })),
|
|
19
36
|
};
|
|
20
37
|
const refs = makeRefResolver(db);
|
|
21
38
|
const tagsOf = (rows) => fetchTagsForTasks(db, rows.map((r) => r.uuid));
|
|
22
39
|
const boundary = logBoundary(db, now);
|
|
40
|
+
// OWN-DEADLINE UNIFORM: `--overdue` filters BOTH displayed row kinds by each
|
|
41
|
+
// row's OWN deadline (open, strictly before today) via the shared OVERDUE
|
|
42
|
+
// predicate — the area's child PROJECTS by the project's own deadline AND the
|
|
43
|
+
// loose to-dos by theirs. There is NO descent into project contents (an
|
|
44
|
+
// overdue to-do inside a non-overdue project never surfaces here — that is
|
|
45
|
+
// `project show --overdue`). Sections with no surviving rows collapse.
|
|
46
|
+
const overdueSql = overdue ? ` AND ${OVERDUE}` : "";
|
|
47
|
+
const overdueBinds = overdue ? [encodePackedDate(localToday(now))] : [];
|
|
48
|
+
// Tag scope (§9a): BOTH displayed row kinds — the child PROJECTS and the
|
|
49
|
+
// loose to-dos — are filtered by a tag carried DIRECTLY on the row (the
|
|
50
|
+
// container semantics). The area's own tags are inherited by every row it
|
|
51
|
+
// holds, so an inheritance-inclusive `--tag` would be vacuous; suppressing the
|
|
52
|
+
// container hop makes `--tag` mean "rows with this tag on themselves" (still
|
|
53
|
+
// descendant-expanded) and `--untagged` "rows with no direct tag". Computed
|
|
54
|
+
// once, spliced into both queries; NO descent into project contents (a child
|
|
55
|
+
// of a matching project is never inspected here). The tag binds trail each
|
|
56
|
+
// query's own binds.
|
|
57
|
+
const tf = tagFilter(db, filter, { container: true });
|
|
23
58
|
// Open projects PLUS closed ones the log-move sweep has not passed — the
|
|
24
|
-
// GUI keeps those checked in place (completion ≠ logged).
|
|
59
|
+
// GUI keeps those checked in place (completion ≠ logged). Under --overdue the
|
|
60
|
+
// OVERDUE predicate's own `status = 0` narrows this to open overdue projects.
|
|
25
61
|
const projectRows = fetchTaskRows(db, `t.type = 1 AND t.area = ? AND t.trashed = 0
|
|
26
|
-
AND (t.status = 0 OR t.stopDate > ?) ORDER BY t."index" ASC`, [uuid, boundary.getTime() / 1000]);
|
|
62
|
+
AND (t.status = 0 OR t.stopDate > ?)${overdueSql}${tf.sql} ORDER BY t."index" ASC`, [uuid, boundary.getTime() / 1000, ...overdueBinds, ...tf.binds]);
|
|
27
63
|
const projectTags = tagsOf(projectRows);
|
|
28
64
|
const projects = markLogged(projectRows.map((r) => mapProject(r, refs, projectTags.get(r.uuid) ?? [])), boundary);
|
|
29
|
-
const todoRows = fetchTaskRows(db, `t.type = 0 AND t.area =
|
|
65
|
+
const todoRows = fetchTaskRows(db, `t.type = 0 AND t.area = ?${overdueSql}${tf.sql} ORDER BY t."index" ASC`, [uuid, ...overdueBinds, ...tf.binds]);
|
|
30
66
|
const todoTags = tagsOf(todoRows);
|
|
31
67
|
const todos = todoRows.map((r) => ({
|
|
32
68
|
row: r,
|
|
@@ -40,8 +76,8 @@ export function areaView(db, ref, now) {
|
|
|
40
76
|
const someday = [];
|
|
41
77
|
const logged = [];
|
|
42
78
|
const trashed = [];
|
|
43
|
-
for (const { row, todo } of todos) {
|
|
44
|
-
if (
|
|
79
|
+
for (const { row: todoRow, todo } of todos) {
|
|
80
|
+
if (todoRow.trashed === 1) {
|
|
45
81
|
trashed.push(todo);
|
|
46
82
|
continue;
|
|
47
83
|
}
|
|
@@ -49,7 +85,7 @@ export function areaView(db, ref, now) {
|
|
|
49
85
|
repeating.push(todo);
|
|
50
86
|
continue;
|
|
51
87
|
}
|
|
52
|
-
if (
|
|
88
|
+
if (todoRow.status !== 0) {
|
|
53
89
|
// Completion ≠ logged: closed-but-unswept items stay checked in the
|
|
54
90
|
// active block, like the GUI.
|
|
55
91
|
if (todo.logged)
|
|
@@ -58,14 +94,14 @@ export function areaView(db, ref, now) {
|
|
|
58
94
|
active.push(todo);
|
|
59
95
|
continue;
|
|
60
96
|
}
|
|
61
|
-
if (
|
|
97
|
+
if (todoRow.start === 2 && todoRow.startDate === null) {
|
|
62
98
|
someday.push(todo);
|
|
63
99
|
continue;
|
|
64
100
|
}
|
|
65
|
-
if (
|
|
101
|
+
if (todoRow.startDate !== null && todoRow.startDate > packedToday) {
|
|
66
102
|
scheduledRows.push({
|
|
67
|
-
date: decodePackedDate(
|
|
68
|
-
ti:
|
|
103
|
+
date: decodePackedDate(todoRow.startDate) ?? "",
|
|
104
|
+
ti: todoRow.todayIndex ?? 0,
|
|
69
105
|
todo,
|
|
70
106
|
});
|
|
71
107
|
continue;
|
|
@@ -75,7 +111,7 @@ export function areaView(db, ref, now) {
|
|
|
75
111
|
logged.sort((a, b) => (b.stopped?.getTime() ?? 0) - (a.stopped?.getTime() ?? 0));
|
|
76
112
|
const scheduled = [];
|
|
77
113
|
// Within a day the UI sorts by todayIndex ASC (Upcoming drag order).
|
|
78
|
-
for (const { date, todo } of scheduledRows.
|
|
114
|
+
for (const { date, todo } of scheduledRows.toSorted((a, b) => a.date.localeCompare(b.date) || a.ti - b.ti)) {
|
|
79
115
|
const last = scheduled[scheduled.length - 1];
|
|
80
116
|
if (last && last.date === date)
|
|
81
117
|
last.items.push(todo);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"area-view.js","sourceRoot":"","sources":["../../src/read/area-view.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,gBAAgB,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAEnF,OAAO,EAAE,UAAU,EAAE,OAAO,EAAgB,MAAM,qBAAqB,CAAC;AACxE,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAClG,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"area-view.js","sourceRoot":"","sources":["../../src/read/area-view.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,gBAAgB,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAEnF,OAAO,EAAE,UAAU,EAAE,OAAO,EAAgB,MAAM,qBAAqB,CAAC;AACxE,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAClG,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC1C,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACrC,OAAO,EAAE,SAAS,EAAmB,MAAM,YAAY,CAAC;AAmBxD,6EAA6E;AAC7E,6EAA6E;AAC7E,gFAAgF;AAChF,8EAA8E;AAC9E,8EAA8E;AAC9E,yEAAyE;AACzE,wCAAwC;AAExC,6DAA6D;AAC7D,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAU,EAAW,EAAE,CACzD,CAAC,CAAC,MAAM,KAAK,MAAM,IAAI,CAAC,CAAC,KAAK,KAAK,SAAS,IAAI,CAAC,CAAC,SAAS,KAAK,IAAI,CAAC;AAEvE,6EAA6E;AAC7E,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAU,EAAE,QAAgB,EAAW,EAAE,CAC7E,CAAC,CAAC,MAAM,KAAK,MAAM,IAAI,CAAC,CAAC,SAAS,KAAK,IAAI,IAAI,CAAC,CAAC,SAAS,GAAG,QAAQ,CAAC;AAExE,uFAAuF;AACvF,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAU,EAAE,QAAgB,EAAW,EAAE,CAC1E,CAAC,mBAAmB,CAAC,CAAC,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;AAEjE,0FAA0F;AAC1F,MAAM,UAAU,QAAQ,CACtB,EAAgB,EAChB,GAAW,EACX,GAAU,EACV,SAAqB,EAAE;IAEvB,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,KAAK,IAAI,CAAC;IACxC,MAAM,IAAI,GAAG,eAAe,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;IACtC,MAAM,GAAG,GAAG,EAAE;SACX,OAAO,CAAC,iEAAiE,CAAC;SAC1E,GAAG,CAAC,IAAI,CAEE,CAAC;IACd,IAAI,GAAG,KAAK,SAAS;QAAE,MAAM,IAAI,KAAK,CAAC,qBAAqB,IAAI,EAAE,CAAC,CAAC;IACpE,MAAM,IAAI,GAAS;QACjB,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,KAAK,EAAE,GAAG,CAAC,KAAK,IAAI,EAAE;QACtB,OAAO,EAAE,GAAG,CAAC,OAAO,KAAK,CAAC;QAC1B,mDAAmD;QACnD,IAAI,EAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;KAC1D,CAAC;IAEF,MAAM,IAAI,GAAG,eAAe,CAAC,EAAE,CAAC,CAAC;IACjC,MAAM,MAAM,GAAG,CAAC,IAAe,EAAE,EAAE,CACjC,iBAAiB,CACf,EAAE,EACF,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CACxB,CAAC;IAEJ,MAAM,QAAQ,GAAG,WAAW,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;IACtC,6EAA6E;IAC7E,0EAA0E;IAC1E,8EAA8E;IAC9E,wEAAwE;IACxE,2EAA2E;IAC3E,uEAAuE;IACvE,MAAM,UAAU,GAAG,OAAO,CAAC,CAAC,CAAC,QAAQ,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACpD,MAAM,YAAY,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,gBAAgB,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACxE,yEAAyE;IACzE,wEAAwE;IACxE,0EAA0E;IAC1E,+EAA+E;IAC/E,6EAA6E;IAC7E,4EAA4E;IAC5E,6EAA6E;IAC7E,2EAA2E;IAC3E,qBAAqB;IACrB,MAAM,EAAE,GAAG,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACtD,yEAAyE;IACzE,8EAA8E;IAC9E,8EAA8E;IAC9E,MAAM,WAAW,GAAG,aAAa,CAC/B,EAAE,EACF;2CACuC,UAAU,GAAG,EAAE,CAAC,GAAG,yBAAyB,EACnF,CAAC,IAAI,EAAE,QAAQ,CAAC,OAAO,EAAE,GAAG,IAAI,EAAE,GAAG,YAAY,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,CAChE,CAAC;IACF,MAAM,WAAW,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC;IACxC,MAAM,QAAQ,GAAG,UAAU,CACzB,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,IAAI,EAAE,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,EAC1E,QAAQ,CACT,CAAC;IAEF,MAAM,QAAQ,GAAG,aAAa,CAC5B,EAAE,EACF,4BAA4B,UAAU,GAAG,EAAE,CAAC,GAAG,yBAAyB,EACxE,CAAC,IAAI,EAAE,GAAG,YAAY,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,CACrC,CAAC;IACF,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;IAClC,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACjC,GAAG,EAAE,CAAC;QACN,IAAI,EAAE,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;KACnD,CAAC,CAAC,CAAC;IACJ,UAAU,CACR,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EACxB,QAAQ,CACT,CAAC;IAEF,MAAM,WAAW,GAAG,gBAAgB,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC;IACtD,MAAM,MAAM,GAAW,EAAE,CAAC;IAC1B,MAAM,aAAa,GAAoD,EAAE,CAAC;IAC1E,MAAM,SAAS,GAAW,EAAE,CAAC;IAC7B,MAAM,OAAO,GAAW,EAAE,CAAC;IAC3B,MAAM,MAAM,GAAW,EAAE,CAAC;IAC1B,MAAM,OAAO,GAAW,EAAE,CAAC;IAE3B,KAAK,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,KAAK,EAAE,CAAC;QAC3C,IAAI,OAAO,CAAC,OAAO,KAAK,CAAC,EAAE,CAAC;YAC1B,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACnB,SAAS;QACX,CAAC;QACD,IAAI,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,CAAC;YAC9B,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACrB,SAAS;QACX,CAAC;QACD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACzB,oEAAoE;YACpE,8BAA8B;YAC9B,IAAI,IAAI,CAAC,MAAM;gBAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;;gBAC9B,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACvB,SAAS;QACX,CAAC;QACD,IAAI,OAAO,CAAC,KAAK,KAAK,CAAC,IAAI,OAAO,CAAC,SAAS,KAAK,IAAI,EAAE,CAAC;YACtD,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACnB,SAAS;QACX,CAAC;QACD,IAAI,OAAO,CAAC,SAAS,KAAK,IAAI,IAAI,OAAO,CAAC,SAAS,GAAG,WAAW,EAAE,CAAC;YAClE,aAAa,CAAC,IAAI,CAAC;gBACjB,IAAI,EAAE,gBAAgB,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,EAAE;gBAC/C,EAAE,EAAE,OAAO,CAAC,UAAU,IAAI,CAAC;gBAC3B,IAAI;aACL,CAAC,CAAC;YACH,SAAS;QACX,CAAC;QACD,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACpB,CAAC;IAED,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;IACjF,MAAM,SAAS,GAAyB,EAAE,CAAC;IAC3C,qEAAqE;IACrE,KAAK,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,aAAa,CAAC,QAAQ,CACjD,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,CACtD,EAAE,CAAC;QACF,MAAM,IAAI,GAAG,SAAS,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAC7C,IAAI,IAAI,IAAI,IAAI,CAAC,IAAI,KAAK,IAAI;YAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;;YACjD,SAAS,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC/C,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;AAC/F,CAAC"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The per-view filter contract — the ONE declarative statement of which read
|
|
3
|
+
* filters each view honors, plus the shared predicates that enforce them. Both
|
|
4
|
+
* consumer surfaces (CLI commands, MCP tools) derive their per-view guards from
|
|
5
|
+
* this table instead of hand-coding view lists, so a filter's applicability is
|
|
6
|
+
* stated once and can never drift between surfaces.
|
|
7
|
+
*
|
|
8
|
+
* The table says, per view: whether the `overdue` content scope applies; what
|
|
9
|
+
* tag semantics the view carries (inheritance-inclusive `inherited`, container-
|
|
10
|
+
* direct `direct`, or `rejected` where the view has no per-row tags to filter);
|
|
11
|
+
* the bound model the view truncates under (`flat` row cap, `grouped` per-block
|
|
12
|
+
* caps, or `none`); and whether the view offers the status-widening flags
|
|
13
|
+
* (search's logged/trashed/all) that `overdue` refuses to combine with.
|
|
14
|
+
*
|
|
15
|
+
* The exact usage-error COPY stays surface-owned (consumer voice differs per
|
|
16
|
+
* surface — CLI `--tag`, MCP `tag`; docs/design/surface-copy.md): a surface
|
|
17
|
+
* hands {@link validateViewArgs} its own phrasing via {@link FilterVocab}, and
|
|
18
|
+
* this module owns only the DECISION of which message applies.
|
|
19
|
+
*/
|
|
20
|
+
import type { ViewFilter } from "./views.ts";
|
|
21
|
+
/** Every read view whose filter applicability the contract governs. */
|
|
22
|
+
export type ViewName = "today" | "inbox" | "anytime" | "someday" | "upcoming" | "logbook" | "trash" | "search" | "changes" | "projects" | "areas" | "project-show" | "area-show";
|
|
23
|
+
/**
|
|
24
|
+
* How a view's tag filter behaves:
|
|
25
|
+
* - `inherited` — FLAT views: a `--tag` matches direct OR container-inherited
|
|
26
|
+
* membership (plus hierarchy descendants).
|
|
27
|
+
* - `direct` — CONTAINER views (`project show`, `area show`): a `--tag` matches
|
|
28
|
+
* a tag carried DIRECTLY on the row (the container's own inherited tags are
|
|
29
|
+
* suppressed, since every child inherits them).
|
|
30
|
+
* - `rejected` — the view has no per-row tag list to filter (an area list row,
|
|
31
|
+
* a tag list row, the changes feed); tag flags do not apply.
|
|
32
|
+
*/
|
|
33
|
+
export type TagSemantics = "inherited" | "direct" | "rejected";
|
|
34
|
+
/** The truncation model a view bounds under. */
|
|
35
|
+
export type BoundModel = "flat" | "grouped" | "none";
|
|
36
|
+
/** One row of {@link FILTER_CONTRACT}: a single view's filter applicability. */
|
|
37
|
+
export interface ViewFilterSpec {
|
|
38
|
+
/** Whether the `overdue` content scope (open, past-deadline) applies. */
|
|
39
|
+
readonly overdue: boolean;
|
|
40
|
+
/** The view's tag-filter semantics. */
|
|
41
|
+
readonly tag: TagSemantics;
|
|
42
|
+
/** The bound model the view truncates under. */
|
|
43
|
+
readonly bound: BoundModel;
|
|
44
|
+
/**
|
|
45
|
+
* Whether the view offers the status-widening flags (logged/trashed/all).
|
|
46
|
+
* Only `search` does; `overdue` refuses to combine with them there.
|
|
47
|
+
*/
|
|
48
|
+
readonly statusWidening: boolean;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* The single source of truth for read-filter applicability, one row per view.
|
|
52
|
+
* Consumed by both surfaces' per-view guards (CLI command actions, MCP tool
|
|
53
|
+
* handlers) so no surface hand-maintains its own view list.
|
|
54
|
+
*/
|
|
55
|
+
export declare const FILTER_CONTRACT: Record<ViewName, ViewFilterSpec>;
|
|
56
|
+
/**
|
|
57
|
+
* The three universal tag-filter inputs in their canonical (CLI-flag) spelling.
|
|
58
|
+
* The CLI's parsed options match this verbatim; the MCP surface maps its
|
|
59
|
+
* snake_case inputs (`exact_tag`) onto it before calling the shared predicates.
|
|
60
|
+
*/
|
|
61
|
+
export interface TagPresence {
|
|
62
|
+
tag?: readonly string[] | undefined;
|
|
63
|
+
exactTag?: boolean | undefined;
|
|
64
|
+
untagged?: boolean | undefined;
|
|
65
|
+
}
|
|
66
|
+
/** True when any tag-PRESENCE flag was passed (a positive filter, not a negation). */
|
|
67
|
+
export declare function hasTagPresence(a: TagPresence): boolean;
|
|
68
|
+
/**
|
|
69
|
+
* True when the tag flags are mutually incoherent: the `untagged` negation
|
|
70
|
+
* inverts a tag presence, so it does not combine with a tag-presence flag. A
|
|
71
|
+
* pure predicate — each surface emits its own usage error in its own voice.
|
|
72
|
+
*/
|
|
73
|
+
export declare function tagFlagConflict(a: TagPresence): boolean;
|
|
74
|
+
/** Build the {@link ViewFilter} tag fields from the parsed flags (empty keys omitted). */
|
|
75
|
+
export declare function tagFilterFields(a: TagPresence): ViewFilter;
|
|
76
|
+
/** The parsed read-filter inputs {@link validateViewArgs} inspects. */
|
|
77
|
+
export interface FilterArgs extends TagPresence {
|
|
78
|
+
overdue?: boolean | undefined;
|
|
79
|
+
/** search only: the status-widening flags overdue refuses to combine with. */
|
|
80
|
+
logged?: boolean | undefined;
|
|
81
|
+
trashed?: boolean | undefined;
|
|
82
|
+
all?: boolean | undefined;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* The surface's exact usage-error copy for each conflict {@link validateViewArgs}
|
|
86
|
+
* can detect. Built per call by the surface (it knows the view/kind and its own
|
|
87
|
+
* flag spellings), so the SHARED module decides WHICH message applies while the
|
|
88
|
+
* message text stays consumer-voiced per surface.
|
|
89
|
+
*/
|
|
90
|
+
export interface FilterVocab {
|
|
91
|
+
/** `untagged` combined with a tag-presence flag. */
|
|
92
|
+
untaggedConflict: string;
|
|
93
|
+
/** `overdue` passed to a view that does not honor it. */
|
|
94
|
+
overdueRejected: string;
|
|
95
|
+
/** `overdue` combined with a status-widening flag (search). */
|
|
96
|
+
overdueStatusWiden: string;
|
|
97
|
+
}
|
|
98
|
+
/** {@link validateViewArgs} outcome: the built filter, or the usage message to emit. */
|
|
99
|
+
export type ViewValidation = {
|
|
100
|
+
ok: true;
|
|
101
|
+
filter: ViewFilter;
|
|
102
|
+
} | {
|
|
103
|
+
ok: false;
|
|
104
|
+
message: string;
|
|
105
|
+
};
|
|
106
|
+
/**
|
|
107
|
+
* Validate a view's filter arguments against {@link FILTER_CONTRACT} and, when
|
|
108
|
+
* coherent, build the {@link ViewFilter} tag+overdue portion. The checks (and
|
|
109
|
+
* their precedence) live here so both surfaces stay in lockstep; the surface
|
|
110
|
+
* supplies its own exact copy via {@link FilterVocab}.
|
|
111
|
+
*
|
|
112
|
+
* Does NOT enforce tag-REJECTION (a view refusing tag flags outright): that
|
|
113
|
+
* lives at the two call sites that currently reject — the areas/projects/tags
|
|
114
|
+
* collection listers — so this stays a pure no-op for the flat/container views
|
|
115
|
+
* that merely ignore tags on a sub-path (e.g. `trash`).
|
|
116
|
+
*/
|
|
117
|
+
export declare function validateViewArgs(view: ViewName, args: FilterArgs, vocab: FilterVocab): ViewValidation;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The single source of truth for read-filter applicability, one row per view.
|
|
3
|
+
* Consumed by both surfaces' per-view guards (CLI command actions, MCP tool
|
|
4
|
+
* handlers) so no surface hand-maintains its own view list.
|
|
5
|
+
*/
|
|
6
|
+
export const FILTER_CONTRACT = {
|
|
7
|
+
// Current-work flat views: overdue applies, tags are inheritance-inclusive.
|
|
8
|
+
today: { overdue: true, tag: "inherited", bound: "flat", statusWidening: false },
|
|
9
|
+
inbox: { overdue: true, tag: "inherited", bound: "flat", statusWidening: false },
|
|
10
|
+
// Grouped catalogues: overdue applies, per-block caps.
|
|
11
|
+
anytime: { overdue: true, tag: "inherited", bound: "grouped", statusWidening: false },
|
|
12
|
+
someday: { overdue: true, tag: "inherited", bound: "grouped", statusWidening: false },
|
|
13
|
+
// Forward-looking / closed-item flat views: overdue deliberately does NOT apply.
|
|
14
|
+
upcoming: { overdue: false, tag: "inherited", bound: "flat", statusWidening: false },
|
|
15
|
+
logbook: { overdue: false, tag: "inherited", bound: "flat", statusWidening: false },
|
|
16
|
+
trash: { overdue: false, tag: "rejected", bound: "flat", statusWidening: false },
|
|
17
|
+
// Search: overdue applies but refuses the status-widening flags.
|
|
18
|
+
search: { overdue: true, tag: "inherited", bound: "flat", statusWidening: true },
|
|
19
|
+
// Sync feed: neither overdue nor tags apply.
|
|
20
|
+
changes: { overdue: false, tag: "rejected", bound: "flat", statusWidening: false },
|
|
21
|
+
// Projects LIST is a FLAT view — each project row filtered by its own
|
|
22
|
+
// (inheritance-inclusive) tags; overdue keeps past-deadline projects.
|
|
23
|
+
projects: { overdue: true, tag: "inherited", bound: "none", statusWidening: false },
|
|
24
|
+
// Areas LIST rows carry no deadline and no per-row tag filter.
|
|
25
|
+
areas: { overdue: false, tag: "rejected", bound: "none", statusWidening: false },
|
|
26
|
+
// Single-container views: overdue and DIRECT-tag filtering apply to children.
|
|
27
|
+
"project-show": { overdue: true, tag: "direct", bound: "grouped", statusWidening: false },
|
|
28
|
+
"area-show": { overdue: true, tag: "direct", bound: "grouped", statusWidening: false },
|
|
29
|
+
};
|
|
30
|
+
/** True when any tag-PRESENCE flag was passed (a positive filter, not a negation). */
|
|
31
|
+
export function hasTagPresence(a) {
|
|
32
|
+
return (a.tag?.length ?? 0) > 0 || a.exactTag === true;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* True when the tag flags are mutually incoherent: the `untagged` negation
|
|
36
|
+
* inverts a tag presence, so it does not combine with a tag-presence flag. A
|
|
37
|
+
* pure predicate — each surface emits its own usage error in its own voice.
|
|
38
|
+
*/
|
|
39
|
+
export function tagFlagConflict(a) {
|
|
40
|
+
return a.untagged === true && hasTagPresence(a);
|
|
41
|
+
}
|
|
42
|
+
/** Build the {@link ViewFilter} tag fields from the parsed flags (empty keys omitted). */
|
|
43
|
+
export function tagFilterFields(a) {
|
|
44
|
+
return {
|
|
45
|
+
...(a.tag !== undefined && a.tag.length > 0 && { tags: [...a.tag] }),
|
|
46
|
+
...(a.exactTag === true && { exactTag: true }),
|
|
47
|
+
...(a.untagged === true && { untagged: true }),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Validate a view's filter arguments against {@link FILTER_CONTRACT} and, when
|
|
52
|
+
* coherent, build the {@link ViewFilter} tag+overdue portion. The checks (and
|
|
53
|
+
* their precedence) live here so both surfaces stay in lockstep; the surface
|
|
54
|
+
* supplies its own exact copy via {@link FilterVocab}.
|
|
55
|
+
*
|
|
56
|
+
* Does NOT enforce tag-REJECTION (a view refusing tag flags outright): that
|
|
57
|
+
* lives at the two call sites that currently reject — the areas/projects/tags
|
|
58
|
+
* collection listers — so this stays a pure no-op for the flat/container views
|
|
59
|
+
* that merely ignore tags on a sub-path (e.g. `trash`).
|
|
60
|
+
*/
|
|
61
|
+
export function validateViewArgs(view, args, vocab) {
|
|
62
|
+
const spec = FILTER_CONTRACT[view];
|
|
63
|
+
if (tagFlagConflict(args))
|
|
64
|
+
return { ok: false, message: vocab.untaggedConflict };
|
|
65
|
+
if (args.overdue === true && !spec.overdue) {
|
|
66
|
+
return { ok: false, message: vocab.overdueRejected };
|
|
67
|
+
}
|
|
68
|
+
if (args.overdue === true &&
|
|
69
|
+
spec.statusWidening &&
|
|
70
|
+
(args.logged === true || args.trashed === true || args.all === true)) {
|
|
71
|
+
return { ok: false, message: vocab.overdueStatusWiden };
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
ok: true,
|
|
75
|
+
filter: { ...tagFilterFields(args), ...(args.overdue === true && { overdue: true }) },
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=filter-contract.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filter-contract.js","sourceRoot":"","sources":["../../src/read/filter-contract.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,6 +2,36 @@
|
|
|
2
2
|
export declare const LIVE = "t.type IN (0, 1) AND t.trashed = 0 AND (t.rt1_recurrenceRule IS NULL AND t.repeater IS NULL)";
|
|
3
3
|
/** {@link LIVE} restricted to open (status = 0) rows. */
|
|
4
4
|
export declare const OPEN = "t.type IN (0, 1) AND t.trashed = 0 AND (t.rt1_recurrenceRule IS NULL AND t.repeater IS NULL) AND t.status = 0";
|
|
5
|
+
/**
|
|
6
|
+
* The GUI-visible set: open rows PLUS closed (completed/canceled) rows the
|
|
7
|
+
* periodic log-move sweep has NOT yet carried to the Logbook — completion ≠
|
|
8
|
+
* logged (src/read/log-boundary.ts). A closed row keeps its slot in its list
|
|
9
|
+
* until the sweep passes it, exactly as the GUI keeps it checked in place; it
|
|
10
|
+
* leaves when the sweep boundary advances past its stopDate (boundary-relative,
|
|
11
|
+
* no new state). This is {@link LIVE} widened to that set: swept iff
|
|
12
|
+
* status IN (2,3) AND stopDate IS NOT NULL AND stopDate <= boundary — the
|
|
13
|
+
* negation of the {@link markLogged} `logged` predicate, so an included closed
|
|
14
|
+
* row always materializes with `logged=false`. Mirrors the project/area card
|
|
15
|
+
* precedent (project-view.ts / area-view.ts keep a closed-but-unswept row
|
|
16
|
+
* checked in place). ONE bind: the log boundary as epoch seconds
|
|
17
|
+
* (logBoundary(db, now).getTime() / 1000). Ruling 2026-07-14 (Mike): GUI parity
|
|
18
|
+
* — `today` and `anytime` show checked-but-unswept rows.
|
|
19
|
+
*/
|
|
20
|
+
export declare const OPEN_OR_UNSWEPT = "t.type IN (0, 1) AND t.trashed = 0 AND (t.rt1_recurrenceRule IS NULL AND t.repeater IS NULL) AND NOT (t.status IN (2, 3) AND t.stopDate IS NOT NULL AND t.stopDate <= ?)";
|
|
21
|
+
/**
|
|
22
|
+
* The `--overdue` content scope: OPEN items whose deadline falls strictly
|
|
23
|
+
* BEFORE today. Due-TODAY is deliberately NOT overdue (`<`, not `<=`) —
|
|
24
|
+
* mirroring the app's own Today badge split, where a deadline EQUAL to today
|
|
25
|
+
* reads as "due" and only an EARLIER deadline reads as "overdue". One bind:
|
|
26
|
+
* today as a packed-date int (encodePackedDate(localToday(now))), so the
|
|
27
|
+
* boundary rides the same injected clock every other view uses — never a
|
|
28
|
+
* hardcoded date. The `t.status = 0` clause re-tightens the OPEN_OR_UNSWEPT
|
|
29
|
+
* views (today/anytime) so a checked-but-unswept row that happens to sit past
|
|
30
|
+
* a deadline is excluded — overdue is remaining, OPEN work. Trash exclusion is
|
|
31
|
+
* the hosting view's job (every view this composes into already drops trashed
|
|
32
|
+
* and derived-trashed rows), so it is not repeated here.
|
|
33
|
+
*/
|
|
34
|
+
export declare const OVERDUE = "t.deadline IS NOT NULL AND t.deadline < ? AND t.status = 0";
|
|
5
35
|
/** An item's own anytime membership: unscheduled-active, or dated <= today. */
|
|
6
36
|
export declare const ANYTIME_SELF: (col: string) => string;
|
|
7
37
|
/**
|
package/dist/read/predicates.js
CHANGED
|
@@ -12,6 +12,36 @@ import { NOT_TEMPLATE } from "./queries.js";
|
|
|
12
12
|
export const LIVE = `t.type IN (0, 1) AND t.trashed = 0 AND ${NOT_TEMPLATE}`;
|
|
13
13
|
/** {@link LIVE} restricted to open (status = 0) rows. */
|
|
14
14
|
export const OPEN = `${LIVE} AND t.status = 0`;
|
|
15
|
+
/**
|
|
16
|
+
* The GUI-visible set: open rows PLUS closed (completed/canceled) rows the
|
|
17
|
+
* periodic log-move sweep has NOT yet carried to the Logbook — completion ≠
|
|
18
|
+
* logged (src/read/log-boundary.ts). A closed row keeps its slot in its list
|
|
19
|
+
* until the sweep passes it, exactly as the GUI keeps it checked in place; it
|
|
20
|
+
* leaves when the sweep boundary advances past its stopDate (boundary-relative,
|
|
21
|
+
* no new state). This is {@link LIVE} widened to that set: swept iff
|
|
22
|
+
* status IN (2,3) AND stopDate IS NOT NULL AND stopDate <= boundary — the
|
|
23
|
+
* negation of the {@link markLogged} `logged` predicate, so an included closed
|
|
24
|
+
* row always materializes with `logged=false`. Mirrors the project/area card
|
|
25
|
+
* precedent (project-view.ts / area-view.ts keep a closed-but-unswept row
|
|
26
|
+
* checked in place). ONE bind: the log boundary as epoch seconds
|
|
27
|
+
* (logBoundary(db, now).getTime() / 1000). Ruling 2026-07-14 (Mike): GUI parity
|
|
28
|
+
* — `today` and `anytime` show checked-but-unswept rows.
|
|
29
|
+
*/
|
|
30
|
+
export const OPEN_OR_UNSWEPT = `${LIVE} AND NOT (t.status IN (2, 3) AND t.stopDate IS NOT NULL AND t.stopDate <= ?)`;
|
|
31
|
+
/**
|
|
32
|
+
* The `--overdue` content scope: OPEN items whose deadline falls strictly
|
|
33
|
+
* BEFORE today. Due-TODAY is deliberately NOT overdue (`<`, not `<=`) —
|
|
34
|
+
* mirroring the app's own Today badge split, where a deadline EQUAL to today
|
|
35
|
+
* reads as "due" and only an EARLIER deadline reads as "overdue". One bind:
|
|
36
|
+
* today as a packed-date int (encodePackedDate(localToday(now))), so the
|
|
37
|
+
* boundary rides the same injected clock every other view uses — never a
|
|
38
|
+
* hardcoded date. The `t.status = 0` clause re-tightens the OPEN_OR_UNSWEPT
|
|
39
|
+
* views (today/anytime) so a checked-but-unswept row that happens to sit past
|
|
40
|
+
* a deadline is excluded — overdue is remaining, OPEN work. Trash exclusion is
|
|
41
|
+
* the hosting view's job (every view this composes into already drops trashed
|
|
42
|
+
* and derived-trashed rows), so it is not repeated here.
|
|
43
|
+
*/
|
|
44
|
+
export const OVERDUE = `t.deadline IS NOT NULL AND t.deadline < ? AND t.status = 0`;
|
|
15
45
|
/** An item's own anytime membership: unscheduled-active, or dated <= today. */
|
|
16
46
|
export const ANYTIME_SELF = (col) => `((${col}.start = 1 AND (${col}.startDate IS NULL OR ${col}.startDate <= ?))
|
|
17
47
|
OR (${col}.start = 2 AND ${col}.startDate IS NOT NULL AND ${col}.startDate <= ?))`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"predicates.js","sourceRoot":"","sources":["../../src/read/predicates.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE5C,oFAAoF;AACpF,MAAM,CAAC,MAAM,IAAI,GAAG,0CAA0C,YAAY,EAAE,CAAC;AAE7E,yDAAyD;AACzD,MAAM,CAAC,MAAM,IAAI,GAAG,GAAG,IAAI,mBAAmB,CAAC;AAE/C,+EAA+E;AAC/E,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,GAAW,EAAU,EAAE,CAClD,KAAK,GAAG,mBAAmB,GAAG,yBAAyB,GAAG;UAClD,GAAG,kBAAkB,GAAG,8BAA8B,GAAG,mBAAmB,CAAC;AAEvF;;;GAGG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,gFAAgF,CAAC;AAE5G;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,IAAI,WAAW;6CACR,WAAW;8CACV,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC;AAEpE;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG;;QAE3B,WAAW;+CAC4B,WAAW,uBAAuB,CAAC"}
|
|
1
|
+
{"version":3,"file":"predicates.js","sourceRoot":"","sources":["../../src/read/predicates.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE5C,oFAAoF;AACpF,MAAM,CAAC,MAAM,IAAI,GAAG,0CAA0C,YAAY,EAAE,CAAC;AAE7E,yDAAyD;AACzD,MAAM,CAAC,MAAM,IAAI,GAAG,GAAG,IAAI,mBAAmB,CAAC;AAE/C;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,GAAG,IAAI,8EAA8E,CAAC;AAErH;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,4DAA4D,CAAC;AAEpF,+EAA+E;AAC/E,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,GAAW,EAAU,EAAE,CAClD,KAAK,GAAG,mBAAmB,GAAG,yBAAyB,GAAG;UAClD,GAAG,kBAAkB,GAAG,8BAA8B,GAAG,mBAAmB,CAAC;AAEvF;;;GAGG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,gFAAgF,CAAC;AAE5G;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,IAAI,WAAW;6CACR,WAAW;8CACV,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC;AAEpE;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG;;QAE3B,WAAW;+CAC4B,WAAW,uBAAuB,CAAC"}
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import type { DatabaseSync } from "node:sqlite";
|
|
11
11
|
import type { Heading, IsoDateGroup, Project, Todo } from "../model/entities.ts";
|
|
12
|
+
import { type ViewFilter } from "./views.ts";
|
|
12
13
|
export interface ProjectView {
|
|
13
14
|
project: Project;
|
|
14
15
|
/** Open, unscheduled/current children not under a heading, by index. */
|
|
@@ -27,8 +28,18 @@ export interface ProjectView {
|
|
|
27
28
|
};
|
|
28
29
|
logged: Todo[];
|
|
29
30
|
trashed: Todo[];
|
|
31
|
+
/**
|
|
32
|
+
* PLOG1 (additive): count of untrashed OPEN (status=0) children this project
|
|
33
|
+
* still holds while it is ITSELF completed or canceled — including once swept
|
|
34
|
+
* to the Logbook. Such items are invisible in every live app view (Today /
|
|
35
|
+
* Anytime / Inbox / Upcoming), reachable only by drilling into this card
|
|
36
|
+
* (the app's GUI "Put Back" into a completed parent can strand them —
|
|
37
|
+
* docs/lab/plog1-research.md). 0 when the project is open, or holds no such
|
|
38
|
+
* child.
|
|
39
|
+
*/
|
|
40
|
+
openChildrenWhileResolved: number;
|
|
30
41
|
}
|
|
31
42
|
export declare class ProjectNotFoundError extends Error {
|
|
32
43
|
constructor(uuid: string);
|
|
33
44
|
}
|
|
34
|
-
export declare function projectView(db: DatabaseSync, uuid: string, now?: Date): ProjectView;
|
|
45
|
+
export declare function projectView(db: DatabaseSync, uuid: string, now?: Date, filter?: ViewFilter): ProjectView;
|
|
@@ -2,14 +2,17 @@ import { encodePackedDate, localToday, decodePackedDate } from "../model/dates.j
|
|
|
2
2
|
import { mapHeading, mapProject, mapTodo } from "../model/mappers.js";
|
|
3
3
|
import { fetchTagsForTasks, fetchTaskByUuid, fetchTaskRows, makeRefResolver } from "./queries.js";
|
|
4
4
|
import { logBoundary, markLogged } from "./log-boundary.js";
|
|
5
|
+
import { OVERDUE } from "./predicates.js";
|
|
5
6
|
import { inheritedTagsFor } from "./tags.js";
|
|
7
|
+
import { tagFilter } from "./views.js";
|
|
6
8
|
export class ProjectNotFoundError extends Error {
|
|
7
9
|
constructor(uuid) {
|
|
8
10
|
super(`no project with uuid ${uuid}`);
|
|
9
11
|
this.name = "ProjectNotFoundError";
|
|
10
12
|
}
|
|
11
13
|
}
|
|
12
|
-
export function projectView(db, uuid, now) {
|
|
14
|
+
export function projectView(db, uuid, now, filter = {}) {
|
|
15
|
+
const overdue = filter.overdue === true;
|
|
13
16
|
const projectRow = fetchTaskByUuid(db, uuid);
|
|
14
17
|
if (!projectRow || projectRow.type !== 1)
|
|
15
18
|
throw new ProjectNotFoundError(uuid);
|
|
@@ -21,10 +24,26 @@ export function projectView(db, uuid, now) {
|
|
|
21
24
|
project.inheritedTags = inheritedTagsFor(db, projectRow);
|
|
22
25
|
const headingRows = fetchTaskRows(db, `t.type = 2 AND t.project = ? AND t.trashed = 0 ORDER BY t."index" ASC`, [uuid]);
|
|
23
26
|
const headings = headingRows.map((h) => mapHeading(h, refs));
|
|
27
|
+
// OWN-DEADLINE UNIFORM: `--overdue` narrows the child TO-DOS to those whose
|
|
28
|
+
// OWN deadline is overdue (open, strictly before today) via the shared
|
|
29
|
+
// OVERDUE predicate — NO recursion into anything, and the project header
|
|
30
|
+
// itself always renders. Headings that keep no surviving child collapse
|
|
31
|
+
// (below). Its single packed-today bind trails the two uuid binds.
|
|
32
|
+
const overdueSql = overdue ? ` AND ${OVERDUE}` : "";
|
|
33
|
+
const overdueBinds = overdue ? [encodePackedDate(localToday(now))] : [];
|
|
34
|
+
// Tag scope (§9a): the child to-dos are filtered by a tag carried DIRECTLY on
|
|
35
|
+
// the row — the container semantics. The project's own tags are inherited by
|
|
36
|
+
// EVERY child, so an inheritance-inclusive `--tag` would be vacuous here;
|
|
37
|
+
// suppressing the container hop makes `--tag` mean "children with this tag on
|
|
38
|
+
// themselves" (still descendant-expanded), and `--untagged` "children with no
|
|
39
|
+
// direct tag". No recursion; the header always renders. The tag binds trail
|
|
40
|
+
// the two uuid binds and lead the overdue bind, matching their left-to-right
|
|
41
|
+
// order in the SQL.
|
|
42
|
+
const tf = tagFilter(db, filter, { container: true });
|
|
24
43
|
const childRows = fetchTaskRows(db, `t.type = 0 AND (t.project = ? OR t.heading IN (
|
|
25
44
|
SELECT uuid FROM TMTask WHERE type = 2 AND project = ?
|
|
26
|
-
))
|
|
27
|
-
ORDER BY t."index" ASC`, [uuid, uuid]);
|
|
45
|
+
))${tf.sql}${overdueSql}
|
|
46
|
+
ORDER BY t."index" ASC`, [uuid, uuid, ...tf.binds, ...overdueBinds]);
|
|
28
47
|
const childTags = tagsOf(childRows);
|
|
29
48
|
const boundary = logBoundary(db, now);
|
|
30
49
|
const todos = childRows.map((r) => ({
|
|
@@ -40,6 +59,10 @@ export function projectView(db, uuid, now) {
|
|
|
40
59
|
const someday = [];
|
|
41
60
|
const logged = [];
|
|
42
61
|
const trashed = [];
|
|
62
|
+
// Every untrashed, non-template OPEN child, regardless of which bucket it
|
|
63
|
+
// lands in below — surfaced (only when the project is itself resolved) as
|
|
64
|
+
// the PLOG1 stranded-open-child count.
|
|
65
|
+
let openChildren = 0;
|
|
43
66
|
for (const { row, todo } of todos) {
|
|
44
67
|
if (row.trashed === 1) {
|
|
45
68
|
trashed.push(todo);
|
|
@@ -49,6 +72,8 @@ export function projectView(db, uuid, now) {
|
|
|
49
72
|
repeating.push(todo);
|
|
50
73
|
continue;
|
|
51
74
|
}
|
|
75
|
+
if (row.status === 0)
|
|
76
|
+
openChildren += 1;
|
|
52
77
|
if (row.status !== 0) {
|
|
53
78
|
// Completion ≠ logged: closed items the log-move sweep has not
|
|
54
79
|
// passed stay checked IN PLACE (their heading / the active block),
|
|
@@ -90,23 +115,29 @@ export function projectView(db, uuid, now) {
|
|
|
90
115
|
logged.sort((a, b) => (b.stopped?.getTime() ?? 0) - (a.stopped?.getTime() ?? 0));
|
|
91
116
|
const scheduled = [];
|
|
92
117
|
// Within a day the UI sorts by todayIndex ASC (Upcoming drag order).
|
|
93
|
-
for (const { date, todo } of scheduledRows.
|
|
118
|
+
for (const { date, todo } of scheduledRows.toSorted((a, b) => a.date.localeCompare(b.date) || a.ti - b.ti)) {
|
|
94
119
|
const last = scheduled[scheduled.length - 1];
|
|
95
120
|
if (last && last.date === date)
|
|
96
121
|
last.items.push(todo);
|
|
97
122
|
else
|
|
98
123
|
scheduled.push({ date, items: [todo] });
|
|
99
124
|
}
|
|
125
|
+
// Under any content scope (`--overdue` or a tag filter), a heading whose
|
|
126
|
+
// children were all filtered out collapses rather than rendering an empty
|
|
127
|
+
// section; a heading with a surviving child is kept. With no scope active
|
|
128
|
+
// every heading renders (its own empty state).
|
|
129
|
+
const contentScoped = overdue || tf.sql !== "";
|
|
130
|
+
const headingGroups = headings
|
|
131
|
+
.map((heading) => ({ heading, items: byHeading.get(heading.uuid) ?? [] }))
|
|
132
|
+
.filter((g) => !contentScoped || g.items.length > 0);
|
|
100
133
|
return {
|
|
101
134
|
project,
|
|
102
135
|
active,
|
|
103
|
-
headings:
|
|
104
|
-
heading,
|
|
105
|
-
items: byHeading.get(heading.uuid) ?? [],
|
|
106
|
-
})),
|
|
136
|
+
headings: headingGroups,
|
|
107
137
|
later: { scheduled, repeating, someday },
|
|
108
138
|
logged,
|
|
109
139
|
trashed,
|
|
140
|
+
openChildrenWhileResolved: project.status === "open" ? 0 : openChildren,
|
|
110
141
|
};
|
|
111
142
|
}
|
|
112
143
|
//# sourceMappingURL=project-view.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project-view.js","sourceRoot":"","sources":["../../src/read/project-view.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,gBAAgB,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAEnF,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,OAAO,EAAgB,MAAM,qBAAqB,CAAC;AACpF,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAClG,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"project-view.js","sourceRoot":"","sources":["../../src/read/project-view.ts"],"names":[],"mappings":"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"}
|