things-api 0.20.0 → 0.20.2

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.
Files changed (52) hide show
  1. package/README.md +1 -1
  2. package/deputy/prebuilt/Things API Helper.app/Contents/CodeResources +0 -0
  3. package/deputy/prebuilt/Things API Helper.app/Contents/Helpers/things-reader.app/Contents/MacOS/things-reader +0 -0
  4. package/deputy/prebuilt/Things API Helper.app/Contents/MacOS/things-deputy +0 -0
  5. package/dist/capability.d.ts +20 -0
  6. package/dist/capability.js +24 -0
  7. package/dist/capability.js.map +1 -1
  8. package/dist/cli/help.js +1 -1
  9. package/dist/cli/skill.d.ts +5 -3
  10. package/dist/cli/skill.js +6 -2
  11. package/dist/cli/skill.js.map +1 -1
  12. package/dist/contracts.d.ts +1 -1
  13. package/dist/contracts.js +1 -1
  14. package/dist/index.d.ts +1 -1
  15. package/dist/index.js +1 -1
  16. package/dist/index.js.map +1 -1
  17. package/dist/model/mappers.js +11 -9
  18. package/dist/model/mappers.js.map +1 -1
  19. package/dist/model/today-placement.d.ts +65 -0
  20. package/dist/model/today-placement.js +59 -0
  21. package/dist/model/today-placement.js.map +1 -0
  22. package/dist/rescue.d.ts +15 -2
  23. package/dist/rescue.js +74 -11
  24. package/dist/rescue.js.map +1 -1
  25. package/dist/sync-health.js +22 -1
  26. package/dist/sync-health.js.map +1 -1
  27. package/dist/write/commands.js +3 -1
  28. package/dist/write/commands.js.map +1 -1
  29. package/dist/write/disclosures.d.ts +12 -0
  30. package/dist/write/disclosures.js +12 -0
  31. package/dist/write/disclosures.js.map +1 -1
  32. package/dist/write/guards.js +28 -5
  33. package/dist/write/guards.js.map +1 -1
  34. package/dist/write/move.js +33 -6
  35. package/dist/write/move.js.map +1 -1
  36. package/dist/write/pipeline.js +49 -4
  37. package/dist/write/pipeline.js.map +1 -1
  38. package/dist/write/pre-state.d.ts +9 -9
  39. package/dist/write/pre-state.js +44 -38
  40. package/dist/write/pre-state.js.map +1 -1
  41. package/dist/write/reorder.js +28 -7
  42. package/dist/write/reorder.js.map +1 -1
  43. package/dist/write/vectors/types.d.ts +10 -0
  44. package/dist/write/vectors/ui-drag.d.ts +153 -5
  45. package/dist/write/vectors/ui-drag.js +784 -82
  46. package/dist/write/vectors/ui-drag.js.map +1 -1
  47. package/dist/write/vectors/ui.d.ts +13 -0
  48. package/dist/write/vectors/ui.js +36 -2
  49. package/dist/write/vectors/ui.js.map +1 -1
  50. package/package.json +1 -1
  51. package/skills/things-cli/SKILL.md +4 -3
  52. package/skills/things-cli/references/bug-reports.md +56 -0
@@ -61,15 +61,30 @@ export interface SidebarSnapshot {
61
61
  scroll: number | null;
62
62
  rows: SidebarRowInfo[];
63
63
  }
