@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
|
@@ -40,6 +40,10 @@ import {
|
|
|
40
40
|
import { OverlayPanel, PanelDivider, PanelRows } from "../chrome/overlay-box";
|
|
41
41
|
import { formatKeyHint } from "../app-keybindings";
|
|
42
42
|
import { editorKey, editorKeys } from "../chrome/keybinding-hints";
|
|
43
|
+
import type { TspSpan, TspTableColumn, TspText, TspTone } from "@oh-my-pi/pi-wire";
|
|
44
|
+
import { col, elapsed, node, span, text } from "../native/describe";
|
|
45
|
+
import { type DescribeContext, leafKey, type NativeChild, type NativeNode, type NativeUiEvent } from "../native/node";
|
|
46
|
+
import { actionButton } from "../native/overlay";
|
|
43
47
|
|
|
44
48
|
/** Local calendar-day activity consumed by the usage heatmap. */
|
|
45
49
|
export interface DailyActivityPoint {
|
|
@@ -281,6 +285,8 @@ export interface HeatmapLayout {
|
|
|
281
285
|
cells: (number | null)[][];
|
|
282
286
|
totalCost: number;
|
|
283
287
|
totalRequests: number;
|
|
288
|
+
/** Local midnight of the first cell (column 0, Monday). */
|
|
289
|
+
start: Date;
|
|
284
290
|
}
|
|
285
291
|
|
|
286
292
|
const MONTH_NAMES = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
|
@@ -346,9 +352,129 @@ export function buildHeatmapLayout(points: DailyActivityPoint[], weeks: number,
|
|
|
346
352
|
cells,
|
|
347
353
|
totalCost: inRange.reduce((sum, point) => sum + point.cost, 0),
|
|
348
354
|
totalRequests: inRange.reduce((sum, point) => sum + point.requests, 0),
|
|
355
|
+
start,
|
|
349
356
|
};
|
|
350
357
|
}
|
|
351
358
|
|
|
359
|
+
// =============================================================================
|
|
360
|
+
// Native description helpers
|
|
361
|
+
// =============================================================================
|
|
362
|
+
|
|
363
|
+
/** Weeks of history the native heatmap carries (a year, GitHub-style). */
|
|
364
|
+
const NATIVE_HEATMAP_WEEKS = 53;
|
|
365
|
+
/** Span tokens for heatmap intensity levels 1..4 (the `table` fallback). */
|
|
366
|
+
const HEAT_LEVEL_TOKENS = ["dim", "accent dim", "accent", "accent strong"];
|
|
367
|
+
/** Heatmap row labels, Monday first like omp's grid: M/W/F only. */
|
|
368
|
+
const HEATMAP_ROW_LABELS = ["M", "", "W", "", "F", "", ""];
|
|
369
|
+
const WEEKDAY_NAMES = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
|
370
|
+
|
|
371
|
+
/** Meter tone of a quota window: ok is the accent fill, pressure warns, exhaustion errors. */
|
|
372
|
+
function statusTone(status: UsageLimit["status"]): TspTone {
|
|
373
|
+
if (status === "exhausted") return "error";
|
|
374
|
+
if (status === "warning") return "warning";
|
|
375
|
+
if (status === "ok") return "accent";
|
|
376
|
+
return "muted";
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/** Status dot of a provider frame, toned by the card's aggregate status. */
|
|
380
|
+
function statusDot(status: UsageLimit["status"]): NativeNode {
|
|
381
|
+
const token =
|
|
382
|
+
status === "exhausted" ? "error" : status === "warning" ? "warning" : status === "ok" ? "success" : "dim";
|
|
383
|
+
const label =
|
|
384
|
+
status === "exhausted"
|
|
385
|
+
? "Exhausted"
|
|
386
|
+
: status === "warning"
|
|
387
|
+
? "Under pressure"
|
|
388
|
+
: status === "ok"
|
|
389
|
+
? "OK"
|
|
390
|
+
: "Unknown";
|
|
391
|
+
return text([span("●", token)], { title: label, aria: label });
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Used-fraction bar of a quota window, toned by status: a `meter` where the
|
|
396
|
+
* terminal draws one, else a `progress` bar.
|
|
397
|
+
*/
|
|
398
|
+
function usageMeter(fraction: number, status: UsageLimit["status"], meter: boolean): NativeNode {
|
|
399
|
+
const value = Math.min(Math.max(fraction, 0), 1);
|
|
400
|
+
const tone = statusTone(status);
|
|
401
|
+
return meter ? node("meter", { value, style: "bar", size: "md", tone }) : node("progress", { value, tone, grow: 1 });
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/** `62% left` for a used fraction (overage reads as 0%). */
|
|
405
|
+
function leftText(fraction: number): string {
|
|
406
|
+
return `${Math.max(0, Math.round((1 - fraction) * 100))}% left`;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/** `Mon 28 Sep` for a local date. */
|
|
410
|
+
function shortDate(date: Date): string {
|
|
411
|
+
return `${WEEKDAY_NAMES[date.getDay()]} ${date.getDate()} ${MONTH_NAMES[date.getMonth()]}`;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/** `18:00` for a local time. */
|
|
415
|
+
function clockTime(date: Date): string {
|
|
416
|
+
return `${String(date.getHours()).padStart(2, "0")}:${String(date.getMinutes()).padStart(2, "0")}`;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* When a window resets, relative to `nowMs`: `resets 18:00` today, `resets
|
|
421
|
+
* Tue 18:00` within the week, `resets 3 Oct` beyond; the title carries the
|
|
422
|
+
* absolute date and the countdown.
|
|
423
|
+
*/
|
|
424
|
+
function resetLabel(nowMs: number, resetMs: number): { text: string; title: string } {
|
|
425
|
+
const at = new Date(nowMs + resetMs);
|
|
426
|
+
const now = new Date(nowMs);
|
|
427
|
+
const sameDay = at.toDateString() === now.toDateString();
|
|
428
|
+
const text = sameDay
|
|
429
|
+
? `resets ${clockTime(at)}`
|
|
430
|
+
: resetMs < 6 * 86_400_000
|
|
431
|
+
? `resets ${WEEKDAY_NAMES[at.getDay()]} ${clockTime(at)}`
|
|
432
|
+
: `resets ${at.getDate()} ${MONTH_NAMES[at.getMonth()]}`;
|
|
433
|
+
return {
|
|
434
|
+
text,
|
|
435
|
+
title: `Resets ${shortDate(at)} ${at.getFullYear()}, ${clockTime(at)} (in ${formatDuration(resetMs)})`,
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
function mutedText(content: string, wrap = false): NativeNode {
|
|
440
|
+
return text([span(content, "muted")], wrap ? { wrap: "word" } : { truncate: "end" });
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/** Stable, human account name for a report in the detail view. */
|
|
444
|
+
function reportAccountLabel(report: UsageReport, limit: UsageLimit | undefined, index: number): string {
|
|
445
|
+
const metadata = report.metadata;
|
|
446
|
+
const pick = (value: unknown): string | undefined => (typeof value === "string" && value ? value : undefined);
|
|
447
|
+
const identity =
|
|
448
|
+
pick(metadata?.email) ??
|
|
449
|
+
pick(metadata?.accountId) ??
|
|
450
|
+
pick(metadata?.projectId) ??
|
|
451
|
+
pick(limit?.scope.projectId) ??
|
|
452
|
+
`account ${index + 1}`;
|
|
453
|
+
const org = pick(metadata?.orgName);
|
|
454
|
+
return sanitizeDisplayLine(org && org !== identity ? `${identity} (${org})` : identity);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
/** Window qualifier worth showing after a limit title (none when the title already names it). */
|
|
458
|
+
function detailWindowLabel(label: string, limit: UsageLimit): string | undefined {
|
|
459
|
+
const windowLabel = limit.window?.label ?? limit.window?.id ?? limit.scope.windowId;
|
|
460
|
+
if (!windowLabel) return undefined;
|
|
461
|
+
const normalized = windowLabel.toLowerCase();
|
|
462
|
+
if (normalized === "quota window" || label.toLowerCase().includes(normalized)) return undefined;
|
|
463
|
+
return sanitizeDisplayLine(windowLabel);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
/** `$1,234 · 5.6K requests` totals for the activity summary. */
|
|
467
|
+
function formatActivityTotals(layout: HeatmapLayout): string {
|
|
468
|
+
const cost =
|
|
469
|
+
layout.totalCost >= 1
|
|
470
|
+
? `$${new Intl.NumberFormat("en-US", { maximumFractionDigits: 0 }).format(layout.totalCost)}`
|
|
471
|
+
: `$${layout.totalCost.toFixed(2)}`;
|
|
472
|
+
const requests = new Intl.NumberFormat("en-US", { notation: "compact", maximumFractionDigits: 1 }).format(
|
|
473
|
+
layout.totalRequests,
|
|
474
|
+
);
|
|
475
|
+
return `${cost} · ${requests} requests`;
|
|
476
|
+
}
|
|
477
|
+
|
|
352
478
|
// =============================================================================
|
|
353
479
|
// Component
|
|
354
480
|
// =============================================================================
|
|
@@ -358,10 +484,10 @@ export interface UsageDashboardOptions {
|
|
|
358
484
|
reports: UsageReport[];
|
|
359
485
|
unavailableAccounts?: readonly UnavailableUsageAccount[];
|
|
360
486
|
/**
|
|
361
|
-
* Full classic `/usage` report
|
|
362
|
-
* terminal width.
|
|
487
|
+
* Full classic `/usage` report of `reports` (the latest refresh) for the
|
|
488
|
+
* expanded detail view; re-invoked per terminal width.
|
|
363
489
|
*/
|
|
364
|
-
renderDetail: (width: number) => string;
|
|
490
|
+
renderDetail: (width: number, reports: UsageReport[]) => string;
|
|
365
491
|
/**
|
|
366
492
|
* Stream daily activity into the heatmap: push cached DB rows immediately,
|
|
367
493
|
* then push again after an incremental session sync. Resolves when the sync
|
|
@@ -369,6 +495,8 @@ export interface UsageDashboardOptions {
|
|
|
369
495
|
* the dashboard closes so an in-flight sync can stop early.
|
|
370
496
|
*/
|
|
371
497
|
loadActivity: (push: (points: DailyActivityPoint[]) => void, signal: AbortSignal) => Promise<void>;
|
|
498
|
+
/** Re-fetch the usage reports (`r`); omitted when the host can't. Resolves null when nothing came back. */
|
|
499
|
+
refresh?: () => Promise<UsageReport[] | null>;
|
|
372
500
|
requestRender: () => void;
|
|
373
501
|
onClose: () => void;
|
|
374
502
|
}
|
|
@@ -403,9 +531,16 @@ interface CardRowLayout {
|
|
|
403
531
|
}
|
|
404
532
|
|
|
405
533
|
export class UsageDashboardComponent implements Component {
|
|
534
|
+
/** The terminal draws the sheet: a large glass overlay titled Usage. */
|
|
535
|
+
readonly nativeOverlay = { role: "omp.overlay.usage", size: "lg", anchor: "center", head: "Usage" } as const;
|
|
406
536
|
#options: UsageDashboardOptions;
|
|
537
|
+
#reports: UsageReport[];
|
|
407
538
|
#cards: ProviderCard[];
|
|
408
539
|
#nowMs: number;
|
|
540
|
+
#refreshing = false;
|
|
541
|
+
#refreshError: string | null = null;
|
|
542
|
+
/** Bumped whenever anything the native description shows changes. */
|
|
543
|
+
#revision = 0;
|
|
409
544
|
#view: "overview" | "detail" = "overview";
|
|
410
545
|
#scroll = 0;
|
|
411
546
|
#activity: DailyActivityPoint[] | null = null;
|
|
@@ -419,12 +554,14 @@ export class UsageDashboardComponent implements Component {
|
|
|
419
554
|
readonly #body: PanelRows;
|
|
420
555
|
readonly #footer: PanelRows;
|
|
421
556
|
readonly #closeController = new AbortController();
|
|
557
|
+
#nativeCache: { revision: number; meter: boolean; chart: boolean; node: NativeNode } | undefined;
|
|
422
558
|
|
|
423
559
|
constructor(options: UsageDashboardOptions) {
|
|
424
560
|
ensureThemeSync();
|
|
425
561
|
this.#options = options;
|
|
562
|
+
this.#reports = options.reports;
|
|
426
563
|
this.#nowMs = Date.now();
|
|
427
|
-
this.#cards = buildProviderCards(
|
|
564
|
+
this.#cards = buildProviderCards(this.#reports, this.#nowMs, options.unavailableAccounts);
|
|
428
565
|
this.#panel = new OverlayPanel("Usage");
|
|
429
566
|
this.#header = new PanelRows();
|
|
430
567
|
this.#header.setHeight(1);
|
|
@@ -443,13 +580,13 @@ export class UsageDashboardComponent implements Component {
|
|
|
443
580
|
await this.#options.loadActivity(points => {
|
|
444
581
|
if (this.#closed) return;
|
|
445
582
|
this.#activity = points;
|
|
446
|
-
this.#
|
|
583
|
+
this.#changed();
|
|
447
584
|
}, this.#closeController.signal);
|
|
448
585
|
} catch (error) {
|
|
449
586
|
this.#activityError = error instanceof Error ? error.message : String(error);
|
|
450
587
|
} finally {
|
|
451
588
|
this.#syncing = false;
|
|
452
|
-
if (!this.#closed) this.#
|
|
589
|
+
if (!this.#closed) this.#changed();
|
|
453
590
|
}
|
|
454
591
|
}
|
|
455
592
|
|
|
@@ -748,7 +885,10 @@ export class UsageDashboardComponent implements Component {
|
|
|
748
885
|
|
|
749
886
|
#detailLines(innerWidth: number): string[] {
|
|
750
887
|
if (this.#detailCache?.width !== innerWidth) {
|
|
751
|
-
this.#detailCache = {
|
|
888
|
+
this.#detailCache = {
|
|
889
|
+
width: innerWidth,
|
|
890
|
+
lines: this.#options.renderDetail(innerWidth, this.#reports).split("\n"),
|
|
891
|
+
};
|
|
752
892
|
}
|
|
753
893
|
return this.#detailCache.lines;
|
|
754
894
|
}
|
|
@@ -764,16 +904,21 @@ export class UsageDashboardComponent implements Component {
|
|
|
764
904
|
const maxScroll = Math.max(0, contentSource.length - contentRows);
|
|
765
905
|
if (this.#scroll > maxScroll) this.#scroll = maxScroll;
|
|
766
906
|
|
|
767
|
-
const latestFetchedAt = Math.max(0, ...this.#
|
|
768
|
-
const checkedText =
|
|
907
|
+
const latestFetchedAt = Math.max(0, ...this.#reports.map(report => report.fetchedAt ?? 0));
|
|
908
|
+
const checkedText = this.#refreshing
|
|
909
|
+
? "refreshing…"
|
|
910
|
+
: latestFetchedAt
|
|
911
|
+
? `checked ${formatDuration(this.#nowMs - latestFetchedAt)} ago`
|
|
912
|
+
: "";
|
|
769
913
|
const title = this.#view === "detail" ? "Usage · Details" : "Usage";
|
|
770
914
|
|
|
771
915
|
const scrollHint = maxScroll > 0 ? `${editorKeys("tui.select.up", "tui.select.down")} scroll · ` : "";
|
|
772
916
|
const cancel = editorKey("tui.select.cancel");
|
|
917
|
+
const refreshHint = this.#options.refresh ? "r refresh · " : "";
|
|
773
918
|
const hint =
|
|
774
919
|
this.#view === "detail"
|
|
775
|
-
? `${scrollHint}${cancel} back`
|
|
776
|
-
: `${scrollHint}${formatKeyHint("enter")} details · ${cancel} close`;
|
|
920
|
+
? `${scrollHint}${refreshHint}${cancel} back`
|
|
921
|
+
: `${scrollHint}${refreshHint}${formatKeyHint("enter")} details · ${cancel} close`;
|
|
777
922
|
this.#panel.title = title;
|
|
778
923
|
this.#header.setLines([checkedText ? theme.fg("dim", checkedText) : ""]);
|
|
779
924
|
this.#body.setLines(contentSource.slice(this.#scroll, this.#scroll + contentRows));
|
|
@@ -782,6 +927,432 @@ export class UsageDashboardComponent implements Component {
|
|
|
782
927
|
return this.#panel.render(width);
|
|
783
928
|
}
|
|
784
929
|
|
|
930
|
+
// ---------------------------------------------------------------------------
|
|
931
|
+
// Native description
|
|
932
|
+
// ---------------------------------------------------------------------------
|
|
933
|
+
|
|
934
|
+
/**
|
|
935
|
+
* The sheet body (the terminal's `lg` overlay is the frame): a head row
|
|
936
|
+
* (checked ago, Overview/Details tabs, Refresh), then either the provider
|
|
937
|
+
* grid of frames with window meters and the activity heatmap, or the
|
|
938
|
+
* per-provider detail tables. `meter`/`chart` fall back to
|
|
939
|
+
* `progress`/`table` on terminals without them. Rebuilt only when its
|
|
940
|
+
* inputs change.
|
|
941
|
+
*/
|
|
942
|
+
describe(cx: DescribeContext): NativeNode {
|
|
943
|
+
const meter = cx.supports("meter");
|
|
944
|
+
const chart = cx.supports("chart");
|
|
945
|
+
const cache = this.#nativeCache;
|
|
946
|
+
if (cache && cache.revision === this.#revision && cache.meter === meter && cache.chart === chart) {
|
|
947
|
+
return cache.node;
|
|
948
|
+
}
|
|
949
|
+
const body = this.#view === "detail" ? this.#describeDetail() : this.#describeOverview(meter, chart);
|
|
950
|
+
const root = col([this.#describeHead(), node("col", { gap: "lg" }, body, this.#view)], { gap: "lg" });
|
|
951
|
+
this.#nativeCache = { revision: this.#revision, meter, chart, node: root };
|
|
952
|
+
return root;
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
/** Tab clicks switch views like Enter/Esc; the Refresh button runs `r`. */
|
|
956
|
+
handleNativeEvent(event: NativeUiEvent): void {
|
|
957
|
+
if (event.type === "action") {
|
|
958
|
+
if (event.act === "refresh") void this.#refresh();
|
|
959
|
+
return;
|
|
960
|
+
}
|
|
961
|
+
if (event.type !== "select" && event.type !== "activate") return;
|
|
962
|
+
if (leafKey(event.key) !== "tabs") return;
|
|
963
|
+
if ((event.item === "overview" || event.item === "detail") && event.item !== this.#view) {
|
|
964
|
+
this.#setView(event.item);
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
#describeHead(): NativeNode {
|
|
969
|
+
const checked: NativeChild[] = [];
|
|
970
|
+
const latestFetchedAt = Math.max(0, ...this.#reports.map(report => report.fetchedAt ?? 0));
|
|
971
|
+
if (this.#refreshing) {
|
|
972
|
+
checked.push(node("spinner", { label: [span("Refreshing…", "dim")] }));
|
|
973
|
+
} else if (this.#refreshError) {
|
|
974
|
+
checked.push(
|
|
975
|
+
text([span(`Refresh failed: ${sanitizeDisplayLine(this.#refreshError)}`, "warning")], {
|
|
976
|
+
truncate: "end",
|
|
977
|
+
}),
|
|
978
|
+
);
|
|
979
|
+
} else if (latestFetchedAt) {
|
|
980
|
+
checked.push(
|
|
981
|
+
text([span("checked", "dim")]),
|
|
982
|
+
elapsed(Date.now() - latestFetchedAt),
|
|
983
|
+
text([span("ago", "dim")]),
|
|
984
|
+
);
|
|
985
|
+
}
|
|
986
|
+
const children: NativeChild[] = [
|
|
987
|
+
node("row", { gap: "xs", align: "baseline", shrink: 1 }, checked, "checked"),
|
|
988
|
+
node("spacer", { grow: 1 }),
|
|
989
|
+
node(
|
|
990
|
+
"tabs",
|
|
991
|
+
{
|
|
992
|
+
items: [
|
|
993
|
+
{ id: "overview", label: "Overview" },
|
|
994
|
+
{ id: "detail", label: "Details" },
|
|
995
|
+
],
|
|
996
|
+
active: this.#view,
|
|
997
|
+
},
|
|
998
|
+
undefined,
|
|
999
|
+
"tabs",
|
|
1000
|
+
),
|
|
1001
|
+
];
|
|
1002
|
+
if (this.#options.refresh) children.push(actionButton("Refresh", "refresh", { keys: "r" }));
|
|
1003
|
+
return node("row", { role: "omp.usage.head", gap: "sm", align: "center" }, children, "head");
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
#describeOverview(meter: boolean, chart: boolean): NativeChild[] {
|
|
1007
|
+
const children: NativeChild[] = [];
|
|
1008
|
+
if (this.#cards.length === 0) {
|
|
1009
|
+
children.push(
|
|
1010
|
+
node("text", { spans: [span("No usage data available.")], role: "omp.usage.untouched" }, undefined, "none"),
|
|
1011
|
+
);
|
|
1012
|
+
} else {
|
|
1013
|
+
// Unlimited providers keep a frame reading "No limits"; only untouched ones collapse.
|
|
1014
|
+
const active = this.#cards.filter(entry => !entry.idle || entry.unlimited);
|
|
1015
|
+
const idle = this.#cards.filter(entry => entry.idle && !entry.unlimited);
|
|
1016
|
+
if (active.length > 0) {
|
|
1017
|
+
children.push(
|
|
1018
|
+
node(
|
|
1019
|
+
"row",
|
|
1020
|
+
{ wrap: true, gap: "md", role: "omp.usage.grid" },
|
|
1021
|
+
active.map(entry => this.#describeCard(entry, meter)),
|
|
1022
|
+
"providers",
|
|
1023
|
+
),
|
|
1024
|
+
);
|
|
1025
|
+
}
|
|
1026
|
+
if (idle.length > 0) {
|
|
1027
|
+
children.push(
|
|
1028
|
+
node(
|
|
1029
|
+
"text",
|
|
1030
|
+
{
|
|
1031
|
+
spans: [span(`Untouched: ${idle.map(entry => entry.name).join(", ")}`)],
|
|
1032
|
+
wrap: "word",
|
|
1033
|
+
role: "omp.usage.untouched",
|
|
1034
|
+
},
|
|
1035
|
+
undefined,
|
|
1036
|
+
"idle",
|
|
1037
|
+
),
|
|
1038
|
+
);
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
children.push(this.#describeActivity(chart));
|
|
1042
|
+
return children;
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
#describeCard(entry: ProviderCard, meter: boolean): NativeNode {
|
|
1046
|
+
const cardStatus =
|
|
1047
|
+
entry.unavailableAccounts.length > 0
|
|
1048
|
+
? statusWithUnavailableAccounts(entry.windows)
|
|
1049
|
+
: entry.unlimited
|
|
1050
|
+
? "ok"
|
|
1051
|
+
: aggregateStatus(entry.windows);
|
|
1052
|
+
const head: NativeChild[] = [text([span(entry.name, "strong")], { truncate: "end" })];
|
|
1053
|
+
if (entry.accounts > 1) head.push(text([span(`${entry.accounts} accounts`, "muted")]));
|
|
1054
|
+
head.push(node("spacer", { grow: 1 }), statusDot(cardStatus));
|
|
1055
|
+
const children: NativeChild[] = [
|
|
1056
|
+
node("row", { role: "omp.usage.provider.head", gap: "sm", align: "center" }, head, "title"),
|
|
1057
|
+
];
|
|
1058
|
+
for (const account of entry.daybreakAccounts ?? []) {
|
|
1059
|
+
children.push(text([span(`Daybreak · ${sanitizeDisplayLine(account)}`, "success")], { truncate: "end" }));
|
|
1060
|
+
}
|
|
1061
|
+
if (entry.resetCredits) {
|
|
1062
|
+
const resets = entry.resetCredits;
|
|
1063
|
+
const details: string[] = [];
|
|
1064
|
+
if (resets.redeemableCount !== resets.bankedCount) details.push(`${resets.redeemableCount} usable now`);
|
|
1065
|
+
if (resets.soonestExpiryMs !== undefined) {
|
|
1066
|
+
details.push(
|
|
1067
|
+
resets.soonestExpiryMs > 0 ? `expires in ${formatDuration(resets.soonestExpiryMs)}` : "expired",
|
|
1068
|
+
);
|
|
1069
|
+
}
|
|
1070
|
+
if (resets.redeemableCount === 0 && resets.unavailableReasons.length > 0) {
|
|
1071
|
+
details.push(`unavailable: ${sanitizeDisplayLine(resets.unavailableReasons.join(" • "))}`);
|
|
1072
|
+
}
|
|
1073
|
+
const label = `${resets.bankedCount} reset${resets.bankedCount === 1 ? "" : "s"} banked`;
|
|
1074
|
+
children.push(
|
|
1075
|
+
node(
|
|
1076
|
+
"row",
|
|
1077
|
+
{ gap: "sm", align: "center" },
|
|
1078
|
+
[
|
|
1079
|
+
node("badge", {
|
|
1080
|
+
text: label,
|
|
1081
|
+
tone: resets.redeemableCount > 0 ? "success" : "warning",
|
|
1082
|
+
title: details.length > 0 ? `${label} · ${details.join(" · ")}` : label,
|
|
1083
|
+
}),
|
|
1084
|
+
],
|
|
1085
|
+
"resets",
|
|
1086
|
+
),
|
|
1087
|
+
);
|
|
1088
|
+
}
|
|
1089
|
+
for (const account of entry.unavailableAccounts) {
|
|
1090
|
+
children.push(mutedText(`${sanitizeDisplayLine(account)}: usage unavailable`, true));
|
|
1091
|
+
}
|
|
1092
|
+
if (entry.unlimited) {
|
|
1093
|
+
children.push(mutedText("No limits"));
|
|
1094
|
+
} else {
|
|
1095
|
+
for (const [index, window] of entry.windows.slice(0, CARD_MAX_WINDOWS).entries()) {
|
|
1096
|
+
const label: TspSpan[] = [span(sanitizeDisplayLine(window.label))];
|
|
1097
|
+
if (window.windowTag) label.push(span(` ${sanitizeDisplayLine(window.windowTag)}`, "dim"));
|
|
1098
|
+
const cells: NativeChild[] = [
|
|
1099
|
+
text(label, { role: "omp.usage.label", truncate: "middle", title: sanitizeDisplayLine(window.label) }),
|
|
1100
|
+
];
|
|
1101
|
+
if (window.fraction === undefined) {
|
|
1102
|
+
cells.push(text([span(window.usedText ?? "No data", "muted")], { truncate: "end" }));
|
|
1103
|
+
} else {
|
|
1104
|
+
const token =
|
|
1105
|
+
window.status === "exhausted" ? "error" : window.status === "warning" ? "warning" : undefined;
|
|
1106
|
+
cells.push(
|
|
1107
|
+
usageMeter(window.fraction, window.status, meter),
|
|
1108
|
+
text([span(leftText(window.fraction), token)], { role: "omp.usage.pct" }),
|
|
1109
|
+
);
|
|
1110
|
+
if (window.resetMs !== undefined) {
|
|
1111
|
+
const reset = resetLabel(this.#nowMs, window.resetMs);
|
|
1112
|
+
cells.push(text([span(reset.text, "dim")], { role: "omp.usage.reset", title: reset.title }));
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
children.push(node("row", { role: "omp.usage.window", gap: "sm", align: "center" }, cells, `w${index}`));
|
|
1116
|
+
}
|
|
1117
|
+
const hidden = entry.windows.length - CARD_MAX_WINDOWS;
|
|
1118
|
+
if (hidden > 0) children.push(mutedText(`+${hidden} more`));
|
|
1119
|
+
}
|
|
1120
|
+
return node(
|
|
1121
|
+
"card",
|
|
1122
|
+
{ role: "omp.usage.provider", grow: 1, min: { w: `${CARD_MIN_WIDTH}ch` } },
|
|
1123
|
+
children,
|
|
1124
|
+
entry.provider,
|
|
1125
|
+
);
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
#describeActivity(chart: boolean): NativeNode {
|
|
1129
|
+
const head: NativeChild[] = [text([span("Activity", "strong")])];
|
|
1130
|
+
if (this.#syncing && this.#activity) head.push(node("spinner", { label: [span("Syncing", "dim")] }));
|
|
1131
|
+
const children: NativeChild[] = [node("row", { gap: "sm", align: "center" }, head, "head")];
|
|
1132
|
+
const points = this.#activity;
|
|
1133
|
+
if (this.#activityError) {
|
|
1134
|
+
const detail = formatActivityErrorDetail(this.#activityError);
|
|
1135
|
+
children.push(
|
|
1136
|
+
mutedText(detail ? `Usage history unavailable (${detail}).` : "Usage history unavailable.", true),
|
|
1137
|
+
);
|
|
1138
|
+
} else if (!points) {
|
|
1139
|
+
children.push(node("spinner", { label: [span("Loading usage history…", "muted")] }));
|
|
1140
|
+
} else {
|
|
1141
|
+
const layout = buildHeatmapLayout(points, NATIVE_HEATMAP_WEEKS);
|
|
1142
|
+
const summary =
|
|
1143
|
+
layout.totalRequests > 0 || layout.totalCost > 0
|
|
1144
|
+
? `${formatActivityTotals(layout)} · last ${NATIVE_HEATMAP_WEEKS} weeks`
|
|
1145
|
+
: `No activity in the last ${NATIVE_HEATMAP_WEEKS} weeks`;
|
|
1146
|
+
children.push(chart ? this.#heatmapChart(layout, points, summary) : this.#heatmapTable(layout, summary));
|
|
1147
|
+
}
|
|
1148
|
+
return node("col", { role: "omp.usage.activity", gap: "sm" }, children, "activity");
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
/** The heatmap as a `chart`: 0–1 intensities, month columns, M/W/F rows, per-day tooltips. */
|
|
1152
|
+
#heatmapChart(layout: HeatmapLayout, points: readonly DailyActivityPoint[], summary: string): NativeNode {
|
|
1153
|
+
const byDay = new Map(points.map(point => [point.day, point]));
|
|
1154
|
+
const cells = layout.cells.map(days => days.map(level => (level === null ? null : level / 4)));
|
|
1155
|
+
const tips = layout.cells.map((days, day) =>
|
|
1156
|
+
days.map((level, week) => {
|
|
1157
|
+
if (level === null) return null;
|
|
1158
|
+
const date = addDays(layout.start, week * 7 + day);
|
|
1159
|
+
const point = byDay.get(localIso(date));
|
|
1160
|
+
if (!point || (point.cost <= 0 && point.requests <= 0)) return `${shortDate(date)} · No activity`;
|
|
1161
|
+
const requests = `${point.requests} request${point.requests === 1 ? "" : "s"}`;
|
|
1162
|
+
const cost = point.cost >= 100 ? `$${Math.round(point.cost)}` : `$${point.cost.toFixed(2)}`;
|
|
1163
|
+
return `${shortDate(date)} · ${cost} · ${requests}`;
|
|
1164
|
+
}),
|
|
1165
|
+
);
|
|
1166
|
+
// A month label needs room: drop one whose successor starts within two columns (GitHub's rule).
|
|
1167
|
+
const starts = layout.monthLabels.flatMap((label, at) => (label ? [{ at, label }] : []));
|
|
1168
|
+
const cols = starts.filter((entry, index) => {
|
|
1169
|
+
const next = starts[index + 1];
|
|
1170
|
+
return !next || next.at - entry.at > 2;
|
|
1171
|
+
});
|
|
1172
|
+
return node(
|
|
1173
|
+
"chart",
|
|
1174
|
+
{ kind: "heatmap", cells, cols, rows: HEATMAP_ROW_LABELS, tips, token: "accent", summary, size: "md" },
|
|
1175
|
+
undefined,
|
|
1176
|
+
"heatmap",
|
|
1177
|
+
);
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
/** The heatmap as a `table` of glyph cells, for terminals without `chart`. */
|
|
1181
|
+
#heatmapTable(layout: HeatmapLayout, summary: string): NativeNode {
|
|
1182
|
+
const cols: TspTableColumn[] = [{ id: "day", priority: NATIVE_HEATMAP_WEEKS }];
|
|
1183
|
+
for (let week = 0; week < NATIVE_HEATMAP_WEEKS; week++) {
|
|
1184
|
+
cols.push({ id: `w${week}`, align: "center", priority: week });
|
|
1185
|
+
}
|
|
1186
|
+
const rows = HEATMAP_DAY_LABELS.map((label, day) => {
|
|
1187
|
+
const cells: Record<string, TspText> = { day: [span(label, "dim")] };
|
|
1188
|
+
for (let week = 0; week < NATIVE_HEATMAP_WEEKS; week++) {
|
|
1189
|
+
const level = layout.cells[day][week];
|
|
1190
|
+
if (level === null) continue;
|
|
1191
|
+
cells[`w${week}`] = level === 0 ? [span("·", "dim")] : [span("■", HEAT_LEVEL_TOKENS[level - 1])];
|
|
1192
|
+
}
|
|
1193
|
+
return { id: `d${day}`, cells };
|
|
1194
|
+
});
|
|
1195
|
+
return node(
|
|
1196
|
+
"col",
|
|
1197
|
+
{ gap: "sm" },
|
|
1198
|
+
[mutedText(summary), node("table", { cols, rows }, undefined, "table")],
|
|
1199
|
+
"heatmap",
|
|
1200
|
+
);
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
/** Per provider: notes and saved resets as a `kv`, the limit buckets as a `table` (one row per account). */
|
|
1204
|
+
#describeDetail(): NativeChild[] {
|
|
1205
|
+
const nowMs = this.#nowMs;
|
|
1206
|
+
const grouped = new Map<string, UsageReport[]>();
|
|
1207
|
+
for (const report of collapseSharedUsageReports(this.#reports)) {
|
|
1208
|
+
const list = grouped.get(report.provider) ?? [];
|
|
1209
|
+
list.push(report);
|
|
1210
|
+
grouped.set(report.provider, list);
|
|
1211
|
+
}
|
|
1212
|
+
const unavailable = this.#options.unavailableAccounts ?? [];
|
|
1213
|
+
const sections: NativeChild[] = [];
|
|
1214
|
+
for (const entry of this.#cards) {
|
|
1215
|
+
const reports = grouped.get(entry.provider) ?? [];
|
|
1216
|
+
const children: NativeChild[] = [];
|
|
1217
|
+
for (const account of unavailable) {
|
|
1218
|
+
if (account.provider !== entry.provider) continue;
|
|
1219
|
+
children.push(mutedText(`${sanitizeDisplayLine(account.label)}: usage unavailable`, true));
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
const facts: { k: TspText; v: TspText }[] = [];
|
|
1223
|
+
const notes = [...new Set(reports.flatMap(report => report.notes ?? []))];
|
|
1224
|
+
if (notes.length > 0) facts.push({ k: [span("Notes", "muted")], v: sanitizeDisplayLine(notes.join(" • ")) });
|
|
1225
|
+
for (const [index, report] of reports.entries()) {
|
|
1226
|
+
const resets = summarizeUsageResetCredits(report.resetCredits, nowMs);
|
|
1227
|
+
if (!resets || resets.bankedCount <= 0) continue;
|
|
1228
|
+
const value: TspSpan[] = [
|
|
1229
|
+
span(
|
|
1230
|
+
`${resets.bankedCount} saved reset${resets.bankedCount === 1 ? "" : "s"}`,
|
|
1231
|
+
resets.redeemableCount > 0 ? "success" : "warning",
|
|
1232
|
+
),
|
|
1233
|
+
];
|
|
1234
|
+
if (resets.redeemableCount !== resets.bankedCount) {
|
|
1235
|
+
value.push(span(` · ${resets.redeemableCount} usable now`, "muted"));
|
|
1236
|
+
}
|
|
1237
|
+
if (resets.soonestExpiry) {
|
|
1238
|
+
const remaining = Date.parse(resets.soonestExpiry) - nowMs;
|
|
1239
|
+
const date = resets.soonestExpiry.slice(0, 10);
|
|
1240
|
+
value.push(
|
|
1241
|
+
span(
|
|
1242
|
+
remaining > 0 ? ` · expires in ${formatDuration(remaining)} (${date})` : ` · expired (${date})`,
|
|
1243
|
+
"muted",
|
|
1244
|
+
),
|
|
1245
|
+
);
|
|
1246
|
+
}
|
|
1247
|
+
if (resets.redeemableCount === 0 && resets.unavailableReason) {
|
|
1248
|
+
value.push(span(` · unavailable: ${sanitizeDisplayLine(resets.unavailableReason)}`, "muted"));
|
|
1249
|
+
}
|
|
1250
|
+
facts.push({
|
|
1251
|
+
k: [span(`Resets · ${reportAccountLabel(report, report.limits[0], index)}`, "muted")],
|
|
1252
|
+
v: value,
|
|
1253
|
+
});
|
|
1254
|
+
}
|
|
1255
|
+
if (facts.length > 0) children.push(node("kv", { items: facts, layout: "grid" }, undefined, "facts"));
|
|
1256
|
+
|
|
1257
|
+
const buckets = new Map<string, { label: string; window?: string; rows: Record<string, TspText>[] }>();
|
|
1258
|
+
const limitNotes: string[] = [];
|
|
1259
|
+
for (const [index, report] of reports.entries()) {
|
|
1260
|
+
for (const limit of report.limits) {
|
|
1261
|
+
const label = sanitizeDisplayLine(formatLimitTitle(limit));
|
|
1262
|
+
const key = `${label}|${limit.window?.id ?? limit.scope.windowId ?? "default"}`;
|
|
1263
|
+
let bucket = buckets.get(key);
|
|
1264
|
+
if (!bucket) {
|
|
1265
|
+
bucket = { label, window: detailWindowLabel(label, limit), rows: [] };
|
|
1266
|
+
buckets.set(key, bucket);
|
|
1267
|
+
}
|
|
1268
|
+
const fraction = resolveUsedFraction(limit);
|
|
1269
|
+
const token =
|
|
1270
|
+
limit.status === "exhausted" ? "error" : limit.status === "warning" ? "warning" : undefined;
|
|
1271
|
+
const resetsAt = limit.window?.resetsAt;
|
|
1272
|
+
bucket.rows.push({
|
|
1273
|
+
account: [span(reportAccountLabel(report, limit, index), "muted")],
|
|
1274
|
+
left:
|
|
1275
|
+
fraction === undefined
|
|
1276
|
+
? [span(formatAbsoluteOnlyAmount([limit]) ?? "No data", "muted")]
|
|
1277
|
+
: [span(leftText(fraction), token)],
|
|
1278
|
+
reset:
|
|
1279
|
+
resetsAt !== undefined && resetsAt > nowMs
|
|
1280
|
+
? [
|
|
1281
|
+
span(
|
|
1282
|
+
`${limit.window?.resetLabel ?? "resets"} in ${formatDuration(resetsAt - nowMs)}`,
|
|
1283
|
+
"dim",
|
|
1284
|
+
),
|
|
1285
|
+
]
|
|
1286
|
+
: "",
|
|
1287
|
+
});
|
|
1288
|
+
for (const note of limit.notes ?? []) limitNotes.push(sanitizeDisplayLine(note));
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1291
|
+
if (buckets.size > 0) {
|
|
1292
|
+
const rows: { id: string; cells: Record<string, TspText> }[] = [];
|
|
1293
|
+
for (const bucket of buckets.values()) {
|
|
1294
|
+
for (const [index, cells] of bucket.rows.entries()) {
|
|
1295
|
+
const limitCell: TspSpan[] =
|
|
1296
|
+
index === 0
|
|
1297
|
+
? [span(bucket.label), ...(bucket.window ? [span(` ${bucket.window}`, "dim")] : [])]
|
|
1298
|
+
: [];
|
|
1299
|
+
rows.push({ id: `r${rows.length}`, cells: { limit: limitCell, ...cells } });
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
const cols: TspTableColumn[] = [
|
|
1303
|
+
{ id: "limit", head: "Limit", grow: 1, priority: 4 },
|
|
1304
|
+
{ id: "account", head: "Account", truncate: "middle", priority: 1 },
|
|
1305
|
+
{ id: "left", head: "Left", align: "end", priority: 3 },
|
|
1306
|
+
{ id: "reset", head: "Resets", align: "end", priority: 2 },
|
|
1307
|
+
];
|
|
1308
|
+
children.push(node("table", { cols, rows }, undefined, "limits"));
|
|
1309
|
+
for (const note of new Set(limitNotes)) children.push(mutedText(note, true));
|
|
1310
|
+
} else if (unavailable.every(account => account.provider !== entry.provider)) {
|
|
1311
|
+
children.push(mutedText("No limits"));
|
|
1312
|
+
}
|
|
1313
|
+
sections.push(
|
|
1314
|
+
node(
|
|
1315
|
+
"section",
|
|
1316
|
+
{ head: [span(entry.name, "strong")], role: "omp.usage.report" },
|
|
1317
|
+
[col(children, { gap: "sm" })],
|
|
1318
|
+
entry.provider,
|
|
1319
|
+
),
|
|
1320
|
+
);
|
|
1321
|
+
}
|
|
1322
|
+
if (sections.length === 0) sections.push(mutedText("No usage data available."));
|
|
1323
|
+
return sections;
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1326
|
+
/** Re-fetch the reports (`r` / Refresh); the last reports stay on failure, with the error in the head. */
|
|
1327
|
+
async #refresh(): Promise<void> {
|
|
1328
|
+
const refresh = this.#options.refresh;
|
|
1329
|
+
if (!refresh || this.#refreshing || this.#closed) return;
|
|
1330
|
+
this.#refreshing = true;
|
|
1331
|
+
this.#refreshError = null;
|
|
1332
|
+
this.#changed();
|
|
1333
|
+
try {
|
|
1334
|
+
const reports = await refresh();
|
|
1335
|
+
if (this.#closed) return;
|
|
1336
|
+
if (reports) {
|
|
1337
|
+
this.#reports = reports;
|
|
1338
|
+
this.#nowMs = Date.now();
|
|
1339
|
+
this.#cards = buildProviderCards(reports, this.#nowMs, this.#options.unavailableAccounts);
|
|
1340
|
+
this.#detailCache = null;
|
|
1341
|
+
}
|
|
1342
|
+
} catch (error) {
|
|
1343
|
+
this.#refreshError = error instanceof Error ? error.message : String(error);
|
|
1344
|
+
} finally {
|
|
1345
|
+
this.#refreshing = false;
|
|
1346
|
+
if (!this.#closed) this.#changed();
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
/** Invalidate the native description and repaint. */
|
|
1351
|
+
#changed(): void {
|
|
1352
|
+
this.#revision++;
|
|
1353
|
+
this.#options.requestRender();
|
|
1354
|
+
}
|
|
1355
|
+
|
|
785
1356
|
#scrollBy(delta: number): void {
|
|
786
1357
|
this.#scroll = Math.max(0, this.#scroll + delta);
|
|
787
1358
|
this.#options.requestRender();
|
|
@@ -790,7 +1361,7 @@ export class UsageDashboardComponent implements Component {
|
|
|
790
1361
|
#setView(view: "overview" | "detail"): void {
|
|
791
1362
|
this.#view = view;
|
|
792
1363
|
this.#scroll = 0;
|
|
793
|
-
this.#
|
|
1364
|
+
this.#changed();
|
|
794
1365
|
}
|
|
795
1366
|
|
|
796
1367
|
handleInput(data: string): void {
|
|
@@ -812,6 +1383,10 @@ export class UsageDashboardComponent implements Component {
|
|
|
812
1383
|
this.#options.onClose();
|
|
813
1384
|
return;
|
|
814
1385
|
}
|
|
1386
|
+
if (matchesKey(data, "r")) {
|
|
1387
|
+
void this.#refresh();
|
|
1388
|
+
return;
|
|
1389
|
+
}
|
|
815
1390
|
if (
|
|
816
1391
|
this.#view === "overview" &&
|
|
817
1392
|
(matchesKey(data, "return") || matchesKey(data, "tab") || matchesKey(data, "d"))
|