@oh-my-pi/pi-tui 18.4.2 → 18.4.4
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 +56 -0
- package/dist/types/apps/cleanse-board.d.ts +17 -0
- package/dist/types/apps/git/help.d.ts +16 -0
- package/dist/types/apps/git/sidebar.d.ts +18 -0
- package/dist/types/apps/live-visualizer.d.ts +10 -0
- package/dist/types/apps/ps-top.d.ts +15 -0
- package/dist/types/apps/standalone-picker.d.ts +3 -0
- package/dist/types/autocomplete.d.ts +8 -0
- package/dist/types/chat/advisor-message.d.ts +12 -2
- package/dist/types/chat/assistant-message.d.ts +20 -0
- package/dist/types/chat/bash-execution.d.ts +9 -0
- package/dist/types/chat/chat-transcript-builder.d.ts +6 -1
- package/dist/types/chat/collab-prompt-message.d.ts +4 -0
- package/dist/types/chat/compaction-summary-message.d.ts +15 -0
- package/dist/types/chat/eval-execution.d.ts +8 -0
- package/dist/types/chat/execution-shared.d.ts +58 -0
- package/dist/types/chat/late-diagnostics-message.d.ts +15 -1
- package/dist/types/chat/read-tool-group.d.ts +15 -0
- package/dist/types/chat/skill-message.d.ts +9 -0
- package/dist/types/chat/stripped-tool-calls-placeholder.d.ts +3 -0
- package/dist/types/chat/thinking-display.d.ts +2 -0
- package/dist/types/chat/todo-reminder.d.ts +4 -1
- package/dist/types/chat/tool-execution.d.ts +25 -1
- package/dist/types/chat/transcript-actions.d.ts +37 -0
- package/dist/types/chat/transcript-entry.d.ts +3 -1
- package/dist/types/chat/user-message.d.ts +25 -0
- package/dist/types/chrome/collab-qrcode.d.ts +7 -0
- package/dist/types/chrome/context-thresholds.d.ts +11 -0
- package/dist/types/chrome/countdown-timer.d.ts +9 -3
- package/dist/types/chrome/diff.d.ts +12 -0
- package/dist/types/chrome/dynamic-border.d.ts +3 -0
- package/dist/types/chrome/format.d.ts +6 -0
- package/dist/types/chrome/message-divider.d.ts +13 -0
- package/dist/types/chrome/message-frame.d.ts +10 -0
- package/dist/types/chrome/message-notice.d.ts +23 -0
- package/dist/types/chrome/overlay-box.d.ts +11 -2
- package/dist/types/chrome/qrcode.d.ts +7 -0
- package/dist/types/chrome/segment-track.d.ts +7 -0
- package/dist/types/chrome/selector-helpers.d.ts +0 -6
- package/dist/types/chrome/status-notice.d.ts +14 -0
- package/dist/types/chrome/streaming-panel.d.ts +3 -0
- package/dist/types/chrome/tool-activity.d.ts +3 -0
- package/dist/types/chrome/transcript-container.d.ts +13 -0
- package/dist/types/chrome/transcript-status.d.ts +15 -3
- package/dist/types/components/box.d.ts +7 -0
- package/dist/types/components/disclosure.d.ts +9 -0
- package/dist/types/components/editor.d.ts +42 -0
- package/dist/types/components/form.d.ts +14 -0
- package/dist/types/components/image.d.ts +7 -0
- package/dist/types/components/input.d.ts +20 -1
- package/dist/types/components/key-value-list.d.ts +3 -0
- package/dist/types/components/layout/geometry.d.ts +16 -0
- package/dist/types/components/layout/row.d.ts +7 -0
- package/dist/types/components/layout/split-pane.d.ts +8 -0
- package/dist/types/components/layout/stack.d.ts +6 -0
- package/dist/types/components/loader.d.ts +48 -0
- package/dist/types/components/markdown.d.ts +8 -0
- package/dist/types/components/metric.d.ts +7 -0
- package/dist/types/components/progress-bar.d.ts +3 -0
- package/dist/types/components/scroll-view.d.ts +6 -0
- package/dist/types/components/section.d.ts +3 -0
- package/dist/types/components/select-list.d.ts +33 -0
- package/dist/types/components/settings-list.d.ts +68 -0
- package/dist/types/components/spacer.d.ts +4 -0
- package/dist/types/components/tab-bar.d.ts +9 -0
- package/dist/types/components/table.d.ts +7 -0
- package/dist/types/components/text.d.ts +6 -0
- package/dist/types/components/tree-view.d.ts +9 -0
- package/dist/types/components/truncated-text.d.ts +3 -0
- package/dist/types/components/wizard-step.d.ts +6 -0
- package/dist/types/hotkeys-markdown.d.ts +24 -0
- package/dist/types/index.d.ts +5 -0
- package/dist/types/latex-block.d.ts +7 -0
- package/dist/types/loop-watchdog.d.ts +9 -0
- package/dist/types/native/apply.d.ts +39 -0
- package/dist/types/native/backend.d.ts +94 -0
- package/dist/types/native/blobs.d.ts +29 -0
- package/dist/types/native/describe.d.ts +56 -0
- package/dist/types/native/encode.d.ts +50 -0
- package/dist/types/native/icons.d.ts +18 -0
- package/dist/types/native/memo.d.ts +27 -0
- package/dist/types/native/node.d.ts +107 -0
- package/dist/types/native/overlay.d.ts +64 -0
- package/dist/types/native/picker.d.ts +124 -0
- package/dist/types/native/reconcile.d.ts +41 -0
- package/dist/types/native/settle.d.ts +11 -0
- package/dist/types/native/spans.d.ts +21 -0
- package/dist/types/native/state.d.ts +6 -0
- package/dist/types/native/tone.d.ts +24 -0
- package/dist/types/overlays/advisor-config.d.ts +20 -0
- package/dist/types/overlays/agent-hub-renderer.d.ts +27 -11
- package/dist/types/overlays/agent-hub.d.ts +10 -0
- package/dist/types/overlays/agent-transcript-viewer.d.ts +7 -0
- package/dist/types/overlays/agents-hub.d.ts +8 -0
- package/dist/types/overlays/annotation-overlay.d.ts +3 -0
- package/dist/types/overlays/ask-dialog.d.ts +23 -0
- package/dist/types/overlays/bordered-loader.d.ts +3 -0
- package/dist/types/overlays/btw-history-panel.d.ts +9 -0
- package/dist/types/overlays/btw-panel.d.ts +3 -0
- package/dist/types/overlays/cleanse-panel.d.ts +5 -2
- package/dist/types/overlays/composer-shape-preview.d.ts +7 -1
- package/dist/types/overlays/copy-selector.d.ts +38 -0
- package/dist/types/overlays/error-banner.d.ts +9 -0
- package/dist/types/overlays/extensions/extension-list.d.ts +1 -0
- package/dist/types/overlays/history-search.d.ts +18 -0
- package/dist/types/overlays/hook-editor.d.ts +28 -1
- package/dist/types/overlays/hook-input.d.ts +3 -0
- package/dist/types/overlays/hook-selector.d.ts +10 -0
- package/dist/types/overlays/hub-frame.d.ts +18 -0
- package/dist/types/overlays/jobs-panel.d.ts +24 -0
- package/dist/types/overlays/login-dialog.d.ts +10 -0
- package/dist/types/overlays/logout-account-selector.d.ts +4 -0
- package/dist/types/overlays/mcp-add-wizard.d.ts +4 -0
- package/dist/types/overlays/model-browser.d.ts +69 -0
- package/dist/types/overlays/model-hub.d.ts +7 -0
- package/dist/types/overlays/model-picker.d.ts +9 -1
- package/dist/types/overlays/move-overlay.d.ts +3 -0
- package/dist/types/overlays/oauth-selector.d.ts +4 -0
- package/dist/types/overlays/omfg-panel.d.ts +3 -0
- package/dist/types/overlays/pause-screen.d.ts +12 -0
- package/dist/types/overlays/plan-review-overlay.d.ts +5 -0
- package/dist/types/overlays/plan-save-overlay.d.ts +10 -0
- package/dist/types/overlays/plugin-selector.d.ts +4 -0
- package/dist/types/overlays/plugin-settings.d.ts +30 -0
- package/dist/types/overlays/queue-mode-selector.d.ts +4 -0
- package/dist/types/overlays/reset-usage-selector.d.ts +8 -0
- package/dist/types/overlays/rewind-selector.d.ts +5 -0
- package/dist/types/overlays/session-info-overlay.d.ts +19 -1
- package/dist/types/overlays/session-selector.d.ts +64 -1
- package/dist/types/overlays/settings-defs.d.ts +14 -0
- package/dist/types/overlays/settings-selector.d.ts +21 -0
- package/dist/types/overlays/show-images-selector.d.ts +4 -0
- package/dist/types/overlays/snapcompact-shape-preview.d.ts +2 -0
- package/dist/types/overlays/stats-notice.d.ts +8 -0
- package/dist/types/overlays/theme-selector.d.ts +11 -0
- package/dist/types/overlays/thinking-selector.d.ts +4 -0
- package/dist/types/overlays/tree-selector.d.ts +60 -2
- package/dist/types/overlays/usage-dashboard.d.ts +26 -3
- package/dist/types/overlays/usage-row.d.ts +36 -2
- package/dist/types/prompt/attachment-chips.d.ts +8 -0
- package/dist/types/prompt/composer-attachments.d.ts +15 -3
- package/dist/types/prompt/composer.d.ts +18 -1
- package/dist/types/prompt/custom-editor.d.ts +48 -2
- package/dist/types/prompt/editor-top-gap.d.ts +2 -0
- package/dist/types/prompt/macos-spelling.d.ts +6 -0
- package/dist/types/prompt/magic-keywords.d.ts +9 -0
- package/dist/types/prompt/queued-messages.d.ts +23 -0
- package/dist/types/prompt/skill-tokens.d.ts +4 -2
- package/dist/types/prompt/welcome.d.ts +16 -0
- package/dist/types/render/code-cell.d.ts +10 -0
- package/dist/types/render/file-list.d.ts +8 -0
- package/dist/types/render/hyperlink.d.ts +14 -0
- package/dist/types/render/output-block.d.ts +43 -0
- package/dist/types/render/output-pane.d.ts +17 -0
- package/dist/types/render/tool-card.d.ts +8 -0
- package/dist/types/render/tree-list.d.ts +8 -0
- package/dist/types/render/width-aware-text.d.ts +7 -0
- package/dist/types/setup/scenes/outro.d.ts +7 -0
- package/dist/types/setup/scenes/sign-in.d.ts +16 -9
- package/dist/types/setup/scenes/splash.d.ts +8 -0
- package/dist/types/setup/scenes/types.d.ts +6 -27
- package/dist/types/setup/wizard-overlay.d.ts +9 -0
- package/dist/types/status-line/component.d.ts +49 -5
- package/dist/types/status-line/context-usage.d.ts +20 -0
- package/dist/types/status-line/footer.d.ts +8 -0
- package/dist/types/status-line/metrics.d.ts +1 -2
- package/dist/types/status-line/segments.d.ts +10 -1
- package/dist/types/status-line/types.d.ts +40 -1
- package/dist/types/stdin-buffer.d.ts +10 -7
- package/dist/types/terminal-capabilities.d.ts +1 -1
- package/dist/types/terminal.d.ts +36 -0
- package/dist/types/theme/defaults/index.d.ts +100 -0
- package/dist/types/theme/schema.d.ts +2 -0
- package/dist/types/theme/shimmer.d.ts +8 -0
- package/dist/types/theme/theme-class.d.ts +2 -0
- package/dist/types/theme/theme.d.ts +33 -1
- package/dist/types/tools/ask.d.ts +3 -0
- package/dist/types/tools/ast-edit.d.ts +10 -1
- package/dist/types/tools/ast-grep.d.ts +10 -1
- package/dist/types/tools/autoresearch.d.ts +21 -4
- package/dist/types/tools/bash.d.ts +30 -1
- package/dist/types/tools/cfg-render.d.ts +5 -1
- package/dist/types/tools/debug.d.ts +3 -1
- package/dist/types/tools/default-renderer.d.ts +6 -1
- package/dist/types/tools/edit.d.ts +14 -1
- package/dist/types/tools/eval.d.ts +12 -1
- package/dist/types/tools/fetch.d.ts +16 -1
- package/dist/types/tools/find.d.ts +15 -1
- package/dist/types/tools/github.d.ts +3 -0
- package/dist/types/tools/glob.d.ts +14 -1
- package/dist/types/tools/goal.d.ts +3 -1
- package/dist/types/tools/grep.d.ts +14 -1
- package/dist/types/tools/grouped-file-output.d.ts +58 -0
- package/dist/types/tools/json-tree.d.ts +20 -0
- package/dist/types/tools/lsp.d.ts +3 -10
- package/dist/types/tools/mcp.d.ts +16 -1
- package/dist/types/tools/memory.d.ts +26 -0
- package/dist/types/tools/native-view.d.ts +90 -0
- package/dist/types/tools/proc-render.d.ts +7 -1
- package/dist/types/tools/read.d.ts +23 -1
- package/dist/types/tools/renderer.d.ts +57 -0
- package/dist/types/tools/report-tool-issue.d.ts +3 -0
- package/dist/types/tools/resolve.d.ts +14 -1
- package/dist/types/tools/task.d.ts +3 -0
- package/dist/types/tools/think.d.ts +3 -0
- package/dist/types/tools/todo.d.ts +12 -0
- package/dist/types/tools/vibe.d.ts +3 -1
- package/dist/types/tools/wait.d.ts +3 -0
- package/dist/types/tools/web-search-types.d.ts +90 -0
- package/dist/types/tools/web-search.d.ts +9 -168
- package/dist/types/tools/write.d.ts +14 -1
- package/dist/types/tools/xdev.d.ts +13 -0
- package/dist/types/tui.d.ts +49 -0
- package/package.json +9 -9
- package/src/apps/autoresearch-dashboard.ts +362 -4
- package/src/apps/cleanse-board.ts +219 -1
- package/src/apps/cleanse-picker.ts +9 -1
- package/src/apps/debug/log-viewer.ts +15 -15
- package/src/apps/debug/protocol-probe.ts +2 -20
- package/src/apps/git/avatar.ts +2 -22
- package/src/apps/git/git-tui.ts +406 -59
- package/src/apps/git/help.ts +130 -0
- package/src/apps/git/sidebar.ts +491 -0
- package/src/apps/live-visualizer.ts +65 -0
- package/src/apps/ps-top.ts +448 -17
- package/src/apps/session-picker.ts +1 -0
- package/src/apps/setup-model-picker.ts +5 -1
- package/src/apps/standalone-picker.ts +55 -1
- package/src/autocomplete.ts +44 -1
- package/src/chat/advisor-message.ts +72 -3
- package/src/chat/assistant-message.ts +538 -51
- package/src/chat/background-tan-message.ts +4 -4
- package/src/chat/bash-execution.ts +81 -0
- package/src/chat/cache-invalidation-marker.ts +8 -0
- package/src/chat/chat-transcript-builder.ts +37 -6
- package/src/chat/collab-prompt-message.ts +15 -3
- package/src/chat/compaction-summary-message.ts +74 -1
- package/src/chat/custom-message.ts +3 -1
- package/src/chat/eval-execution.ts +57 -0
- package/src/chat/execution-shared.ts +153 -1
- package/src/chat/hook-message.ts +1 -0
- package/src/chat/late-diagnostics-message.ts +85 -2
- package/src/chat/read-tool-group.ts +341 -1
- package/src/chat/served-model-marker.ts +8 -0
- package/src/chat/skill-message.ts +63 -0
- package/src/chat/stripped-tool-calls-placeholder.ts +18 -10
- package/src/chat/thinking-display.ts +73 -11
- package/src/chat/todo-reminder.ts +34 -4
- package/src/chat/tool-execution.ts +385 -10
- package/src/chat/transcript-actions.ts +38 -0
- package/src/chat/transcript-browser.ts +2 -9
- package/src/chat/transcript-entry.ts +10 -1
- package/src/chat/transcript-render-helpers.ts +16 -15
- package/src/chat/ttsr-notification.ts +38 -1
- package/src/chat/user-message.ts +167 -4
- package/src/chrome/collab-qrcode.ts +27 -2
- package/src/chrome/context-thresholds.ts +34 -0
- package/src/chrome/countdown-timer.ts +24 -1
- package/src/chrome/diff.ts +60 -1
- package/src/chrome/dynamic-border.ts +7 -0
- package/src/chrome/format.ts +12 -0
- package/src/chrome/message-divider.ts +33 -1
- package/src/chrome/message-frame.ts +53 -10
- package/src/chrome/message-notice.ts +71 -0
- package/src/chrome/overlay-box.ts +45 -2
- package/src/chrome/qrcode.ts +28 -0
- package/src/chrome/segment-track.ts +26 -0
- package/src/chrome/selector-helpers.ts +1 -17
- package/src/chrome/status-notice.ts +46 -0
- package/src/chrome/streaming-panel.ts +25 -1
- package/src/chrome/tool-activity.ts +20 -0
- package/src/chrome/transcript-container.ts +139 -17
- package/src/chrome/transcript-status.ts +40 -11
- package/src/components/box.ts +51 -0
- package/src/components/disclosure.ts +33 -0
- package/src/components/editor.ts +239 -36
- package/src/components/form.ts +93 -9
- package/src/components/image.ts +30 -0
- package/src/components/input.ts +77 -26
- package/src/components/key-value-list.ts +18 -0
- package/src/components/layout/geometry.ts +44 -0
- package/src/components/layout/row.ts +30 -0
- package/src/components/layout/split-pane.ts +65 -0
- package/src/components/layout/stack.ts +27 -0
- package/src/components/loader.ts +183 -1
- package/src/components/markdown.ts +131 -56
- package/src/components/metric.ts +39 -0
- package/src/components/progress-bar.ts +28 -0
- package/src/components/scroll-view.ts +23 -0
- package/src/components/section.ts +16 -0
- package/src/components/select-list.ts +174 -25
- package/src/components/settings-list.ts +264 -7
- package/src/components/spacer.ts +17 -0
- package/src/components/tab-bar.ts +42 -0
- package/src/components/table.ts +34 -0
- package/src/components/text.ts +23 -0
- package/src/components/tree-view.ts +65 -0
- package/src/components/truncated-text.ts +14 -0
- package/src/components/wizard-step.ts +18 -9
- package/src/debug-server.ts +14 -7
- package/src/hotkeys-markdown.ts +251 -69
- package/src/index.ts +6 -0
- package/src/latex-block.ts +11 -0
- package/src/loop-watchdog.ts +38 -10
- package/src/native/apply.ts +275 -0
- package/src/native/backend.ts +682 -0
- package/src/native/blobs.ts +70 -0
- package/src/native/describe.ts +124 -0
- package/src/native/encode.ts +233 -0
- package/src/native/icons.ts +184 -0
- package/src/native/memo.ts +64 -0
- package/src/native/node.ts +112 -0
- package/src/native/overlay.ts +131 -0
- package/src/native/picker.ts +333 -0
- package/src/native/reconcile.ts +1063 -0
- package/src/native/settle.ts +23 -0
- package/src/native/spans.ts +308 -0
- package/src/native/state.ts +28 -0
- package/src/native/tone.ts +79 -0
- package/src/overlays/advisor-config.ts +420 -0
- package/src/overlays/agent-hub-renderer.ts +155 -18
- package/src/overlays/agent-hub.ts +1037 -44
- package/src/overlays/agent-transcript-viewer.ts +129 -0
- package/src/overlays/agents-hub.ts +688 -53
- package/src/overlays/annotation-overlay.ts +391 -52
- package/src/overlays/ask-dialog.ts +435 -24
- package/src/overlays/bordered-loader.ts +17 -0
- package/src/overlays/btw-history-panel.ts +182 -0
- package/src/overlays/btw-panel.ts +68 -2
- package/src/overlays/cleanse-panel.ts +105 -15
- package/src/overlays/codex-reset-fireworks.ts +65 -5
- package/src/overlays/composer-shape-preview.ts +26 -1
- package/src/overlays/copy-selector.ts +545 -23
- package/src/overlays/error-banner.ts +44 -0
- package/src/overlays/extensions/extension-list.ts +23 -17
- package/src/overlays/history-search.ts +175 -33
- package/src/overlays/hook-editor.ts +113 -6
- package/src/overlays/hook-input.ts +22 -1
- package/src/overlays/hook-selector.ts +311 -22
- package/src/overlays/hub-frame.ts +80 -0
- package/src/overlays/jobs-panel.ts +112 -0
- package/src/overlays/login-dialog.ts +183 -1
- package/src/overlays/logout-account-selector.ts +102 -4
- package/src/overlays/mcp-add-wizard.ts +232 -53
- package/src/overlays/model-browser.ts +681 -22
- package/src/overlays/model-hub.ts +1402 -78
- package/src/overlays/model-picker.ts +184 -7
- package/src/overlays/move-overlay.ts +45 -0
- package/src/overlays/oauth-selector.ts +227 -23
- package/src/overlays/omfg-panel.ts +86 -2
- package/src/overlays/pause-screen.ts +55 -4
- package/src/overlays/plan-review-overlay.ts +352 -0
- package/src/overlays/plan-save-overlay.ts +45 -0
- package/src/overlays/plugin-selector.ts +21 -1
- package/src/overlays/plugin-settings.ts +195 -5
- package/src/overlays/queue-mode-selector.ts +19 -1
- package/src/overlays/reset-usage-selector.ts +110 -40
- package/src/overlays/rewind-selector.ts +458 -44
- package/src/overlays/session-account-selector.ts +1 -1
- package/src/overlays/session-info-overlay.ts +137 -1
- package/src/overlays/session-selector.ts +807 -46
- package/src/overlays/settings-defs.ts +41 -0
- package/src/overlays/settings-selector.ts +585 -33
- package/src/overlays/show-images-selector.ts +18 -1
- package/src/overlays/snapcompact-shape-preview.ts +48 -3
- package/src/overlays/stats-notice.ts +31 -0
- package/src/overlays/theme-selector.ts +31 -1
- package/src/overlays/thinking-selector.ts +22 -1
- package/src/overlays/tree-selector.ts +570 -110
- package/src/overlays/usage-dashboard.ts +587 -12
- package/src/overlays/usage-row.ts +161 -25
- package/src/prompt/attachment-chips.ts +60 -0
- package/src/prompt/composer-attachments.ts +31 -9
- package/src/prompt/composer.ts +55 -2
- package/src/prompt/custom-editor.ts +302 -4
- package/src/prompt/editor-top-gap.ts +8 -0
- package/src/prompt/macos-spelling.ts +24 -9
- package/src/prompt/magic-keywords.ts +22 -1
- package/src/prompt/model-mention-autocomplete.ts +1 -0
- package/src/prompt/queued-messages.ts +101 -0
- package/src/prompt/skill-tokens.ts +5 -3
- package/src/prompt/welcome.ts +213 -6
- package/src/render/code-cell.ts +103 -2
- package/src/render/file-list.ts +29 -0
- package/src/render/hyperlink.ts +32 -0
- package/src/render/output-block.ts +110 -0
- package/src/render/output-pane.ts +37 -0
- package/src/render/tool-card.ts +137 -0
- package/src/render/tree-list.ts +39 -0
- package/src/render/width-aware-text.ts +20 -0
- package/src/setup/scenes/composer.ts +29 -0
- package/src/setup/scenes/glyph.ts +15 -0
- package/src/setup/scenes/model.ts +36 -8
- package/src/setup/scenes/outro.ts +28 -1
- package/src/setup/scenes/sign-in.ts +138 -24
- package/src/setup/scenes/splash.ts +28 -1
- package/src/setup/scenes/theme.ts +88 -3
- package/src/setup/scenes/types.ts +7 -28
- package/src/setup/startup-splash.ts +46 -9
- package/src/setup/wizard-overlay.ts +140 -23
- package/src/setup/wizard.ts +1 -1
- package/src/status-line/component.ts +414 -18
- package/src/status-line/context-usage.ts +279 -50
- package/src/status-line/footer.ts +94 -1
- package/src/status-line/metrics.ts +3 -6
- package/src/status-line/segments.ts +463 -97
- package/src/status-line/separators.ts +3 -16
- package/src/status-line/types.ts +43 -1
- package/src/stdin-buffer.ts +27 -21
- package/src/terminal-capabilities.ts +7 -0
- package/src/terminal.ts +153 -11
- package/src/theme/defaults/dark-neon-noir.json +100 -0
- package/src/theme/defaults/index.ts +2 -0
- package/src/theme/defaults/light-prism.json +5 -5
- package/src/theme/defaults/onyx.json +62 -62
- package/src/theme/mermaid-cache.ts +10 -1
- package/src/theme/schema.ts +15 -0
- package/src/theme/shimmer.ts +33 -0
- package/src/theme/theme-class.ts +24 -19
- package/src/theme/theme.ts +111 -9
- package/src/theme/tui-adapters.ts +14 -1
- package/src/tools/ask.ts +157 -1
- package/src/tools/ast-edit.ts +174 -1
- package/src/tools/ast-grep.ts +82 -2
- package/src/tools/autoresearch.ts +149 -1
- package/src/tools/bash.ts +233 -62
- package/src/tools/cfg-render.ts +66 -1
- package/src/tools/debug.ts +78 -1
- package/src/tools/default-renderer.ts +35 -1
- package/src/tools/edit.ts +357 -31
- package/src/tools/eval.ts +257 -1
- package/src/tools/fetch.ts +102 -3
- package/src/tools/find.ts +127 -1
- package/src/tools/github.ts +119 -10
- package/src/tools/glob.ts +116 -1
- package/src/tools/goal.ts +56 -1
- package/src/tools/grep.ts +83 -2
- package/src/tools/grouped-file-output.ts +205 -0
- package/src/tools/json-tree.ts +116 -1
- package/src/tools/lsp.ts +318 -1
- package/src/tools/mcp.ts +80 -1
- package/src/tools/memory.ts +93 -11
- package/src/tools/native-view.ts +292 -0
- package/src/tools/proc-render.ts +221 -1
- package/src/tools/read.ts +199 -4
- package/src/tools/renderer.ts +61 -0
- package/src/tools/report-tool-issue.ts +7 -1
- package/src/tools/resolve.ts +63 -1
- package/src/tools/streaming-output.ts +79 -16
- package/src/tools/task.ts +406 -4
- package/src/tools/think.ts +19 -1
- package/src/tools/todo.ts +89 -0
- package/src/tools/vibe.ts +230 -2
- package/src/tools/wait.ts +181 -1
- package/src/tools/web-search-types.ts +99 -0
- package/src/tools/web-search.ts +111 -170
- package/src/tools/write.ts +144 -5
- package/src/tools/xdev.ts +64 -1
- package/src/tui.ts +295 -4
- package/dist/types/setup/scenes/providers.d.ts +0 -3
- package/dist/types/setup/scenes/web-search.d.ts +0 -22
- package/src/setup/scenes/providers.ts +0 -110
- package/src/setup/scenes/web-search.ts +0 -165
|
@@ -18,8 +18,17 @@ import { summarizeLoopCondition } from "./loop";
|
|
|
18
18
|
import { formatMetric } from "../components/metric";
|
|
19
19
|
import { formatBillingSummary } from "./metrics";
|
|
20
20
|
import { sanitizeStatusText } from "../chrome/shared";
|
|
21
|
-
import {
|
|
22
|
-
|
|
21
|
+
import {
|
|
22
|
+
formatContextUsage,
|
|
23
|
+
getContextUsageLevel,
|
|
24
|
+
getContextUsageThemeColor,
|
|
25
|
+
getContextUsageTone,
|
|
26
|
+
} from "../chrome/context-thresholds";
|
|
27
|
+
import type { TspSpan, TspTone } from "@oh-my-pi/pi-wire";
|
|
28
|
+
import { node, span } from "../native/describe";
|
|
29
|
+
import { thinkingLevelToken } from "../theme/theme-class";
|
|
30
|
+
import type { StatusLineSession } from "./host";
|
|
31
|
+
import type { RenderedSegment, SegmentContext, SegmentView, StatusLineSegment, StatusLineSegmentId } from "./types";
|
|
23
32
|
|
|
24
33
|
export type { SegmentContext } from "./types";
|
|
25
34
|
|
|
@@ -31,6 +40,30 @@ function withIcon(icon: string, text: string): string {
|
|
|
31
40
|
return icon ? `${icon} ${text}` : text;
|
|
32
41
|
}
|
|
33
42
|
|
|
43
|
+
/** Native segment description helper. */
|
|
44
|
+
function segView(spans: readonly TspSpan[], icon?: string, tone?: TspTone): SegmentView {
|
|
45
|
+
return tone === undefined ? (icon === undefined ? { spans } : { spans, icon }) : { spans, icon, tone };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Span token for accent-role text; the session accent is a raw hue, so it travels as `accent`. */
|
|
49
|
+
function accentToken(ctx: SegmentContext, color: ThemeColor): string {
|
|
50
|
+
return sessionAccentAnsi(ctx) ? "accent" : color;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Tone of a status/usage theme color, for the segment chrome. */
|
|
54
|
+
function toneOf(color: ThemeColor): TspTone | undefined {
|
|
55
|
+
switch (color) {
|
|
56
|
+
case "warning":
|
|
57
|
+
return "warning";
|
|
58
|
+
case "error":
|
|
59
|
+
return "error";
|
|
60
|
+
case "success":
|
|
61
|
+
return "success";
|
|
62
|
+
default:
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
34
67
|
/**
|
|
35
68
|
* Hash-derived accent ANSI for the session title (or preview stand-in title).
|
|
36
69
|
* Undefined when `statusLine.sessionAccent` is off or the session is unnamed,
|
|
@@ -91,6 +124,11 @@ function singleStatSegment(
|
|
|
91
124
|
});
|
|
92
125
|
return { content: theme.fg(color, content ?? ""), visible: true };
|
|
93
126
|
},
|
|
127
|
+
describe(ctx) {
|
|
128
|
+
const value = ctx.usageStats[field];
|
|
129
|
+
if (!value) return null;
|
|
130
|
+
return segView([span(formatNumber(value), color)], iconKey);
|
|
131
|
+
},
|
|
94
132
|
};
|
|
95
133
|
}
|
|
96
134
|
|
|
@@ -187,6 +225,11 @@ const piSegment: StatusLineSegment = {
|
|
|
187
225
|
: "";
|
|
188
226
|
return { content: `${fgAnsi}${content}\x1b[39m`, visible: true };
|
|
189
227
|
},
|
|
228
|
+
describe(ctx) {
|
|
229
|
+
if (ctx.focusedAgentId) return segView([span(ctx.focusedAgentId, "warning")], "ghost", "warning");
|
|
230
|
+
// The dock's working row owns activity natively: the brand stays still.
|
|
231
|
+
return segView([], "omp", "muted");
|
|
232
|
+
},
|
|
190
233
|
};
|
|
191
234
|
/** Current braille-spinner glyph on the shared clock, at the Loader's 80ms cadence. */
|
|
192
235
|
function brandSpinnerFrame(nowMs = Date.now()): string {
|
|
@@ -216,38 +259,87 @@ const statusSegment: StatusLineSegment = {
|
|
|
216
259
|
visible: text.length > 0,
|
|
217
260
|
};
|
|
218
261
|
},
|
|
262
|
+
describe(ctx) {
|
|
263
|
+
let text = "";
|
|
264
|
+
for (const status of ctx.hookStatuses ?? []) {
|
|
265
|
+
const sanitized = sanitizeStatusText(status);
|
|
266
|
+
if (!sanitized) continue;
|
|
267
|
+
text += text ? `${theme.sep.dot}${sanitized}` : sanitized;
|
|
268
|
+
}
|
|
269
|
+
return text ? segView([span(text, accentToken(ctx, "accent"))]) : null;
|
|
270
|
+
},
|
|
219
271
|
};
|
|
220
272
|
|
|
273
|
+
/** Display name of the active model (`Claude ` prefix dropped). */
|
|
274
|
+
function modelDisplayName(ctx: SegmentContext): string {
|
|
275
|
+
const state = ctx.session.state;
|
|
276
|
+
const modelName = state.model?.name || state.model?.id || "no-model";
|
|
277
|
+
return modelName.startsWith("Claude ") ? modelName.slice(7) : modelName;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* The thinking-effort word (`high`, `off`; `auto` until auto thinking resolves
|
|
282
|
+
* a level), or undefined when the model has no thinking. Drives the native
|
|
283
|
+
* model segment and the composer's effort chip.
|
|
284
|
+
*/
|
|
285
|
+
export function thinkingLevelWord(
|
|
286
|
+
session: Pick<StatusLineSession, "state" | "isAutoThinking" | "autoResolvedThinkingLevel">,
|
|
287
|
+
): string | undefined {
|
|
288
|
+
const state = session.state;
|
|
289
|
+
if (!state.model?.thinking) return undefined;
|
|
290
|
+
if (session.isAutoThinking) return session.autoResolvedThinkingLevel() ?? "auto";
|
|
291
|
+
return state.thinkingLevel ?? ThinkingLevel.Off;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/** Thinking-level display ("◉ xhigh", "⟳ auto", …), or "" when hidden or unsupported. */
|
|
295
|
+
function modelThinkingDisplay(ctx: SegmentContext): string {
|
|
296
|
+
const state = ctx.session.state;
|
|
297
|
+
const opts = ctx.options.model ?? {};
|
|
298
|
+
if (opts.showThinkingLevel === false || !state.model?.thinking) return "";
|
|
299
|
+
if (ctx.session.isAutoThinking) {
|
|
300
|
+
// Pending (no turn classified yet / classifying) shows a symbol-theme
|
|
301
|
+
// question-box marker; once resolved it shows `<level>`.
|
|
302
|
+
const resolved = ctx.session.autoResolvedThinkingLevel();
|
|
303
|
+
return resolved
|
|
304
|
+
? (theme.thinking[resolved as keyof Theme["thinking"]] ?? resolved)
|
|
305
|
+
: `${theme.thinking.autoPending} auto`;
|
|
306
|
+
}
|
|
307
|
+
const level = state.thinkingLevel ?? ThinkingLevel.Off;
|
|
308
|
+
return level === ThinkingLevel.Off
|
|
309
|
+
? `${theme.status.disabled} off`
|
|
310
|
+
: (theme.thinking[level as keyof Theme["thinking"]] ?? level);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Advisor symbol, colored by the worst status in the roster: success = all
|
|
315
|
+
* running, warning = quota-exhausted, error = failed, dim = everything
|
|
316
|
+
* paused/no-model. Undefined when no advisor is configured.
|
|
317
|
+
*/
|
|
318
|
+
function modelAdvisorBadge(ctx: SegmentContext): { icon: string; color: ThemeColor } | undefined {
|
|
319
|
+
// Optional chaining: lightweight session doubles (test mocks) that don't
|
|
320
|
+
// implement getAdvisorStatusOverview skip the badge instead of crashing.
|
|
321
|
+
const advisorStats = ctx.session.getAdvisorStatusOverview?.();
|
|
322
|
+
if (!advisorStats?.configured || advisorStats.advisors.length === 0) return undefined;
|
|
323
|
+
const statuses = advisorStats.advisors.map(a => a.status);
|
|
324
|
+
const color: ThemeColor = statuses.includes("error")
|
|
325
|
+
? "error"
|
|
326
|
+
: statuses.includes("quota_exhausted")
|
|
327
|
+
? "warning"
|
|
328
|
+
: statuses.includes("running")
|
|
329
|
+
? "success"
|
|
330
|
+
: "dim";
|
|
331
|
+
// Closed eye once every advisor has finished reviewing the yielded
|
|
332
|
+
// turn — no more comments until a new primary turn starts.
|
|
333
|
+
const allYielded = advisorStats.advisors.every(a => a.yielded);
|
|
334
|
+
const icon = allYielded ? theme.icon.advisorClosed || theme.icon.advisor : theme.icon.advisor;
|
|
335
|
+
return icon ? { icon, color } : undefined;
|
|
336
|
+
}
|
|
337
|
+
|
|
221
338
|
const modelSegment: StatusLineSegment = {
|
|
222
339
|
id: "model",
|
|
223
340
|
render(ctx) {
|
|
224
|
-
const
|
|
225
|
-
const
|
|
226
|
-
|
|
227
|
-
let modelName = state.model?.name || state.model?.id || "no-model";
|
|
228
|
-
if (modelName.startsWith("Claude ")) {
|
|
229
|
-
modelName = modelName.slice(7);
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
// Resolve the current thinking-level display ("◉ xhigh", "⟳ auto", …)
|
|
233
|
-
// when the model supports thinking and the segment isn't hiding it.
|
|
234
|
-
let thinkingDisplay = "";
|
|
235
|
-
if (opts.showThinkingLevel !== false && state.model?.thinking) {
|
|
236
|
-
if (ctx.session.isAutoThinking) {
|
|
237
|
-
// Pending (no turn classified yet / classifying) shows a symbol-theme
|
|
238
|
-
// question-box marker; once resolved it shows `<level>`.
|
|
239
|
-
const resolved = ctx.session.autoResolvedThinkingLevel();
|
|
240
|
-
thinkingDisplay = resolved
|
|
241
|
-
? (theme.thinking[resolved as keyof Theme["thinking"]] ?? resolved)
|
|
242
|
-
: `${theme.thinking.autoPending} auto`;
|
|
243
|
-
} else {
|
|
244
|
-
const level = state.thinkingLevel ?? ThinkingLevel.Off;
|
|
245
|
-
thinkingDisplay =
|
|
246
|
-
level === ThinkingLevel.Off
|
|
247
|
-
? `${theme.status.disabled} off`
|
|
248
|
-
: (theme.thinking[level as keyof Theme["thinking"]] ?? level);
|
|
249
|
-
}
|
|
250
|
-
}
|
|
341
|
+
const modelName = modelDisplayName(ctx);
|
|
342
|
+
const thinkingDisplay = modelThinkingDisplay(ctx);
|
|
251
343
|
|
|
252
344
|
// Compact mode swaps the model icon for the thinking-level glyph and drops
|
|
253
345
|
// the " · <level>" tail, keeping the level visible as a single icon.
|
|
@@ -270,28 +362,9 @@ const modelSegment: StatusLineSegment = {
|
|
|
270
362
|
// `statusLineModel` is aliased to `accent` in many themes, so the badge
|
|
271
363
|
// uses status colors to stay visibly distinct from the model name color.
|
|
272
364
|
let content = accentFg(ctx, "statusLineModel", withIcon(modelIcon, modelName));
|
|
273
|
-
//
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
// `/advisor status`.
|
|
277
|
-
// Optional chaining: lightweight session doubles (test mocks) that don't
|
|
278
|
-
// implement getAdvisorStatusOverview skip the badge instead of crashing.
|
|
279
|
-
const advisorStats = ctx.session.getAdvisorStatusOverview?.();
|
|
280
|
-
if (advisorStats?.configured && advisorStats.advisors.length > 0) {
|
|
281
|
-
const statuses = advisorStats.advisors.map(a => a.status);
|
|
282
|
-
const badgeColor = statuses.includes("error")
|
|
283
|
-
? "error"
|
|
284
|
-
: statuses.includes("quota_exhausted")
|
|
285
|
-
? "warning"
|
|
286
|
-
: statuses.includes("running")
|
|
287
|
-
? "success"
|
|
288
|
-
: "dim";
|
|
289
|
-
// Closed eye once every advisor has finished reviewing the yielded
|
|
290
|
-
// turn — no more comments until a new primary turn starts.
|
|
291
|
-
const allYielded = advisorStats.advisors.every(a => a.yielded);
|
|
292
|
-
const advisorIcon = allYielded ? theme.icon.advisorClosed || theme.icon.advisor : theme.icon.advisor;
|
|
293
|
-
if (advisorIcon) content += theme.fg(badgeColor, ` ${advisorIcon}`);
|
|
294
|
-
}
|
|
365
|
+
// Per-advisor detail lives in `/advisor status`.
|
|
366
|
+
const advisor = modelAdvisorBadge(ctx);
|
|
367
|
+
if (advisor) content += theme.fg(advisor.color, ` ${advisor.icon}`);
|
|
295
368
|
if (tail) {
|
|
296
369
|
content += accentFg(ctx, "statusLineModel", tail);
|
|
297
370
|
}
|
|
@@ -302,6 +375,20 @@ const modelSegment: StatusLineSegment = {
|
|
|
302
375
|
if (slowModeLabel) content += theme.fg("warning", `${theme.sep.dot}${slowModeLabel}`);
|
|
303
376
|
return { content, visible: true };
|
|
304
377
|
},
|
|
378
|
+
describe(ctx) {
|
|
379
|
+
// Name, then the thinking level as a word in its level colour after a
|
|
380
|
+
// quiet separator; the terminal draws the model icon.
|
|
381
|
+
const token = accentToken(ctx, "statusLineModel");
|
|
382
|
+
const spans: TspSpan[] = [span(modelDisplayName(ctx), token)];
|
|
383
|
+
const advisor = modelAdvisorBadge(ctx);
|
|
384
|
+
if (advisor) spans.push(span(` ${advisor.icon}`, advisor.color));
|
|
385
|
+
if (ctx.session.isFastModeActive() && theme.icon.fast) spans.push(span(` ${theme.icon.fast}`, token));
|
|
386
|
+
const level = ctx.options.model?.showThinkingLevel === false ? undefined : thinkingLevelWord(ctx.session);
|
|
387
|
+
if (level !== undefined) spans.push(span(" · ", "dim"), span(level, thinkingLevelToken(level)));
|
|
388
|
+
const slowModeLabel = ctx.session.getAnthropicSlowModeLabel?.();
|
|
389
|
+
if (slowModeLabel) spans.push(span(`${theme.sep.dot}${slowModeLabel}`, "warning"));
|
|
390
|
+
return segView(spans, "model", slowModeLabel ? "warning" : undefined);
|
|
391
|
+
},
|
|
305
392
|
};
|
|
306
393
|
|
|
307
394
|
function formatGoalBudget(current: number, budget?: number): string {
|
|
@@ -310,6 +397,38 @@ function formatGoalBudget(current: number, budget?: number): string {
|
|
|
310
397
|
return `${used}/${formatNumber(budget)}`;
|
|
311
398
|
}
|
|
312
399
|
|
|
400
|
+
/** Native twin of {@link renderGoalMode}: the status names its icon instead of drawing a glyph. */
|
|
401
|
+
function describeGoalMode(ctx: SegmentContext, mode: { enabled: boolean; paused: boolean }): SegmentView {
|
|
402
|
+
const goal = ctx.session.getGoalModeState()?.goal;
|
|
403
|
+
const status = goal?.status ?? (mode.paused ? "paused" : "active");
|
|
404
|
+
let icon = "goal";
|
|
405
|
+
let color: ThemeColor = "accent";
|
|
406
|
+
switch (status) {
|
|
407
|
+
case "paused":
|
|
408
|
+
icon = "pause";
|
|
409
|
+
color = "warning";
|
|
410
|
+
break;
|
|
411
|
+
case "complete":
|
|
412
|
+
icon = "success";
|
|
413
|
+
color = "success";
|
|
414
|
+
break;
|
|
415
|
+
case "budget-limited":
|
|
416
|
+
icon = "warning";
|
|
417
|
+
color = "warning";
|
|
418
|
+
break;
|
|
419
|
+
case "dropped":
|
|
420
|
+
icon = "aborted";
|
|
421
|
+
color = "dim";
|
|
422
|
+
break;
|
|
423
|
+
default:
|
|
424
|
+
break;
|
|
425
|
+
}
|
|
426
|
+
let label = "Goal";
|
|
427
|
+
if (ctx.goalStatusInFooter === true && goal) label += ` ${formatGoalBudget(goal.tokensUsed, goal.tokenBudget)}`;
|
|
428
|
+
const token = color === "accent" ? accentToken(ctx, color) : color;
|
|
429
|
+
return segView([span(label, token)], icon, toneOf(color));
|
|
430
|
+
}
|
|
431
|
+
|
|
313
432
|
function renderGoalMode(ctx: SegmentContext, mode: { enabled: boolean; paused: boolean }): RenderedSegment {
|
|
314
433
|
const goal = ctx.session.getGoalModeState()?.goal;
|
|
315
434
|
const status = goal?.status ?? (mode.paused ? "paused" : "active");
|
|
@@ -413,6 +532,33 @@ const modeSegment: StatusLineSegment = {
|
|
|
413
532
|
|
|
414
533
|
return { content: "", visible: false };
|
|
415
534
|
},
|
|
535
|
+
describe(ctx) {
|
|
536
|
+
const plan = ctx.planMode;
|
|
537
|
+
if (plan && (plan.enabled || plan.paused)) {
|
|
538
|
+
return plan.paused
|
|
539
|
+
? segView([span("Plan (paused)", "warning")], "plan", "warning")
|
|
540
|
+
: segView([span("Plan", accentToken(ctx, "accent"))], "plan");
|
|
541
|
+
}
|
|
542
|
+
if (ctx.prewalk?.enabled) return segView([span("Prewalk", accentToken(ctx, "accent"))], "prewalk");
|
|
543
|
+
const goal = ctx.goalMode;
|
|
544
|
+
if (goal && (goal.enabled || goal.paused)) return describeGoalMode(ctx, goal);
|
|
545
|
+
if (ctx.vibeMode?.enabled) return segView([span("Vibe", accentToken(ctx, "accent"))], "agents");
|
|
546
|
+
const loop = ctx.loopMode;
|
|
547
|
+
if (loop) {
|
|
548
|
+
const paused = loop.state === "paused";
|
|
549
|
+
const stateLabel = loop.state === "waiting" ? "next prompt repeats" : loop.state;
|
|
550
|
+
const parts = [`Loop${loop.state === "waiting" ? ":" : ""} ${stateLabel}`];
|
|
551
|
+
const limit = formatLoopLimit(loop.limit, ctx.now?.getTime());
|
|
552
|
+
if (limit) parts.push(limit);
|
|
553
|
+
if (loop.condition) parts.push(summarizeLoopCondition(loop.condition, TRUNCATE_LENGTHS.SHORT));
|
|
554
|
+
return segView(
|
|
555
|
+
[span(parts.join(" "), paused ? "warning" : "customMessageLabel")],
|
|
556
|
+
paused ? "pause" : "loop",
|
|
557
|
+
paused ? "warning" : undefined,
|
|
558
|
+
);
|
|
559
|
+
}
|
|
560
|
+
return null;
|
|
561
|
+
},
|
|
416
562
|
};
|
|
417
563
|
|
|
418
564
|
const pathSegment: StatusLineSegment = {
|
|
@@ -449,8 +595,35 @@ const pathSegment: StatusLineSegment = {
|
|
|
449
595
|
const content = withIcon(icon, text);
|
|
450
596
|
return { content: theme.fg("statusLinePath", content), visible: true };
|
|
451
597
|
},
|
|
598
|
+
describe(ctx) {
|
|
599
|
+
const opts = ctx.options.path ?? {};
|
|
600
|
+
const stripPrefix = opts.stripWorkPrefix !== false;
|
|
601
|
+
// The parent dims and the leaf reads strong; a click opens the directory.
|
|
602
|
+
if (stripPrefix && ctx.worktree) {
|
|
603
|
+
const { projectName, worktreeName } = ctx.worktree;
|
|
604
|
+
const label = ctx.git.branch === worktreeName ? projectName : `${projectName}/${worktreeName}`;
|
|
605
|
+
return segView(pathSpans(label), "worktree");
|
|
606
|
+
}
|
|
607
|
+
const projectDir = ctx.activeRepo?.cwd ?? getProjectDir();
|
|
608
|
+
const { scratch, displayPath } = getProjectDirDisplay(projectDir);
|
|
609
|
+
let pwd = stripPrefix ? displayPath : projectDir;
|
|
610
|
+
if (opts.abbreviate !== false) pwd = shortenPath(pwd);
|
|
611
|
+
// `maxLength` is the user's configured cap; width fitting is the terminal's.
|
|
612
|
+
if (opts.maxLength !== undefined) pwd = clampPathLength(pwd, opts.maxLength);
|
|
613
|
+
const spans = pathSpans(pwd);
|
|
614
|
+
if (ctx.activeRepo) spans.push(span(` ↳ ${ctx.activeRepo.relativeRepoRoot}`, "statusLinePath dim"));
|
|
615
|
+
return segView(spans, scratch && stripPrefix ? "scratch-folder" : "folder");
|
|
616
|
+
},
|
|
452
617
|
};
|
|
453
618
|
|
|
619
|
+
/** A path as its dim parent directories and its strong leaf. */
|
|
620
|
+
function pathSpans(value: string): TspSpan[] {
|
|
621
|
+
const slash = value.lastIndexOf("/", value.length - 2);
|
|
622
|
+
if (slash < 0 || slash + 1 >= value.length) return [span(value, "statusLinePath strong")];
|
|
623
|
+
const leaf = span(value.slice(slash + 1), "statusLinePath strong");
|
|
624
|
+
return [span(value.slice(0, slash + 1), "statusLinePath dim"), leaf];
|
|
625
|
+
}
|
|
626
|
+
|
|
454
627
|
const gitSegment: StatusLineSegment = {
|
|
455
628
|
id: "git",
|
|
456
629
|
render(ctx) {
|
|
@@ -494,6 +667,31 @@ const gitSegment: StatusLineSegment = {
|
|
|
494
667
|
const colorName = isDirty ? "statusLineGitDirty" : "statusLineGitClean";
|
|
495
668
|
return { content: theme.fg(colorName, content), visible: true };
|
|
496
669
|
},
|
|
670
|
+
describe(ctx) {
|
|
671
|
+
const { branch, status } = ctx.git;
|
|
672
|
+
if (!branch && !status) return null;
|
|
673
|
+
const opts = ctx.options.git ?? {};
|
|
674
|
+
const isDirty = status !== null && (status.staged > 0 || status.unstaged > 0 || status.untracked > 0);
|
|
675
|
+
const colorName = isDirty ? "statusLineGitDirty" : "statusLineGitClean";
|
|
676
|
+
const showBranch = opts.showBranch !== false;
|
|
677
|
+
const spans: TspSpan[] = [];
|
|
678
|
+
if (showBranch && branch) spans.push(span(branch, colorName));
|
|
679
|
+
const indicators: TspSpan[] = [];
|
|
680
|
+
if (status) {
|
|
681
|
+
if (opts.showUnstaged !== false && status.unstaged > 0) {
|
|
682
|
+
indicators.push(span(`*${status.unstaged}`, "statusLineDirty"));
|
|
683
|
+
}
|
|
684
|
+
if (opts.showStaged !== false && status.staged > 0) {
|
|
685
|
+
indicators.push(span(`+${status.staged}`, "statusLineStaged"));
|
|
686
|
+
}
|
|
687
|
+
if (opts.showUntracked !== false && status.untracked > 0) {
|
|
688
|
+
indicators.push(span(`?${status.untracked}`, "statusLineUntracked"));
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
spans.push(...indicators);
|
|
692
|
+
if (spans.length === 0) return null;
|
|
693
|
+
return segView(spans, showBranch && branch ? "branch" : "git", isDirty ? "warning" : undefined);
|
|
694
|
+
},
|
|
497
695
|
};
|
|
498
696
|
|
|
499
697
|
const prSegment: StatusLineSegment = {
|
|
@@ -506,6 +704,11 @@ const prSegment: StatusLineSegment = {
|
|
|
506
704
|
const content = TERMINAL.hyperlinks ? `\x1b]8;;${pr.url}\x07${label}\x1b]8;;\x07` : label;
|
|
507
705
|
return { content: accentFg(ctx, "accent", content), visible: true };
|
|
508
706
|
},
|
|
707
|
+
describe(ctx) {
|
|
708
|
+
const { pr } = ctx.git;
|
|
709
|
+
if (!pr) return null;
|
|
710
|
+
return segView([span(`#${pr.number}`, `${accentToken(ctx, "accent")} link`, { href: pr.url })], "pr");
|
|
711
|
+
},
|
|
509
712
|
};
|
|
510
713
|
|
|
511
714
|
const subagentsSegment: StatusLineSegment = {
|
|
@@ -517,6 +720,10 @@ const subagentsSegment: StatusLineSegment = {
|
|
|
517
720
|
const content = withIcon(theme.icon.agents, `${ctx.subagentCount}`);
|
|
518
721
|
return { content: theme.fg("statusLineSubagents", content), visible: true };
|
|
519
722
|
},
|
|
723
|
+
describe(ctx) {
|
|
724
|
+
if (ctx.subagentCount === 0) return null;
|
|
725
|
+
return segView([span(`${ctx.subagentCount}`, "statusLineSubagents")], "agents");
|
|
726
|
+
},
|
|
520
727
|
};
|
|
521
728
|
|
|
522
729
|
const tokenInSegment: StatusLineSegment = singleStatSegment("token_in", "input", "input", "statusLineSpend");
|
|
@@ -540,6 +747,12 @@ const tokenTotalSegment: StatusLineSegment = {
|
|
|
540
747
|
});
|
|
541
748
|
return { content: theme.fg("statusLineSpend", content ?? ""), visible: true };
|
|
542
749
|
},
|
|
750
|
+
describe(ctx) {
|
|
751
|
+
const { input, output, cacheWrite, orchestrationInput, orchestrationOutput } = ctx.usageStats;
|
|
752
|
+
const total = input + output + cacheWrite + orchestrationInput + orchestrationOutput;
|
|
753
|
+
if (!total) return null;
|
|
754
|
+
return segView([span(formatNumber(total), "statusLineSpend")], "tokens");
|
|
755
|
+
},
|
|
543
756
|
};
|
|
544
757
|
|
|
545
758
|
const tokenRateSegment: StatusLineSegment = {
|
|
@@ -554,43 +767,58 @@ const tokenRateSegment: StatusLineSegment = {
|
|
|
554
767
|
});
|
|
555
768
|
return { content: theme.fg("statusLineOutput", content ?? ""), visible: true };
|
|
556
769
|
},
|
|
770
|
+
describe(ctx) {
|
|
771
|
+
const { tokensPerSecond } = ctx.usageStats;
|
|
772
|
+
if (!tokensPerSecond) return null;
|
|
773
|
+
// The terminal eases the readout between updates.
|
|
774
|
+
return {
|
|
775
|
+
spans: [],
|
|
776
|
+
icon: "throughput",
|
|
777
|
+
motion: [node("rate", { value: Number(tokensPerSecond.toFixed(1)), unit: "tok/s" }, undefined, "rate")],
|
|
778
|
+
};
|
|
779
|
+
},
|
|
557
780
|
};
|
|
558
781
|
|
|
782
|
+
/** Billing summary for the `cost` segment, or undefined when there is nothing to bill. */
|
|
783
|
+
function costSummary(ctx: SegmentContext): string | undefined {
|
|
784
|
+
const { cost, premiumRequests } = ctx.usageStats;
|
|
785
|
+
const advisorCost = ctx.session.getAdvisorCost?.() ?? 0;
|
|
786
|
+
const state = ctx.session.state;
|
|
787
|
+
const pricingPeriod = state.model?.cost
|
|
788
|
+
? getTimeBasedPricingPeriod(state.model.cost, ctx.now?.getTime())
|
|
789
|
+
: undefined;
|
|
790
|
+
const usingSubscription = state.model ? (ctx.session.modelRegistry?.isUsingOAuth(state.model) ?? false) : false;
|
|
791
|
+
// Resolve the advisor subscription flag lazily: with no active advisor
|
|
792
|
+
// it walks the whole model catalog (getAvailable → hasAuth per provider
|
|
793
|
+
// → credential-file reads), and the status line re-renders at the
|
|
794
|
+
// working-spinner cadence, so an eager per-frame probe pinned CPU (#10129).
|
|
795
|
+
return formatBillingSummary(
|
|
796
|
+
{
|
|
797
|
+
cost,
|
|
798
|
+
usingSubscription,
|
|
799
|
+
premiumRequests,
|
|
800
|
+
fractionDigits: 2,
|
|
801
|
+
pricingPeriod,
|
|
802
|
+
advisor: advisorCost
|
|
803
|
+
? { cost: advisorCost, usingSubscription: ctx.session.isAdvisorUsingSubscription?.() ?? false }
|
|
804
|
+
: undefined,
|
|
805
|
+
},
|
|
806
|
+
theme,
|
|
807
|
+
);
|
|
808
|
+
}
|
|
809
|
+
|
|
559
810
|
const costSegment: StatusLineSegment = {
|
|
560
811
|
id: "cost",
|
|
561
812
|
render(ctx) {
|
|
562
|
-
const
|
|
563
|
-
const advisorCost = ctx.session.getAdvisorCost?.() ?? 0;
|
|
564
|
-
const state = ctx.session.state;
|
|
565
|
-
const pricingPeriod = state.model?.cost
|
|
566
|
-
? getTimeBasedPricingPeriod(state.model.cost, ctx.now?.getTime())
|
|
567
|
-
: undefined;
|
|
568
|
-
const usingSubscription = state.model ? (ctx.session.modelRegistry?.isUsingOAuth(state.model) ?? false) : false;
|
|
569
|
-
|
|
570
|
-
// Resolve the advisor subscription flag lazily: with no active advisor
|
|
571
|
-
// it walks the whole model catalog (getAvailable → hasAuth per provider
|
|
572
|
-
// → credential-file reads), and the status line re-renders at the
|
|
573
|
-
// working-spinner cadence, so an eager per-frame probe pinned CPU (#10129).
|
|
574
|
-
const billing = formatBillingSummary(
|
|
575
|
-
{
|
|
576
|
-
cost,
|
|
577
|
-
usingSubscription,
|
|
578
|
-
premiumRequests,
|
|
579
|
-
fractionDigits: 2,
|
|
580
|
-
pricingPeriod,
|
|
581
|
-
advisor: advisorCost
|
|
582
|
-
? {
|
|
583
|
-
cost: advisorCost,
|
|
584
|
-
usingSubscription: ctx.session.isAdvisorUsingSubscription?.() ?? false,
|
|
585
|
-
}
|
|
586
|
-
: undefined,
|
|
587
|
-
},
|
|
588
|
-
theme,
|
|
589
|
-
);
|
|
813
|
+
const billing = costSummary(ctx);
|
|
590
814
|
if (!billing) return { content: "", visible: false };
|
|
591
815
|
|
|
592
816
|
return { content: theme.fg("statusLineCost", billing), visible: true };
|
|
593
817
|
},
|
|
818
|
+
describe(ctx) {
|
|
819
|
+
const billing = costSummary(ctx);
|
|
820
|
+
return billing ? segView([span(billing, "statusLineCost")]) : null;
|
|
821
|
+
},
|
|
594
822
|
};
|
|
595
823
|
|
|
596
824
|
const contextPctSegment: StatusLineSegment = {
|
|
@@ -625,6 +853,29 @@ const contextPctSegment: StatusLineSegment = {
|
|
|
625
853
|
|
|
626
854
|
return { content, visible: true };
|
|
627
855
|
},
|
|
856
|
+
describe(ctx) {
|
|
857
|
+
const pct = ctx.contextPercent;
|
|
858
|
+
const window = ctx.contextWindow;
|
|
859
|
+
const level = getContextUsageLevel(pct ?? 0, window);
|
|
860
|
+
const color = getContextUsageThemeColor(level);
|
|
861
|
+
const spans: TspSpan[] = [
|
|
862
|
+
span(
|
|
863
|
+
pct === null && window > 0 ? formatNumber(window) : formatContextUsage(pct, window, ctx.contextTokens),
|
|
864
|
+
color,
|
|
865
|
+
),
|
|
866
|
+
];
|
|
867
|
+
if (ctx.autoCompactEnabled && theme.icon.auto) {
|
|
868
|
+
// Async-compaction indicator: the terminal pulses the auto icon while a
|
|
869
|
+
// background speculation runs; an armed result holds it in accent.
|
|
870
|
+
const speculation = ctx.compactionSpeculation;
|
|
871
|
+
if (speculation === "running") {
|
|
872
|
+
spans.push(span(` ${theme.icon.auto}`, accentToken(ctx, "accent"), { fx: "pulse" }));
|
|
873
|
+
} else {
|
|
874
|
+
spans.push(span(` ${theme.icon.auto}`, speculation === "armed" ? accentToken(ctx, "accent") : color));
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
return segView(spans, "context", getContextUsageTone(level));
|
|
878
|
+
},
|
|
628
879
|
};
|
|
629
880
|
|
|
630
881
|
const contextTotalSegment: StatusLineSegment = {
|
|
@@ -637,6 +888,10 @@ const contextTotalSegment: StatusLineSegment = {
|
|
|
637
888
|
visible: true,
|
|
638
889
|
};
|
|
639
890
|
},
|
|
891
|
+
describe(ctx) {
|
|
892
|
+
const window = ctx.contextWindow;
|
|
893
|
+
return window ? segView([span(formatNumber(window), "statusLineContext")], "context") : null;
|
|
894
|
+
},
|
|
640
895
|
};
|
|
641
896
|
|
|
642
897
|
/**
|
|
@@ -653,29 +908,39 @@ const timeSpentSegment: StatusLineSegment = {
|
|
|
653
908
|
if (ctx.activeMs < 1000) return { content: "", visible: false };
|
|
654
909
|
return { content: withIcon(theme.icon.time, formatDuration(ctx.activeMs)), visible: true };
|
|
655
910
|
},
|
|
911
|
+
describe(ctx) {
|
|
912
|
+
const running = ctx.turnElapsedMs != null;
|
|
913
|
+
if (!running && ctx.activeMs < 1000) return null;
|
|
914
|
+
// Ticks on the terminal's clock during a turn; frozen between turns.
|
|
915
|
+
const elapsed = running
|
|
916
|
+
? node("elapsed", { age: ctx.activeMs, format: "short" }, undefined, "timer")
|
|
917
|
+
: node("elapsed", { age: ctx.activeMs, stopped: ctx.activeMs, format: "short" }, undefined, "timer");
|
|
918
|
+
return { spans: [], icon: "time", motion: [elapsed] };
|
|
919
|
+
},
|
|
656
920
|
};
|
|
657
921
|
|
|
922
|
+
/** Wall-clock label for the `time` segment. */
|
|
923
|
+
function formatClock(ctx: SegmentContext): string {
|
|
924
|
+
const opts = ctx.options.time ?? {};
|
|
925
|
+
const now = ctx.now ?? new Date();
|
|
926
|
+
let hours = now.getHours();
|
|
927
|
+
let suffix = "";
|
|
928
|
+
if (opts.format === "12h") {
|
|
929
|
+
suffix = hours >= 12 ? "pm" : "am";
|
|
930
|
+
hours = hours % 12 || 12;
|
|
931
|
+
}
|
|
932
|
+
let timeStr = `${hours}:${now.getMinutes().toString().padStart(2, "0")}`;
|
|
933
|
+
if (opts.showSeconds) timeStr += `:${now.getSeconds().toString().padStart(2, "0")}`;
|
|
934
|
+
return timeStr + suffix;
|
|
935
|
+
}
|
|
936
|
+
|
|
658
937
|
const timeSegment: StatusLineSegment = {
|
|
659
938
|
id: "time",
|
|
660
939
|
render(ctx) {
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
let suffix = "";
|
|
666
|
-
if (opts.format === "12h") {
|
|
667
|
-
suffix = hours >= 12 ? "pm" : "am";
|
|
668
|
-
hours = hours % 12 || 12;
|
|
669
|
-
}
|
|
670
|
-
|
|
671
|
-
const mins = now.getMinutes().toString().padStart(2, "0");
|
|
672
|
-
let timeStr = `${hours}:${mins}`;
|
|
673
|
-
if (opts.showSeconds) {
|
|
674
|
-
timeStr += `:${now.getSeconds().toString().padStart(2, "0")}`;
|
|
675
|
-
}
|
|
676
|
-
timeStr += suffix;
|
|
677
|
-
|
|
678
|
-
return { content: withIcon(theme.icon.time, timeStr), visible: true };
|
|
940
|
+
return { content: withIcon(theme.icon.time, formatClock(ctx)), visible: true };
|
|
941
|
+
},
|
|
942
|
+
describe(ctx) {
|
|
943
|
+
return segView([span(formatClock(ctx))], "time");
|
|
679
944
|
},
|
|
680
945
|
};
|
|
681
946
|
|
|
@@ -688,6 +953,10 @@ const sessionSegment: StatusLineSegment = {
|
|
|
688
953
|
|
|
689
954
|
return { content: withIcon(theme.icon.session, display), visible: true };
|
|
690
955
|
},
|
|
956
|
+
describe(ctx) {
|
|
957
|
+
const sessionId = ctx.session.sessionManager?.getSessionId?.();
|
|
958
|
+
return segView([span(sessionId?.slice(0, 8) || "new")], "session");
|
|
959
|
+
},
|
|
691
960
|
};
|
|
692
961
|
|
|
693
962
|
const hostnameSegment: StatusLineSegment = {
|
|
@@ -698,6 +967,10 @@ const hostnameSegment: StatusLineSegment = {
|
|
|
698
967
|
const ansi = sessionAccentAnsi(ctx);
|
|
699
968
|
return { content: ansi ? `${ansi}${content}\x1b[39m` : content, visible: true };
|
|
700
969
|
},
|
|
970
|
+
describe(ctx) {
|
|
971
|
+
const name = ctx.hostname ?? os.hostname().split(".")[0];
|
|
972
|
+
return segView([span(name, sessionAccentAnsi(ctx) ? "accent" : undefined)], "host");
|
|
973
|
+
},
|
|
701
974
|
};
|
|
702
975
|
|
|
703
976
|
const cacheReadSegment: StatusLineSegment = singleStatSegment("cache_read", "cacheRead", "cache", "statusLineSpend");
|
|
@@ -729,6 +1002,12 @@ const cacheHitSegment: StatusLineSegment = {
|
|
|
729
1002
|
parts.push(theme.fg("statusLineSpend", `${rateStr}%`));
|
|
730
1003
|
return { content: parts.join(" "), visible: true };
|
|
731
1004
|
},
|
|
1005
|
+
describe(ctx) {
|
|
1006
|
+
const { cacheRead, cacheWrite, input } = ctx.usageStats;
|
|
1007
|
+
if (!cacheRead) return null;
|
|
1008
|
+
const rate = (cacheRead / (cacheRead + cacheWrite + input)) * 100;
|
|
1009
|
+
return segView([span(`${rate.toFixed(2)}%`, "statusLineSpend")], "cache");
|
|
1010
|
+
},
|
|
732
1011
|
};
|
|
733
1012
|
|
|
734
1013
|
const sessionNameSegment: StatusLineSegment = {
|
|
@@ -741,6 +1020,12 @@ const sessionNameSegment: StatusLineSegment = {
|
|
|
741
1020
|
const content = sanitizeStatusText(name);
|
|
742
1021
|
return { content: accentFg(ctx, "accent", content), visible: true };
|
|
743
1022
|
},
|
|
1023
|
+
describe(ctx) {
|
|
1024
|
+
const name = ctx.session.sessionManager?.getSessionName() || ctx.previewTitle;
|
|
1025
|
+
const content = name ? sanitizeStatusText(name) : "";
|
|
1026
|
+
// Plain: the terminal styles the title by its role.
|
|
1027
|
+
return content ? segView([span(content)]) : null;
|
|
1028
|
+
},
|
|
744
1029
|
};
|
|
745
1030
|
|
|
746
1031
|
const collabSegment: StatusLineSegment = {
|
|
@@ -751,6 +1036,12 @@ const collabSegment: StatusLineSegment = {
|
|
|
751
1036
|
const label = ctx.collab.role === "host" ? `⇄ collab:${participants}` : `⇄ collab guest:${participants}`;
|
|
752
1037
|
return { content: accentFg(ctx, "accent", label), visible: true };
|
|
753
1038
|
},
|
|
1039
|
+
describe(ctx) {
|
|
1040
|
+
if (!ctx.collab) return null;
|
|
1041
|
+
const participants = `${ctx.collab.participantCount}`;
|
|
1042
|
+
const label = ctx.collab.role === "host" ? `collab:${participants}` : `collab guest:${participants}`;
|
|
1043
|
+
return segView([span(label, accentToken(ctx, "accent"))], "collab");
|
|
1044
|
+
},
|
|
754
1045
|
};
|
|
755
1046
|
|
|
756
1047
|
const streamSegment: StatusLineSegment = {
|
|
@@ -762,6 +1053,13 @@ const streamSegment: StatusLineSegment = {
|
|
|
762
1053
|
if (badges.length === 0) return { content: "", visible: false };
|
|
763
1054
|
return { content: theme.fg("thinkingHigh", badges.join(" ")), visible: true };
|
|
764
1055
|
},
|
|
1056
|
+
describe(ctx) {
|
|
1057
|
+
const badges: string[] = [];
|
|
1058
|
+
if (ctx.stream) badges.push(`LIVE ${ctx.stream.viewers}`);
|
|
1059
|
+
if (ctx.recording) badges.push("REC");
|
|
1060
|
+
if (badges.length === 0) return null;
|
|
1061
|
+
return segView([span(badges.join(" "), "thinkingHigh")], "live", "error");
|
|
1062
|
+
},
|
|
765
1063
|
};
|
|
766
1064
|
|
|
767
1065
|
/**
|
|
@@ -809,6 +1107,20 @@ const vimSegment: StatusLineSegment = {
|
|
|
809
1107
|
const pending = vim.pending ? theme.fg("muted", ` ${vim.pending}`) : "";
|
|
810
1108
|
return { content: `${content}${pending}`, visible: true };
|
|
811
1109
|
},
|
|
1110
|
+
describe(ctx) {
|
|
1111
|
+
const vim = ctx.vim;
|
|
1112
|
+
if (!vim || vim.display === "none") return null;
|
|
1113
|
+
const color = VIM_MODE_COLORS[vim.mode];
|
|
1114
|
+
const spans: TspSpan[] = [];
|
|
1115
|
+
const lines = vim.selectedLines > 1 ? `${vim.selectedLines}L` : "";
|
|
1116
|
+
if (vim.display === "icon") {
|
|
1117
|
+
if (lines) spans.push(span(lines, color));
|
|
1118
|
+
} else {
|
|
1119
|
+
spans.push(span(lines ? `${VIM_MODE_LABELS[vim.mode]} ${lines}` : VIM_MODE_LABELS[vim.mode], color));
|
|
1120
|
+
}
|
|
1121
|
+
if (vim.pending) spans.push(span(` ${vim.pending}`, "muted"));
|
|
1122
|
+
return segView(spans, vim.display === "icon" ? `vim-${vim.mode}` : undefined, toneOf(color));
|
|
1123
|
+
},
|
|
812
1124
|
};
|
|
813
1125
|
|
|
814
1126
|
function pickUsageColor(percent: number): "muted" | "warning" | "error" {
|
|
@@ -837,6 +1149,20 @@ function formatQuotaWindow(
|
|
|
837
1149
|
return `${label} ${pctText}${resetText}`;
|
|
838
1150
|
}
|
|
839
1151
|
|
|
1152
|
+
/** Native twin of {@link formatQuotaWindow}. */
|
|
1153
|
+
function describeQuotaWindow(
|
|
1154
|
+
label: string,
|
|
1155
|
+
percent: number,
|
|
1156
|
+
reset: number | undefined,
|
|
1157
|
+
resetUnit: "m" | "h",
|
|
1158
|
+
integer: "round" | "floor",
|
|
1159
|
+
): TspSpan[] {
|
|
1160
|
+
const whole = integer === "floor" ? Math.floor(percent) : Math.round(percent);
|
|
1161
|
+
const spans = [span(`${label} `), span(`${whole}%`, pickUsageColor(percent))];
|
|
1162
|
+
if (reset !== undefined) spans.push(span(` (${formatUsageReset(reset, resetUnit)})`, "muted"));
|
|
1163
|
+
return spans;
|
|
1164
|
+
}
|
|
1165
|
+
|
|
840
1166
|
function formatUsageReset(value: number, unit: "m" | "h"): string {
|
|
841
1167
|
if (unit === "m") {
|
|
842
1168
|
// Short-window reset timers retain minute precision.
|
|
@@ -899,6 +1225,41 @@ const usageSegment: StatusLineSegment = {
|
|
|
899
1225
|
const content = withIcon(theme.icon.time, parts.join(theme.sep.dot));
|
|
900
1226
|
return { content, visible: true };
|
|
901
1227
|
},
|
|
1228
|
+
describe(ctx) {
|
|
1229
|
+
const u = ctx.usage;
|
|
1230
|
+
if (!u || (!u.fiveHour && !u.daily && !u.sevenDay && !u.monthly && !u.resetCredits)) return null;
|
|
1231
|
+
const parts: TspSpan[][] = [];
|
|
1232
|
+
if (u.tier) {
|
|
1233
|
+
const tier = sanitizeStatusText(u.tier);
|
|
1234
|
+
if (tier) parts.push([span(tier, accentToken(ctx, "accent"))]);
|
|
1235
|
+
}
|
|
1236
|
+
if (u.fiveHour) {
|
|
1237
|
+
parts.push(describeQuotaWindow("5h", u.fiveHour.percent, u.fiveHour.resetMinutes, "m", "round"));
|
|
1238
|
+
}
|
|
1239
|
+
if (u.daily) parts.push(describeQuotaWindow("1d", u.daily.percent, u.daily.resetMinutes, "m", "round"));
|
|
1240
|
+
if (u.sevenDay) {
|
|
1241
|
+
parts.push(describeQuotaWindow("7d", u.sevenDay.percent, u.sevenDay.resetHours, "h", "round"));
|
|
1242
|
+
}
|
|
1243
|
+
if (u.monthly) parts.push(describeQuotaWindow("mo", u.monthly.percent, u.monthly.resetHours, "h", "floor"));
|
|
1244
|
+
if (u.resetCredits) {
|
|
1245
|
+
const resets = u.resetCredits;
|
|
1246
|
+
let resetText = `✦ ${resets.bankedCount}`;
|
|
1247
|
+
if (resets.redeemableCount !== resets.bankedCount) resetText += ` (${resets.redeemableCount} usable)`;
|
|
1248
|
+
if (resets.expiryHours !== undefined) resetText += ` exp ${formatUsageReset(resets.expiryHours, "h")}`;
|
|
1249
|
+
else if (resets.expired) resetText += " expired";
|
|
1250
|
+
if (resets.redeemableCount === 0 && resets.unavailableReason) {
|
|
1251
|
+
const reason = sanitizeStatusText(resets.unavailableReason);
|
|
1252
|
+
if (reason) resetText += ` ${reason}`;
|
|
1253
|
+
}
|
|
1254
|
+
parts.push([span(resetText, resets.redeemableCount > 0 ? "success" : "warning")]);
|
|
1255
|
+
}
|
|
1256
|
+
const spans: TspSpan[] = [];
|
|
1257
|
+
for (const part of parts) {
|
|
1258
|
+
if (spans.length > 0) spans.push(span(theme.sep.dot, "dim"));
|
|
1259
|
+
spans.push(...part);
|
|
1260
|
+
}
|
|
1261
|
+
return segView(spans, "time");
|
|
1262
|
+
},
|
|
902
1263
|
};
|
|
903
1264
|
|
|
904
1265
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -943,4 +1304,9 @@ export function renderSegment(id: StatusLineSegmentId, ctx: SegmentContext): Ren
|
|
|
943
1304
|
return segment.render(ctx);
|
|
944
1305
|
}
|
|
945
1306
|
|
|
1307
|
+
/** Native description of one segment; null when it is hidden or unknown. */
|
|
1308
|
+
export function describeSegment(id: StatusLineSegmentId, ctx: SegmentContext): SegmentView | null {
|
|
1309
|
+
return SEGMENTS[id]?.describe(ctx) ?? null;
|
|
1310
|
+
}
|
|
1311
|
+
|
|
946
1312
|
export const ALL_SEGMENT_IDS: StatusLineSegmentId[] = Object.keys(SEGMENTS) as StatusLineSegmentId[];
|