64
- /** Snapshot: sidebar rows (text + frames), viewport rect, scroll fraction. */
65
- export declare function jxaSidebarSnapshotScript(): string;
64
+ /**
65
+ * Snapshot: sidebar rows (text + frames), viewport rect, scroll fraction.
66
+ *
67
+ * `areaTitles` is what makes the locator semantic — the sidebar is identified as
68
+ * the list that holds the caller's own areas. It is also the ESCALATION oracle:
69
+ * every area always renders a sidebar row (AXDRAG1: even off-viewport rows
70
+ * expose valid virtualized frames), so a shallow harvest that finds fewer titles
71
+ * than the database holds is re-run at full depth before the ladder sees it.
72
+ */
73
+ export declare function jxaSidebarSnapshotScript(areaTitles: readonly string[]): string;
74
+ /**
75
+ * Show or hide the sidebar through Things' own View menu (SBRES1 normalization
76
+ * rung). English-pinned and fail-closed: a menu without the item — a localized
77
+ * app, or a Things update that moved it — refuses and names what it did find,
78
+ * rather than clicking whatever sits in that position (UIC1 precedent).
79
+ */
80
+ export declare function jxaSidebarVisibilityScript(want: "show" | "hide"): string;
66
81
  /**
67
82
  * Scroll: move the pointer over the sidebar center (wheel events target the
68
83
  * surface under the cursor), then post `clicks` line-unit wheel events.
69
84
  * Positive clicks move the CONTENT down (earlier rows return, row y grows);
70
85
  * negative clicks reveal lower rows (row y shrinks) — AXDRAG1-b.
71
86
  */
72
- export declare function jxaSidebarScrollScript(clicks: number): string;
87
+ export declare function jxaSidebarScrollScript(clicks: number, areaTitles: readonly string[]): string;
73
88
  /**
74
89
  * Drag: the NATIVE1 gesture verbatim — move, down, 3px wiggle to open the drag
75
90
  * session, ~25 interpolated drag events, a settle so the drop indicator locks,
@@ -88,7 +103,28 @@ export declare function jxaSidebarDragScript(sx: number, sy: number, tx: number,
88
103
  * Escape-aborts (AXDRAG1-d: byte-identical index vector) and reports it.
89
104
  */
90
105
  export declare function jxaSidebarHeldScrollDragScript(sx: number, sy: number, anchorTitle: string | null, // null = drop below the last row (to-last)
91
- maxTicks: number): string;
106
+ maxTicks: number, areaTitles: readonly string[]): string;
107
+ /**
108
+ * SBCOL1 — actuate an area row's DISCLOSURE CHEVRON.
109
+ *
110
+ * The toggle is two nodes: an inert `AXImage d="Source Toggle Template"` (what
111
+ * AXDRAG2-b measured and called "not actuatable") inside an `AXUnknown` wrapper
112
+ * that DOES advertise `AXPress` — and that press is DECORATIVE, exactly as REPX1
113
+ * §1.2 predicts for Things' custom rows (`AXError = 0`, zero census delta). What
114
+ * actuates is a synthesized click at the image's OWN resolved frame; SBCOL1
115
+ * toggled it both directions twice with zero beeps and zero focus steal.
116
+ *
117
+ * Fail-closed twice over: the chevron is resolved from the LIVE tree inside this
118
+ * same script (never a frame carried over from an earlier snapshot generation),
119
+ * and a chevron whose center lies outside the scroll-area band is REFUSED rather
120
+ * than clicked — an off-viewport row still exposes a valid virtualized frame
121
+ * (AXDRAG1), so clicking one would land somewhere else entirely.
122
+ *
123
+ * `ordinal` selects among same-titled rows in visual (y) order, the AXDRAG3
124
+ * disambiguation the rest of the driver uses; -1 means "the only row with this
125
+ * title", and an ambiguous match refuses.
126
+ */
127
+ export declare function jxaSidebarChevronClickScript(title: string, ordinal: number, areaTitles: readonly string[]): string;
92
128
  /** Median height of the table's spacer rows (rows with no static text). */
93
129
  export declare function medianSpacerHeight(rows: SidebarRowInfo[]): number | null;
94
130
  /**
@@ -114,7 +150,32 @@ export declare function boundaryAboveRow(allRows: SidebarRowInfo[], ref: Sidebar
114
150
  * scrollable band even when the list is pinned to the bottom.
115
151
  */
