@aragon-agent/cli 0.6.38 → 0.6.40
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/CHANGELOG.md +263 -0
- package/README.md +544 -11
- package/dist/agent/controller.d.ts +245 -10
- package/dist/agent/controller.js +367 -22
- package/dist/agent/model-profile-settings.js +12 -1
- package/dist/agent/plan-prompt.d.ts +1 -3
- package/dist/agent/plan-prompt.js +13 -7
- package/dist/agent/reducer.d.ts +70 -2
- package/dist/agent/reducer.js +59 -6
- package/dist/agent/skill-triage-prompt.d.ts +42 -0
- package/dist/agent/skill-triage-prompt.js +74 -0
- package/dist/agent/system-prompt.d.ts +28 -2
- package/dist/agent/system-prompt.js +12 -4
- package/dist/cli.js +106 -32
- package/dist/commands/builtins.js +24 -3
- package/dist/commands/keys-command.d.ts +23 -0
- package/dist/commands/keys-command.js +38 -0
- package/dist/commands/registry.d.ts +39 -0
- package/dist/commands/skills.js +86 -1
- package/dist/commands/subagent-command.d.ts +25 -0
- package/dist/commands/subagent-command.js +242 -0
- package/dist/commands/task-command.d.ts +39 -0
- package/dist/commands/task-command.js +102 -0
- package/dist/compaction/command.js +16 -3
- package/dist/compaction/compactor.d.ts +25 -0
- package/dist/compaction/compactor.js +111 -10
- package/dist/compaction/digest.d.ts +24 -1
- package/dist/compaction/digest.js +139 -17
- package/dist/compaction/limits.d.ts +21 -0
- package/dist/compaction/limits.js +21 -0
- package/dist/compaction/reason-text.d.ts +36 -0
- package/dist/compaction/reason-text.js +78 -0
- package/dist/compaction/types.d.ts +82 -14
- package/dist/compaction/wiring.d.ts +13 -8
- package/dist/compaction/wiring.js +1 -0
- package/dist/config/cli-commands.d.ts +23 -0
- package/dist/config/cli-commands.js +36 -0
- package/dist/config/env.js +17 -4
- package/dist/config/load.js +55 -3
- package/dist/config/schema.d.ts +125 -10
- package/dist/config/schema.js +138 -14
- package/dist/config/store.js +23 -3
- package/dist/diagnostics/doctor.d.ts +9 -0
- package/dist/diagnostics/doctor.js +34 -0
- package/dist/diagnostics/info.d.ts +71 -0
- package/dist/diagnostics/info.js +91 -5
- package/dist/exec/bridge/caller-tools.d.ts +96 -0
- package/dist/exec/bridge/caller-tools.js +213 -0
- package/dist/exec/bridge/decisions.d.ts +78 -0
- package/dist/exec/bridge/decisions.js +116 -0
- package/dist/exec/bridge/human-answers.d.ts +38 -0
- package/dist/exec/bridge/human-answers.js +164 -0
- package/dist/exec/bridge/limits.d.ts +68 -0
- package/dist/exec/bridge/limits.js +68 -0
- package/dist/exec/bridge/ndjson.d.ts +17 -0
- package/dist/exec/bridge/ndjson.js +83 -0
- package/dist/exec/bridge/pending.d.ts +70 -0
- package/dist/exec/bridge/pending.js +132 -0
- package/dist/exec/bridge/protocol.d.ts +346 -0
- package/dist/exec/bridge/protocol.js +256 -0
- package/dist/exec/bridge/server.d.ts +142 -0
- package/dist/exec/bridge/server.js +502 -0
- package/dist/exec/bridge/source.d.ts +58 -0
- package/dist/exec/bridge/source.js +145 -0
- package/dist/exec/bridge/writer.d.ts +26 -0
- package/dist/exec/bridge/writer.js +156 -0
- package/dist/exec/cli-commands.js +30 -2
- package/dist/exec/emitter.d.ts +16 -2
- package/dist/exec/emitter.js +26 -6
- package/dist/exec/events.d.ts +465 -10
- package/dist/exec/events.js +47 -1
- package/dist/exec/hooks/dispatch.d.ts +76 -0
- package/dist/exec/hooks/dispatch.js +169 -0
- package/dist/exec/hooks/index.d.ts +83 -0
- package/dist/exec/hooks/index.js +205 -0
- package/dist/exec/hooks/limits.d.ts +56 -0
- package/dist/exec/hooks/limits.js +49 -0
- package/dist/exec/hooks/registry.d.ts +49 -0
- package/dist/exec/hooks/registry.js +91 -0
- package/dist/exec/hooks/tool-wrapper.d.ts +94 -0
- package/dist/exec/hooks/tool-wrapper.js +154 -0
- package/dist/exec/hooks/turn.d.ts +61 -0
- package/dist/exec/hooks/turn.js +59 -0
- package/dist/exec/hooks/validate.d.ts +85 -0
- package/dist/exec/hooks/validate.js +310 -0
- package/dist/exec/index.d.ts +130 -4
- package/dist/exec/index.js +501 -10
- package/dist/exec/options.d.ts +124 -2
- package/dist/exec/options.js +413 -6
- package/dist/exec/permission.d.ts +9 -1
- package/dist/exec/permission.js +7 -1
- package/dist/exec/request/limits.d.ts +51 -0
- package/dist/exec/request/limits.js +49 -0
- package/dist/exec/request/scope.d.ts +106 -0
- package/dist/exec/request/scope.js +149 -0
- package/dist/exec/result/limits.d.ts +74 -0
- package/dist/exec/result/limits.js +72 -0
- package/dist/exec/result/prompt.d.ts +28 -0
- package/dist/exec/result/prompt.js +74 -0
- package/dist/exec/result/schema.d.ts +99 -0
- package/dist/exec/result/schema.js +464 -0
- package/dist/exec/result/sink.d.ts +56 -0
- package/dist/exec/result/sink.js +50 -0
- package/dist/exec/result/tool.d.ts +37 -0
- package/dist/exec/result/tool.js +118 -0
- package/dist/exec/runner.d.ts +395 -1
- package/dist/exec/runner.js +860 -8
- package/dist/exec/threads/aggregate.d.ts +34 -0
- package/dist/exec/threads/aggregate.js +30 -0
- package/dist/exec/threads/capacity.d.ts +23 -0
- package/dist/exec/threads/capacity.js +38 -0
- package/dist/exec/threads/dispatch-source.d.ts +60 -0
- package/dist/exec/threads/dispatch-source.js +106 -0
- package/dist/exec/threads/execute-threads.d.ts +53 -0
- package/dist/exec/threads/execute-threads.js +234 -0
- package/dist/exec/threads/gate.d.ts +42 -0
- package/dist/exec/threads/gate.js +81 -0
- package/dist/exec/threads/identity.d.ts +36 -0
- package/dist/exec/threads/identity.js +67 -0
- package/dist/exec/threads/interrupt.d.ts +29 -0
- package/dist/exec/threads/interrupt.js +79 -0
- package/dist/exec/threads/limits.d.ts +58 -0
- package/dist/exec/threads/limits.js +35 -0
- package/dist/exec/threads/outcome.d.ts +47 -0
- package/dist/exec/threads/outcome.js +26 -0
- package/dist/exec/threads/registry.d.ts +81 -0
- package/dist/exec/threads/registry.js +427 -0
- package/dist/exec/threads/runtime.d.ts +107 -0
- package/dist/exec/threads/runtime.js +523 -0
- package/dist/exec/threads/thread-source.d.ts +20 -0
- package/dist/exec/threads/thread-source.js +134 -0
- package/dist/fast/limits.d.ts +54 -0
- package/dist/fast/limits.js +54 -0
- package/dist/fast/summary-call.d.ts +72 -0
- package/dist/fast/summary-call.js +133 -0
- package/dist/fast/summary-prompt.d.ts +106 -0
- package/dist/fast/summary-prompt.js +320 -0
- package/dist/fast/task-summary-types.d.ts +66 -0
- package/dist/fast/task-summary-types.js +17 -0
- package/dist/logging/logger.d.ts +1 -1
- package/dist/proc/limits.d.ts +7 -0
- package/dist/proc/limits.js +7 -0
- package/dist/proc/log-mirror.d.ts +21 -4
- package/dist/proc/log-mirror.js +29 -7
- package/dist/proc/output-streams.d.ts +50 -0
- package/dist/proc/output-streams.js +54 -0
- package/dist/skills/service.d.ts +41 -0
- package/dist/skills/service.js +62 -0
- package/dist/skills/triage.d.ts +194 -0
- package/dist/skills/triage.js +393 -0
- package/dist/team/forced-dispatch.d.ts +95 -0
- package/dist/team/forced-dispatch.js +208 -0
- package/dist/team/limits.d.ts +21 -1
- package/dist/team/limits.js +21 -1
- package/dist/team/normalize.d.ts +15 -0
- package/dist/team/normalize.js +21 -4
- package/dist/team/prompt.d.ts +6 -3
- package/dist/team/prompt.js +9 -5
- package/dist/team/runtime.d.ts +9 -0
- package/dist/team/runtime.js +8 -0
- package/dist/team/subagent.d.ts +15 -0
- package/dist/team/subagent.js +45 -1
- package/dist/team/task-tool.js +11 -7
- package/dist/tools/bash-tool.d.ts +10 -0
- package/dist/tools/bash-tool.js +71 -6
- package/dist/tools/human-input.d.ts +35 -2
- package/dist/tools/human-input.js +38 -0
- package/dist/tools/index.d.ts +93 -1
- package/dist/tools/index.js +112 -3
- package/dist/tools/plan-tools.d.ts +20 -12
- package/dist/tools/plan-tools.js +24 -17
- package/dist/tools/tool-gate.d.ts +100 -0
- package/dist/tools/tool-gate.js +87 -0
- package/dist/ui/App.js +238 -12
- package/dist/ui/Header.d.ts +28 -16
- package/dist/ui/Header.js +21 -19
- package/dist/ui/StatusBar.d.ts +1 -0
- package/dist/ui/StatusBar.js +1 -0
- package/dist/ui/Transcript.js +1 -1
- package/dist/ui/entries/CompactionCard.d.ts +11 -1
- package/dist/ui/entries/CompactionCard.js +65 -4
- package/dist/ui/interaction-hints.js +6 -0
- package/dist/ui/layout/OverlayFrame.d.ts +18 -0
- package/dist/ui/layout/OverlayFrame.js +19 -4
- package/dist/ui/layout/header-hints.d.ts +55 -62
- package/dist/ui/layout/header-hints.js +59 -166
- package/dist/ui/layout/header-left.d.ts +115 -0
- package/dist/ui/layout/header-left.js +206 -0
- package/dist/ui/layout/shortcut-panel-layout.d.ts +191 -0
- package/dist/ui/layout/shortcut-panel-layout.js +321 -0
- package/dist/ui/layout/status-detail-layout.js +5 -1
- package/dist/ui/layout/status-layout.d.ts +10 -0
- package/dist/ui/layout/status-layout.js +18 -1
- package/dist/ui/layout/task-summary-cell.d.ts +61 -0
- package/dist/ui/layout/task-summary-cell.js +65 -0
- package/dist/ui/overlays/HelpOverlay.d.ts +12 -0
- package/dist/ui/overlays/HelpOverlay.js +29 -2
- package/dist/ui/overlays/SettingsScreen.js +29 -5
- package/dist/ui/overlays/ShortcutPanel.d.ts +39 -0
- package/dist/ui/overlays/ShortcutPanel.js +106 -0
- package/dist/ui/overlays/TeamFanoutPage.d.ts +35 -0
- package/dist/ui/overlays/TeamFanoutPage.js +78 -0
- package/dist/ui/overlays/model-profile-state.d.ts +19 -1
- package/dist/ui/overlays/model-profile-state.js +12 -0
- package/dist/ui/overlays/team-fanout-state.d.ts +73 -0
- package/dist/ui/overlays/team-fanout-state.js +97 -0
- package/dist/ui/shortcuts.d.ts +81 -0
- package/dist/ui/shortcuts.js +221 -0
- package/package.json +2 -2
|
@@ -1,25 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The right-hand hint budget for the header row
|
|
2
|
+
* The right-hand hint budget for the header row
|
|
3
|
+
* (tui-shortcut-drawer-and-task-summary §4.2).
|
|
3
4
|
*
|
|
4
5
|
* ASCII ONLY - `src/ui/**` is inside `glyphs.test.ts::inScope`.
|
|
5
6
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
7
|
+
* THIS FILE USED TO BE A SIX-RUNG LADDER. `^L branch`, `^P prompts`, `^I Index`,
|
|
8
|
+
* `^K switch kit`, `^G more` and the expanded variants competed for a width
|
|
9
|
+
* budget, so what a user saw was not "my shortcuts" but "which clauses happen to
|
|
10
|
+
* fit at this width" - and on an 80-column terminal that was three of six. The
|
|
11
|
+
* requirement replaces the whole idea with ONE entry point (`ctrl+G more`) whose
|
|
12
|
+
* panel lists every shortcut, so the ladder is gone and what remains is a
|
|
13
|
+
* three-state single clause.
|
|
13
14
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* WHY `minLeftCells` IS 24: the word mark is 13 cells and `provider:model` + cwd +
|
|
20
|
-
* the key symbol need at least 11 more to stay readable. It is lower than
|
|
21
|
-
* `status-layout`'s 34 because the top-left half has a brand to lose while the
|
|
22
|
-
* bottom-left half has a phase that must not disappear.
|
|
15
|
+
* WHY A PLANNER AND NOT A TERNARY IN THE JSX (R-0-4, unchanged): the header's
|
|
16
|
+
* right side was once one hand-computed width constant, and every later addition
|
|
17
|
+
* to it produced either a clipped hint or a pushed-out word mark with no test
|
|
18
|
+
* able to see it - the only thing lost is trailing characters.
|
|
23
19
|
*/
|
|
24
20
|
/**
|
|
25
21
|
* Bounds for the hint budget.
|
|
@@ -27,183 +23,80 @@
|
|
|
27
23
|
* WHY THEY LIVE IN THIS FILE (R-21, accepted): `CLAUDE.md`'s general rule is that
|
|
28
24
|
* structural bounds live in the subsystem's `limits.ts`, and `ui/layout/` has none
|
|
29
25
|
* - `budget.ts` keeps `HINT_MIN_ROWS` beside its own module for the same reason.
|
|
30
|
-
* This module has exactly one consumer. If `ui/layout/` ever grows a real
|
|
31
|
-
* `limits.ts`, these three constants should move there together; this comment is
|
|
32
|
-
* that future migration's note.
|
|
33
26
|
*/
|
|
34
27
|
export const HEADER_HINT_LIMITS = {
|
|
35
|
-
/**
|
|
28
|
+
/**
|
|
29
|
+
* Cells the LEFT half of the header must keep.
|
|
30
|
+
*
|
|
31
|
+
* 24 = the 13-cell word mark plus at least 11 for `provider:model`, the
|
|
32
|
+
* directory and the key symbol. Lower than `status-layout`'s 34 because the top
|
|
33
|
+
* row has a brand to lose while the bottom row has a phase that must not
|
|
34
|
+
* disappear.
|
|
35
|
+
*/
|
|
36
36
|
minLeftCells: 24,
|
|
37
|
-
/** Fixed spacing between two hint clauses. */
|
|
38
|
-
gapCells: 2,
|
|
39
37
|
/**
|
|
40
38
|
* Blank cells between the header's left half and this hint column (defect 12).
|
|
41
39
|
*
|
|
42
|
-
* A SEPARATE
|
|
43
|
-
* the
|
|
44
|
-
*
|
|
45
|
-
* later change to clause spacing silently re-lays-out the whole row.
|
|
40
|
+
* A SEPARATE CONCEPT FROM CLAUSE SPACING: this one separates the two HALVES of
|
|
41
|
+
* the row, and it is spent out of the same `columns` budget so the row still
|
|
42
|
+
* spans exactly the terminal width.
|
|
46
43
|
*/
|
|
47
44
|
separatorCells: 2,
|
|
48
45
|
};
|
|
49
|
-
/** The verbatim
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
* The requirement says the `ctrl+k` hint appears AFTER `ctrl+G`; the collapsed
|
|
56
|
-
* row is the narrowest budget in the header, and `^K` is a low-frequency action.
|
|
57
|
-
* Spending 13 columns of the everyday row on it would push `provider:model`
|
|
58
|
-
* leftward on every launch for a key pressed twice a session.
|
|
59
|
-
*/
|
|
60
|
-
const KIT_CLAUSE = { expanded: '^K switch kit' };
|
|
61
|
-
/**
|
|
62
|
-
* THE PROMPT LIBRARY KEY HAS EXACTLY ONE FORM, AND IT IS THE SHORT ONE
|
|
63
|
-
* (quick-prompt-library D-15).
|
|
64
|
-
*
|
|
65
|
-
* `^P` is a REQUIREMENT (the user must be able to see that `ctrl+P` exists), and
|
|
66
|
-
* the collapsed row is where that requirement is met. The expanded row's job is to
|
|
67
|
-
* reveal MORE keys than the collapsed one, so inserting a 17-cell
|
|
68
|
-
* `^P prompt library` before `^I` there would trade `^I` and `^K` away on an
|
|
69
|
-
* 80-column terminal - a user who pressed `^G` asking for more would be shown less.
|
|
70
|
-
*
|
|
71
|
-
* The second thing this constant's position encodes is an explicit REVISION of the
|
|
72
|
-
* `RV-P1-2` ordering constraint for the COLLAPSED ladder: `^P` sits before `^I`
|
|
73
|
-
* there, so `^I Index` now appears from 66 columns instead of 54. That is a
|
|
74
|
-
* deliberate, documented loss: `^I` is a once-per-project action and `^P` is the
|
|
75
|
-
* entry point this feature is about. The expanded ladder keeps its full existing
|
|
76
|
-
* order, so `RV-P1-2` still holds there and `^I` never disappears from a row where
|
|
77
|
-
* it used to fit.
|
|
78
|
-
*/
|
|
79
|
-
const PROMPTS_CLAUSE = '^P prompts';
|
|
80
|
-
const DETAIL_CLAUSE = { collapsed: '^G more', expanded: '^G less' };
|
|
81
|
-
/** L3 gives the key name and nothing else: discoverability at the last width. */
|
|
82
|
-
const BRANCH_KEY_ONLY = '^L';
|
|
83
|
-
/** Overlay wording, kept exactly as `Header.tsx` had it. */
|
|
84
|
-
const DETAILS_ON = 'Details on';
|
|
85
|
-
const DETAILS_OFF = 'Details off';
|
|
46
|
+
/** The verbatim text of each state. Exported so a test can name it instead of guessing. */
|
|
47
|
+
export const HEADER_HINTS = {
|
|
48
|
+
none: '^G more',
|
|
49
|
+
shortcuts: '^G close',
|
|
50
|
+
other: 'Esc close',
|
|
51
|
+
};
|
|
86
52
|
/** Above this, the chip stops counting and saturates (`^9999+`), as it always has. */
|
|
87
53
|
const SCROLL_CHIP_MAX = 9999;
|
|
88
54
|
/**
|
|
89
|
-
* Plan the right-hand
|
|
55
|
+
* Plan the right-hand hint.
|
|
90
56
|
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
57
|
+
* NO WIDTH BRANCHING: the clause is always rendered. When the row cannot hold it
|
|
58
|
+
* the LEFT half is what shrinks (to zero) and `overflow="hidden"` keeps that from
|
|
59
|
+
* spilling - the pre-feature behaviour, which is what makes the header safe at 20
|
|
60
|
+
* columns without a ladder. `headerHintFits` below exists only so the threshold
|
|
61
|
+
* is documented and testable, not so the renderer can branch on it.
|
|
94
62
|
*/
|
|
95
63
|
export function planHeaderHints(input) {
|
|
96
|
-
// NORMALISED HERE, NOT TRUSTED (R-14). `Header` defaults to 80, but a test or a
|
|
97
|
-
// future caller may pass anything; `budget.ts::dimension()` already set the rule
|
|
98
|
-
// (non-finite or < 1 falls back, everything else floors) and this follows it.
|
|
99
64
|
const columns = normalizeDimension(input.columns, 80);
|
|
100
65
|
const scrolledLines = Math.max(0, Math.floor(normalizeDimension(input.scrolledLines, 0)));
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
const detail = input.statusExpanded ? DETAILS_ON : DETAILS_OFF;
|
|
107
|
-
return buildPlan([{ id: 'detail', text: detail }], scrollCells, true);
|
|
108
|
-
}
|
|
109
|
-
const ladder = ladderFor(input.statusExpanded, input.kitHint !== false, input.promptsHint !== false);
|
|
110
|
-
// THE SEPARATOR IS SPENT OUT OF THE SAME ROW (defect 12): left half + gap + this
|
|
111
|
-
// column have to add up to `columns`, so the gap comes off the budget instead of
|
|
112
|
-
// being assumed free.
|
|
113
|
-
const budget = columns - HEADER_HINT_LIMITS.minLeftCells
|
|
114
|
-
- HEADER_HINT_LIMITS.separatorCells;
|
|
115
|
-
for (const rung of ladder) {
|
|
116
|
-
const plan = buildPlan(rung, scrollCells, false);
|
|
117
|
-
if (plan.cells <= budget)
|
|
118
|
-
return plan;
|
|
119
|
-
}
|
|
120
|
-
// L4 IS UNCONDITIONAL: `^G` is the only entry in this table that may never be
|
|
121
|
-
// dropped - it is how a user who hid the status detail fields gets them back.
|
|
122
|
-
// A header narrower than the last rung still renders; the LEFT column is what
|
|
123
|
-
// shrinks (to zero), and `overflow="hidden"` keeps that from overflowing.
|
|
124
|
-
return buildPlan(ladder[ladder.length - 1] ?? [], scrollCells, false);
|
|
66
|
+
const overlayOpen = input.panel !== 'none';
|
|
67
|
+
const chip = scrollChip(scrolledLines, overlayOpen);
|
|
68
|
+
const body = HEADER_HINTS[input.panel] ?? HEADER_HINTS.none;
|
|
69
|
+
const text = chip.length > 0 ? `${chip}${body}` : body;
|
|
70
|
+
return { text, cells: text.length, scrollCells: chip.length };
|
|
125
71
|
}
|
|
126
72
|
/**
|
|
127
|
-
*
|
|
73
|
+
* Whether the row can hold the hint column without squeezing the left half below
|
|
74
|
+
* `minLeftCells`.
|
|
128
75
|
*
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
* disappears from a row where it used to fit. Putting `^K` first would have cost
|
|
134
|
-
* `^I` everything from 64 to 78 columns - a permanent loss, paid for a
|
|
135
|
-
* low-frequency key.
|
|
76
|
+
* A FORMULA, NOT A TABLE OF MAGIC NUMBERS: `columns - minLeftCells -
|
|
77
|
+
* separatorCells >= scrollCells + bodyCells`. Substituting the three states gives
|
|
78
|
+
* 32 / 34 / 35 columns with no chip, plus up to 8 more for a saturated `^9999+ `.
|
|
79
|
+
* v1 claimed "any width >= 35", which forgot the chip entirely.
|
|
136
80
|
*/
|
|
137
|
-
function
|
|
138
|
-
const
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
const index = {
|
|
143
|
-
id: 'index',
|
|
144
|
-
text: statusExpanded ? INDEX_CLAUSE.expanded : INDEX_CLAUSE.collapsed,
|
|
145
|
-
};
|
|
146
|
-
const detail = {
|
|
147
|
-
id: 'detail',
|
|
148
|
-
text: statusExpanded ? DETAIL_CLAUSE.expanded : DETAIL_CLAUSE.collapsed,
|
|
149
|
-
};
|
|
150
|
-
const branchKey = { id: 'branch', text: BRANCH_KEY_ONLY };
|
|
151
|
-
const prompts = { id: 'prompts', text: PROMPTS_CLAUSE };
|
|
152
|
-
const kit = { id: 'kit', text: KIT_CLAUSE.expanded };
|
|
153
|
-
// THE LADDERS ARE WRITTEN WITH EVERY CLAUSE IN PLACE AND THEN FILTERED, rather
|
|
154
|
-
// than branched per width. Two independently written tables drift: the day one of
|
|
155
|
-
// them gains a rung, the `prompts.hint=false` / `kitHint=false` degradations stop
|
|
156
|
-
// being "the same row minus that clause" and nothing would notice. Filtering a
|
|
157
|
-
// single table makes that property structural.
|
|
158
|
-
const keep = (clause) => (promptsHint || clause.id !== 'prompts') && (kitHint || clause.id !== 'kit');
|
|
159
|
-
// COLLAPSED (six rungs). `^P prompts` sits between the branch and the index, and
|
|
160
|
-
// the third rung is where `^L` loses its label in favour of `^P` - see the note
|
|
161
|
-
// on `PROMPTS_CLAUSE` above for the revision of `RV-P1-2` this encodes.
|
|
162
|
-
const collapsed = [
|
|
163
|
-
[branch, prompts, index, detail],
|
|
164
|
-
[branch, prompts, detail],
|
|
165
|
-
[branchKey, prompts, detail],
|
|
166
|
-
[branch, detail],
|
|
167
|
-
[branchKey, detail],
|
|
168
|
-
[detail],
|
|
169
|
-
];
|
|
170
|
-
// EXPANDED (seven rungs). `^P` appears in the WIDEST rung only, and AFTER `^K`,
|
|
171
|
-
// so E2..E7 - the rungs that decide what a 34..90 column terminal shows - are
|
|
172
|
-
// byte-identical to the pre-feature answers.
|
|
173
|
-
const expanded = [
|
|
174
|
-
[branch, index, kit, prompts, detail],
|
|
175
|
-
[branch, index, kit, detail],
|
|
176
|
-
[branch, index, detail],
|
|
177
|
-
[branch, kit, detail],
|
|
178
|
-
[branch, detail],
|
|
179
|
-
[branchKey, detail],
|
|
180
|
-
[detail],
|
|
181
|
-
];
|
|
182
|
-
return (statusExpanded ? expanded : collapsed).map((rung) => rung.filter(keep));
|
|
81
|
+
export function headerHintFits(columns, panel) {
|
|
82
|
+
const cols = normalizeDimension(columns, 80);
|
|
83
|
+
const plan = planHeaderHints({ columns: cols, panel, scrolledLines: 0 });
|
|
84
|
+
return cols - HEADER_HINT_LIMITS.minLeftCells - HEADER_HINT_LIMITS.separatorCells
|
|
85
|
+
>= plan.cells;
|
|
183
86
|
}
|
|
184
|
-
/**
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
function buildPlan(clauses, scrollChipText, detailsOnly) {
|
|
190
|
-
const body = clauses.map((clause) => clause.text).join(' '.repeat(HEADER_HINT_LIMITS.gapCells));
|
|
191
|
-
const text = scrollChipText.length > 0 ? `${scrollChipText}${body}` : body;
|
|
192
|
-
return {
|
|
193
|
-
clauses,
|
|
194
|
-
text,
|
|
195
|
-
cells: text.length,
|
|
196
|
-
scrollCells: scrollChipText.length,
|
|
197
|
-
detailsOnly,
|
|
198
|
-
};
|
|
87
|
+
/** The narrowest width at which each state fits with no scroll chip. */
|
|
88
|
+
export function headerHintThreshold(panel) {
|
|
89
|
+
return HEADER_HINTS[panel].length
|
|
90
|
+
+ HEADER_HINT_LIMITS.minLeftCells
|
|
91
|
+
+ HEADER_HINT_LIMITS.separatorCells;
|
|
199
92
|
}
|
|
200
93
|
/**
|
|
201
94
|
* `^<n>` plus its trailing spacing, or `''`.
|
|
202
95
|
*
|
|
203
96
|
* THE SPACING IS PART OF THE CHIP, not something `Header` adds afterwards: with two
|
|
204
97
|
* sources of spacing a reviewer has to read two files to answer "how wide is the
|
|
205
|
-
* chip"
|
|
206
|
-
*
|
|
98
|
+
* chip". One space while an overlay is open, two otherwise - copied byte for byte
|
|
99
|
+
* from the expression the header already used.
|
|
207
100
|
*/
|
|
208
101
|
function scrollChip(scrolledLines, overlayOpen) {
|
|
209
102
|
if (scrolledLines <= 0)
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The header's LEFT half budget (tui-shortcut-drawer-and-task-summary §4.3).
|
|
3
|
+
*
|
|
4
|
+
* ASCII ONLY: `src/ui/**` is inside the glyph scanner's scope.
|
|
5
|
+
*
|
|
6
|
+
* WHY A PLANNER (the same argument `header-hints.ts` makes one file over): the
|
|
7
|
+
* left half used to be a `<Text wrap="truncate">` over four clauses with no
|
|
8
|
+
* clause-level arithmetic anywhere, so the only thing that ever decided what got
|
|
9
|
+
* cut was the terminal. Adding a fifth clause - the task summary - to that shape
|
|
10
|
+
* produces a header where the summary silently eats the directory name on an
|
|
11
|
+
* 80-column terminal and NO ASSERTION WOULD SEE IT, because losing trailing
|
|
12
|
+
* characters is invisible to a snapshot test.
|
|
13
|
+
*
|
|
14
|
+
* THE PRIORITY ORDER IS THE DESIGN: brand, then `provider:model`, then cwd, then
|
|
15
|
+
* the summary, with the key symbol reserved up front. The summary is the only
|
|
16
|
+
* clause that is allowed to disappear entirely - it is the newest and the least
|
|
17
|
+
* load-bearing (D-2) - and it is dropped rather than rendered as `...`, because a
|
|
18
|
+
* cell holding three dots spends eight columns to say nothing.
|
|
19
|
+
*/
|
|
20
|
+
export declare const HEADER_LEFT_LIMITS: {
|
|
21
|
+
/** The `AragonAgent` word mark, the same 13 `header-hints.ts` documents. */
|
|
22
|
+
readonly brandCells: 13;
|
|
23
|
+
/** Blank cells between two left clauses. */
|
|
24
|
+
readonly gapCells: 2;
|
|
25
|
+
/** The has-key symbol (1 cell) plus its gap, always reserved. */
|
|
26
|
+
readonly keyCells: 3;
|
|
27
|
+
/** Below this the model clause is dropped rather than rendered as `a...`. */
|
|
28
|
+
readonly modelMinCells: 8;
|
|
29
|
+
/** Below this the cwd clause is dropped. */
|
|
30
|
+
readonly cwdMinCells: 8;
|
|
31
|
+
/** Below this the summary is dropped instead of rendered as `...`. */
|
|
32
|
+
readonly summaryMinCells: 8;
|
|
33
|
+
/**
|
|
34
|
+
* The separator that belongs to the SUMMARY rather than to the general gap:
|
|
35
|
+
* two spaces, the mid dot and one space. It is reserved together with the label
|
|
36
|
+
* so a summary can never be drawn without its dot, or the dot without it.
|
|
37
|
+
*/
|
|
38
|
+
readonly summaryGapCells: 4;
|
|
39
|
+
};
|
|
40
|
+
export interface HeaderLeftPlan {
|
|
41
|
+
text: {
|
|
42
|
+
model: string;
|
|
43
|
+
cwd: string;
|
|
44
|
+
summary: string;
|
|
45
|
+
};
|
|
46
|
+
cells: {
|
|
47
|
+
model: number;
|
|
48
|
+
cwd: number;
|
|
49
|
+
summary: number;
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* Why the summary cell is empty. `pending` is not a reason: a pending summary
|
|
53
|
+
* draws nothing at all, so the plan is indistinguishable from `empty` - which
|
|
54
|
+
* is exactly what removed the v1 layout jitter on the first submit.
|
|
55
|
+
*/
|
|
56
|
+
summaryReason: 'ok' | 'empty' | 'narrow' | 'disabled';
|
|
57
|
+
}
|
|
58
|
+
export interface HeaderLeftInput {
|
|
59
|
+
/** Cells the header gives the left half (`columns - hintCells - separatorCells`). */
|
|
60
|
+
leftCells: number;
|
|
61
|
+
provider: string;
|
|
62
|
+
model: string;
|
|
63
|
+
cwd: string;
|
|
64
|
+
hasKey: boolean;
|
|
65
|
+
/** `null` while pending, empty or disabled - the planner never sees a placeholder. */
|
|
66
|
+
summary: {
|
|
67
|
+
text: string;
|
|
68
|
+
tone: 'model' | 'fallback';
|
|
69
|
+
} | null;
|
|
70
|
+
summaryEnabled: boolean;
|
|
71
|
+
ellipsis: string;
|
|
72
|
+
}
|
|
73
|
+
/** `provider:model`, the clause `Header` prints after the word mark. */
|
|
74
|
+
export declare function modelClause(provider: string, model: string): string;
|
|
75
|
+
/** The directory name, falling back to the whole path for a bare root. */
|
|
76
|
+
export declare function cwdClause(cwd: string, basename: (path: string) => string): string;
|
|
77
|
+
/**
|
|
78
|
+
* Allocate the left half.
|
|
79
|
+
*
|
|
80
|
+
* THE ORDER OF THE FOUR STEPS IS THE PRIORITY (D-2):
|
|
81
|
+
* 1. reserve the word mark, its gap and the key symbol - they are never cut;
|
|
82
|
+
* 2. `provider:model`, down to `modelMinCells`, then dropped;
|
|
83
|
+
* 3. cwd, down to `cwdMinCells`, then dropped;
|
|
84
|
+
* 4. the summary, capped at `TASK_SUMMARY_LIMITS.maxCells`, dropped below
|
|
85
|
+
* `summaryMinCells` and reported as `narrow`.
|
|
86
|
+
*
|
|
87
|
+
* THE LADDER IS EXPLICIT RATHER THAN GREEDY, and that is a correction to v2's
|
|
88
|
+
* algorithm, which funded the summary out of the reserved budget and could therefore
|
|
89
|
+
* show a label on a row where the model name had already been dropped. Four
|
|
90
|
+
* presence sets are tried from most to least complete; within a set the LAST clause
|
|
91
|
+
* gives way first, each only down to its own minimum. The first set that fits wins.
|
|
92
|
+
*
|
|
93
|
+
* Every returned text already MEASURES its reported width, so `Header` renders
|
|
94
|
+
* cells rather than hoping for them.
|
|
95
|
+
*/
|
|
96
|
+
export declare function planHeaderLeft(input: HeaderLeftInput): HeaderLeftPlan;
|
|
97
|
+
/**
|
|
98
|
+
* Cells the plan actually spends, for the row invariant:
|
|
99
|
+
* `brand + gap + model + gap + cwd + gap + summary + key <= leftCells`.
|
|
100
|
+
*
|
|
101
|
+
* Exported so `header-left.test.ts` can assert the inequality against the SAME
|
|
102
|
+
* arithmetic the renderer uses instead of re-deriving it in the test (which is how
|
|
103
|
+
* the two drift apart).
|
|
104
|
+
*/
|
|
105
|
+
export declare function planHeaderLeftCells(plan: HeaderLeftPlan): number;
|
|
106
|
+
/**
|
|
107
|
+
* The fixed cells a plan needs before any clause can appear: the word mark plus the
|
|
108
|
+
* key symbol and its gap.
|
|
109
|
+
*
|
|
110
|
+
* Below this the row does not "spend too much", it simply cannot show a clause - the
|
|
111
|
+
* renderer truncates the overflow and `Header`'s own `wrap="truncate"` keeps that from
|
|
112
|
+
* spilling. Stating it as a constant keeps the invariant in the tests honest:
|
|
113
|
+
* `planHeaderLeftCells(plan) <= max(leftCells, HEADER_LEFT_BASELINE)`.
|
|
114
|
+
*/
|
|
115
|
+
export declare const HEADER_LEFT_BASELINE: number;
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The header's LEFT half budget (tui-shortcut-drawer-and-task-summary §4.3).
|
|
3
|
+
*
|
|
4
|
+
* ASCII ONLY: `src/ui/**` is inside the glyph scanner's scope.
|
|
5
|
+
*
|
|
6
|
+
* WHY A PLANNER (the same argument `header-hints.ts` makes one file over): the
|
|
7
|
+
* left half used to be a `<Text wrap="truncate">` over four clauses with no
|
|
8
|
+
* clause-level arithmetic anywhere, so the only thing that ever decided what got
|
|
9
|
+
* cut was the terminal. Adding a fifth clause - the task summary - to that shape
|
|
10
|
+
* produces a header where the summary silently eats the directory name on an
|
|
11
|
+
* 80-column terminal and NO ASSERTION WOULD SEE IT, because losing trailing
|
|
12
|
+
* characters is invisible to a snapshot test.
|
|
13
|
+
*
|
|
14
|
+
* THE PRIORITY ORDER IS THE DESIGN: brand, then `provider:model`, then cwd, then
|
|
15
|
+
* the summary, with the key symbol reserved up front. The summary is the only
|
|
16
|
+
* clause that is allowed to disappear entirely - it is the newest and the least
|
|
17
|
+
* load-bearing (D-2) - and it is dropped rather than rendered as `...`, because a
|
|
18
|
+
* cell holding three dots spends eight columns to say nothing.
|
|
19
|
+
*/
|
|
20
|
+
import { cellWidth, fitCells } from './cells.js';
|
|
21
|
+
import { TASK_SUMMARY_LIMITS } from '../../fast/limits.js';
|
|
22
|
+
export const HEADER_LEFT_LIMITS = {
|
|
23
|
+
/** The `AragonAgent` word mark, the same 13 `header-hints.ts` documents. */
|
|
24
|
+
brandCells: 13,
|
|
25
|
+
/** Blank cells between two left clauses. */
|
|
26
|
+
gapCells: 2,
|
|
27
|
+
/** The has-key symbol (1 cell) plus its gap, always reserved. */
|
|
28
|
+
keyCells: 3,
|
|
29
|
+
/** Below this the model clause is dropped rather than rendered as `a...`. */
|
|
30
|
+
modelMinCells: 8,
|
|
31
|
+
/** Below this the cwd clause is dropped. */
|
|
32
|
+
cwdMinCells: 8,
|
|
33
|
+
/** Below this the summary is dropped instead of rendered as `...`. */
|
|
34
|
+
summaryMinCells: 8,
|
|
35
|
+
/**
|
|
36
|
+
* The separator that belongs to the SUMMARY rather than to the general gap:
|
|
37
|
+
* two spaces, the mid dot and one space. It is reserved together with the label
|
|
38
|
+
* so a summary can never be drawn without its dot, or the dot without it.
|
|
39
|
+
*/
|
|
40
|
+
summaryGapCells: 4,
|
|
41
|
+
};
|
|
42
|
+
/** `provider:model`, the clause `Header` prints after the word mark. */
|
|
43
|
+
export function modelClause(provider, model) {
|
|
44
|
+
return `${provider}:${model}`;
|
|
45
|
+
}
|
|
46
|
+
/** The directory name, falling back to the whole path for a bare root. */
|
|
47
|
+
export function cwdClause(cwd, basename) {
|
|
48
|
+
return basename(cwd) || cwd;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Allocate the left half.
|
|
52
|
+
*
|
|
53
|
+
* THE ORDER OF THE FOUR STEPS IS THE PRIORITY (D-2):
|
|
54
|
+
* 1. reserve the word mark, its gap and the key symbol - they are never cut;
|
|
55
|
+
* 2. `provider:model`, down to `modelMinCells`, then dropped;
|
|
56
|
+
* 3. cwd, down to `cwdMinCells`, then dropped;
|
|
57
|
+
* 4. the summary, capped at `TASK_SUMMARY_LIMITS.maxCells`, dropped below
|
|
58
|
+
* `summaryMinCells` and reported as `narrow`.
|
|
59
|
+
*
|
|
60
|
+
* THE LADDER IS EXPLICIT RATHER THAN GREEDY, and that is a correction to v2's
|
|
61
|
+
* algorithm, which funded the summary out of the reserved budget and could therefore
|
|
62
|
+
* show a label on a row where the model name had already been dropped. Four
|
|
63
|
+
* presence sets are tried from most to least complete; within a set the LAST clause
|
|
64
|
+
* gives way first, each only down to its own minimum. The first set that fits wins.
|
|
65
|
+
*
|
|
66
|
+
* Every returned text already MEASURES its reported width, so `Header` renders
|
|
67
|
+
* cells rather than hoping for them.
|
|
68
|
+
*/
|
|
69
|
+
export function planHeaderLeft(input) {
|
|
70
|
+
const leftCells = Number.isFinite(input.leftCells) ? Math.max(0, Math.floor(input.leftCells)) : 0;
|
|
71
|
+
const empty = {
|
|
72
|
+
text: { model: '', cwd: '', summary: '' },
|
|
73
|
+
cells: { model: 0, cwd: 0, summary: 0 },
|
|
74
|
+
summaryReason: input.summaryEnabled ? 'empty' : 'disabled',
|
|
75
|
+
};
|
|
76
|
+
const gap = HEADER_LEFT_LIMITS.gapCells;
|
|
77
|
+
// Everything the planner never touches, taken off the top so no later step can
|
|
78
|
+
// borrow from it.
|
|
79
|
+
// `keyCells` already contains the gap before the symbol, so this is the whole fixed
|
|
80
|
+
// cost and every clause below pays its own gap.
|
|
81
|
+
const budget = leftCells - HEADER_LEFT_LIMITS.brandCells - HEADER_LEFT_LIMITS.keyCells;
|
|
82
|
+
if (budget <= 0) {
|
|
83
|
+
return {
|
|
84
|
+
...empty,
|
|
85
|
+
summaryReason: input.summaryEnabled
|
|
86
|
+
? (input.summary === null ? 'empty' : 'narrow')
|
|
87
|
+
: 'disabled',
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
const model = modelClause(input.provider, input.model);
|
|
91
|
+
const summaryText = input.summary?.text ?? '';
|
|
92
|
+
const wantsSummary = input.summaryEnabled && input.summary !== null;
|
|
93
|
+
/** Desired width and the floor this clause may be cut to. */
|
|
94
|
+
const clause = (text, floor) => ({
|
|
95
|
+
want: cellWidth(text),
|
|
96
|
+
floor: Math.min(cellWidth(text), floor),
|
|
97
|
+
});
|
|
98
|
+
const modelWanted = clause(model, HEADER_LEFT_LIMITS.modelMinCells);
|
|
99
|
+
const cwdWanted = clause(input.cwd, HEADER_LEFT_LIMITS.cwdMinCells);
|
|
100
|
+
const summaryWanted = {
|
|
101
|
+
want: Math.min(cellWidth(summaryText), TASK_SUMMARY_LIMITS.maxCells),
|
|
102
|
+
// A label is never shown below its minimum: a three-cell label is a fragment,
|
|
103
|
+
// not a label, and the cell is dropped instead.
|
|
104
|
+
floor: HEADER_LEFT_LIMITS.summaryMinCells,
|
|
105
|
+
};
|
|
106
|
+
const sets = [
|
|
107
|
+
['model', 'cwd', ...(wantsSummary ? ['summary'] : [])],
|
|
108
|
+
['model', 'cwd'],
|
|
109
|
+
['model'],
|
|
110
|
+
[],
|
|
111
|
+
];
|
|
112
|
+
const wanted = { model: modelWanted, cwd: cwdWanted, summary: summaryWanted };
|
|
113
|
+
for (const set of sets) {
|
|
114
|
+
const assigned = fitClauses(set, wanted, budget, gap);
|
|
115
|
+
if (assigned === null)
|
|
116
|
+
continue;
|
|
117
|
+
const modelCells = assigned.model ?? 0;
|
|
118
|
+
const cwdCells = assigned.cwd ?? 0;
|
|
119
|
+
const summaryCells = assigned.summary ?? 0;
|
|
120
|
+
let summaryReason = 'empty';
|
|
121
|
+
if (!input.summaryEnabled)
|
|
122
|
+
summaryReason = 'disabled';
|
|
123
|
+
else if (input.summary === null)
|
|
124
|
+
summaryReason = 'empty';
|
|
125
|
+
else if (summaryCells === 0)
|
|
126
|
+
summaryReason = 'narrow';
|
|
127
|
+
else
|
|
128
|
+
summaryReason = 'ok';
|
|
129
|
+
return {
|
|
130
|
+
text: {
|
|
131
|
+
model: modelCells > 0 ? fitCells(model, modelCells, input.ellipsis) : '',
|
|
132
|
+
cwd: cwdCells > 0 ? fitCells(input.cwd, cwdCells, input.ellipsis) : '',
|
|
133
|
+
summary: summaryCells > 0 ? fitCells(summaryText, summaryCells, input.ellipsis) : '',
|
|
134
|
+
},
|
|
135
|
+
cells: { model: modelCells, cwd: cwdCells, summary: summaryCells },
|
|
136
|
+
summaryReason,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
return empty;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Fit one presence set into `budget`, cutting from the LAST clause inward.
|
|
143
|
+
*
|
|
144
|
+
* Returns `null` when the set cannot fit even at every clause's floor, which is the
|
|
145
|
+
* signal to try the next (smaller) set. `null` rather than a partial result because
|
|
146
|
+
* "the summary was dropped" and "the summary does not fit" must be the same fact to
|
|
147
|
+
* the caller, and only the set composition may express it.
|
|
148
|
+
*/
|
|
149
|
+
function fitClauses(set, wanted, budget, gap) {
|
|
150
|
+
const widths = { model: 0, cwd: 0, summary: 0 };
|
|
151
|
+
let cost = 0;
|
|
152
|
+
for (const key of set) {
|
|
153
|
+
widths[key] = wanted[key].want;
|
|
154
|
+
// EVERY CLAUSE PAYS ITS OWN SEPARATOR, including the first: the row renders
|
|
155
|
+
// `word mark, gap, clause, gap, clause, gap, key`, so a one-clause plan still
|
|
156
|
+
// spends a gap on each side of that clause.
|
|
157
|
+
cost += (key === 'summary' ? HEADER_LEFT_LIMITS.summaryGapCells : gap)
|
|
158
|
+
+ wanted[key].want;
|
|
159
|
+
}
|
|
160
|
+
let excess = cost - budget;
|
|
161
|
+
for (let index = set.length - 1; index >= 0 && excess > 0; index -= 1) {
|
|
162
|
+
const key = set[index];
|
|
163
|
+
const room = widths[key] - wanted[key].floor;
|
|
164
|
+
if (room <= 0)
|
|
165
|
+
continue;
|
|
166
|
+
const cut = Math.min(room, excess);
|
|
167
|
+
widths[key] -= cut;
|
|
168
|
+
excess -= cut;
|
|
169
|
+
}
|
|
170
|
+
return excess > 0 ? null : widths;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Cells the plan actually spends, for the row invariant:
|
|
174
|
+
* `brand + gap + model + gap + cwd + gap + summary + key <= leftCells`.
|
|
175
|
+
*
|
|
176
|
+
* Exported so `header-left.test.ts` can assert the inequality against the SAME
|
|
177
|
+
* arithmetic the renderer uses instead of re-deriving it in the test (which is how
|
|
178
|
+
* the two drift apart).
|
|
179
|
+
*/
|
|
180
|
+
export function planHeaderLeftCells(plan) {
|
|
181
|
+
const { gapCells, brandCells, keyCells, summaryGapCells } = HEADER_LEFT_LIMITS;
|
|
182
|
+
// The word mark and the key symbol are always drawn; every clause pays its own gap.
|
|
183
|
+
// The gap before the key is INSIDE `keyCells`, so it is counted once here and never
|
|
184
|
+
// again after a clause - the double count that made a one-clause plan read two cells
|
|
185
|
+
// wider than it renders.
|
|
186
|
+
let cells = brandCells + keyCells;
|
|
187
|
+
if (plan.cells.model > 0)
|
|
188
|
+
cells += gapCells + plan.cells.model;
|
|
189
|
+
if (plan.cells.cwd > 0)
|
|
190
|
+
cells += gapCells + plan.cells.cwd;
|
|
191
|
+
// The summary's separator carries the dot, so it is wider than the general gap.
|
|
192
|
+
if (plan.cells.summary > 0)
|
|
193
|
+
cells += summaryGapCells + plan.cells.summary;
|
|
194
|
+
return cells;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* The fixed cells a plan needs before any clause can appear: the word mark plus the
|
|
198
|
+
* key symbol and its gap.
|
|
199
|
+
*
|
|
200
|
+
* Below this the row does not "spend too much", it simply cannot show a clause - the
|
|
201
|
+
* renderer truncates the overflow and `Header`'s own `wrap="truncate"` keeps that from
|
|
202
|
+
* spilling. Stating it as a constant keeps the invariant in the tests honest:
|
|
203
|
+
* `planHeaderLeftCells(plan) <= max(leftCells, HEADER_LEFT_BASELINE)`.
|
|
204
|
+
*/
|
|
205
|
+
export const HEADER_LEFT_BASELINE = HEADER_LEFT_LIMITS.brandCells
|
|
206
|
+
+ HEADER_LEFT_LIMITS.keyCells;
|