@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
package/dist/ui/Header.js
CHANGED
|
@@ -2,24 +2,26 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
2
2
|
import { basename } from 'node:path';
|
|
3
3
|
import { Box, Text } from 'ink';
|
|
4
4
|
import { Logo } from './Logo.js';
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
5
|
+
import { pickGlyphs } from './glyphs.js';
|
|
6
|
+
import { planHeaderHints, HEADER_HINT_LIMITS, } from './layout/header-hints.js';
|
|
7
|
+
import { planHeaderLeft } from './layout/header-left.js';
|
|
8
|
+
export function Header({ cwd, provider, model, hasKey, variant, theme, caps, columns = 80, panel = 'none', scrolledLines = 0, taskSummary = null, summaryEnabled = true, }) {
|
|
9
|
+
const glyphs = pickGlyphs(caps);
|
|
10
|
+
// THE RIGHT COLUMN FIRST: it is the only clause that may never be dropped, so
|
|
11
|
+
// the left half is planned against what is left after it (defect 12).
|
|
12
|
+
const plan = planHeaderHints({ columns, panel, scrolledLines });
|
|
13
|
+
const leftCells = Math.max(0, columns - plan.cells - HEADER_HINT_LIMITS.separatorCells);
|
|
14
|
+
const left = planHeaderLeft({
|
|
15
|
+
leftCells,
|
|
16
|
+
provider,
|
|
17
|
+
model,
|
|
18
|
+
cwd: basename(cwd) || cwd,
|
|
19
|
+
hasKey,
|
|
20
|
+
summary: taskSummary,
|
|
21
|
+
summaryEnabled,
|
|
22
|
+
ellipsis: glyphs.ellipsis,
|
|
18
23
|
});
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
// `aragon...Details off` as one string. The blank column comes out of the left
|
|
23
|
-
// half's width, so the row still spans exactly `columns`.
|
|
24
|
-
return _jsxs(Box, { flexDirection: "row", height: 1, width: columns, flexShrink: 0, overflow: "hidden", children: [_jsx(Box, { width: Math.max(0, columns - plan.cells - HEADER_HINT_LIMITS.separatorCells), flexShrink: 0, overflow: "hidden", children: _jsxs(Text, { wrap: "truncate", children: [_jsx(Logo, { variant: variant === 'mini' ? 'mini' : 'bar', theme: theme, caps: caps }), variant !== 'mini' && _jsxs(_Fragment, { children: [_jsxs(Text, { color: theme.accent, children: [' ', provider, ":", model] }), _jsxs(Text, { color: theme.muted, children: [' ', basename(cwd) || cwd] }), _jsxs(Text, { color: hasKey ? theme.toolDone : theme.toolRunning, children: [' ', hasKey ? theme.symbols.keyOn : theme.symbols.keyOff] })] })] }) }), _jsx(Box, { width: HEADER_HINT_LIMITS.separatorCells, flexShrink: 0 }), _jsx(Box, { width: plan.cells, flexShrink: 0, justifyContent: "flex-end", children: _jsx(Text, { color: theme.muted, wrap: "truncate", children: plan.text }) })] });
|
|
24
|
+
return _jsxs(Box, { flexDirection: "row", height: 1, width: columns, flexShrink: 0, overflow: "hidden", children: [_jsx(Box, { width: leftCells, flexShrink: 0, overflow: "hidden", children: _jsxs(Text, { wrap: "truncate", children: [_jsx(Logo, { variant: variant === 'mini' ? 'mini' : 'bar', theme: theme, caps: caps }), variant !== 'mini' && _jsxs(_Fragment, { children: [left.text.model && _jsxs(Text, { color: theme.accent, children: [' ', left.text.model] }), left.text.cwd && _jsxs(Text, { color: theme.muted, children: [' ', left.text.cwd] }), left.text.summary && _jsxs(Text, { color: theme.muted, children: [' ', glyphs.midDot, ' '] }), left.text.summary && (taskSummary?.tone === 'fallback'
|
|
25
|
+
? _jsx(Text, { color: theme.muted, dimColor: true, children: left.text.summary })
|
|
26
|
+
: _jsx(Text, { color: theme.primary, children: left.text.summary })), _jsxs(Text, { color: hasKey ? theme.toolDone : theme.toolRunning, children: [' ', hasKey ? theme.symbols.keyOn : theme.symbols.keyOff] })] })] }) }), _jsx(Box, { width: HEADER_HINT_LIMITS.separatorCells, flexShrink: 0 }), _jsx(Box, { width: plan.cells, flexShrink: 0, justifyContent: "flex-end", children: _jsx(Text, { color: theme.muted, wrap: "truncate", children: plan.text }) })] });
|
|
25
27
|
}
|
package/dist/ui/StatusBar.d.ts
CHANGED
package/dist/ui/StatusBar.js
CHANGED
|
@@ -39,6 +39,7 @@ export function StatusBar(props) {
|
|
|
39
39
|
interruptPhase: props.interruptPhase,
|
|
40
40
|
waitingForConfirmation: props.waitingForConfirmation, stopping: props.stopping,
|
|
41
41
|
compacting: props.compactionActive?.inFlight, retryActive: props.retryActive,
|
|
42
|
+
compactionPaused: props.compactionActive?.selfDisabled === true,
|
|
42
43
|
mode: props.agentMode, pendingMode: props.pendingAgentMode, servicesActive: props.servicesActive,
|
|
43
44
|
teamActive: props.teamActive, todoActive: props.todoActive,
|
|
44
45
|
...(props.acceptanceActive ? { acceptanceActive: props.acceptanceActive } : {}),
|
package/dist/ui/Transcript.js
CHANGED
|
@@ -137,7 +137,7 @@ function EntryViewImpl({ entry, prev, expanded, thinkingVisible, reducedMotion,
|
|
|
137
137
|
case 'fast':
|
|
138
138
|
return frame(entry.status === 'failed' ? glyphs.toolError : glyphs.teamAgent, fastCardColor(entry.status, theme), _jsx(FastCard, { reviewIndex: entry.reviewIndex, model: entry.model, status: entry.status, text: entry.text, detail: entry.detail, turn: entry.turn, durationMs: entry.durationMs, live: entry.live, reducedMotion: reducedMotion, theme: theme, caps: caps }));
|
|
139
139
|
case 'compaction':
|
|
140
|
-
return frame(entry.applied || entry.live ? glyphs.compaction : glyphs.toolError, compactionCardColor(entry, theme), _jsx(CompactionCard, { index: entry.index, decision: entry.decision, memoryVersion: entry.memoryVersion, trigger: entry.trigger, mode: entry.mode, applied: entry.applied, reason: entry.reason, messagesBefore: entry.messagesBefore, messagesAfter: entry.messagesAfter, tokensBefore: entry.tokensBefore, tokensAfter: entry.tokensAfter, summary: entry.summary, model: entry.model, durationMs: entry.durationMs, tailRelief: entry.tailRelief,
|
|
140
|
+
return frame(entry.applied || entry.live ? glyphs.compaction : glyphs.toolError, compactionCardColor(entry, theme), _jsx(CompactionCard, { index: entry.index, decision: entry.decision, memoryVersion: entry.memoryVersion, trigger: entry.trigger, mode: entry.mode, applied: entry.applied, reason: entry.reason, messagesBefore: entry.messagesBefore, messagesAfter: entry.messagesAfter, tokensBefore: entry.tokensBefore, tokensAfter: entry.tokensAfter, summary: entry.summary, model: entry.model, durationMs: entry.durationMs, tailRelief: entry.tailRelief, phase: entry.phase, keepRelaxed: entry.keepRelaxed, retainedTurns: entry.retainedTurns, retainedMessages: entry.retainedMessages, keepRecentTurnsUsed: entry.keepRecentTurnsUsed, elidedMessages: entry.elidedMessages,
|
|
141
141
|
// ONLY WHILE LIVE, and only when the clock has ticked at least once:
|
|
142
142
|
// `nowSec` is `undefined` until then, and a settled card must keep
|
|
143
143
|
// rendering `durationMs` rather than a figure that would drift.
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
import React from 'react';
|
|
24
24
|
import type { Theme } from '../theme.js';
|
|
25
25
|
import type { TermCapabilities } from '../capabilities.js';
|
|
26
|
-
import type { CompactionDecision, CompactionMode, CompactionUiTrigger } from '../../compaction/types.js';
|
|
26
|
+
import type { CompactionDecision, CompactionMode, CompactionPhase, CompactionUiTrigger } from '../../compaction/types.js';
|
|
27
27
|
export interface CompactionCardProps {
|
|
28
28
|
decision?: CompactionDecision;
|
|
29
29
|
memoryVersion?: 2;
|
|
@@ -39,6 +39,16 @@ export interface CompactionCardProps {
|
|
|
39
39
|
summary?: string;
|
|
40
40
|
model: string;
|
|
41
41
|
durationMs?: number;
|
|
42
|
+
/** The live card's stage, so "compacting" also says WHAT (never-fires C). */
|
|
43
|
+
phase?: CompactionPhase;
|
|
44
|
+
/** Set when this cut existed only because `keepRecentTurns` was relaxed (B). */
|
|
45
|
+
keepRelaxed?: boolean;
|
|
46
|
+
retainedTurns?: number;
|
|
47
|
+
/** Messages the relaxed cut left verbatim, which is not the same as turns. */
|
|
48
|
+
retainedMessages?: number;
|
|
49
|
+
keepRecentTurnsUsed?: number;
|
|
50
|
+
/** Messages the digest left out and therefore cannot be quoted (A1). */
|
|
51
|
+
elidedMessages?: number;
|
|
42
52
|
/**
|
|
43
53
|
* Wall-clock since the card opened, LIVE ONLY
|
|
44
54
|
* (context-auto-compaction-hardening §3.6 / W5).
|
|
@@ -28,6 +28,7 @@ import { pickGlyphs } from '../glyphs.js';
|
|
|
28
28
|
import { railBorderProps } from '../layout/Gutter.js';
|
|
29
29
|
import { formatDuration, formatTokens } from '../../agent/usage.js';
|
|
30
30
|
import { COMPACTION_LIMITS } from '../../compaction/limits.js';
|
|
31
|
+
import { compactionPhaseLabel, compactionReasonLine } from '../../compaction/reason-text.js';
|
|
31
32
|
/**
|
|
32
33
|
* The card's colour.
|
|
33
34
|
*
|
|
@@ -56,12 +57,19 @@ function headline(props, arrow, dot) {
|
|
|
56
57
|
// second 45 this was the only changing information on the card, and there was
|
|
57
58
|
// none.
|
|
58
59
|
const elapsed = props.elapsedMs === undefined ? '' : `${dot}${formatDuration(props.elapsedMs)}`;
|
|
60
|
+
// THE STAGE, NOT JUST "COMPACTING" (never-fires C). Planning and digesting run
|
|
61
|
+
// before any model call, and a user watching a card for twenty seconds cannot
|
|
62
|
+
// tell "thinking hard" from "stuck in a gate" without it.
|
|
63
|
+
const stage = props.phase === undefined ? '' : `${dot}${compactionPhaseLabel(props.phase)}`;
|
|
59
64
|
return props.model
|
|
60
|
-
? `compacting context #${index}${dot}${props.model}${elapsed}`
|
|
61
|
-
: `compacting context #${index}${elapsed}`;
|
|
65
|
+
? `compacting context #${index}${dot}${props.model}${stage}${elapsed}`
|
|
66
|
+
: `compacting context #${index}${stage}${elapsed}`;
|
|
62
67
|
}
|
|
63
68
|
if (!props.applied || props.tokensAfter >= props.tokensBefore) {
|
|
64
|
-
|
|
69
|
+
// THE REASON IN WORDS (never-fires C). `digest_budget_exceeded: $.digest` was
|
|
70
|
+
// a code the user could not act on; `compactionReasonLine` renders the same
|
|
71
|
+
// fact as a clause plus, where there is one, the remedy.
|
|
72
|
+
const why = props.reason ? `: ${compactionReasonLine(props.reason)}` : '';
|
|
65
73
|
return `context not compacted #${index}${why}`;
|
|
66
74
|
}
|
|
67
75
|
const messages = `${props.messagesBefore} ${arrow} ${props.messagesAfter} messages`;
|
|
@@ -87,8 +95,15 @@ function headline(props, arrow, dot) {
|
|
|
87
95
|
*/
|
|
88
96
|
function detailLine(props) {
|
|
89
97
|
if (props.live) {
|
|
98
|
+
// THE ESCAPE, NOT THE STAGE (never-fires C, as corrected in review): the live
|
|
99
|
+
// headline already carries the stage, and a two-line card that names it twice
|
|
100
|
+
// is the one-fact-one-surface rule this file keeps having to restore. With no
|
|
101
|
+
// model resolved there is no call to cancel, so the whole line goes - the same
|
|
102
|
+
// rule the settled branch applies.
|
|
90
103
|
if (!props.model)
|
|
91
104
|
return '';
|
|
105
|
+
if (props.phase !== undefined)
|
|
106
|
+
return 'esc to cancel';
|
|
92
107
|
return `summarizing with ${props.model} - esc to cancel`;
|
|
93
108
|
}
|
|
94
109
|
if (!props.applied || props.tokensAfter >= props.tokensBefore)
|
|
@@ -104,6 +119,50 @@ function detailLine(props) {
|
|
|
104
119
|
const via = props.trigger === 'overflow' ? ' after a context overflow' : '';
|
|
105
120
|
return `summarized ${dropped} messages with ${props.model}${via}`;
|
|
106
121
|
}
|
|
122
|
+
/**
|
|
123
|
+
* The relaxation disclosure (never-fires B).
|
|
124
|
+
*
|
|
125
|
+
* `planCompaction`'s second pass trades "the last N user turns are verbatim" for
|
|
126
|
+
* "a cut exists at all". That trade must be readable, because the difference
|
|
127
|
+
* between the two compactions is exactly what the user asked for: the tail is the
|
|
128
|
+
* part they care most about keeping word for word.
|
|
129
|
+
*/
|
|
130
|
+
function relaxedLine(props) {
|
|
131
|
+
if (props.live || !props.applied || props.keepRelaxed !== true)
|
|
132
|
+
return '';
|
|
133
|
+
const turns = props.retainedTurns ?? 0;
|
|
134
|
+
const wanted = props.keepRecentTurnsUsed;
|
|
135
|
+
const messages = props.retainedMessages;
|
|
136
|
+
// THE TAIL IS MESSAGES, NOT TURNS (review). The dominant relaxed shape is one
|
|
137
|
+
// prompt plus a hundred tool calls, so `retainedTurns` is 0 while forty messages
|
|
138
|
+
// were kept verbatim: a line reading "kept 0 recent turns" would report a total
|
|
139
|
+
// loss that did not happen. Whenever the count is available it leads, and the
|
|
140
|
+
// turn arithmetic follows as the explanation rather than as the headline.
|
|
141
|
+
if (messages !== undefined && wanted !== undefined) {
|
|
142
|
+
return `kept the last ${messages} messages - ${turns} of the ${wanted} requested turns, ` +
|
|
143
|
+
'since this run had too few user turns';
|
|
144
|
+
}
|
|
145
|
+
if (messages !== undefined)
|
|
146
|
+
return `kept the last ${messages} messages verbatim`;
|
|
147
|
+
return wanted === undefined
|
|
148
|
+
? `kept the last ${turns} turns - this run had too few user turns to keep more`
|
|
149
|
+
: `kept ${turns} of the ${wanted} requested turns - this run had too few user turns`;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* The digest-elision disclosure (never-fires A1).
|
|
153
|
+
*
|
|
154
|
+
* The digest never rejects for length any more; it keeps the first and last
|
|
155
|
+
* entries verbatim and elides the middle. The elided messages cannot be quoted by
|
|
156
|
+
* the summarizer, so the card says so rather than presenting a summary as if it
|
|
157
|
+
* had seen everything.
|
|
158
|
+
*/
|
|
159
|
+
function elidedLine(props) {
|
|
160
|
+
const n = props.elidedMessages ?? 0;
|
|
161
|
+
if (props.live || n <= 0)
|
|
162
|
+
return '';
|
|
163
|
+
return `elided ${n} ${n === 1 ? 'message' : 'messages'} from the summary input - ` +
|
|
164
|
+
'the middle is summarized without them';
|
|
165
|
+
}
|
|
107
166
|
/**
|
|
108
167
|
* The relief disclosure, on a settled card that ALSO summarized.
|
|
109
168
|
*
|
|
@@ -139,6 +198,8 @@ function CompactionCardImpl(props) {
|
|
|
139
198
|
const head = headline(props, glyphs.arrowRight, dot);
|
|
140
199
|
const detail = detailLine(props);
|
|
141
200
|
const relief = reliefLine(props);
|
|
201
|
+
const relaxed = relaxedLine(props);
|
|
202
|
+
const elided = elidedLine(props);
|
|
142
203
|
const showMemory = props.memoryVersion !== 2 || expanded === true;
|
|
143
204
|
const rows = summary && showMemory ? summaryRows(summary, expanded === true) : [];
|
|
144
205
|
const totalRows = summary ? summary.split('\n').filter((l) => l.trim().length > 0).length : 0;
|
|
@@ -150,7 +211,7 @@ function CompactionCardImpl(props) {
|
|
|
150
211
|
// the whole run, losing the status colour along with the motion.
|
|
151
212
|
const animate = live && reducedMotion !== true && caps.unicode;
|
|
152
213
|
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: theme.accent, bold: true, children: props.trigger === 'manual' ? 'Manual compaction' : props.trigger === 'overflow'
|
|
153
|
-
? 'Overflow: threshold reached' : 'Automatic: threshold reached' }), _jsxs(Text, { color: live ? color : theme.muted, wrap: caps.unicode ? 'truncate' : 'wrap', children: [animate ? (_jsxs(_Fragment, { children: [_jsx(Spinner, { type: "dots" }), ' '] })) : live ? (`${glyphs.spinnerStill} `) : (''), head] })] }), props.memoryVersion === 2 && (_jsxs(Text, { color: theme.muted, children: ["Task memory v2", expanded ? '' : ' (ctrl+o to expand)'] })), detail.length > 0 && (_jsx(Box, { flexDirection: "column", flexShrink: 0, paddingLeft: 1, children: _jsx(Text, { color: color, wrap: caps.unicode ? 'truncate' : 'wrap', children: detail }) })), relief.length > 0 && (_jsx(Box, { flexDirection: "column", flexShrink: 0, paddingLeft: 1, children: _jsx(Text, { color: theme.noticeWarn, children: relief }) })), rows.length > 0 && (_jsxs(Box, { flexDirection: "column", flexShrink: 0, paddingLeft: 1, ...railBorderProps(glyphs.railVertical, theme.border), children: [rows.map((line, index) => (_jsx(Text, { color: theme.primary, children: line }, index))), hidden > 0 && (_jsxs(Text, { color: theme.muted, children: [glyphs.railEnd, " +", hidden, " more lines (ctrl+o to expand)"] })), hidden === 0 && expanded === true && totalRows > COMPACTION_LIMITS.cardTextRows && (_jsxs(Text, { color: theme.muted, children: [glyphs.railEnd, " ctrl+o to collapse"] }))] }))] }));
|
|
214
|
+
? 'Overflow: threshold reached' : 'Automatic: threshold reached' }), _jsxs(Text, { color: live ? color : theme.muted, wrap: caps.unicode ? 'truncate' : 'wrap', children: [animate ? (_jsxs(_Fragment, { children: [_jsx(Spinner, { type: "dots" }), ' '] })) : live ? (`${glyphs.spinnerStill} `) : (''), head] })] }), props.memoryVersion === 2 && (_jsxs(Text, { color: theme.muted, children: ["Task memory v2", expanded ? '' : ' (ctrl+o to expand)'] })), detail.length > 0 && (_jsx(Box, { flexDirection: "column", flexShrink: 0, paddingLeft: 1, children: _jsx(Text, { color: color, wrap: caps.unicode ? 'truncate' : 'wrap', children: detail }) })), relief.length > 0 && (_jsx(Box, { flexDirection: "column", flexShrink: 0, paddingLeft: 1, children: _jsx(Text, { color: theme.noticeWarn, children: relief }) })), relaxed.length > 0 && (_jsx(Box, { flexDirection: "column", flexShrink: 0, paddingLeft: 1, children: _jsx(Text, { color: theme.muted, children: relaxed }) })), elided.length > 0 && (_jsx(Box, { flexDirection: "column", flexShrink: 0, paddingLeft: 1, children: _jsx(Text, { color: theme.muted, children: elided }) })), rows.length > 0 && (_jsxs(Box, { flexDirection: "column", flexShrink: 0, paddingLeft: 1, ...railBorderProps(glyphs.railVertical, theme.border), children: [rows.map((line, index) => (_jsx(Text, { color: theme.primary, children: line }, index))), hidden > 0 && (_jsxs(Text, { color: theme.muted, children: [glyphs.railEnd, " +", hidden, " more lines (ctrl+o to expand)"] })), hidden === 0 && expanded === true && totalRows > COMPACTION_LIMITS.cardTextRows && (_jsxs(Text, { color: theme.muted, children: [glyphs.railEnd, " ctrl+o to collapse"] }))] }))] }));
|
|
154
215
|
}
|
|
155
216
|
/**
|
|
156
217
|
* `React.memo` with the DEFAULT comparator (tui-render-performance L2 / R3).
|
|
@@ -29,6 +29,12 @@ export function actionCandidates(input) {
|
|
|
29
29
|
if (input.overlay) {
|
|
30
30
|
if (input.overlay === 'confirm')
|
|
31
31
|
return [copy.approveTool, copy.rejectTool];
|
|
32
|
+
// THE DRAWER SELECTS, IT DOES NOT SCROLL (tui-shortcut-drawer-and-task-summary
|
|
33
|
+
// §5). It is a list with a cursor, so the generic `copy.scroll` fallback would
|
|
34
|
+
// describe the wrong interaction - and the drawer's own footer already names
|
|
35
|
+
// every key, so one clause is the whole line.
|
|
36
|
+
if (input.overlay === 'shortcuts')
|
|
37
|
+
return [copy.select];
|
|
32
38
|
const selects = ['question', 'model', 'session', 'settings'].includes(input.overlay);
|
|
33
39
|
return [selects ? copy.select : copy.scroll,
|
|
34
40
|
...(selects ? [input.overlay === 'settings' ? copy.saveSettings : copy.confirm] : [])];
|
|
@@ -10,6 +10,24 @@ interface OverlayFrameBase {
|
|
|
10
10
|
cols?: number;
|
|
11
11
|
theme: Theme;
|
|
12
12
|
caps: TermCapabilities;
|
|
13
|
+
/**
|
|
14
|
+
* Where the frame sits on the row when `width` is given.
|
|
15
|
+
*
|
|
16
|
+
* DEFAULT `'left'` AND NO WRAPPER BOX: with neither new prop set, the render
|
|
17
|
+
* tree is byte-identical to the pre-`shortcut-drawer` one, which is what keeps
|
|
18
|
+
* the eight existing overlays' layout assertions green (`R-16`).
|
|
19
|
+
*/
|
|
20
|
+
align?: 'left' | 'right';
|
|
21
|
+
/**
|
|
22
|
+
* An explicit width for a frame that is NOT the full terminal width - the
|
|
23
|
+
* shortcut drawer's right-hand bar.
|
|
24
|
+
*
|
|
25
|
+
* THE FRAME STILL DECIDES ITS OWN FORM from `cols` (`BORDERLESS_COLS`), so the
|
|
26
|
+
* caller passes the TERMINAL width here and the bar's width separately. Passing
|
|
27
|
+
* the bar's width as `cols` would put a 46-cell drawer on a 200-column terminal
|
|
28
|
+
* into the rail form for ever.
|
|
29
|
+
*/
|
|
30
|
+
width?: number;
|
|
13
31
|
}
|
|
14
32
|
interface OverlayFrameControlled extends OverlayFrameBase {
|
|
15
33
|
/** One element per row, each already `wrap="truncate"`. */
|
|
@@ -60,14 +60,29 @@ export function OverlayFrame(props) {
|
|
|
60
60
|
onScrollClamp?.(overflowOffset);
|
|
61
61
|
}, [overflowOffset, onScrollClamp]);
|
|
62
62
|
const inner = (_jsxs(_Fragment, { children: [_jsxs(Box, { flexDirection: "row", height: 1, flexShrink: 0, children: [_jsx(Box, { flexGrow: 1, flexShrink: 1, children: _jsx(Text, { wrap: "truncate", color: theme.primary, bold: true, children: title }) }), position && _jsx(Box, { width: stringWidth(position), flexShrink: 0, children: _jsx(Text, { color: theme.muted, children: position }) })] }), _jsx(Box, { flexDirection: "column", flexShrink: 0, height: unbounded ? undefined : Math.max(1, maxRows - (compact ? 2 : bordered ? 5 : 3)), overflowY: "hidden", children: body }), _jsx(Text, { wrap: "truncate", color: theme.hintFg ?? theme.muted, children: hint })] }));
|
|
63
|
-
|
|
64
|
-
|
|
63
|
+
// THE NEW PROPS ARE OPTIONAL AND THEIR DEFAULT IS TODAY'S BEHAVIOUR (R-16): the
|
|
64
|
+
// three branches below return exactly what they always returned unless the
|
|
65
|
+
// caller asked for a width or a right-hand placement. When it did, the SAME box
|
|
66
|
+
// is built and then placed inside one row-wide wrapper, which is the only way
|
|
67
|
+
// Ink can push a fixed-width child to the right edge (a `marginLeft: 'auto'`
|
|
68
|
+
// does not exist here).
|
|
69
|
+
const width = props.width;
|
|
70
|
+
const align = props.align ?? 'left';
|
|
71
|
+
const placed = (node) => {
|
|
72
|
+
if (width === undefined && align === 'left')
|
|
73
|
+
return node;
|
|
74
|
+
return (_jsx(Box, { width: cols, flexDirection: "row", flexShrink: 0, justifyContent: align === 'right' ? 'flex-end' : 'flex-start', children: node }));
|
|
75
|
+
};
|
|
76
|
+
const boxWidth = width === undefined ? {} : { width };
|
|
77
|
+
if (compact) {
|
|
78
|
+
return placed(_jsx(Box, { ...boxWidth, width: width ?? cols, height: Math.max(0, maxRows), flexDirection: "column", flexShrink: 0, overflowY: "hidden", children: inner }));
|
|
79
|
+
}
|
|
65
80
|
if (!bordered) {
|
|
66
81
|
// Narrow terminals trade the box for a rail: same grouping, 2 fewer rows
|
|
67
82
|
// and 4 fewer columns (§5.2).
|
|
68
|
-
return (_jsx(Box, { flexDirection: "column", flexShrink: 0, marginTop: 1, children: _jsx(Box, { flexDirection: "column", flexShrink: 0, borderStyle: railBorderStyle, borderTop: false, borderBottom: false, borderRight: false, borderColor: theme.primary, paddingLeft: 1, children: inner }) }));
|
|
83
|
+
return placed(_jsx(Box, { flexDirection: "column", flexShrink: 0, marginTop: 1, ...boxWidth, children: _jsx(Box, { flexDirection: "column", flexShrink: 0, borderStyle: railBorderStyle, borderTop: false, borderBottom: false, borderRight: false, borderColor: theme.primary, paddingLeft: 1, children: inner }) }));
|
|
69
84
|
}
|
|
70
|
-
return (_jsx(Box, { flexDirection: "column", flexShrink: 0, borderStyle: glyphs.boxStyle, borderColor: theme.primary, paddingX: 1, marginTop: 1, children: inner }));
|
|
85
|
+
return placed(_jsx(Box, { flexDirection: "column", flexShrink: 0, borderStyle: glyphs.boxStyle, borderColor: theme.primary, paddingX: 1, marginTop: 1, ...boxWidth, children: inner }));
|
|
71
86
|
}
|
|
72
87
|
/**
|
|
73
88
|
* Rows one PgUp/PgDn - and one Shift+wheel notch - turns inside a controlled
|
|
@@ -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,71 +23,68 @@
|
|
|
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 declare 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
|
readonly minLeftCells: 24;
|
|
37
|
-
/** Fixed spacing between two hint clauses. */
|
|
38
|
-
readonly 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
|
readonly separatorCells: 2;
|
|
48
45
|
};
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
46
|
+
/**
|
|
47
|
+
* What the right-hand column says. THREE STATES, NO LADDER.
|
|
48
|
+
*
|
|
49
|
+
* `none` - no overlay: `^G more`, the only shortcut hint the header teaches.
|
|
50
|
+
* `shortcuts` - this panel is open: `^G close`, the key that closes it.
|
|
51
|
+
* `other` - some other overlay owns the keyboard: `Esc close`, which is true
|
|
52
|
+
* for every overlay and is the only key that works in all of them.
|
|
53
|
+
*/
|
|
54
|
+
export type HeaderPanelState = 'none' | 'shortcuts' | 'other';
|
|
54
55
|
export interface HeaderHintPlan {
|
|
55
|
-
/** Clauses left to right (including the overlay's `Details on/off` replacement). */
|
|
56
|
-
clauses: HeaderHintClause[];
|
|
57
56
|
/** The whole right-hand string, scroll chip included with its trailing spacing. */
|
|
58
57
|
text: string;
|
|
59
|
-
/** Total cells of the right column; `Header`
|
|
58
|
+
/** Total cells of the right column; `Header` sizes the left one from it. */
|
|
60
59
|
cells: number;
|
|
61
60
|
/** Cells taken by the scroll chip (0 when there is none). */
|
|
62
61
|
scrollCells: number;
|
|
63
|
-
/** True while an overlay owns the keyboard: only the detail word remains. */
|
|
64
|
-
detailsOnly: boolean;
|
|
65
62
|
}
|
|
63
|
+
/** The verbatim text of each state. Exported so a test can name it instead of guessing. */
|
|
64
|
+
export declare const HEADER_HINTS: Readonly<Record<HeaderPanelState, string>>;
|
|
66
65
|
/**
|
|
67
|
-
* Plan the right-hand
|
|
66
|
+
* Plan the right-hand hint.
|
|
68
67
|
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
68
|
+
* NO WIDTH BRANCHING: the clause is always rendered. When the row cannot hold it
|
|
69
|
+
* the LEFT half is what shrinks (to zero) and `overflow="hidden"` keeps that from
|
|
70
|
+
* spilling - the pre-feature behaviour, which is what makes the header safe at 20
|
|
71
|
+
* columns without a ladder. `headerHintFits` below exists only so the threshold
|
|
72
|
+
* is documented and testable, not so the renderer can branch on it.
|
|
72
73
|
*/
|
|
73
74
|
export declare function planHeaderHints(input: {
|
|
74
75
|
columns: number;
|
|
75
|
-
|
|
76
|
-
overlayOpen: boolean;
|
|
76
|
+
panel: HeaderPanelState;
|
|
77
77
|
scrolledLines: number;
|
|
78
|
-
/**
|
|
79
|
-
* Whether the `^K switch kit` clause exists at all (default `true`).
|
|
80
|
-
*
|
|
81
|
-
* `App` ALWAYS PASSES TRUE, including for a user with no kits: the whole point
|
|
82
|
-
* of the requirement is discoverability of `ctrl+K`, and an empty library is
|
|
83
|
-
* exactly the state that needs it (the picker's own `No kits yet` row tells
|
|
84
|
-
* them where to create one). `false` exists for tests and for the future
|
|
85
|
-
* switch, and it degrades the six expanded rungs back into today's four.
|
|
86
|
-
*/
|
|
87
|
-
kitHint?: boolean;
|
|
88
|
-
/**
|
|
89
|
-
* Whether the `^P prompts` clause exists at all (default `true`).
|
|
90
|
-
*
|
|
91
|
-
* `false` is the `prompts.hint` config key and the only reason the ladder is
|
|
92
|
-
* parameterised: a user who turned the header hint off must get exactly the row
|
|
93
|
-
* they had before this feature, which is what the unfiltered ladder below
|
|
94
|
-
* degenerates into.
|
|
95
|
-
*/
|
|
96
|
-
promptsHint?: boolean;
|
|
97
78
|
}): HeaderHintPlan;
|
|
79
|
+
/**
|
|
80
|
+
* Whether the row can hold the hint column without squeezing the left half below
|
|
81
|
+
* `minLeftCells`.
|
|
82
|
+
*
|
|
83
|
+
* A FORMULA, NOT A TABLE OF MAGIC NUMBERS: `columns - minLeftCells -
|
|
84
|
+
* separatorCells >= scrollCells + bodyCells`. Substituting the three states gives
|
|
85
|
+
* 32 / 34 / 35 columns with no chip, plus up to 8 more for a saturated `^9999+ `.
|
|
86
|
+
* v1 claimed "any width >= 35", which forgot the chip entirely.
|
|
87
|
+
*/
|
|
88
|
+
export declare function headerHintFits(columns: number, panel: HeaderPanelState): boolean;
|
|
89
|
+
/** The narrowest width at which each state fits with no scroll chip. */
|
|
90
|
+
export declare function headerHintThreshold(panel: HeaderPanelState): number;
|