116
152
  export declare function boundaryBelowLast(allRows: SidebarRowInfo[]): number | null;
117
- export declare function parseSidebarSnapshot(stdout: string): SidebarSnapshot | null;
153
+ /**
154
+ * Why a snapshot did not come back. These were ONE sentence until SBRES1 — "the
155
+ * sidebar did not resolve (is the window open and the sidebar visible?)" — which
156
+ * is how a 30-second timeout, a locator that never matched, and a sidebar the
157
+ * user had genuinely hidden all reached the field wearing the same words, none
158
+ * of them true for two of the three cases (issues #665, #651).
159
+ */
160
+ export type SnapshotFailure = "timeout" | "dispatch-failed" | "unparsable" | "no-window" | "no-list-candidates" | "no-title-match" | "ambiguous-sidebar" | "sidebar-hidden" | "no-viewport" | "no-rows";
161
+ export interface SnapshotRefusal {
162
+ ok: false;
163
+ why: SnapshotFailure;
164
+ /** What the locator looked at, when it has something to name. */
165
+ searched?: {
166
+ frame: SidebarRect | null;
167
+ rows: number;
168
+ }[];
169
+ /** How many area titles the locator was hunting for. */
170
+ titles?: number;
171
+ /** The dispatcher's own words, for `dispatch-failed`. */
172
+ stderr?: string;
173
+ }
174
+ export type SnapshotOutcome = {
175
+ ok: true;
176
+ snapshot: SidebarSnapshot;
177
+ } | SnapshotRefusal;
178
+ export declare function parseSidebarSnapshot(stdout: string): SnapshotOutcome;
118
179
  /**
119
180
  * Does a row's static-text carry this exact title as a segment? Sidebar row
120
181
  * text concatenates descendant static texts with "|" (AXDRAG1: e.g.
@@ -182,6 +243,74 @@ export declare function staticBoundaryY(orderedAreaRows: {
182
243
  */
183
244
  export declare function correctedDropY(staticY: number, sourceCenterY: number, span: number): number;
184
245
  export declare function inBand(y: number, viewport: SidebarRect, pad?: number): boolean;
