things-api 0.9.0 → 0.11.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 +57 -12
- 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/clock.d.ts +8 -0
- package/dist/cli/clock.js +24 -0
- package/dist/cli/clock.js.map +1 -0
- package/dist/cli/commands/area.d.ts +26 -3
- package/dist/cli/commands/area.js +129 -75
- 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/install-skill.d.ts +45 -0
- package/dist/cli/commands/install-skill.js +207 -0
- package/dist/cli/commands/install-skill.js.map +1 -0
- 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 +74 -35
- package/dist/cli/commands/project.js.map +1 -1
- package/dist/cli/commands/reads.d.ts +15 -2
- package/dist/cli/commands/reads.js +459 -165
- 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 +8 -4
- package/dist/cli/commands/setup.js.map +1 -1
- package/dist/cli/commands/show.js +53 -30
- package/dist/cli/commands/show.js.map +1 -1
- package/dist/cli/commands/todo.d.ts +1 -1
- package/dist/cli/commands/todo.js +20 -13
- package/dist/cli/commands/todo.js.map +1 -1
- package/dist/cli/commands/writes.js +388 -161
- 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 +55 -2
- package/dist/cli/glyphs.js +114 -21
- package/dist/cli/glyphs.js.map +1 -1
- package/dist/cli/help.d.ts +54 -0
- package/dist/cli/help.js +405 -0
- package/dist/cli/help.js.map +1 -0
- package/dist/cli/main.js +64 -26
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/move-hint.d.ts +45 -0
- package/dist/cli/move-hint.js +196 -0
- package/dist/cli/move-hint.js.map +1 -0
- package/dist/cli/period.d.ts +9 -8
- package/dist/cli/period.js +56 -30
- package/dist/cli/period.js.map +1 -1
- package/dist/cli/read-driver.d.ts +26 -19
- package/dist/cli/read-driver.js +77 -33
- package/dist/cli/read-driver.js.map +1 -1
- package/dist/cli/render.d.ts +92 -28
- package/dist/cli/render.js +333 -103
- 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/skill-check.d.ts +21 -0
- package/dist/cli/skill-check.js +82 -0
- package/dist/cli/skill-check.js.map +1 -0
- package/dist/cli/skill.d.ts +62 -0
- package/dist/cli/skill.js +142 -0
- package/dist/cli/skill.js.map +1 -0
- package/dist/cli/tag-filters.d.ts +63 -0
- package/dist/cli/tag-filters.js +59 -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 +163 -0
- package/dist/cli/verb-hint.js.map +1 -0
- package/dist/cli/version.d.ts +4 -0
- package/dist/cli/version.js +18 -0
- package/dist/cli/version.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 +160 -24
- package/dist/client.js +132 -19
- 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 +97 -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 +36 -5
- package/dist/index.js +38 -2
- package/dist/index.js.map +1 -1
- package/dist/mcp/server.d.ts +9 -1
- package/dist/mcp/server.js +1038 -436
- package/dist/mcp/server.js.map +1 -1
- package/dist/model/clock.d.ts +61 -0
- package/dist/model/clock.js +71 -0
- package/dist/model/clock.js.map +1 -0
- package/dist/model/dates.d.ts +26 -3
- package/dist/model/dates.js +84 -3
- package/dist/model/dates.js.map +1 -1
- package/dist/model/entities.d.ts +48 -10
- package/dist/model/entities.js.map +1 -1
- package/dist/model/mappers.d.ts +9 -2
- package/dist/model/mappers.js +32 -9
- 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 +55 -17
- package/dist/read/area-view.js.map +1 -1
- package/dist/read/detail.d.ts +1 -1
- package/dist/read/detail.js +11 -4
- package/dist/read/detail.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/log-boundary.d.ts +1 -1
- package/dist/read/log-boundary.js +13 -3
- package/dist/read/log-boundary.js.map +1 -1
- 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 +45 -12
- package/dist/read/project-view.js.map +1 -1
- package/dist/read/queries.d.ts +152 -7
- package/dist/read/queries.js +277 -36
- 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/snapshot.d.ts +1 -1
- package/dist/read/snapshot.js +9 -2
- package/dist/read/snapshot.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 +147 -22
- package/dist/read/views.js +282 -80
- 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 +71 -0
- package/dist/write/accessibility-probe.js.map +1 -0
- package/dist/write/automation-probe.d.ts +8 -1
- package/dist/write/automation-probe.js +16 -1
- package/dist/write/automation-probe.js.map +1 -1
- package/dist/write/batch.js +5 -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 +3 -1
- package/dist/write/commands.js +602 -49
- 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 +87 -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 +73 -2
- package/dist/write/pipeline.js +218 -43
- package/dist/write/pipeline.js.map +1 -1
- package/dist/write/planner.js +8 -1
- package/dist/write/planner.js.map +1 -1
- package/dist/write/pre-state.d.ts +105 -3
- package/dist/write/pre-state.js +144 -3
- package/dist/write/pre-state.js.map +1 -1
- package/dist/write/recurrence-rule-blob.d.ts +65 -0
- package/dist/write/recurrence-rule-blob.js +150 -0
- package/dist/write/recurrence-rule-blob.js.map +1 -0
- 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 +15 -1
- package/dist/write/vectors/registry.js +65 -2
- package/dist/write/vectors/registry.js.map +1 -1
- package/dist/write/vectors/simulator.d.ts +44 -0
- package/dist/write/vectors/simulator.js +1136 -0
- package/dist/write/vectors/simulator.js.map +1 -0
- package/dist/write/vectors/types.d.ts +154 -3
- package/dist/write/vectors/ui-certification.d.ts +54 -0
- package/dist/write/vectors/ui-certification.js +43 -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 +83 -0
- package/dist/write/vectors/ui-recipes.js +610 -0
- package/dist/write/vectors/ui-recipes.js.map +1 -0
- package/dist/write/vectors/ui.d.ts +152 -0
- package/dist/write/vectors/ui.js +704 -0
- package/dist/write/vectors/ui.js.map +1 -0
- package/dist/write/verify/delta.d.ts +141 -4
- package/dist/write/verify/delta.js +199 -21
- package/dist/write/verify/delta.js.map +1 -1
- package/dist/write/verify/poller.d.ts +7 -1
- package/dist/write/verify/poller.js +21 -1
- package/dist/write/verify/poller.js.map +1 -1
- package/package.json +18 -3
- package/skills/things-cli/SKILL.md +53 -0
- package/skills/things-cli/references/contracts.md +41 -0
- package/skills/things-cli/references/gui.md +11 -0
- package/skills/things-cli/references/model.md +51 -0
- 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
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Width-aware TTY row fitting (docs/design/width-aware-tty.md). Terminal list
|
|
3
|
+
* rows never wrap: like the Things GUI, an over-long row truncates its title
|
|
4
|
+
* with a trailing `…` and folds its tags, keeping the always-present metadata
|
|
5
|
+
* (uuid, box, chips, tail markers, the full deadline token) intact. Everything
|
|
6
|
+
* here is pure and takes the target width explicitly, so it is unit-testable
|
|
7
|
+
* without a TTY and byte-stable off one: fitting engages ONLY when the driver
|
|
8
|
+
* resolves a positive width (setFitWidth) — the default is null (no fitting),
|
|
9
|
+
* so pipes, grep, and `--json` are untouched by construction.
|
|
10
|
+
*
|
|
11
|
+
* No runtime dependency (guest e2e bundles ship node + dist + commander only),
|
|
12
|
+
* so the display-width math is a small vendored wcwidth: SGR escapes strip to
|
|
13
|
+
* zero, East-Asian-Wide + emoji-presentation codepoints count two cells, the
|
|
14
|
+
* rest count one. ZWJ-sequence emoji can be off by a cell (accepted — worst
|
|
15
|
+
* case a row runs a hair short); the glyph vocabulary (★ ⏾ ⚑ ↻ ⍾ ≡ ◷) is all
|
|
16
|
+
* ambiguous/narrow class and counts one, so the layout math matches the render.
|
|
17
|
+
*/
|
|
18
|
+
const SGR = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`, "g");
|
|
19
|
+
/** Drop every SGR (color/dim/bold/…) escape, leaving the visible characters. */
|
|
20
|
+
export function stripSgr(s) {
|
|
21
|
+
return s.replace(SGR, "");
|
|
22
|
+
}
|
|
23
|
+
// Codepoint ranges that occupy TWO terminal cells: the East-Asian Wide/Fullwidth
|
|
24
|
+
// core plus the default-emoji-presentation set (so "⭐️" counts 2 while the
|
|
25
|
+
// ambiguous-class "★" counts 1 — the property that keeps our glyph set narrow).
|
|
26
|
+
const WIDE_RANGES = [
|
|
27
|
+
[0x1100, 0x115f], // Hangul Jamo
|
|
28
|
+
[0x2e80, 0x303e], // CJK radicals … Kangxi
|
|
29
|
+
[0x3041, 0x33ff], // Hiragana … CJK compat
|
|
30
|
+
[0x3400, 0x4dbf], // CJK Ext A
|
|
31
|
+
[0x4e00, 0x9fff], // CJK Unified
|
|
32
|
+
[0xa000, 0xa4cf], // Yi
|
|
33
|
+
[0xac00, 0xd7a3], // Hangul Syllables
|
|
34
|
+
[0xf900, 0xfaff], // CJK Compat Ideographs
|
|
35
|
+
[0xfe30, 0xfe4f], // CJK Compat Forms
|
|
36
|
+
[0xff00, 0xff60], // Fullwidth Forms
|
|
37
|
+
[0xffe0, 0xffe6], // Fullwidth signs
|
|
38
|
+
// Emoji-presentation (default-wide) — the codepoints Unicode marks
|
|
39
|
+
// Emoji_Presentation=Yes outside the SMP blocks below.
|
|
40
|
+
[0x231a, 0x231b],
|
|
41
|
+
[0x23e9, 0x23ec],
|
|
42
|
+
[0x23f0, 0x23f0],
|
|
43
|
+
[0x23f3, 0x23f3],
|
|
44
|
+
[0x25fd, 0x25fe],
|
|
45
|
+
[0x2614, 0x2615],
|
|
46
|
+
[0x2648, 0x2653],
|
|
47
|
+
[0x267f, 0x267f],
|
|
48
|
+
[0x2693, 0x2693],
|
|
49
|
+
[0x26a1, 0x26a1],
|
|
50
|
+
[0x26aa, 0x26ab],
|
|
51
|
+
[0x26bd, 0x26be],
|
|
52
|
+
[0x26c4, 0x26c5],
|
|
53
|
+
[0x26ce, 0x26ce],
|
|
54
|
+
[0x26d4, 0x26d4],
|
|
55
|
+
[0x26ea, 0x26ea],
|
|
56
|
+
[0x26f2, 0x26f3],
|
|
57
|
+
[0x26f5, 0x26f5],
|
|
58
|
+
[0x26fa, 0x26fa],
|
|
59
|
+
[0x26fd, 0x26fd],
|
|
60
|
+
[0x2705, 0x2705],
|
|
61
|
+
[0x270a, 0x270b],
|
|
62
|
+
[0x2728, 0x2728],
|
|
63
|
+
[0x274c, 0x274c],
|
|
64
|
+
[0x274e, 0x274e],
|
|
65
|
+
[0x2753, 0x2755],
|
|
66
|
+
[0x2757, 0x2757],
|
|
67
|
+
[0x2795, 0x2797],
|
|
68
|
+
[0x27b0, 0x27b0],
|
|
69
|
+
[0x27bf, 0x27bf],
|
|
70
|
+
[0x2b1b, 0x2b1c],
|
|
71
|
+
[0x2b50, 0x2b50], // ⭐ WHITE MEDIUM STAR (Emoji_Presentation — 2 cells)
|
|
72
|
+
[0x2b55, 0x2b55],
|
|
73
|
+
[0x1f000, 0x1faff], // SMP emoji blocks (mahjong … symbols & pictographs ext)
|
|
74
|
+
[0x20000, 0x3fffd], // CJK Ext B+ (SIP/TIP)
|
|
75
|
+
];
|
|
76
|
+
// Zero-width: combining marks, ZW(SP/NJ/J), and variation selectors. Treating
|
|
77
|
+
// VS16 (FE0F) as zero-width means an emoji-presentation base already counted 2
|
|
78
|
+
// stays 2 (rather than 3) — the "⭐️" case.
|
|
79
|
+
const ZERO_RANGES = [
|
|
80
|
+
[0x0300, 0x036f], // combining diacritics
|
|
81
|
+
[0x200b, 0x200f], // ZWSP … RLM (incl. ZWJ 200d)
|
|
82
|
+
[0xfe00, 0xfe0f], // variation selectors
|
|
83
|
+
];
|
|
84
|
+
function inRanges(cp, ranges) {
|
|
85
|
+
for (const [lo, hi] of ranges)
|
|
86
|
+
if (cp >= lo && cp <= hi)
|
|
87
|
+
return true;
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
/** Terminal cells a single codepoint occupies: 0 (combining/zero-width), 2 (wide), else 1. */
|
|
91
|
+
export function charWidth(cp) {
|
|
92
|
+
if (cp === 0)
|
|
93
|
+
return 0;
|
|
94
|
+
if (inRanges(cp, ZERO_RANGES))
|
|
95
|
+
return 0;
|
|
96
|
+
if (inRanges(cp, WIDE_RANGES))
|
|
97
|
+
return 2;
|
|
98
|
+
return 1;
|
|
99
|
+
}
|
|
100
|
+
/** Visible terminal width of a string: SGR-stripped, summed per codepoint. */
|
|
101
|
+
export function visibleWidth(s) {
|
|
102
|
+
let w = 0;
|
|
103
|
+
for (const ch of stripSgr(s))
|
|
104
|
+
w += charWidth(ch.codePointAt(0) ?? 0);
|
|
105
|
+
return w;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Truncate PLAIN text (no SGR) to at most `cols` terminal cells, never splitting
|
|
109
|
+
* a wide codepoint across the boundary. Used to clip a raw title before its
|
|
110
|
+
* styling wrappers are re-applied, so the ellipsis/clip boundary lands OUTSIDE
|
|
111
|
+
* the SGR runs (the fitter never cuts an escape sequence).
|
|
112
|
+
*/
|
|
113
|
+
export function clipPlain(s, cols) {
|
|
114
|
+
if (cols <= 0)
|
|
115
|
+
return "";
|
|
116
|
+
let w = 0;
|
|
117
|
+
let out = "";
|
|
118
|
+
for (const ch of s) {
|
|
119
|
+
const cw = charWidth(ch.codePointAt(0) ?? 0);
|
|
120
|
+
if (w + cw > cols)
|
|
121
|
+
break;
|
|
122
|
+
out += ch;
|
|
123
|
+
w += cw;
|
|
124
|
+
}
|
|
125
|
+
return out;
|
|
126
|
+
}
|
|
127
|
+
// ── Row fitting ─────────────────────────────────────────────────────────────
|
|
128
|
+
/**
|
|
129
|
+
* TITLE : TAGS relative max-width ratio (tunable — ONE place). It arbitrates the
|
|
130
|
+
* collapsible budget ONLY under contention — when BOTH segments' natural widths
|
|
131
|
+
* exceed their shares. Otherwise the fit is lazy: the side within its share keeps
|
|
132
|
+
* its natural width and the other takes everything left (see {@link fitRow}). 4:1
|
|
133
|
+
* keeps the title dominant when they do genuinely contend.
|
|
134
|
+
*/
|
|
135
|
+
const TITLE_RATIO = 4;
|
|
136
|
+
const TAGS_RATIO = 1;
|
|
137
|
+
/**
|
|
138
|
+
* The title's protected minimum, in columns. NOT a per-row clamp: it is the
|
|
139
|
+
* input to the derived floors (render.ts FULL_FIT_FLOOR / COMPACT_FIT_FLOOR =
|
|
140
|
+
* worst-case furniture + TITLE_MIN) and the threshold the sacrifice order
|
|
141
|
+
* protects the title down to. Because the driver fits every row to at least the
|
|
142
|
+
* compact floor (max(width, COMPACT_FIT_FLOOR)),
|
|
143
|
+
* the worst-furniture row's title lands at exactly TITLE_MIN and every lighter
|
|
144
|
+
* row's title is automatically wider — so this floor is always satisfiable and
|
|
145
|
+
* there is no sub-floor clip (below the floor the terminal wraps, losing nothing).
|
|
146
|
+
*/
|
|
147
|
+
export const TITLE_MIN = 16;
|
|
148
|
+
/** Overflow marker for folded tags (dim styling applied by the row's styleTags). */
|
|
149
|
+
const TAG_OVERFLOW = "#…";
|
|
150
|
+
/**
|
|
151
|
+
* Plain form (no styling, no leading space) of the tag run showing the first `k`
|
|
152
|
+
* real tags; when `k` is below the total, the dropped tags fold into the dim
|
|
153
|
+
* `#…` marker. `k === 0` is the bare marker.
|
|
154
|
+
*/
|
|
155
|
+
function tagForm(names, k) {
|
|
156
|
+
const shown = names.slice(0, k).map((n) => `#${n}`);
|
|
157
|
+
if (k < names.length)
|
|
158
|
+
shown.push(TAG_OVERFLOW);
|
|
159
|
+
return shown.join(" ");
|
|
160
|
+
}
|
|
161
|
+
/** Visible width of the rendered tag run at level `k` (its leading space + form). */
|
|
162
|
+
function tagWidth(names, k) {
|
|
163
|
+
if (names.length === 0)
|
|
164
|
+
return 0;
|
|
165
|
+
return 1 + visibleWidth(tagForm(names, k));
|
|
166
|
+
}
|
|
167
|
+
/** Widest tag level (most real tags) whose render fits `cap`, floored at `floorK`. */
|
|
168
|
+
function pickTagLevel(names, cap, floorK) {
|
|
169
|
+
for (let k = names.length; k >= floorK; k--) {
|
|
170
|
+
if (tagWidth(names, k) <= cap)
|
|
171
|
+
return k;
|
|
172
|
+
}
|
|
173
|
+
return floorK;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* EXPERIMENTAL — the right-aligned deadline gutter (GUI flag-column parity). When
|
|
177
|
+
* fitting is active, every fitted row's `⚑` token is pushed flush to the effective
|
|
178
|
+
* width so all deadlines in a view line up in one gutter, exactly like the app's
|
|
179
|
+
* right-pinned flag column. `body` is the composed row WITHOUT its deadline and
|
|
180
|
+
* `deadline` the pre-styled ` ⚑ …` segment (its leading space + token) or "".
|
|
181
|
+
* The padding is plain spaces OUTSIDE the styled token, and there is always ≥ 1
|
|
182
|
+
* space — so a row that already fills the width is byte-identical to the inline
|
|
183
|
+
* (unguttered) form, and a row with no deadline is returned untouched (ragged
|
|
184
|
+
* right, as the GUI leaves flag-less rows). This is only ever reached from
|
|
185
|
+
* {@link fitRow} (fitting active), so the byte-stable null path never sees it.
|
|
186
|
+
* Reverting the experiment is deleting this function and its two `fitRow` calls.
|
|
187
|
+
*/
|
|
188
|
+
function alignDeadline(body, deadline, width) {
|
|
189
|
+
if (deadline === "")
|
|
190
|
+
return body;
|
|
191
|
+
// deadline = " " + token; swap that single inline space for the gutter run.
|
|
192
|
+
const pad = width - visibleWidth(body) - visibleWidth(deadline) + 1;
|
|
193
|
+
return `${body}${" ".repeat(Math.max(1, pad))}${deadline.slice(1)}`;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Fit one row into `width` terminal cells per the ratified collapse order. The
|
|
197
|
+
* always-present parts (uuid, box, meta chips, tail markers, full deadline)
|
|
198
|
+
* never shrink; title and tags divvy up what remains LAZILY — tags fold to the
|
|
199
|
+
* widest progressive level that fits and the 4:1 ratio arbitrates only when both
|
|
200
|
+
* genuinely contend (slack an under-share side leaves flows to the other). Then
|
|
201
|
+
* the CLI-only container is sacrificed WHOLE (before the last tag folds), then
|
|
202
|
+
* the last tag folds to bare `#…`, and the title bottoms at TITLE_MIN. `width` is
|
|
203
|
+
* the caller's effective width (already max'd with COMPACT_FIT_FLOOR), so the final
|
|
204
|
+
* stage always satisfies the floor; a sub-floor width would simply let the
|
|
205
|
+
* terminal wrap (the title is kept whole rather than clipped). A row that already
|
|
206
|
+
* fits keeps its full content; the only always-applied transform is the
|
|
207
|
+
* experimental deadline gutter ({@link alignDeadline}), which right-pins the `⚑`.
|
|
208
|
+
*/
|
|
209
|
+
export function fitRow(seg, width) {
|
|
210
|
+
const names = seg.tagNames;
|
|
211
|
+
const wLeft = visibleWidth(seg.left) + 1; // + the space before the title
|
|
212
|
+
const wTail = visibleWidth(seg.tail);
|
|
213
|
+
const wCtx = visibleWidth(seg.context);
|
|
214
|
+
const wDead = visibleWidth(seg.deadline);
|
|
215
|
+
const wTitle = visibleWidth(seg.rawTitle);
|
|
216
|
+
// A naturally short title never floors at TITLE_MIN — it simply fits whole.
|
|
217
|
+
const titleNeed = Math.min(TITLE_MIN, wTitle);
|
|
218
|
+
const fixed = (ctx) => wLeft + wTail + wDead + (ctx ? wCtx : 0);
|
|
219
|
+
const fitTitle = (budget) => wTitle <= budget
|
|
220
|
+
? seg.styleTitle(seg.rawTitle)
|
|
221
|
+
: seg.styleTitle(`${clipPlain(seg.rawTitle, budget - 1)}…`);
|
|
222
|
+
const renderRow = (title, k, ctx) => {
|
|
223
|
+
const tags = names.length > 0 ? seg.styleTags(tagForm(names, k)) : "";
|
|
224
|
+
const body = `${seg.left} ${title}${seg.tail}${tags}${ctx ? seg.context : ""}`;
|
|
225
|
+
return alignDeadline(body, seg.deadline, width);
|
|
226
|
+
};
|
|
227
|
+
// Stage "full": everything fits with tags whole and the container present.
|
|
228
|
+
// (The deadline is still pushed to the gutter — GUI flags pin right even on a
|
|
229
|
+
// short row — but the null path returns seg.full before reaching the fitter.)
|
|
230
|
+
const budgetFull = width - fixed(true);
|
|
231
|
+
if (wTitle + tagWidth(names, names.length) <= budgetFull) {
|
|
232
|
+
const body = seg.full.slice(0, seg.full.length - seg.deadline.length);
|
|
233
|
+
return alignDeadline(body, seg.deadline, width);
|
|
234
|
+
}
|
|
235
|
+
// Stage "both-shrink": tags fold LAZILY — to the WIDEST progressive level that
|
|
236
|
+
// fits, not an eager 1/5 cap. The 4:1 ratio arbitrates ONLY under contention
|
|
237
|
+
// (both natural widths exceed their shares of the collapsible budget). When the
|
|
238
|
+
// title is within its 4/5 share it keeps its natural width and the tags get
|
|
239
|
+
// everything left (their widest fitting level); when instead the tags are
|
|
240
|
+
// within their 1/5 share they stay whole and the title gets the rest. Either
|
|
241
|
+
// way, slack a discrete tag level does not consume flows BACK to the title, and
|
|
242
|
+
// neither side is shrunk below what the budget actually forces. Container kept.
|
|
243
|
+
const tagShare = Math.floor((budgetFull * TAGS_RATIO) / (TITLE_RATIO + TAGS_RATIO));
|
|
244
|
+
const titleShare = budgetFull - tagShare;
|
|
245
|
+
const tagCap = wTitle <= titleShare ? budgetFull - wTitle : tagShare;
|
|
246
|
+
const k = pickTagLevel(names, tagCap, 1);
|
|
247
|
+
const titleBudgetB = budgetFull - tagWidth(names, k);
|
|
248
|
+
if (titleBudgetB >= titleNeed)
|
|
249
|
+
return renderRow(fitTitle(titleBudgetB), k, true);
|
|
250
|
+
// Stage "container-drop": the CLI-only container yields WHOLE (a truncated
|
|
251
|
+
// parenthetical is worse than none) BEFORE the last tag folds — tags hold at
|
|
252
|
+
// `#first #…`, the reclaimed width goes to the title.
|
|
253
|
+
const budgetNoCtx = width - fixed(false);
|
|
254
|
+
const titleBudgetC = budgetNoCtx - tagWidth(names, 1);
|
|
255
|
+
if (titleBudgetC >= titleNeed)
|
|
256
|
+
return renderRow(fitTitle(titleBudgetC), 1, false);
|
|
257
|
+
// Stage "tags-bare": the last tag folds to the bare `#…` marker (container
|
|
258
|
+
// already gone), the title floors at TITLE_MIN. No end-clip: at an effective
|
|
259
|
+
// width this budget clears the floor; a sub-floor width keeps the title whole
|
|
260
|
+
// and lets the terminal wrap (never dropping the deadline).
|
|
261
|
+
const titleBudgetD = budgetNoCtx - tagWidth(names, 0);
|
|
262
|
+
const dTitle = titleBudgetD >= 1 ? fitTitle(titleBudgetD) : seg.styleTitle(seg.rawTitle);
|
|
263
|
+
return renderRow(dTitle, 0, false);
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Resolve the effective fit from the once-resolved fit width and the two
|
|
267
|
+
* glyph-derived floors (`render.ts`: FULL_FIT_FLOOR / COMPACT_FIT_FLOOR). This
|
|
268
|
+
* is the SINGLE decision point for the full-vs-compact deadline form:
|
|
269
|
+
* - `width ≥ full` → full forms, fit to width (today's behavior);
|
|
270
|
+
* - `compact ≤ width < full` → all deadlines compact, fit to width;
|
|
271
|
+
* - `width < compact` → clamp to the compact floor and wrap (sub-floor
|
|
272
|
+
* rule at the lower floor), still compact.
|
|
273
|
+
* The floors live in render.ts because their derivation needs the glyph
|
|
274
|
+
* inventory; the branching lives here so it is pure and unit-testable.
|
|
275
|
+
*/
|
|
276
|
+
export function resolveFit(fitWidth, fullFloor, compactFloor) {
|
|
277
|
+
const width = Math.max(fitWidth, compactFloor);
|
|
278
|
+
return { width, compact: width < fullFloor };
|
|
279
|
+
}
|
|
280
|
+
// ── Width resolution + the module-level fit width ────────────────────────────
|
|
281
|
+
let fitWidth = null;
|
|
282
|
+
/**
|
|
283
|
+
* Set the process-wide fit width (columns), resolved ONCE by the CLI driver at
|
|
284
|
+
* startup. `null` disables fitting (the default — every non-TTY path stays
|
|
285
|
+
* byte-stable). Tests set this explicitly and reset it afterward.
|
|
286
|
+
*/
|
|
287
|
+
export function setFitWidth(width) {
|
|
288
|
+
fitWidth = width;
|
|
289
|
+
}
|
|
290
|
+
/** The current fit width, or null when fitting is disabled. */
|
|
291
|
+
export function getFitWidth() {
|
|
292
|
+
return fitWidth;
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Resolve the fit width from the environment and stdout: `THINGS_WIDTH` wins
|
|
296
|
+
* when set (a positive integer forces that width; `0` disables fitting), else
|
|
297
|
+
* the terminal's column count when stdout is a TTY, else null (pipes, grep,
|
|
298
|
+
* captured output — no fitting, byte-stable). A malformed `THINGS_WIDTH` is
|
|
299
|
+
* ignored (falls through to the TTY/null resolution).
|
|
300
|
+
*/
|
|
301
|
+
export function resolveWidth(opts) {
|
|
302
|
+
const raw = opts.env["THINGS_WIDTH"];
|
|
303
|
+
if (raw !== undefined) {
|
|
304
|
+
const n = Number(raw);
|
|
305
|
+
if (Number.isInteger(n) && n >= 0)
|
|
306
|
+
return n === 0 ? null : n;
|
|
307
|
+
// malformed — fall through to the TTY resolution
|
|
308
|
+
}
|
|
309
|
+
if (opts.isTTY && typeof opts.columns === "number" && opts.columns > 0)
|
|
310
|
+
return opts.columns;
|
|
311
|
+
return null;
|
|
312
|
+
}
|
|
313
|
+
//# sourceMappingURL=width.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"width.js","sourceRoot":"","sources":["../../src/cli/width.ts"],"names":[],"mappings":"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"}
|
package/dist/client.d.ts
CHANGED
|
@@ -4,14 +4,16 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import type { AuditWriter } from "./audit/log.ts";
|
|
6
6
|
import { type ThingsApiConfig } from "./config.ts";
|
|
7
|
-
import { type
|
|
7
|
+
import { type ClockMeta } from "./model/clock.ts";
|
|
8
|
+
import { type GroupedTruncation, type Truncation } from "./contracts.ts";
|
|
9
|
+
import { type FingerprintStatus, type SchemaStatus } from "./db/fingerprint.ts";
|
|
8
10
|
import type { AnyTask, Area, Project, Tag } from "./model/entities.ts";
|
|
9
11
|
import { type AreaView } from "./read/area-view.ts";
|
|
10
12
|
import { type ProjectView } from "./read/project-view.ts";
|
|
11
13
|
import { type Snapshot } from "./read/snapshot.ts";
|
|
12
14
|
import { type ShowTarget } from "./read/show-target.ts";
|
|
13
|
-
import { type ChangedItem, type LiteSearchResult, type ListItem, type LogbookFilter, type SearchOptions, type SearchResultItem, type SidebarSection, type SomedayFilter, type TodayView, type UpcomingFilter, type ViewFilter } from "./read/views.ts";
|
|
14
|
-
import type { AreaAddParams, AreaUpdateParams, ContainerRef, ProjectCancelParams, OperationKind, OperationParamsMap, ProjectAddParams, ProjectCompleteParams, ProjectUpdateParams, ReorderParams, TagAddParams, TagUpdateParams, HeadingArchiveParams, HeadingUnarchiveParams, TodoAddLoggedParams, TodoAddParams, TodoBackdateParams, TodoMoveParams, TodoUpdateParams } from "./write/operations.ts";
|
|
15
|
+
import { type ChangedItem, type InboxFilter, type LiteSearchResult, type ListItem, type LogbookFilter, type SearchOptions, type SearchResultItem, type SidebarSection, type SomedayFilter, type TodayFilter, type TodayView, type UpcomingFilter, type ViewFilter } from "./read/views.ts";
|
|
16
|
+
import type { AreaAddParams, AreaUpdateParams, ContainerRef, ProjectCancelParams, OperationKind, OperationParamsMap, ProjectAddParams, ProjectCompleteParams, ProjectCreateRepeatingParams, ProjectUpdateParams, RepeatRuleParams, ReorderParams, TagAddParams, TagUpdateParams, HeadingArchiveParams, HeadingUnarchiveParams, TodoAddLoggedParams, TodoAddParams, TodoBackdateParams, TodoMoveParams, TodoUpdateParams } from "./write/operations.ts";
|
|
15
17
|
import { type MutationResult, type WriteOptions } from "./write/pipeline.ts";
|
|
16
18
|
import { type BatchItemResult, type BatchOp, type BatchOptions } from "./write/batch.ts";
|
|
17
19
|
import { type EnvironmentTracker } from "./write/environment.ts";
|
|
@@ -26,6 +28,12 @@ export interface OpenOptions {
|
|
|
26
28
|
dbPath?: string;
|
|
27
29
|
/** Injectable clock (tests, pinned-clock lab runs). */
|
|
28
30
|
now?: () => Date;
|
|
31
|
+
/**
|
|
32
|
+
* Default consumer IANA zone for every date boundary (tests / explicit
|
|
33
|
+
* embedding). Overrides `THINGS_TZ` from the environment; a per-read `zone`
|
|
34
|
+
* still overrides this. Absent uses `THINGS_TZ`, else the host zone.
|
|
35
|
+
*/
|
|
36
|
+
zone?: string;
|
|
29
37
|
/** Injectable write vectors (tests: FakeVector; lab: probe vectors). */
|
|
30
38
|
vectors?: WriteVector[];
|
|
31
39
|
/** Env for config/state-dir resolution (tests). */
|
|
@@ -40,20 +48,109 @@ export interface OpenOptions {
|
|
|
40
48
|
environment?: EnvironmentTracker;
|
|
41
49
|
};
|
|
42
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* Row cap for a bounded FLAT view (inbox/today/upcoming/logbook/trash/search/
|
|
53
|
+
* changes). Resolution follows resolveCap: omitted → the 50-row default,
|
|
54
|
+
* an explicit number caps at it, `null` or `all: true` returns every row.
|
|
55
|
+
*/
|
|
56
|
+
export interface ListBound {
|
|
57
|
+
limit?: number | null;
|
|
58
|
+
all?: boolean;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Per-read consumer-zone override (the MCP `tz` argument). Overrides the
|
|
62
|
+
* process default (`THINGS_TZ` / the `OpenOptions.zone` embedding / the host)
|
|
63
|
+
* for THIS read only. Absent uses that default; an invalid zone is rejected by
|
|
64
|
+
* the calling surface before it reaches here.
|
|
65
|
+
*/
|
|
66
|
+
export interface ClockScopedRead {
|
|
67
|
+
zone?: string;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Per-block caps for a bounded GROUPED view (anytime/someday) or the composite
|
|
71
|
+
* area card. Each omitted cap falls back to the view's own default (anytime:
|
|
72
|
+
* 30 per area, 3 per project; someday: 30 per area, every active-project item;
|
|
73
|
+
* area card: 30 per section); `null` on a cap, or `all: true`, lifts it.
|
|
74
|
+
*/
|
|
75
|
+
export interface GroupedBound {
|
|
76
|
+
areaLimit?: number | null;
|
|
77
|
+
projectLimit?: number | null;
|
|
78
|
+
all?: boolean;
|
|
79
|
+
}
|
|
80
|
+
/** A bounded flat view: the shown rows plus the exact truncation counts. */
|
|
81
|
+
export interface BoundedList<T> {
|
|
82
|
+
items: T[];
|
|
83
|
+
truncation: Truncation;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* A bounded Today view: `view` is the shown split (capped in render order —
|
|
87
|
+
* Today, then This Evening) and `truncation` the exact counts, including the
|
|
88
|
+
* per-section (`today`/`evening`) breakdown a renderer needs to stay honest.
|
|
89
|
+
*/
|
|
90
|
+
export interface BoundedTodayView {
|
|
91
|
+
view: TodayView;
|
|
92
|
+
truncation: Truncation;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* A bounded sidebar catalogue (anytime/someday): `view` is the
|
|
96
|
+
* per-block-capped sections and `grouped` the per-block counts (identity-
|
|
97
|
+
* carrying, project blocks nested under their area/loose block).
|
|
98
|
+
*/
|
|
99
|
+
export interface BoundedSectionsView {
|
|
100
|
+
view: SidebarSection[];
|
|
101
|
+
grouped: GroupedTruncation;
|
|
102
|
+
}
|
|
103
|
+
/** A bounded composite area card: the per-section-capped view and the per-block counts. */
|
|
104
|
+
export interface BoundedAreaView {
|
|
105
|
+
view: AreaView;
|
|
106
|
+
grouped: GroupedTruncation;
|
|
107
|
+
}
|
|
43
108
|
export interface ThingsClient {
|
|
44
109
|
dbPath: string;
|
|
45
110
|
config: ThingsApiConfig;
|
|
46
111
|
fingerprint(): FingerprintStatus;
|
|
112
|
+
/**
|
|
113
|
+
* The read-path schema check: the cached fingerprint comparison reduced to a
|
|
114
|
+
* warn-or-not verdict (ok / drift / unknown-version) with detail. Reuses the
|
|
115
|
+
* SAME lazily-built fingerprint the write path gates on — computed at most
|
|
116
|
+
* once per client, so it costs nothing after the first read.
|
|
117
|
+
*/
|
|
118
|
+
schemaStatus(): SchemaStatus;
|
|
119
|
+
/**
|
|
120
|
+
* The additive `meta.clock` honesty field for this client's effective clock,
|
|
121
|
+
* or undefined when the host clock is in force (no `THINGS_TZ`/`THINGS_NOW`
|
|
122
|
+
* and no per-read override). `zoneOverride` reflects a per-read zone (the MCP
|
|
123
|
+
* `tz` argument) so the reported `today` matches what that read computed.
|
|
124
|
+
*/
|
|
125
|
+
clockMeta(zoneOverride?: string): ClockMeta | undefined;
|
|
47
126
|
read: {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
127
|
+
/**
|
|
128
|
+
* The Today list (Today + This Evening split) with the sidebar badge,
|
|
129
|
+
* bounded to `limit` rows (default 50) counted in render order — Today
|
|
130
|
+
* first, then This Evening. `all`/`limit: null` returns every row; the
|
|
131
|
+
* `truncation` metadata carries the per-section (`today`/`evening`) counts.
|
|
132
|
+
*/
|
|
133
|
+
today(options?: TodayFilter & ListBound & ClockScopedRead): BoundedTodayView;
|
|
134
|
+
/** Inbox captures, bounded (default 50). */
|
|
135
|
+
inbox(options?: InboxFilter & ListBound & ClockScopedRead): BoundedList<ListItem>;
|
|
136
|
+
/**
|
|
137
|
+
* Anytime catalogue: every area header and project row is always present;
|
|
138
|
+
* `areaLimit` (default 30) caps each area/loose block, `projectLimit`
|
|
139
|
+
* (default 3) each project block. `all` lifts both.
|
|
140
|
+
*/
|
|
141
|
+
anytime(options?: ViewFilter & GroupedBound & ClockScopedRead): BoundedSectionsView;
|
|
142
|
+
/** Future-scheduled items in date order, bounded (default 50). */
|
|
143
|
+
upcoming(options?: UpcomingFilter & ListBound & ClockScopedRead): BoundedList<ListItem>;
|
|
144
|
+
/**
|
|
145
|
+
* Someday catalogue: `areaLimit` (default 30) caps each group; with
|
|
146
|
+
* `activeProjectItems`, `projectLimit` (default: every item) caps each
|
|
147
|
+
* active project's trailing child list. `all` lifts both.
|
|
148
|
+
*/
|
|
149
|
+
someday(options?: SomedayFilter & GroupedBound & ClockScopedRead): BoundedSectionsView;
|
|
150
|
+
/** Logbook entries (most recent first), bounded (default 50). */
|
|
151
|
+
logbook(options?: Omit<LogbookFilter, "limit"> & ListBound & ClockScopedRead): BoundedList<ListItem>;
|
|
152
|
+
/** Trashed items (most recently modified first), bounded (default 50). */
|
|
153
|
+
trash(options?: ListBound & ClockScopedRead): BoundedList<ListItem>;
|
|
57
154
|
/**
|
|
58
155
|
* Projects in sidebar order. LATER (someday + future-scheduled) projects
|
|
59
156
|
* are excluded by default — `later: true` appends them after the active
|
|
@@ -62,14 +159,32 @@ export interface ThingsClient {
|
|
|
62
159
|
projects(options?: {
|
|
63
160
|
areaUuid?: string;
|
|
64
161
|
later?: boolean;
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
162
|
+
overdue?: boolean;
|
|
163
|
+
} & ViewFilter & ClockScopedRead): Project[];
|
|
164
|
+
/**
|
|
165
|
+
* Composite project view. Targets by uuid, unique name, or uuid prefix.
|
|
166
|
+
* `overdue: true` keeps only child to-dos whose own deadline is overdue
|
|
167
|
+
* (open, before today); the tag filters (`tags`/`untagged`) keep only the
|
|
168
|
+
* child to-dos carrying the tag DIRECTLY (the container semantics — tags
|
|
169
|
+
* inherited from this project are ignored). Any content scope collapses
|
|
170
|
+
* headings left with no surviving child.
|
|
171
|
+
*/
|
|
172
|
+
projectView(ref: string, options?: ViewFilter & ClockScopedRead): ProjectView;
|
|
173
|
+
/**
|
|
174
|
+
* Composite area view: direct to-dos, projects in sidebar order, later,
|
|
175
|
+
* logged. `overdue: true` keeps only the loose to-dos AND child projects
|
|
176
|
+
* whose OWN deadline is overdue; the tag filters keep only the rows
|
|
177
|
+
* matching by their own tags — no descent into project contents. Bounded
|
|
178
|
+
* per section: `projectLimit`/`areaLimit` (default 30 each) cap the ACTIVE
|
|
179
|
+
* project-rows and direct-to-dos sections (scheduled/someday project rows
|
|
180
|
+
* always survive, routed to the card's later sections); `all` lifts both.
|
|
181
|
+
* The `grouped` metadata carries the per-section counts.
|
|
182
|
+
*/
|
|
183
|
+
areaView(ref: string, options?: ViewFilter & GroupedBound & ClockScopedRead): BoundedAreaView;
|
|
70
184
|
areas(): Area[];
|
|
71
185
|
tags(): Tag[];
|
|
72
|
-
search
|
|
186
|
+
/** Title/notes substring search, ranked, bounded (default 50). */
|
|
187
|
+
search(query: string, options?: SearchOptions & ClockScopedRead): BoundedList<SearchResultItem>;
|
|
73
188
|
/**
|
|
74
189
|
* Did-you-mean fallback: case-insensitive title-only substring match over
|
|
75
190
|
* areas/projects/to-dos (open + untrashed), ordered and capped. `type`
|
|
@@ -79,11 +194,10 @@ export interface ThingsClient {
|
|
|
79
194
|
type?: "to-do" | "project" | "area";
|
|
80
195
|
limit?: number;
|
|
81
196
|
}): LiteSearchResult;
|
|
82
|
-
/** Rows created/modified since a moment — incl. trashed/logged/templates. */
|
|
197
|
+
/** Rows created/modified since a moment — incl. trashed/logged/templates — bounded (default 50). */
|
|
83
198
|
changes(options: {
|
|
84
199
|
since: Date;
|
|
85
|
-
|
|
86
|
-
}): ChangedItem[];
|
|
200
|
+
} & ListBound & ClockScopedRead): BoundedList<ChangedItem>;
|
|
87
201
|
byUuid(uuid: string): AnyTask | null;
|
|
88
202
|
/**
|
|
89
203
|
* Classify a loose reference (uuid, >=6-char prefix, share link, or
|
|
@@ -176,6 +290,24 @@ export interface ThingsClient {
|
|
|
176
290
|
/** Duplicate a project INCLUDING its children; the copy's uuid is on the result. */
|
|
177
291
|
duplicateProject(uuid: string, options?: WriteOptions): Promise<MutationResult>;
|
|
178
292
|
deleteProject(uuid: string, options?: WriteOptions): Promise<MutationResult>;
|
|
293
|
+
/**
|
|
294
|
+
* Turn an existing project into a repeating series. Drives the local Things
|
|
295
|
+
* app through the Accessibility API (two-key gated: `ui.enabled` config +
|
|
296
|
+
* `dangerouslyDriveGui`). This REPLACES the project with a new repeating
|
|
297
|
+
* template (its area is kept, its schedule is normalized to Someday); the
|
|
298
|
+
* original's identity is gone and it cannot be undone. The new template's
|
|
299
|
+
* uuid is on the result. An area-less Anytime project is moved to Someday
|
|
300
|
+
* first — a cleanup-free intermediate step surfaced in the plan.
|
|
301
|
+
*/
|
|
302
|
+
makeRepeatingProject(uuid: string, rule: Omit<RepeatRuleParams, "uuid">, options?: WriteOptions): Promise<MutationResult>;
|
|
303
|
+
/**
|
|
304
|
+
* Create a project and, in the same call, turn it into a repeating series.
|
|
305
|
+
* TWO operations: the project is created first (and persists even if the
|
|
306
|
+
* make-repeating step refuses); then it is promoted (which drives the GUI —
|
|
307
|
+
* two-key gated, same as makeRepeatingProject). Give an `area` to place it,
|
|
308
|
+
* or omit it to create in Someday. The new template's uuid is on the result.
|
|
309
|
+
*/
|
|
310
|
+
createRepeatingProject(params: ProjectCreateRepeatingParams, options?: WriteOptions): Promise<MutationResult>;
|
|
179
311
|
/** Replace a project's full tag set (an empty list clears all tags). */
|
|
180
312
|
setProjectTags(uuid: string, tags: string[], options?: WriteOptions): Promise<MutationResult>;
|
|
181
313
|
/** Merge: current project tags + new ones, then replace. */
|
|
@@ -198,9 +330,13 @@ export interface ThingsClient {
|
|
|
198
330
|
*/
|
|
199
331
|
batch(ops: BatchOp[], options?: BatchOptions, onResult?: (result: BatchItemResult) => void): Promise<BatchItemResult[]>;
|
|
200
332
|
/**
|
|
201
|
-
* Undo
|
|
202
|
-
*
|
|
203
|
-
*
|
|
333
|
+
* Undo changes made through this client, newest first, by applying the
|
|
334
|
+
* inverse change. Selection: `last` trailing changes (default 1), narrowed
|
|
335
|
+
* to author `by` (exact actor, or `*`/undefined for all), or `txn` for one
|
|
336
|
+
* exact change by its `undoToken`. Irreversible changes are reported as
|
|
337
|
+
* such, never guessed at. An inverse is refused when the item changed
|
|
338
|
+
* outside things-api since (its container, status, schedule, trashed state,
|
|
339
|
+
* or a content field moved); `acknowledgeOutOfBandChanges` overwrites anyway.
|
|
204
340
|
*/
|
|
205
341
|
undo(options?: UndoOptions, onItem?: (item: UndoItemResult) => void): Promise<UndoItemResult[]>;
|
|
206
342
|
};
|