things-api 0.8.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 +31 -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 +34 -2
- package/dist/cli/commands/area.js +140 -28
- 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 +23 -1
- package/dist/cli/commands/project.js +82 -15
- package/dist/cli/commands/project.js.map +1 -1
- package/dist/cli/commands/reads.d.ts +6 -125
- package/dist/cli/commands/reads.js +649 -657
- 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.d.ts +1 -1
- package/dist/cli/commands/show.js +169 -18
- package/dist/cli/commands/show.js.map +1 -1
- package/dist/cli/commands/snapshot.js +1 -1
- package/dist/cli/commands/snapshot.js.map +1 -1
- package/dist/cli/commands/todo.d.ts +1 -1
- package/dist/cli/commands/todo.js +21 -9
- 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 +29 -0
- package/dist/cli/did-you-mean.js +53 -0
- package/dist/cli/did-you-mean.js.map +1 -0
- 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 +67 -7
- package/dist/cli/glyphs.js +202 -19
- 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 +36 -21
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/period.d.ts +39 -0
- package/dist/cli/period.js +141 -0
- package/dist/cli/period.js.map +1 -0
- package/dist/cli/read-driver.d.ts +83 -0
- package/dist/cli/read-driver.js +215 -0
- package/dist/cli/read-driver.js.map +1 -0
- package/dist/cli/render.d.ts +246 -0
- package/dist/cli/render.js +806 -0
- package/dist/cli/render.js.map +1 -0
- package/dist/cli/resolve-invocation.d.ts +116 -0
- package/dist/cli/resolve-invocation.js +261 -0
- package/dist/cli/resolve-invocation.js.map +1 -0
- package/dist/cli/shell-quote.d.ts +9 -0
- package/dist/cli/shell-quote.js +12 -0
- package/dist/cli/shell-quote.js.map +1 -0
- 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 +146 -24
- package/dist/client.js +106 -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 +136 -1
- 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 +850 -128
- package/dist/mcp/server.js.map +1 -1
- package/dist/model/entities.d.ts +40 -7
- package/dist/model/entities.js.map +1 -1
- package/dist/model/mappers.d.ts +7 -0
- package/dist/model/mappers.js +20 -3
- package/dist/model/mappers.js.map +1 -1
- package/dist/model/occurrences.d.ts +6 -1
- package/dist/model/occurrences.js +9 -5
- package/dist/model/occurrences.js.map +1 -1
- package/dist/model/recurrence.d.ts +17 -8
- package/dist/model/recurrence.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/caps.d.ts +14 -0
- package/dist/read/caps.js +21 -0
- package/dist/read/caps.js.map +1 -0
- 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 +9 -4
- package/dist/read/log-boundary.js.map +1 -1
- package/dist/read/predicates.d.ts +61 -0
- package/dist/read/predicates.js +78 -0
- package/dist/read/predicates.js.map +1 -0
- 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 +156 -8
- package/dist/read/queries.js +261 -38
- package/dist/read/queries.js.map +1 -1
- package/dist/read/search-rank.d.ts +36 -0
- package/dist/read/search-rank.js +31 -0
- package/dist/read/search-rank.js.map +1 -0
- 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/show-target.d.ts +13 -4
- package/dist/read/show-target.js +11 -2
- package/dist/read/show-target.js.map +1 -1
- package/dist/read/sidebar-order.d.ts +19 -0
- package/dist/read/sidebar-order.js +93 -0
- package/dist/read/sidebar-order.js.map +1 -0
- 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/truncation.js +217 -0
- package/dist/read/truncation.js.map +1 -0
- package/dist/read/views.d.ts +177 -19
- package/dist/read/views.js +390 -213
- package/dist/read/views.js.map +1 -1
- package/dist/surface-copy.d.ts +42 -0
- package/dist/surface-copy.js +55 -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 -5
- 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 +494 -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
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { errorEnvelope, ExitCode, openThings } from "../index.js";
|
|
2
|
+
import { indexPastLeadingFlags, subcommandsOf, WRITE_GROUP_ORDER } from "./resolve-invocation.js";
|
|
3
|
+
import { shellQuote } from "./shell-quote.js";
|
|
4
|
+
/** The type-group a resolved reference maps to a write namespace by. */
|
|
5
|
+
const KIND_GROUP = { "to-do": "todo", project: "project", area: "area" };
|
|
6
|
+
/** Split the raw argv into verb, ref, echo-tail, and the global read opts. */
|
|
7
|
+
function parse(args) {
|
|
8
|
+
const at = indexPastLeadingFlags(args) ?? 0;
|
|
9
|
+
const verb = args[at] ?? "";
|
|
10
|
+
const after = args.slice(at + 1);
|
|
11
|
+
let json = false;
|
|
12
|
+
let db;
|
|
13
|
+
let ref = null;
|
|
14
|
+
const tail = [];
|
|
15
|
+
for (let i = 0; i < after.length; i++) {
|
|
16
|
+
const tok = after[i] ?? "";
|
|
17
|
+
if (tok === "--json") {
|
|
18
|
+
json = true;
|
|
19
|
+
}
|
|
20
|
+
else if (tok === "--db") {
|
|
21
|
+
db = after[++i];
|
|
22
|
+
}
|
|
23
|
+
else if (tok.startsWith("--db=")) {
|
|
24
|
+
db = tok.slice("--db=".length);
|
|
25
|
+
}
|
|
26
|
+
else if (!tok.startsWith("-") && ref === null) {
|
|
27
|
+
ref = tok; // the first positional is the write target
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
tail.push(tok);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
// A leading --json/--db (before the verb) still governs output/target.
|
|
34
|
+
for (let i = 0; i < at; i++) {
|
|
35
|
+
const tok = args[i] ?? "";
|
|
36
|
+
if (tok === "--json")
|
|
37
|
+
json = true;
|
|
38
|
+
else if (tok === "--db")
|
|
39
|
+
db = args[++i];
|
|
40
|
+
else if (tok.startsWith("--db="))
|
|
41
|
+
db = tok.slice("--db=".length);
|
|
42
|
+
}
|
|
43
|
+
return { verb, ref, tail, json, db };
|
|
44
|
+
}
|
|
45
|
+
/** Build `things <group> <verb> <ref?> <trailing flags…>` for the suggestion echo. */
|
|
46
|
+
function typedForm(group, verb, ref, tail) {
|
|
47
|
+
const parts = ["things", group, verb, ...(ref !== null ? [shellQuote(ref)] : []), ...tail];
|
|
48
|
+
return parts.join(" ");
|
|
49
|
+
}
|
|
50
|
+
/** The write groups (in a stable order) whose registered verbs include `verb`. */
|
|
51
|
+
function groupsWithVerb(program, verb) {
|
|
52
|
+
return WRITE_GROUP_ORDER.filter((g) => subcommandsOf(program, g).has(verb));
|
|
53
|
+
}
|
|
54
|
+
/** Classify the ref's type with the read client; null when it does not uniquely resolve. */
|
|
55
|
+
function resolvedGroup(db, ref) {
|
|
56
|
+
let client = null;
|
|
57
|
+
try {
|
|
58
|
+
client = openThings(db ? { dbPath: db } : {});
|
|
59
|
+
const target = client.read.showTarget(ref);
|
|
60
|
+
return KIND_GROUP[target.kind] ?? null;
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
// Ambiguous, unresolvable, or an unreadable db all fall to the generic hint.
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
finally {
|
|
67
|
+
client?.close();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Compose the suggestion set for a bare verb. The first line is the best guess;
|
|
72
|
+
* the rest are the other namespaced forms worth offering.
|
|
73
|
+
*/
|
|
74
|
+
function suggestionsFor(program, p) {
|
|
75
|
+
const verbLower = p.verb.toLowerCase();
|
|
76
|
+
// add/create: the target is a NEW title, so a name never "resolves" — offer
|
|
77
|
+
// to-do first (the common intent), then the container namespaces.
|
|
78
|
+
if (verbLower === "add" || verbLower === "create") {
|
|
79
|
+
if (p.ref === null)
|
|
80
|
+
return [];
|
|
81
|
+
return [
|
|
82
|
+
typedForm("todo", "add", p.ref, p.tail),
|
|
83
|
+
typedForm("project", "add", p.ref, p.tail),
|
|
84
|
+
typedForm("area", "add", p.ref, p.tail),
|
|
85
|
+
];
|
|
86
|
+
}
|
|
87
|
+
const applicable = groupsWithVerb(program, verbLower);
|
|
88
|
+
// A ref that uniquely resolves to an area/project/to-do gets ONE concrete
|
|
89
|
+
// suggestion — but only when that type actually offers the verb.
|
|
90
|
+
if (p.ref !== null) {
|
|
91
|
+
const group = resolvedGroup(p.db, p.ref);
|
|
92
|
+
if (group !== null && applicable.includes(group)) {
|
|
93
|
+
return [typedForm(group, p.verb, p.ref, p.tail)];
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
// Generic: every namespace that offers the verb (fall back to all write
|
|
97
|
+
// groups if the verb is a synonym none register directly).
|
|
98
|
+
const groups = applicable.length > 0 ? applicable : WRITE_GROUP_ORDER;
|
|
99
|
+
return groups.map((g) => typedForm(g, p.verb, p.ref, p.tail));
|
|
100
|
+
}
|
|
101
|
+
/** Human render: the error line, the suggestion(s), and the writes signpost. */
|
|
102
|
+
function renderHuman(verb, suggestions) {
|
|
103
|
+
const lines = [];
|
|
104
|
+
if (suggestions.length === 0) {
|
|
105
|
+
lines.push(`error: \`things ${verb}\` is a write verb but names no target — writes are namespaced.`);
|
|
106
|
+
}
|
|
107
|
+
else if (suggestions.length === 1) {
|
|
108
|
+
lines.push(`error: \`things ${verb} …\` is not a command — did you mean:`);
|
|
109
|
+
lines.push(` ${suggestions[0]}`);
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
lines.push(`error: \`things ${verb} …\` is not a command — writes are namespaced. Try:`);
|
|
113
|
+
for (const s of suggestions)
|
|
114
|
+
lines.push(` ${s}`);
|
|
115
|
+
}
|
|
116
|
+
lines.push("", "See `things help writes` for the full write grammar.");
|
|
117
|
+
return lines.join("\n");
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Run the bare-verb hint: emit the suggestion(s) and set the Usage exit code.
|
|
121
|
+
* Never opens the write pipeline — this only reads to classify the ref.
|
|
122
|
+
*/
|
|
123
|
+
export function runVerbHint(program, args) {
|
|
124
|
+
const p = parse(args);
|
|
125
|
+
const suggestions = suggestionsFor(program, p);
|
|
126
|
+
if (p.json) {
|
|
127
|
+
const meta = { dbVersion: null, fingerprint: "unknown", elapsedMs: 0 };
|
|
128
|
+
const message = suggestions.length === 1
|
|
129
|
+
? `\`things ${p.verb} …\` is not a command — did you mean \`${suggestions[0]}\``
|
|
130
|
+
: `\`things ${p.verb} …\` is not a command — writes are namespaced (see \`things help writes\`)`;
|
|
131
|
+
process.stdout.write(`${JSON.stringify(errorEnvelope({ code: "usage", message, details: { suggestions } }, meta))}\n`);
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
process.stderr.write(`${renderHuman(p.verb, suggestions)}\n`);
|
|
135
|
+
}
|
|
136
|
+
process.exitCode = ExitCode.Usage;
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=verb-hint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verb-hint.js","sourceRoot":"","sources":["../../src/cli/verb-hint.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,135 @@
|
|
|
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
|
+
/** Drop every SGR (color/dim/bold/…) escape, leaving the visible characters. */
|
|
19
|
+
export declare function stripSgr(s: string): string;
|
|
20
|
+
/** Terminal cells a single codepoint occupies: 0 (combining/zero-width), 2 (wide), else 1. */
|
|
21
|
+
export declare function charWidth(cp: number): number;
|
|
22
|
+
/** Visible terminal width of a string: SGR-stripped, summed per codepoint. */
|
|
23
|
+
export declare function visibleWidth(s: string): number;
|
|
24
|
+
/**
|
|
25
|
+
* Truncate PLAIN text (no SGR) to at most `cols` terminal cells, never splitting
|
|
26
|
+
* a wide codepoint across the boundary. Used to clip a raw title before its
|
|
27
|
+
* styling wrappers are re-applied, so the ellipsis/clip boundary lands OUTSIDE
|
|
28
|
+
* the SGR runs (the fitter never cuts an escape sequence).
|
|
29
|
+
*/
|
|
30
|
+
export declare function clipPlain(s: string, cols: number): string;
|
|
31
|
+
/**
|
|
32
|
+
* The title's protected minimum, in columns. NOT a per-row clamp: it is the
|
|
33
|
+
* input to the derived floors (render.ts FULL_FIT_FLOOR / COMPACT_FIT_FLOOR =
|
|
34
|
+
* worst-case furniture + TITLE_MIN) and the threshold the sacrifice order
|
|
35
|
+
* protects the title down to. Because the driver fits every row to at least the
|
|
36
|
+
* compact floor (max(width, COMPACT_FIT_FLOOR)),
|
|
37
|
+
* the worst-furniture row's title lands at exactly TITLE_MIN and every lighter
|
|
38
|
+
* row's title is automatically wider — so this floor is always satisfiable and
|
|
39
|
+
* there is no sub-floor clip (below the floor the terminal wraps, losing nothing).
|
|
40
|
+
*/
|
|
41
|
+
export declare const TITLE_MIN = 16;
|
|
42
|
+
/**
|
|
43
|
+
* The named, ordered stages of a row's collapse — the ratified sacrifice order
|
|
44
|
+
* (docs/design/width-aware-tty.md, GUI-measured oracle + the CLI-only container
|
|
45
|
+
* rule). Reordering the sacrifice is editing this list plus {@link fitRow}.
|
|
46
|
+
*/
|
|
47
|
+
export type FitStage = "full" | "both-shrink" | "container-drop" | "tags-bare";
|
|
48
|
+
/**
|
|
49
|
+
* The segments of a row the fitter composes. Fixed parts are pre-styled strings
|
|
50
|
+
* (measured, never altered); the two collapsible parts arrive as raw material
|
|
51
|
+
* plus their styling closures so truncation happens on plain text and the SGR
|
|
52
|
+
* wraps the result:
|
|
53
|
+
* - `left` uuid column + box + meta chips (measured; the space before the
|
|
54
|
+
* title is added by the fitter)
|
|
55
|
+
* - `rawTitle` + `styleTitle` — the plain title text and its wrapper
|
|
56
|
+
* - `tail` trailing markers (count chip, ◷ ≡ ≔) — pre-styled incl. its
|
|
57
|
+
* leading space, or ""
|
|
58
|
+
* - `tagNames` raw tag names (no `#`) + `styleTags` wrapping a `#a #b` form
|
|
59
|
+
* (incl. the leading space) — the fitter folds the list
|
|
60
|
+
* - `context` the ` (container)` suffix — pre-styled incl. leading space, or ""
|
|
61
|
+
* - `deadline` the full ⚑ token — pre-styled incl. leading space, or ""
|
|
62
|
+
* - `full` the fully-composed row (the byte-identical no-fit output)
|
|
63
|
+
*/
|
|
64
|
+
export interface RowSegments {
|
|
65
|
+
left: string;
|
|
66
|
+
rawTitle: string;
|
|
67
|
+
styleTitle: (text: string) => string;
|
|
68
|
+
tail: string;
|
|
69
|
+
tagNames: string[];
|
|
70
|
+
styleTags: (form: string) => string;
|
|
71
|
+
context: string;
|
|
72
|
+
deadline: string;
|
|
73
|
+
full: string;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Fit one row into `width` terminal cells per the ratified collapse order. The
|
|
77
|
+
* always-present parts (uuid, box, meta chips, tail markers, full deadline)
|
|
78
|
+
* never shrink; title and tags divvy up what remains LAZILY — tags fold to the
|
|
79
|
+
* widest progressive level that fits and the 4:1 ratio arbitrates only when both
|
|
80
|
+
* genuinely contend (slack an under-share side leaves flows to the other). Then
|
|
81
|
+
* the CLI-only container is sacrificed WHOLE (before the last tag folds), then
|
|
82
|
+
* the last tag folds to bare `#…`, and the title bottoms at TITLE_MIN. `width` is
|
|
83
|
+
* the caller's effective width (already max'd with COMPACT_FIT_FLOOR), so the final
|
|
84
|
+
* stage always satisfies the floor; a sub-floor width would simply let the
|
|
85
|
+
* terminal wrap (the title is kept whole rather than clipped). A row that already
|
|
86
|
+
* fits keeps its full content; the only always-applied transform is the
|
|
87
|
+
* experimental deadline gutter ({@link alignDeadline}), which right-pins the `⚑`.
|
|
88
|
+
*/
|
|
89
|
+
export declare function fitRow(seg: RowSegments, width: number): string;
|
|
90
|
+
/**
|
|
91
|
+
* The resolved fit for a view: the effective width every row is fitted to, plus
|
|
92
|
+
* whether deadline tokens render in the COMPACT form (the Things iOS narrow-
|
|
93
|
+
* width oracle — `8/12`, `58d ago` — docs/design/width-aware-tty.md § Compact
|
|
94
|
+
* deadline forms). `compact` is decided ONCE from the effective width against
|
|
95
|
+
* the two derived floors, never per row, so a view's right-pinned gutter never
|
|
96
|
+
* mixes `Aug 12` and `8/12`.
|
|
97
|
+
*/
|
|
98
|
+
export interface EffectiveFit {
|
|
99
|
+
/** Width to fit each row to — the raw fit width clamped up to the compact floor. */
|
|
100
|
+
width: number;
|
|
101
|
+
/** True when the effective width sits below the full-form floor. */
|
|
102
|
+
compact: boolean;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Resolve the effective fit from the once-resolved fit width and the two
|
|
106
|
+
* glyph-derived floors (`render.ts`: FULL_FIT_FLOOR / COMPACT_FIT_FLOOR). This
|
|
107
|
+
* is the SINGLE decision point for the full-vs-compact deadline form:
|
|
108
|
+
* - `width ≥ full` → full forms, fit to width (today's behavior);
|
|
109
|
+
* - `compact ≤ width < full` → all deadlines compact, fit to width;
|
|
110
|
+
* - `width < compact` → clamp to the compact floor and wrap (sub-floor
|
|
111
|
+
* rule at the lower floor), still compact.
|
|
112
|
+
* The floors live in render.ts because their derivation needs the glyph
|
|
113
|
+
* inventory; the branching lives here so it is pure and unit-testable.
|
|
114
|
+
*/
|
|
115
|
+
export declare function resolveFit(fitWidth: number, fullFloor: number, compactFloor: number): EffectiveFit;
|
|
116
|
+
/**
|
|
117
|
+
* Set the process-wide fit width (columns), resolved ONCE by the CLI driver at
|
|
118
|
+
* startup. `null` disables fitting (the default — every non-TTY path stays
|
|
119
|
+
* byte-stable). Tests set this explicitly and reset it afterward.
|
|
120
|
+
*/
|
|
121
|
+
export declare function setFitWidth(width: number | null): void;
|
|
122
|
+
/** The current fit width, or null when fitting is disabled. */
|
|
123
|
+
export declare function getFitWidth(): number | null;
|
|
124
|
+
/**
|
|
125
|
+
* Resolve the fit width from the environment and stdout: `THINGS_WIDTH` wins
|
|
126
|
+
* when set (a positive integer forces that width; `0` disables fitting), else
|
|
127
|
+
* the terminal's column count when stdout is a TTY, else null (pipes, grep,
|
|
128
|
+
* captured output — no fitting, byte-stable). A malformed `THINGS_WIDTH` is
|
|
129
|
+
* ignored (falls through to the TTY/null resolution).
|
|
130
|
+
*/
|
|
131
|
+
export declare function resolveWidth(opts: {
|
|
132
|
+
env: Record<string, string | undefined>;
|
|
133
|
+
columns: number | undefined;
|
|
134
|
+
isTTY: boolean;
|
|
135
|
+
}): number | null;
|
|
@@ -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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"}
|