@deepseek-harness-tui/dsh-tui 0.10.2 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +171 -124
- package/bin/dsh-tui.js +748 -22
- package/cordis.patch.yml +38 -10
- package/cordis.yml +2 -0
- package/lib/types/adapter/channel/session-projection.d.ts.map +1 -1
- package/lib/types/adapter/channel/session-projection.js +2 -0
- package/lib/types/adapter/channel/ui-policy.d.ts +4 -1
- package/lib/types/adapter/channel/ui-policy.d.ts.map +1 -1
- package/lib/types/adapter/channel/ui-policy.js +5 -1
- package/lib/types/adapter/kernel/host-facade.d.ts.map +1 -1
- package/lib/types/adapter/kernel/host-facade.js +1 -0
- package/lib/types/adapter/kernel/replay.d.ts.map +1 -1
- package/lib/types/adapter/kernel/replay.js +3 -0
- package/lib/types/adapter/kernel/runtime.d.ts.map +1 -1
- package/lib/types/adapter/kernel/runtime.js +2 -0
- package/lib/types/adapter/kernel/slices/workspace.d.ts.map +1 -1
- package/lib/types/adapter/kernel/slices/workspace.js +1 -0
- package/lib/types/adapter/ports/channel-ui.d.ts +22 -2
- package/lib/types/adapter/ports/channel-ui.d.ts.map +1 -1
- package/lib/types/adapter/ports/channel-view.d.ts +66 -0
- package/lib/types/adapter/ports/channel-view.d.ts.map +1 -1
- package/lib/types/adapter/ports/channel-workspace.d.ts +23 -0
- package/lib/types/adapter/ports/channel-workspace.d.ts.map +1 -1
- package/lib/types/adapter/ports/workspace.d.ts +2 -0
- package/lib/types/adapter/ports/workspace.d.ts.map +1 -1
- package/lib/types/adapter/upstream/workspace-driver.d.ts.map +1 -1
- package/lib/types/adapter/upstream/workspace-driver.js +10 -2
- package/lib/types/commands.d.ts.map +1 -1
- package/lib/types/commands.js +1 -0
- package/lib/types/components/ContextBarView.d.ts +15 -7
- package/lib/types/components/ContextBarView.d.ts.map +1 -1
- package/lib/types/components/ContextBarView.js +26 -22
- package/lib/types/components/ImagePreviewOverlay.d.ts.map +1 -1
- package/lib/types/components/ImagePreviewOverlay.js +11 -5
- package/lib/types/components/Markdown.d.ts +11 -1
- package/lib/types/components/Markdown.d.ts.map +1 -1
- package/lib/types/components/Markdown.js +20 -2
- package/lib/types/components/MermaidDiagram.d.ts +13 -0
- package/lib/types/components/MermaidDiagram.d.ts.map +1 -0
- package/lib/types/components/MermaidDiagram.js +61 -0
- package/lib/types/components/MessageList.d.ts +26 -1
- package/lib/types/components/MessageList.d.ts.map +1 -1
- package/lib/types/components/MessageList.js +72 -7
- package/lib/types/components/PageMargin.d.ts +1 -1
- package/lib/types/components/PromptInput.d.ts +37 -1
- package/lib/types/components/PromptInput.d.ts.map +1 -1
- package/lib/types/components/PromptInput.js +243 -28
- package/lib/types/components/ScrollbarGutter.d.ts +20 -5
- package/lib/types/components/ScrollbarGutter.d.ts.map +1 -1
- package/lib/types/components/ScrollbarGutter.js +88 -10
- package/lib/types/components/StreamingMarkdown.d.ts.map +1 -1
- package/lib/types/components/StreamingMarkdown.js +30 -28
- package/lib/types/components/Tooltip.d.ts.map +1 -1
- package/lib/types/components/Tooltip.js +45 -7
- package/lib/types/components/messages/UserPromptMessage.d.ts.map +1 -1
- package/lib/types/components/messages/UserPromptMessage.js +5 -7
- package/lib/types/components/messages/useImageInspection.d.ts.map +1 -1
- package/lib/types/components/messages/useImageInspection.js +19 -9
- package/lib/types/components/promptDraftCache.d.ts +102 -0
- package/lib/types/components/promptDraftCache.d.ts.map +1 -0
- package/lib/types/components/promptDraftCache.js +76 -0
- package/lib/types/components/questions/AskUserQuestionPanel.d.ts +17 -1
- package/lib/types/components/questions/AskUserQuestionPanel.d.ts.map +1 -1
- package/lib/types/components/questions/AskUserQuestionPanel.js +40 -7
- package/lib/types/components/questions/QuestionMinimizedBar.d.ts +12 -0
- package/lib/types/components/questions/QuestionMinimizedBar.d.ts.map +1 -0
- package/lib/types/components/questions/QuestionMinimizedBar.js +43 -0
- package/lib/types/components/sessions/SessionListRow.d.ts +23 -1
- package/lib/types/components/sessions/SessionListRow.d.ts.map +1 -1
- package/lib/types/components/sessions/SessionListRow.js +52 -5
- package/lib/types/components/workspaces/HomeWorkspaceRow.d.ts +49 -0
- package/lib/types/components/workspaces/HomeWorkspaceRow.d.ts.map +1 -0
- package/lib/types/components/workspaces/HomeWorkspaceRow.js +50 -0
- package/lib/types/dsh-adapter/bundled-presets.d.ts +10 -0
- package/lib/types/dsh-adapter/bundled-presets.d.ts.map +1 -0
- package/lib/types/dsh-adapter/bundled-presets.js +80 -0
- package/lib/types/dsh-adapter/channel/action-readiness.d.ts +1 -1
- package/lib/types/dsh-adapter/channel/action-readiness.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/action-readiness.js +3 -0
- package/lib/types/dsh-adapter/channel/agent-view-projection.d.ts +1 -1
- package/lib/types/dsh-adapter/channel/agent-view-projection.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/agent-view-projection.js +19 -2
- package/lib/types/dsh-adapter/channel/background-action.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/background-action.js +52 -29
- package/lib/types/dsh-adapter/channel/binding.d.ts +19 -2
- package/lib/types/dsh-adapter/channel/binding.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/binding.js +78 -18
- package/lib/types/dsh-adapter/channel/composer-images.d.ts +5 -0
- package/lib/types/dsh-adapter/channel/composer-images.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/composer-images.js +105 -6
- package/lib/types/dsh-adapter/channel/context-bookkeeping.d.ts +8 -0
- package/lib/types/dsh-adapter/channel/context-bookkeeping.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/context-bookkeeping.js +3 -2
- package/lib/types/dsh-adapter/channel/ide-selection.d.ts +58 -0
- package/lib/types/dsh-adapter/channel/ide-selection.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/ide-selection.js +226 -0
- package/lib/types/dsh-adapter/channel/input-delivery.d.ts +6 -1
- package/lib/types/dsh-adapter/channel/input-delivery.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/input-delivery.js +9 -1
- package/lib/types/dsh-adapter/channel/local-actions.d.ts +3 -19
- package/lib/types/dsh-adapter/channel/local-actions.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/local-actions.js +2 -1
- package/lib/types/dsh-adapter/channel/model-actions.d.ts +4 -1
- package/lib/types/dsh-adapter/channel/model-actions.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/model-actions.js +82 -6
- package/lib/types/dsh-adapter/channel/model-switch.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/model-switch.js +49 -31
- package/lib/types/dsh-adapter/channel/projection.d.ts +4 -0
- package/lib/types/dsh-adapter/channel/projection.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/projection.js +29 -19
- package/lib/types/dsh-adapter/channel/reports.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/reports.js +4 -12
- package/lib/types/dsh-adapter/channel/session-fork.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/session-fork.js +26 -2
- package/lib/types/dsh-adapter/channel/session-live-adoption.d.ts +3 -0
- package/lib/types/dsh-adapter/channel/session-live-adoption.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/session-live-adoption.js +1 -0
- package/lib/types/dsh-adapter/channel/session-metadata.d.ts +10 -0
- package/lib/types/dsh-adapter/channel/session-metadata.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/session-metadata.js +2 -2
- package/lib/types/dsh-adapter/channel/session-resume.d.ts +11 -2
- package/lib/types/dsh-adapter/channel/session-resume.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/session-resume.js +291 -94
- package/lib/types/dsh-adapter/channel/session-rewind.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/session-rewind.js +34 -16
- package/lib/types/dsh-adapter/channel/session-tree-actions.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/session-tree-actions.js +19 -3
- package/lib/types/dsh-adapter/channel/settings-host.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/settings-host.js +4 -3
- package/lib/types/dsh-adapter/channel/state.d.ts +1 -1
- package/lib/types/dsh-adapter/channel/state.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/state.js +1 -1
- package/lib/types/dsh-adapter/channel/transcript.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/transcript.js +6 -12
- package/lib/types/dsh-adapter/channel/types.d.ts +6 -1
- package/lib/types/dsh-adapter/channel/types.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/workspace-actions.d.ts +4 -1
- package/lib/types/dsh-adapter/channel/workspace-actions.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/workspace-actions.js +41 -1
- package/lib/types/dsh-adapter/channel.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel.js +67 -6
- package/lib/types/dsh-adapter/compat/messages.d.ts +15 -0
- package/lib/types/dsh-adapter/compat/messages.d.ts.map +1 -0
- package/lib/types/dsh-adapter/compat/messages.js +15 -0
- package/lib/types/dsh-adapter/compat/settings.d.ts +33 -0
- package/lib/types/dsh-adapter/compat/settings.d.ts.map +1 -0
- package/lib/types/dsh-adapter/compat/settings.js +43 -0
- package/lib/types/dsh-adapter/compat/shell.d.ts +25 -0
- package/lib/types/dsh-adapter/compat/shell.d.ts.map +1 -0
- package/lib/types/dsh-adapter/compat/shell.js +11 -0
- package/lib/types/dsh-adapter/contract.d.ts +4 -4
- package/lib/types/dsh-adapter/contract.d.ts.map +1 -1
- package/lib/types/dsh-adapter/contract.js +15 -6
- package/lib/types/dsh-adapter/ide-channel.d.ts +224 -0
- package/lib/types/dsh-adapter/ide-channel.d.ts.map +1 -0
- package/lib/types/dsh-adapter/ide-channel.js +556 -0
- package/lib/types/dsh-adapter/index.d.ts +18 -3
- package/lib/types/dsh-adapter/index.d.ts.map +1 -1
- package/lib/types/dsh-adapter/index.js +32 -5
- package/lib/types/dsh-adapter/plugin.d.ts +5 -2
- package/lib/types/dsh-adapter/plugin.d.ts.map +1 -1
- package/lib/types/dsh-adapter/plugin.js +173 -42
- package/lib/types/dsh-adapter/preset-resolution.d.ts +8 -0
- package/lib/types/dsh-adapter/preset-resolution.d.ts.map +1 -1
- package/lib/types/dsh-adapter/preset-resolution.js +10 -0
- package/lib/types/dsh-adapter/session-mount-heartbeat.d.ts +53 -0
- package/lib/types/dsh-adapter/session-mount-heartbeat.d.ts.map +1 -0
- package/lib/types/dsh-adapter/session-mount-heartbeat.js +100 -0
- package/lib/types/dsh-adapter/sessionTree.d.ts.map +1 -1
- package/lib/types/dsh-adapter/sessionTree.js +3 -2
- package/lib/types/dsh-adapter/subagents.d.ts.map +1 -1
- package/lib/types/dsh-adapter/subagents.js +6 -5
- package/lib/types/dsh-adapter/workspaces.d.ts +46 -3
- package/lib/types/dsh-adapter/workspaces.d.ts.map +1 -1
- package/lib/types/dsh-adapter/workspaces.js +116 -0
- package/lib/types/effortPrefs.d.ts +15 -3
- package/lib/types/effortPrefs.d.ts.map +1 -1
- package/lib/types/effortPrefs.js +37 -3
- package/lib/types/homePrefs.d.ts +15 -0
- package/lib/types/homePrefs.d.ts.map +1 -0
- package/lib/types/homePrefs.js +53 -0
- package/lib/types/i18n.d.ts +257 -296
- package/lib/types/i18n.d.ts.map +1 -1
- package/lib/types/i18n.js +78 -84
- package/lib/types/ink/colorize.d.ts +10 -0
- package/lib/types/ink/colorize.d.ts.map +1 -1
- package/lib/types/ink/colorize.js +21 -0
- package/lib/types/ink/components/ScrollBox.d.ts.map +1 -1
- package/lib/types/ink/components/ScrollBox.js +12 -1
- package/lib/types/ink/dom.d.ts +1 -0
- package/lib/types/ink/dom.d.ts.map +1 -1
- package/lib/types/ink/hooks/use-copy-on-select.d.ts +5 -1
- package/lib/types/ink/hooks/use-copy-on-select.d.ts.map +1 -1
- package/lib/types/ink/hooks/use-copy-on-select.js +15 -1
- package/lib/types/ink/ink.d.ts.map +1 -1
- package/lib/types/ink/ink.js +36 -1
- package/lib/types/ink/output.d.ts +10 -1
- package/lib/types/ink/output.d.ts.map +1 -1
- package/lib/types/ink/output.js +109 -17
- package/lib/types/ink/render-border.d.ts +6 -1
- package/lib/types/ink/render-border.d.ts.map +1 -1
- package/lib/types/ink/render-border.js +25 -9
- package/lib/types/ink/render-node-to-output.d.ts.map +1 -1
- package/lib/types/ink/render-node-to-output.js +48 -6
- package/lib/types/ink/renderer.d.ts +6 -0
- package/lib/types/ink/renderer.d.ts.map +1 -1
- package/lib/types/ink/renderer.js +2 -1
- package/lib/types/ink/selection.d.ts +60 -0
- package/lib/types/ink/selection.d.ts.map +1 -1
- package/lib/types/ink/selection.js +129 -0
- package/lib/types/ink/sixel-graphics.d.ts +19 -1
- package/lib/types/ink/sixel-graphics.d.ts.map +1 -1
- package/lib/types/ink/sixel-graphics.js +124 -12
- package/lib/types/ink/terminal-image.d.ts +18 -1
- package/lib/types/ink/terminal-image.d.ts.map +1 -1
- package/lib/types/ink/terminal.d.ts +17 -0
- package/lib/types/ink/terminal.d.ts.map +1 -1
- package/lib/types/ink/terminal.js +20 -1
- package/lib/types/screens/Chat.d.ts +17 -1
- package/lib/types/screens/Chat.d.ts.map +1 -1
- package/lib/types/screens/Chat.js +284 -72
- package/lib/types/screens/SessionSupervisor.d.ts +84 -0
- package/lib/types/screens/SessionSupervisor.d.ts.map +1 -0
- package/lib/types/screens/SessionSupervisor.js +350 -0
- package/lib/types/screens/SessionTree.js +2 -2
- package/lib/types/screens/Settings.d.ts.map +1 -1
- package/lib/types/screens/Settings.js +32 -9
- package/lib/types/screens/StatusLine.d.ts +13 -0
- package/lib/types/screens/StatusLine.d.ts.map +1 -1
- package/lib/types/screens/StatusLine.js +36 -17
- package/lib/types/screens/StatusMetrics.d.ts +79 -22
- package/lib/types/screens/StatusMetrics.d.ts.map +1 -1
- package/lib/types/screens/StatusMetrics.js +153 -88
- package/lib/types/screens/sessionSupervisor/model.d.ts +83 -0
- package/lib/types/screens/sessionSupervisor/model.d.ts.map +1 -0
- package/lib/types/screens/sessionSupervisor/model.js +69 -0
- package/lib/types/screens/sessionSupervisor/useSessionSupervisor.d.ts +120 -0
- package/lib/types/screens/sessionSupervisor/useSessionSupervisor.d.ts.map +1 -0
- package/lib/types/screens/sessionSupervisor/useSessionSupervisor.js +593 -0
- package/lib/types/sessionMounts.d.ts +272 -0
- package/lib/types/sessionMounts.d.ts.map +1 -0
- package/lib/types/sessionMounts.js +706 -0
- package/lib/types/sessions/format.d.ts.map +1 -1
- package/lib/types/sessions/format.js +4 -1
- package/lib/types/sessions/resumeFailure.d.ts +38 -0
- package/lib/types/sessions/resumeFailure.d.ts.map +1 -0
- package/lib/types/sessions/resumeFailure.js +58 -0
- package/lib/types/terminal-utils/mermaid.d.ts +44 -0
- package/lib/types/terminal-utils/mermaid.d.ts.map +1 -0
- package/lib/types/terminal-utils/mermaid.js +83 -0
- package/lib/types/tips.d.ts.map +1 -1
- package/lib/types/tips.js +175 -19
- package/lib/types/tuiDisplayPrefs.d.ts +7 -9
- package/lib/types/tuiDisplayPrefs.d.ts.map +1 -1
- package/lib/types/tuiDisplayPrefs.js +31 -28
- package/lib/types/update.d.ts +61 -0
- package/lib/types/update.d.ts.map +1 -1
- package/lib/types/update.js +194 -0
- package/lib/types/utils/imageResize.d.ts +90 -0
- package/lib/types/utils/imageResize.d.ts.map +1 -0
- package/lib/types/utils/imageResize.js +272 -0
- package/lib/types/utils/keymap.d.ts +5 -1
- package/lib/types/utils/keymap.d.ts.map +1 -1
- package/lib/types/utils/keymap.js +23 -0
- package/lib/types/workspaces.d.ts +2 -1
- package/lib/types/workspaces.d.ts.map +1 -1
- package/lib/types/workspaces.js +1 -1
- package/node_modules/@deepseek-harness-tui/dsh-auth/package.json +10 -10
- package/package.json +120 -103
- package/presets/liangshen/.dsh-tui-managed.json +1 -1
- package/presets/liangshen/agent.cordis.yml +15 -0
- package/lib/types/screens/AgentView.d.ts +0 -23
- package/lib/types/screens/AgentView.d.ts.map +0 -1
- package/lib/types/screens/AgentView.js +0 -546
- package/lib/types/screens/SessionBrowser.d.ts +0 -42
- package/lib/types/screens/SessionBrowser.d.ts.map +0 -1
- package/lib/types/screens/SessionBrowser.js +0 -911
|
@@ -17,7 +17,7 @@ import { ContextBarView } from '../components/ContextBarView.js';
|
|
|
17
17
|
import { TooltipTarget } from '../components/Tooltip.js';
|
|
18
18
|
import { formatProject } from '../sessions/format.js';
|
|
19
19
|
import { homeDir } from '../utils/paths.js';
|
|
20
|
-
import { USED_SEGMENTS, renderMiniContextBar, renderTpsGauge, renderTpsSparkline, speedColor, tpsStats, } from './StatusMetrics.js';
|
|
20
|
+
import { FREE_SEGMENT_FILL, USED_SEGMENTS, contextBarBreakdown, renderMiniContextBar, renderTpsGauge, renderTpsSparkline, speedColor, tpsStats, } from './StatusMetrics.js';
|
|
21
21
|
/**
|
|
22
22
|
* The footer under the prompt input: the segmented context progress bar on
|
|
23
23
|
* its own first line, the
|
|
@@ -31,7 +31,8 @@ import { USED_SEGMENTS, renderMiniContextBar, renderTpsGauge, renderTpsSparkline
|
|
|
31
31
|
* swaps the ctx readout for a mini pressure gauge and parks that field's
|
|
32
32
|
* detailed breakdown on the supplemental row where the idle hint lives —
|
|
33
33
|
* the footer stays one line tall, the detail is a peek, not a layout
|
|
34
|
-
* change.
|
|
34
|
+
* change. The context bar answers the same way: it carries no labels of its
|
|
35
|
+
* own, so hovering it is how its colors get their names and numbers.
|
|
35
36
|
*/
|
|
36
37
|
/**
|
|
37
38
|
* Minimal mode's footer config. It ignores every SAVED preference (built from
|
|
@@ -182,6 +183,7 @@ export function StatusLine({ channel, selectionActive = false, helpOpen = false,
|
|
|
182
183
|
};
|
|
183
184
|
}
|
|
184
185
|
}
|
|
186
|
+
const selectionBadge = formatSelectionBadge(channel.selection);
|
|
185
187
|
// Background-job chip (ctx.jobs; /jobs): live count of running/stopping
|
|
186
188
|
// jobs, shown only while non-zero — a silent zero is not information.
|
|
187
189
|
// Not preference-gated: it is transient situational state like the goal
|
|
@@ -228,6 +230,15 @@ export function StatusLine({ channel, selectionActive = false, helpOpen = false,
|
|
|
228
230
|
: []),
|
|
229
231
|
];
|
|
230
232
|
const rightFields = [
|
|
233
|
+
// Live IDE-selection badge first: it tracks the user's in-editor gesture,
|
|
234
|
+
// the freshest signal in the footer (T-FIX-02). Absent without an IDE —
|
|
235
|
+
// no placeholder, matching the official Claude Code footer.
|
|
236
|
+
...(selectionBadge !== undefined
|
|
237
|
+
? [{
|
|
238
|
+
key: 'ideSelection',
|
|
239
|
+
node: (_jsx(Text, { color: "ide", wrap: "truncate", children: selectionBadge })),
|
|
240
|
+
}]
|
|
241
|
+
: []),
|
|
231
242
|
// Goal chip first: session-level state outranks repo/location details.
|
|
232
243
|
...(statusBar.goal && channel.goal !== undefined
|
|
233
244
|
? [{
|
|
@@ -298,7 +309,7 @@ export function StatusLine({ channel, selectionActive = false, helpOpen = false,
|
|
|
298
309
|
channel.contextWindow !== undefined;
|
|
299
310
|
// The supplemental-row readout for the hovered field: replaces the idle
|
|
300
311
|
// hint (never the activity line) while the pointer dwells on a field.
|
|
301
|
-
const detail = buildHoverDetail(hover, channel, usage, contextUsed);
|
|
312
|
+
const detail = buildHoverDetail(hover, channel, usage, contextUsed, columns, barColors);
|
|
302
313
|
const trailer = detail !== null
|
|
303
314
|
? detail
|
|
304
315
|
: hint !== ''
|
|
@@ -321,9 +332,7 @@ export function StatusLine({ channel, selectionActive = false, helpOpen = false,
|
|
|
321
332
|
showActivity ||
|
|
322
333
|
showTrajectory ||
|
|
323
334
|
hint !== '';
|
|
324
|
-
return (_jsx(Box, { paddingX: 1, width: columns, flexShrink: 0, children: _jsxs(Box, { flexDirection: "column", width: "100%", children: [barVisible ? (_jsx(ContextBarView, { segments: channel.contextSegments, usedTokens: contextUsed ?? 0, contextWindow: channel.contextWindow ?? 0, width: barWidth, colors: barColors, onHover:
|
|
325
|
-
? (current !== null && current.startsWith('segment:') ? null : current)
|
|
326
|
-
: `segment:${segment}`) })) : null, hasStatusFields ? statusBar.compact ? (_jsxs(Box, { flexDirection: "row", justifyContent: "space-between", gap: 2, children: [_jsx(Box, { flexGrow: 1, flexShrink: 1, flexDirection: "row", overflow: "hidden", children: _jsx(FieldLine, { parts: compactFields, hoverProps: hoverProps }) }), ctxNode !== undefined ? (_jsx(Box, { flexShrink: 0, ...hoverProps('ctx'), children: _jsx(Text, { wrap: "truncate", children: ctxNode }) })) : null] })) : (_jsxs(Box, { flexDirection: "row", justifyContent: "space-between", gap: 2, children: [_jsx(Box, { flexGrow: 1, flexShrink: 1, flexDirection: "row", overflow: "hidden", children: _jsx(FieldLine, { parts: fullLeftFields, hoverProps: hoverProps }) }), _jsx(Box, { justifyContent: "flex-end", flexShrink: 2, flexDirection: "row", overflow: "hidden", children: _jsx(FieldLine, { parts: rightFields, hoverProps: hoverProps }) })] })) : null, showSupplementalRow ? _jsxs(Box, { height: 1, overflow: "hidden", flexDirection: "row", justifyContent: "space-between", gap: 2, children: [_jsxs(Box, { flexDirection: "row", flexGrow: 1, justifyContent: showActivity && trailer !== null ? 'space-between' : 'flex-start', gap: 2, children: [showActivity && activity !== undefined ? (_jsx(ActivityLine, { activity: activity, activityFrames: channel.activityFrames, warnPct: contextPressurePct(usage, channel.contextWindow), warnDanger: (contextPressurePct(usage, channel.contextWindow) ?? 0) >= 95 })) : trailer, showActivity ? trailer : null] }), showTrajectory && wake !== undefined ? (_jsx(MiniWake, { band: wake.band, hint: wake.hint, tick: wake.tick })) : null] }) : null] }) }));
|
|
335
|
+
return (_jsx(Box, { paddingX: 1, width: columns, flexShrink: 0, children: _jsxs(Box, { flexDirection: "column", width: "100%", children: [barVisible ? (_jsx(ContextBarView, { segments: channel.contextSegments, usedTokens: contextUsed ?? 0, contextWindow: channel.contextWindow ?? 0, width: barWidth, colors: barColors, onHover: hovered => setHover(current => hovered ? 'bar' : current === 'bar' ? null : current) })) : null, hasStatusFields ? statusBar.compact ? (_jsxs(Box, { flexDirection: "row", justifyContent: "space-between", gap: 2, children: [_jsx(Box, { flexGrow: 1, flexShrink: 1, flexDirection: "row", overflow: "hidden", children: _jsx(FieldLine, { parts: compactFields, hoverProps: hoverProps }) }), ctxNode !== undefined ? (_jsx(Box, { flexShrink: 0, ...hoverProps('ctx'), children: _jsx(Text, { wrap: "truncate", children: ctxNode }) })) : null] })) : (_jsxs(Box, { flexDirection: "row", justifyContent: "space-between", gap: 2, children: [_jsx(Box, { flexGrow: 1, flexShrink: 1, flexDirection: "row", overflow: "hidden", children: _jsx(FieldLine, { parts: fullLeftFields, hoverProps: hoverProps }) }), _jsx(Box, { justifyContent: "flex-end", flexShrink: 2, flexDirection: "row", overflow: "hidden", children: _jsx(FieldLine, { parts: rightFields, hoverProps: hoverProps }) })] })) : null, showSupplementalRow ? _jsxs(Box, { height: 1, overflow: "hidden", flexDirection: "row", justifyContent: "space-between", gap: 2, children: [_jsxs(Box, { flexDirection: "row", flexGrow: 1, justifyContent: showActivity && trailer !== null ? 'space-between' : 'flex-start', gap: 2, children: [showActivity && activity !== undefined ? (_jsx(ActivityLine, { activity: activity, activityFrames: channel.activityFrames, warnPct: contextPressurePct(usage, channel.contextWindow), warnDanger: (contextPressurePct(usage, channel.contextWindow) ?? 0) >= 95 })) : trailer, showActivity ? trailer : null] }), showTrajectory && wake !== undefined ? (_jsx(MiniWake, { band: wake.band, hint: wake.hint, tick: wake.tick })) : null] }) : null] }) }));
|
|
327
336
|
}
|
|
328
337
|
/**
|
|
329
338
|
* The supplemental-row readout for a hovered footer field. Technical label
|
|
@@ -332,24 +341,20 @@ export function StatusLine({ channel, selectionActive = false, helpOpen = false,
|
|
|
332
341
|
* hovered (or the hover outlived its data, which the field gating makes
|
|
333
342
|
* near-impossible).
|
|
334
343
|
*/
|
|
335
|
-
function buildHoverDetail(hover, channel, usage, contextUsed) {
|
|
344
|
+
function buildHoverDetail(hover, channel, usage, contextUsed, columns, barColors) {
|
|
336
345
|
if (hover === null)
|
|
337
346
|
return null;
|
|
338
347
|
const window = channel.contextWindow;
|
|
339
348
|
const dim = (label) => _jsx(Text, { dimColor: true, children: label });
|
|
340
|
-
if (hover
|
|
349
|
+
if (hover === 'bar') {
|
|
341
350
|
if (window === undefined || window <= 0 || contextUsed === undefined)
|
|
342
351
|
return null;
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
}
|
|
348
|
-
const segment = USED_SEGMENTS.find(s => s.key === key);
|
|
349
|
-
if (segment === undefined)
|
|
352
|
+
// The bar's text-free design pays off here: this line is its legend, so
|
|
353
|
+
// every entry leads with a chip of the very color it names.
|
|
354
|
+
const { entries, separator } = contextBarBreakdown(channel.contextSegments, contextUsed, window, columns, barColors?.freeFill ?? FREE_SEGMENT_FILL);
|
|
355
|
+
if (entries.length === 0)
|
|
350
356
|
return null;
|
|
351
|
-
|
|
352
|
-
return (_jsxs(Text, { wrap: "truncate", children: [dim(`${segment.labels[1] ?? segment.key} `), formatTokens(tokens), " \u00B7", ' ', ((tokens / window) * 100).toFixed(1), "% ", t('status-detail-of-window')] }));
|
|
357
|
+
return (_jsx(Text, { wrap: "truncate", children: entries.map((entry, index) => (_jsxs(React.Fragment, { children: [index > 0 ? dim(separator) : null, _jsx(Text, { backgroundColor: entry.color, children: " " }), entry.label] }, entry.key))) }));
|
|
353
358
|
}
|
|
354
359
|
switch (hover) {
|
|
355
360
|
case 'ctx': {
|
|
@@ -428,6 +433,20 @@ export function formatCacheHitRate(usage) {
|
|
|
428
433
|
return undefined;
|
|
429
434
|
return `${((usage.cacheRead / total) * 100).toFixed(1)}%`;
|
|
430
435
|
}
|
|
436
|
+
/**
|
|
437
|
+
* The prompt footer's live IDE-selection badge (T-FIX-02), mirroring Claude
|
|
438
|
+
* Code's `⧉ N lines selected`: English only (like every other footer field),
|
|
439
|
+
* singular/plural aware, line count from the snapshot's 0-based inclusive
|
|
440
|
+
* range. `undefined` (no IDE yet / cleared by an isEmpty notification)
|
|
441
|
+
* renders no field at all — a manually launched session never shows a
|
|
442
|
+
* placeholder. Exported for scripts/verify-ide-channel.tsx.
|
|
443
|
+
*/
|
|
444
|
+
export function formatSelectionBadge(selection) {
|
|
445
|
+
if (selection === undefined || selection.isEmpty === true)
|
|
446
|
+
return undefined;
|
|
447
|
+
const lines = selection.endLine - selection.startLine + 1;
|
|
448
|
+
return `⧉ ${lines} ${lines === 1 ? 'line' : 'lines'} selected`;
|
|
449
|
+
}
|
|
431
450
|
function basename(path) {
|
|
432
451
|
const parts = path.split(/[\\/]/);
|
|
433
452
|
return parts[parts.length - 1] ?? path;
|
|
@@ -7,58 +7,115 @@
|
|
|
7
7
|
* normalized sparkline after each completed turn; colors green ≥ 50 tps,
|
|
8
8
|
* yellow ≥ 20, red below.
|
|
9
9
|
*/
|
|
10
|
+
import type { Color } from '../ink/styles.js';
|
|
10
11
|
/** Context bar segments — DeepSeek blue family (dark-theme friendly: deep
|
|
11
|
-
* navy → brand blue, neutral grey free segment
|
|
12
|
-
*
|
|
13
|
-
*
|
|
12
|
+
* navy → brand blue, neutral grey free segment).
|
|
13
|
+
*
|
|
14
|
+
* The bar draws NO text inside a used segment: the fill color is the whole
|
|
15
|
+
* signal (community feedback — the old `s`/`p`/`t` letters read as noise on
|
|
16
|
+
* a row that is already decorative). `labels` therefore belongs to the hover
|
|
17
|
+
* breakdown only: index 0 is the readable name, index 1 the short form the
|
|
18
|
+
* supplemental row falls back to on a narrow terminal. Exported for the
|
|
19
|
+
* hoverable JSX bar (ContextBarView), which re-derives the same column split
|
|
20
|
+
* this module's ANSI path renders. */
|
|
14
21
|
export declare const USED_SEGMENTS: readonly [{
|
|
15
22
|
readonly key: "system";
|
|
16
23
|
readonly color: "#22305F";
|
|
17
|
-
readonly labels: readonly ["system", "sys"
|
|
24
|
+
readonly labels: readonly ["system", "sys"];
|
|
18
25
|
}, {
|
|
19
26
|
readonly key: "prompt";
|
|
20
27
|
readonly color: "#2B3D78";
|
|
21
|
-
readonly labels: readonly ["prompt", "pr"
|
|
28
|
+
readonly labels: readonly ["prompt", "pr"];
|
|
22
29
|
}, {
|
|
23
30
|
readonly key: "assistant";
|
|
24
31
|
readonly color: "#344A92";
|
|
25
|
-
readonly labels: readonly ["assistant", "ast"
|
|
32
|
+
readonly labels: readonly ["assistant", "ast"];
|
|
26
33
|
}, {
|
|
27
34
|
readonly key: "thinking";
|
|
28
35
|
readonly color: "#4D6BFE";
|
|
29
|
-
readonly labels: readonly ["
|
|
36
|
+
readonly labels: readonly ["thinking", "th"];
|
|
30
37
|
}, {
|
|
31
38
|
readonly key: "tools";
|
|
32
39
|
readonly color: "#5A7CFF";
|
|
33
|
-
readonly labels: readonly ["tools", "tl"
|
|
40
|
+
readonly labels: readonly ["tools", "tl"];
|
|
34
41
|
}];
|
|
35
42
|
/** Used tokens per context content type (system, prompt, assistant, thinking, tools). */
|
|
36
43
|
export type ContextSegments = Record<(typeof USED_SEGMENTS)[number]['key'], number>;
|
|
44
|
+
/** Free-segment colors: light grey fill, dark grey readout. Exported so the
|
|
45
|
+
* JSX bar (ContextBarView) and the hover chip paint the same free color the
|
|
46
|
+
* ANSI path does instead of re-declaring the hex. */
|
|
47
|
+
export declare const FREE_SEGMENT_FILL = "#E8E8E8";
|
|
48
|
+
export declare const FREE_SEGMENT_TEXT = "#4A4A4A";
|
|
37
49
|
/** Compact token count like pi's: `988`, `3.4k`, `12k`, `1.0M`.
|
|
38
50
|
* @param count - The raw token count; negative values clamp to zero.
|
|
39
51
|
* @returns The compact count string.
|
|
40
52
|
*/
|
|
41
53
|
export declare function formatTokens(count: number): string;
|
|
42
|
-
/**
|
|
43
|
-
*
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
export declare function
|
|
54
|
+
/** Which pressure step a context occupancy falls in: undefined while the
|
|
55
|
+
* context is comfortable, then the shared `warning` / `error` theme colors.
|
|
56
|
+
* @param pct - Context occupancy percent (0–100+).
|
|
57
|
+
* @returns The theme color key, or undefined below the amber threshold.
|
|
58
|
+
*/
|
|
59
|
+
export declare function contextPressureStep(pct: number): 'warning' | 'error' | undefined;
|
|
60
|
+
/**
|
|
61
|
+
* The bar's right-aligned usage readout, longest form first: token counts and
|
|
62
|
+
* percent (`13k/64k 19.5%`), then the percent alone once the free segment is
|
|
63
|
+
* too narrow to carry the counts.
|
|
64
|
+
* @param usedTokens - Total context tokens in use.
|
|
65
|
+
* @param contextWindow - The context window size in tokens.
|
|
66
|
+
* @returns The readout ladder, widest option first.
|
|
67
|
+
*/
|
|
68
|
+
export declare function contextBarReadout(usedTokens: number, contextWindow: number): readonly string[];
|
|
48
69
|
/**
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
|
|
70
|
+
* Blank-pad to `width` cells and right-align the first readout option that
|
|
71
|
+
* fits. Shared by the ANSI string path (renderContextBar) and the hoverable
|
|
72
|
+
* JSX bar (ContextBarView) so both render identical readouts.
|
|
73
|
+
*/
|
|
74
|
+
export declare function rightAlignBarText(options: readonly string[], width: number): string;
|
|
75
|
+
/** One entry of the context bar's hover breakdown: the fill color, the name
|
|
76
|
+
* to put next to a swatch of it, and the token count already folded into
|
|
77
|
+
* `label` (`system 1.2k`). */
|
|
78
|
+
export type ContextBarBreakdownEntry = {
|
|
79
|
+
/** `system` … `tools`, or `free`. */
|
|
80
|
+
key: string;
|
|
81
|
+
/** The chip label, e.g. `thinking 5.0k`. */
|
|
82
|
+
label: string;
|
|
83
|
+
/** The segment's fill color — the hover chip paints with it, which is what
|
|
84
|
+
* ties each number back to a slice of the bar. */
|
|
85
|
+
color: Color;
|
|
86
|
+
};
|
|
87
|
+
/** Separator between breakdown entries, chosen by the width ladder below. */
|
|
88
|
+
export type ContextBarBreakdown = {
|
|
89
|
+
readonly entries: readonly ContextBarBreakdownEntry[];
|
|
90
|
+
readonly separator: string;
|
|
91
|
+
};
|
|
92
|
+
/**
|
|
93
|
+
* The context bar's hover breakdown — the legend the bar no longer carries
|
|
94
|
+
* itself. One entry per segment the bar actually paints (a zero-token content
|
|
95
|
+
* type gets no columns, so it gets no entry either), in bar order, free last.
|
|
96
|
+
*
|
|
97
|
+
* `columns` picks the label form: readable names with a ` · ` separator while
|
|
98
|
+
* the line fits, then the short forms, then a bare space separator (the color
|
|
99
|
+
* chip already separates the entries). The caller renders each entry as
|
|
100
|
+
* `chip + space + label`, which is what the fit test measures.
|
|
101
|
+
*
|
|
102
|
+
* @param segments - Used tokens per content type.
|
|
103
|
+
* @param usedTokens - Total used tokens; the remainder is the free entry.
|
|
104
|
+
* @param contextWindow - The context window size in tokens.
|
|
105
|
+
* @param columns - Terminal width; the footer's own padding is subtracted here.
|
|
106
|
+
* @param freeFill - The free segment's fill color (callers pass a theme override).
|
|
107
|
+
* @returns The breakdown entries and the separator to join them with.
|
|
53
108
|
*/
|
|
54
|
-
export declare function
|
|
109
|
+
export declare function contextBarBreakdown(segments: ContextSegments, usedTokens: number, contextWindow: number, columns: number, freeFill?: Color): ContextBarBreakdown;
|
|
55
110
|
/** Give every visible used segment at least one column before sharing the
|
|
56
111
|
* rest. Exported for ContextBarView (hoverable JSX twin of the bar). */
|
|
57
112
|
export declare function allocateBarColumns(values: readonly number[], width: number): number[];
|
|
58
113
|
/**
|
|
59
|
-
* The segmented context bar: used segments by content type, the
|
|
60
|
-
* a light free segment whose right edge carries the usage
|
|
61
|
-
* (`
|
|
114
|
+
* The segmented context bar: used segments by content type, then the
|
|
115
|
+
* remainder as a light free segment whose right edge carries the usage
|
|
116
|
+
* readout (`13k/64k 19.5%`). No other text — the bar is read by color, and
|
|
117
|
+
* the pointer supplies the names and numbers (contextBarBreakdown). The
|
|
118
|
+
* readout tints amber / red as the context fills (contextPressureStep).
|
|
62
119
|
* @param segments - Used tokens per content type.
|
|
63
120
|
* @param usedTokens - Total used tokens, driving the usage readout.
|
|
64
121
|
* @param contextWindow - The context window size in tokens.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StatusMetrics.d.ts","sourceRoot":"","sources":["../../../src/screens/StatusMetrics.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;
|
|
1
|
+
{"version":3,"file":"StatusMetrics.d.ts","sourceRoot":"","sources":["../../../src/screens/StatusMetrics.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,27 +1,26 @@
|
|
|
1
|
-
|
|
2
|
-
* Status-line metric renderers, ported from two pi extensions:
|
|
3
|
-
* - `pi-nano-context`: segmented context progress bar (morandi pastel
|
|
4
|
-
* segments by content type, free space right-aligned with the usage
|
|
5
|
-
* readout, largest-remainder column allocation).
|
|
6
|
-
* - `pi-tps-meter`: live 1/8-cell gauge while streaming and a min-max
|
|
7
|
-
* normalized sparkline after each completed turn; colors green ≥ 50 tps,
|
|
8
|
-
* yellow ≥ 20, red below.
|
|
9
|
-
*/
|
|
1
|
+
import { stringWidth } from '../ink/stringWidth.js';
|
|
10
2
|
/** Context bar segments — DeepSeek blue family (dark-theme friendly: deep
|
|
11
|
-
* navy → brand blue, neutral grey free segment
|
|
12
|
-
*
|
|
13
|
-
*
|
|
3
|
+
* navy → brand blue, neutral grey free segment).
|
|
4
|
+
*
|
|
5
|
+
* The bar draws NO text inside a used segment: the fill color is the whole
|
|
6
|
+
* signal (community feedback — the old `s`/`p`/`t` letters read as noise on
|
|
7
|
+
* a row that is already decorative). `labels` therefore belongs to the hover
|
|
8
|
+
* breakdown only: index 0 is the readable name, index 1 the short form the
|
|
9
|
+
* supplemental row falls back to on a narrow terminal. Exported for the
|
|
10
|
+
* hoverable JSX bar (ContextBarView), which re-derives the same column split
|
|
11
|
+
* this module's ANSI path renders. */
|
|
14
12
|
export const USED_SEGMENTS = [
|
|
15
|
-
{ key: 'system', color: '#22305F', labels: ['system', 'sys'
|
|
16
|
-
{ key: 'prompt', color: '#2B3D78', labels: ['prompt', 'pr'
|
|
17
|
-
{ key: 'assistant', color: '#344A92', labels: ['assistant', 'ast'
|
|
18
|
-
{ key: 'thinking', color: '#4D6BFE', labels: ['
|
|
19
|
-
{ key: 'tools', color: '#5A7CFF', labels: ['tools', 'tl'
|
|
13
|
+
{ key: 'system', color: '#22305F', labels: ['system', 'sys'] }, // deep navy
|
|
14
|
+
{ key: 'prompt', color: '#2B3D78', labels: ['prompt', 'pr'] }, // navy
|
|
15
|
+
{ key: 'assistant', color: '#344A92', labels: ['assistant', 'ast'] }, // indigo
|
|
16
|
+
{ key: 'thinking', color: '#4D6BFE', labels: ['thinking', 'th'] }, // DeepSeek brand blue
|
|
17
|
+
{ key: 'tools', color: '#5A7CFF', labels: ['tools', 'tl'] }, // lighter blue
|
|
20
18
|
];
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const
|
|
19
|
+
/** Free-segment colors: light grey fill, dark grey readout. Exported so the
|
|
20
|
+
* JSX bar (ContextBarView) and the hover chip paint the same free color the
|
|
21
|
+
* ANSI path does instead of re-declaring the hex. */
|
|
22
|
+
export const FREE_SEGMENT_FILL = '#E8E8E8';
|
|
23
|
+
export const FREE_SEGMENT_TEXT = '#4A4A4A';
|
|
25
24
|
const ANSI_RE = /\x1b\[[0-9;]*m/g;
|
|
26
25
|
const stripAnsi = (text) => text.replace(ANSI_RE, '');
|
|
27
26
|
const plainWidth = (text) => Array.from(stripAnsi(text)).length;
|
|
@@ -50,74 +49,138 @@ export function formatTokens(count) {
|
|
|
50
49
|
return `${(value / 1000000).toFixed(1)}M`;
|
|
51
50
|
return `${Math.round(value / 1000000)}M`;
|
|
52
51
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
*
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
return label;
|
|
70
|
-
}
|
|
71
|
-
return '';
|
|
72
|
-
}
|
|
73
|
-
function renderUsedSegment(labels, color, width) {
|
|
74
|
-
if (width <= 0)
|
|
75
|
-
return '';
|
|
76
|
-
const label = chooseLabel(labels, width);
|
|
77
|
-
const text = label.length > 0
|
|
78
|
-
? foreground(USED_SEGMENT_TEXT, centeredText(label, width))
|
|
79
|
-
: ' '.repeat(width);
|
|
80
|
-
return background(color, text);
|
|
52
|
+
/** Context-pressure thresholds, shared by the bar readout's tint, the ctx
|
|
53
|
+
* field's hover gauge, and contextPressurePct's amber/red footer convention
|
|
54
|
+
* (amber ≥ 80%, red ≥ 95%). */
|
|
55
|
+
const PRESSURE_WARN = 80;
|
|
56
|
+
const PRESSURE_DANGER = 95;
|
|
57
|
+
/** Which pressure step a context occupancy falls in: undefined while the
|
|
58
|
+
* context is comfortable, then the shared `warning` / `error` theme colors.
|
|
59
|
+
* @param pct - Context occupancy percent (0–100+).
|
|
60
|
+
* @returns The theme color key, or undefined below the amber threshold.
|
|
61
|
+
*/
|
|
62
|
+
export function contextPressureStep(pct) {
|
|
63
|
+
if (pct >= PRESSURE_DANGER)
|
|
64
|
+
return 'error';
|
|
65
|
+
if (pct >= PRESSURE_WARN)
|
|
66
|
+
return 'warning';
|
|
67
|
+
return undefined;
|
|
81
68
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
69
|
+
// --- Context bar (pi-nano-context) ---
|
|
70
|
+
/**
|
|
71
|
+
* The bar's right-aligned usage readout, longest form first: token counts and
|
|
72
|
+
* percent (`13k/64k 19.5%`), then the percent alone once the free segment is
|
|
73
|
+
* too narrow to carry the counts.
|
|
74
|
+
* @param usedTokens - Total context tokens in use.
|
|
75
|
+
* @param contextWindow - The context window size in tokens.
|
|
76
|
+
* @returns The readout ladder, widest option first.
|
|
77
|
+
*/
|
|
78
|
+
export function contextBarReadout(usedTokens, contextWindow) {
|
|
79
|
+
const percent = `${((usedTokens / contextWindow) * 100).toFixed(1)}%`;
|
|
80
|
+
return [`${formatTokens(usedTokens)}/${formatTokens(contextWindow)} ${percent}`, percent];
|
|
89
81
|
}
|
|
82
|
+
// --- Context bar (pi-nano-context) ---
|
|
90
83
|
/**
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
* (ContextBarView) so both render pixel-identical content.
|
|
84
|
+
* Blank-pad to `width` cells and right-align the first readout option that
|
|
85
|
+
* fits. Shared by the ANSI string path (renderContextBar) and the hoverable
|
|
86
|
+
* JSX bar (ContextBarView) so both render identical readouts.
|
|
95
87
|
*/
|
|
96
|
-
export function
|
|
88
|
+
export function rightAlignBarText(options, width) {
|
|
97
89
|
if (width <= 0)
|
|
98
90
|
return '';
|
|
99
91
|
const content = Array.from({ length: width }, () => ' ');
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
const start = width - plainWidth(rightText);
|
|
106
|
-
for (const [offset, char] of Array.from(rightText).entries()) {
|
|
92
|
+
for (const option of options) {
|
|
93
|
+
const start = width - plainWidth(option);
|
|
94
|
+
if (start < 0)
|
|
95
|
+
continue;
|
|
96
|
+
for (const [offset, char] of Array.from(option).entries()) {
|
|
107
97
|
content[start + offset] = char;
|
|
108
98
|
}
|
|
109
|
-
|
|
110
|
-
if (label) {
|
|
111
|
-
for (const [offset, char] of Array.from(label).entries()) {
|
|
112
|
-
content[labelStart + offset] = char;
|
|
113
|
-
}
|
|
99
|
+
break;
|
|
114
100
|
}
|
|
115
101
|
return content.join('');
|
|
116
102
|
}
|
|
117
|
-
|
|
103
|
+
/** A used segment's fill: background color only, no text. Letters inside the
|
|
104
|
+
* bar read as noise (community feedback) and never fit the narrow segments
|
|
105
|
+
* anyway — the pointer names a color now (contextBarBreakdown). */
|
|
106
|
+
function renderUsedSegment(color, width) {
|
|
118
107
|
if (width <= 0)
|
|
119
108
|
return '';
|
|
120
|
-
return background(
|
|
109
|
+
return background(color, ' '.repeat(width));
|
|
110
|
+
}
|
|
111
|
+
function renderFreeSegment(options, width, fill, style) {
|
|
112
|
+
if (width <= 0)
|
|
113
|
+
return '';
|
|
114
|
+
return background(fill, style(rightAlignBarText(options, width)));
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* The context bar's hover breakdown — the legend the bar no longer carries
|
|
118
|
+
* itself. One entry per segment the bar actually paints (a zero-token content
|
|
119
|
+
* type gets no columns, so it gets no entry either), in bar order, free last.
|
|
120
|
+
*
|
|
121
|
+
* `columns` picks the label form: readable names with a ` · ` separator while
|
|
122
|
+
* the line fits, then the short forms, then a bare space separator (the color
|
|
123
|
+
* chip already separates the entries). The caller renders each entry as
|
|
124
|
+
* `chip + space + label`, which is what the fit test measures.
|
|
125
|
+
*
|
|
126
|
+
* @param segments - Used tokens per content type.
|
|
127
|
+
* @param usedTokens - Total used tokens; the remainder is the free entry.
|
|
128
|
+
* @param contextWindow - The context window size in tokens.
|
|
129
|
+
* @param columns - Terminal width; the footer's own padding is subtracted here.
|
|
130
|
+
* @param freeFill - The free segment's fill color (callers pass a theme override).
|
|
131
|
+
* @returns The breakdown entries and the separator to join them with.
|
|
132
|
+
*/
|
|
133
|
+
export function contextBarBreakdown(segments, usedTokens, contextWindow, columns, freeFill = FREE_SEGMENT_FILL) {
|
|
134
|
+
if (contextWindow <= 0)
|
|
135
|
+
return { entries: [], separator: ' · ' };
|
|
136
|
+
const freeTokens = Math.max(0, contextWindow - usedTokens);
|
|
137
|
+
const raw = [];
|
|
138
|
+
for (const segment of USED_SEGMENTS) {
|
|
139
|
+
const tokens = segments[segment.key];
|
|
140
|
+
if (tokens > 0) {
|
|
141
|
+
raw.push({ key: segment.key, tokens, color: segment.color, labels: segment.labels });
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
if (freeTokens > 0)
|
|
145
|
+
raw.push({ key: 'free', tokens: freeTokens, color: freeFill, labels: ['free'] });
|
|
146
|
+
if (raw.length === 0)
|
|
147
|
+
return { entries: [], separator: ' · ' };
|
|
148
|
+
// Footer padding (1 cell each side) plus slack for the trajectory wake that
|
|
149
|
+
// shares this row: a breakdown one cell too long would truncate its tail.
|
|
150
|
+
const budget = columns - 6;
|
|
151
|
+
// Widest form first; the last rung wins when nothing fits (the row then
|
|
152
|
+
// truncates like every other hover detail).
|
|
153
|
+
const rungs = [
|
|
154
|
+
{ separator: ' · ', labelIndex: 0 },
|
|
155
|
+
{ separator: ' ', labelIndex: 0 },
|
|
156
|
+
{ separator: ' ', labelIndex: 1 },
|
|
157
|
+
];
|
|
158
|
+
let rung = { separator: ' ', labelIndex: 1 };
|
|
159
|
+
for (const candidate of rungs) {
|
|
160
|
+
rung = candidate;
|
|
161
|
+
const labels = raw.map(entry => breakdownLabel(entry, candidate.labelIndex));
|
|
162
|
+
// One chip cell per entry prefixes each label on the supplemental row.
|
|
163
|
+
// Measured in terminal cells with the renderer's own helper, not UTF-16
|
|
164
|
+
// units: the ` · ` separator is East-Asian ambiguous, and the label set is
|
|
165
|
+
// free to gain non-ASCII names later.
|
|
166
|
+
const rendered = labels.reduce((sum, label) => sum + stringWidth(label), 0)
|
|
167
|
+
+ labels.length
|
|
168
|
+
+ stringWidth(candidate.separator) * (labels.length - 1);
|
|
169
|
+
if (rendered <= budget)
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
return {
|
|
173
|
+
entries: raw.map(entry => ({
|
|
174
|
+
key: entry.key,
|
|
175
|
+
color: entry.color,
|
|
176
|
+
label: breakdownLabel(entry, rung.labelIndex),
|
|
177
|
+
})),
|
|
178
|
+
separator: rung.separator,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
function breakdownLabel(entry, labelIndex) {
|
|
182
|
+
const name = entry.labels[labelIndex] ?? entry.labels[0] ?? entry.key;
|
|
183
|
+
return `${name} ${formatTokens(entry.tokens)}`;
|
|
121
184
|
}
|
|
122
185
|
/** Largest-remainder column allocation (pi-nano-context). */
|
|
123
186
|
function allocateProportionally(values, columns) {
|
|
@@ -157,9 +220,11 @@ export function allocateBarColumns(values, width) {
|
|
|
157
220
|
return minimumColumns.map((minimum, index) => minimum + (remainingColumns[index] ?? 0));
|
|
158
221
|
}
|
|
159
222
|
/**
|
|
160
|
-
* The segmented context bar: used segments by content type, the
|
|
161
|
-
* a light free segment whose right edge carries the usage
|
|
162
|
-
* (`
|
|
223
|
+
* The segmented context bar: used segments by content type, then the
|
|
224
|
+
* remainder as a light free segment whose right edge carries the usage
|
|
225
|
+
* readout (`13k/64k 19.5%`). No other text — the bar is read by color, and
|
|
226
|
+
* the pointer supplies the names and numbers (contextBarBreakdown). The
|
|
227
|
+
* readout tints amber / red as the context fills (contextPressureStep).
|
|
163
228
|
* @param segments - Used tokens per content type.
|
|
164
229
|
* @param usedTokens - Total used tokens, driving the usage readout.
|
|
165
230
|
* @param contextWindow - The context window size in tokens.
|
|
@@ -172,25 +237,25 @@ export function renderContextBar(segments, usedTokens, contextWindow, width, col
|
|
|
172
237
|
const freeTokens = Math.max(0, contextWindow - usedTokens);
|
|
173
238
|
const values = [...USED_SEGMENTS.map(segment => segments[segment.key]), freeTokens];
|
|
174
239
|
const columns = allocateBarColumns(values, width);
|
|
175
|
-
const used = USED_SEGMENTS.map((segment, index) => renderUsedSegment(segment.
|
|
240
|
+
const used = USED_SEGMENTS.map((segment, index) => renderUsedSegment(segment.color, columns[index] ?? 0)).join('');
|
|
176
241
|
const freeWidth = columns[USED_SEGMENTS.length] ?? 0;
|
|
177
|
-
const
|
|
178
|
-
const
|
|
179
|
-
|
|
180
|
-
|
|
242
|
+
const pct = (usedTokens / contextWindow) * 100;
|
|
243
|
+
const step = contextPressureStep(pct);
|
|
244
|
+
// Same two-path convention as the free-segment colors: the JSX bar tints
|
|
245
|
+
// through the theme key, this string path through the raw ANSI twin.
|
|
246
|
+
const style = step === undefined
|
|
247
|
+
? (text) => foreground(colors?.freeText ?? FREE_SEGMENT_TEXT, text)
|
|
248
|
+
: (text) => pressureColor(pct, text);
|
|
249
|
+
return `${used}${renderFreeSegment(contextBarReadout(usedTokens, contextWindow), freeWidth, colors?.freeFill ?? FREE_SEGMENT_FILL, style)}`;
|
|
181
250
|
}
|
|
182
251
|
// --- Mini context bar (footer ctx-field hover) ---
|
|
183
|
-
/** Context-pressure thresholds, shared with contextPressurePct's amber/red
|
|
184
|
-
* footer convention (amber ≥ 80%, red ≥ 95%). */
|
|
185
|
-
const PRESSURE_WARN = 80;
|
|
186
|
-
const PRESSURE_DANGER = 95;
|
|
187
252
|
/** Pressure-colored text: green below 80%, amber ≥ 80, red ≥ 95.
|
|
188
253
|
* @param pct - Context occupancy percent (0–100+).
|
|
189
254
|
* @param text - Text to color.
|
|
190
255
|
* @returns The ANSI 24-bit color-wrapped text.
|
|
191
256
|
*/
|
|
192
257
|
export function pressureColor(pct, text) {
|
|
193
|
-
const key = pct
|
|
258
|
+
const key = contextPressureStep(pct) ?? 'success';
|
|
194
259
|
return `\x1b[38;2;${colorHex(key)}m${text}\x1b[39m`;
|
|
195
260
|
}
|
|
196
261
|
/** Compact 1/8-cell context gauge for the footer's ctx field on hover:
|