246
+ /**
247
+ * The travel a single certified drag can cover inside this viewport: the band
248
+ * minus the grab/drop margins at both ends. The rung-1 shared-viewport test and
249
+ * the tall-section pre-flight both measure against this ONE number.
250
+ */
251
+ export declare function usableDragSpan(viewport: SidebarRect): number;
252
+ /**
253
+ * A sidebar SECTION is an area row plus every row Things renders under it (its
254
+ * projects, and its "Later Projects" row). `bottom` is the next area row's top —
255
+ * the last section runs to the bottom of the table.
256
+ */
257
+ export interface SidebarSectionSpan {
258
+ title: string;
259
+ /** Section height in points (area row top → next area row top). */
260
+ height: number;
261
+ /** How many table rows the section contains (its own row included). */
262
+ rows: number;
263
+ /**
264
+ * Which same-titled sidebar row this section belongs to, in visual order
265
+ * (AXDRAG3); -1 when the title is unique. The collapse rung needs it to
266
+ * actuate the right chevron when two areas share a name.
267
+ */
268
+ ordinal: number;
269
+ }
270
+ /**
271
+ * The TALLEST section the gesture would have to climb over, or null when every
272
+ * one of them fits. #658: an area's projects render beneath it, so a section can
273
+ * be taller than the whole sidebar viewport — and BOTH shipped rungs need the
274
+ * source row and the drop boundary visible AT ONCE, so such a section is a wall
275
+ * no amount of scrolling gets around. Measured over the travel span between the
276
+ * grab point and the aimed boundary; the source's OWN section is excluded (
277
+ * lifting it collapses it) and a section is only counted when the snapshot
278
+ * actually resolved rows inside it, so a partially-materialized AX tree cannot
279
+ * fabricate a wall out of a gap between two distant rows.
280
+ */
281
+ export declare function sectionsInSpan(orderedAreaRows: {
282
+ title: string;
283
+ row: SidebarRowInfo;
284
+ }[], allRows: SidebarRowInfo[], fromY: number, toY: number, sourceTitle: string): SidebarSectionSpan[];
285
+ /** The tallest section in the travel span, or null when every one of them fits. */
286
+ export declare function tallestSectionInSpan(orderedAreaRows: {
287
+ title: string;
288
+ row: SidebarRowInfo;
289
+ }[], allRows: SidebarRowInfo[], fromY: number, toY: number, sourceTitle: string): SidebarSectionSpan | null;
290
+ /**
291
+ * Every section in the travel span the ladder cannot climb — tallest FIRST, so
292
+ * the collapse rung clears the worst obstruction before spending a gesture on a
293
+ * marginal one. SBCOL1 §4 measured a two-wall span: collapsing both turned a
294
+ * ten-position move into an ordinary multi-hop.
295
+ */
296
+ export declare function blockingSectionsInSpan(orderedAreaRows: {
297
+ title: string;
298
+ row: SidebarRowInfo;
299
+ }[], allRows: SidebarRowInfo[], fromY: number, toY: number, sourceTitle: string, viewport: SidebarRect): SidebarSectionSpan[];
300
+ /**
301
+ * Is this section a WALL for the shipped ladder (rung 1 + the multi-hop floor)?
302
+ * A section taller than one drag's usable span can never be crossed, because
303
+ * every hop must land the source on the far side of it in ONE gesture.
304
+ */
305
+ export declare function sectionBlocks(section: SidebarSectionSpan, viewport: SidebarRect): boolean;
306
+ /**
307
+ * The refusal an unclimbable section earns once the COLLAPSE RUNG has failed on
308
+ * it (SBCOL1) — the honest twin of the old "the viewport is too small to make
309
+ * progress" copy, which blamed the window size for a geometry no window size
310
+ * fixes (#658). `whyCollapseFailed` names what stopped the driver from folding
311
+ * the section away itself, so the advice to do it by hand is not offered blind.
312
+ */
313
+ export declare function blockedSectionDetail(section: SidebarSectionSpan, viewport: SidebarRect, destination: string, whyCollapseFailed?: string): string;
185
314
  /**
186
315
  * Grab/drop x: a fixed FRACTION of the row's resolved width (≈ the label area
187
316
  * NATIVE1 clicked at x+170 on a 240px row), clear of the leading icon and the
@@ -199,7 +328,26 @@ export interface DragDriveResult {
199
328
  detail: string;
200
329
  /** A gesture may have landed before the failure — recovery state, honestly. */
201
330
  recovered?: boolean;
331
+ /**
332
+ * The collapse rung folded these sidebar areas away to clear the path, and put
333
+ * every one of them back. Present only when the rung actually ran, so an
334
+ * ordinary move carries nothing (SBCOL1).
335
+ */
336
+ collapsed?: string[];
337
+ /**
338
+ * A collapsed area the driver could NOT re-expand. The disclosure state lives
339
+ * in the app's own preferences and SURVIVES a relaunch (SBCOL1 §3), so a
340
+ * failed restore is a durable change to the user's sidebar and is never
341
+ * silent — even on an otherwise successful move.
342
+ */
343
+ restoreFailed?: string[];
202
344
  }
345
+ /**
346
+ * One honest sentence per real cause, each naming the thing to change. The
347
+ * remediation matters as much as the diagnosis: the old copy told a user with a
348
+ * plainly-open sidebar to check whether the sidebar was open.
349
+ */
350
+ export declare function describeSnapshotFailure(refusal: SnapshotRefusal): string;
203
351
  /**
204
352
  * The rung-2 anchor: every placement reduces to "drop above this area row"
205
353
  * (title) or "drop below the last row" (null). `undefined` = unresolvable.