@robota-sdk/agent-ui-terminal 3.0.0-beta.81
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/LICENSE +661 -0
- package/README.md +27 -0
- package/dist/node/index.cjs +32 -0
- package/dist/node/index.d.cts +1066 -0
- package/dist/node/index.d.cts.map +1 -0
- package/dist/node/index.d.ts +1066 -0
- package/dist/node/index.d.ts.map +1 -0
- package/dist/node/index.js +33 -0
- package/dist/node/index.js.map +1 -0
- package/package.json +93 -0
- package/src/App.tsx +146 -0
- package/src/AppPresentation.tsx +190 -0
- package/src/AppView.tsx +26 -0
- package/src/BackgroundTaskPanel.tsx +73 -0
- package/src/CjkTextInput.tsx +336 -0
- package/src/ConfirmPrompt.tsx +140 -0
- package/src/ContextWarningBanner.tsx +45 -0
- package/src/ExecutionWorkspaceDetailPane.tsx +85 -0
- package/src/ExecutionWorkspaceSwitcher.tsx +213 -0
- package/src/HistorySearchOverlay.tsx +150 -0
- package/src/InputArea.tsx +338 -0
- package/src/ListPicker.tsx +158 -0
- package/src/MenuSelect.tsx +159 -0
- package/src/MessageList.tsx +308 -0
- package/src/MultiSelectList.tsx +294 -0
- package/src/PendingActionPrompt.tsx +121 -0
- package/src/PermissionPrompt.tsx +186 -0
- package/src/PluginTUI.tsx +258 -0
- package/src/RoleLabel.tsx +72 -0
- package/src/SafeText.tsx +66 -0
- package/src/SessionEventNotices.tsx +27 -0
- package/src/SessionPicker.tsx +72 -0
- package/src/SessionStatusBar.tsx +76 -0
- package/src/SlashAutocomplete.tsx +154 -0
- package/src/StatusBar.tsx +284 -0
- package/src/StreamingIndicator.tsx +145 -0
- package/src/SupervisedSessionView.tsx +455 -0
- package/src/TextPrompt.tsx +112 -0
- package/src/ThemePicker.tsx +366 -0
- package/src/ToolCommandOutput.tsx +48 -0
- package/src/ToolDiffBlock.tsx +44 -0
- package/src/TransportTUI.tsx +166 -0
- package/src/TuiInteractionChannel.ts +567 -0
- package/src/UpdateNotice.tsx +18 -0
- package/src/UsageSummaryEntry.tsx +49 -0
- package/src/WaveText.tsx +70 -0
- package/src/__tests__/ListPicker.test.tsx +188 -0
- package/src/__tests__/MenuSelect.test.tsx +127 -0
- package/src/__tests__/PendingActionPrompt.test.tsx +193 -0
- package/src/__tests__/PluginTUI.test.tsx +167 -0
- package/src/__tests__/SlashAutocomplete.test.tsx +162 -0
- package/src/__tests__/SupervisedSessionView-render.test.tsx +24 -0
- package/src/__tests__/SupervisedSessionView.test.tsx +629 -0
- package/src/__tests__/TextPrompt.test.tsx +112 -0
- package/src/__tests__/TransportTUI.test.tsx +124 -0
- package/src/__tests__/TuiInteractionChannel.askUser.test.ts +124 -0
- package/src/__tests__/TuiInteractionChannel.display-contract.test.ts +284 -0
- package/src/__tests__/TuiInteractionChannel.lifecycle.test.ts +642 -0
- package/src/__tests__/UpdateNotice.test.tsx +15 -0
- package/src/__tests__/abort-after-permission.test.tsx +160 -0
- package/src/__tests__/abort-streaming-e2e.test.tsx +183 -0
- package/src/__tests__/attention-bell-and-marks.test.ts +144 -0
- package/src/__tests__/autocomplete-editing-keys.test.tsx +127 -0
- package/src/__tests__/background-task-panel-countdown.test.tsx +75 -0
- package/src/__tests__/background-task-panel.test.tsx +98 -0
- package/src/__tests__/background-task-row-format.test.ts +152 -0
- package/src/__tests__/channel-factory-integration.test.ts +150 -0
- package/src/__tests__/cjk-defer-submit.test.tsx +67 -0
- package/src/__tests__/cjk-fallback-render.test.tsx +78 -0
- package/src/__tests__/cjk-text-input-flow.test.ts +186 -0
- package/src/__tests__/cjk-text-input.test.ts +191 -0
- package/src/__tests__/command-handoff-pty-e2e.test.ts +107 -0
- package/src/__tests__/command-output-summary.test.ts +95 -0
- package/src/__tests__/command-result-handler.test.ts +171 -0
- package/src/__tests__/compact-event-bridge.test.ts +20 -0
- package/src/__tests__/confirm-permission-flow.test.ts +149 -0
- package/src/__tests__/confirm-prompt.test.tsx +103 -0
- package/src/__tests__/contextual-keybindings-input.test.tsx +158 -0
- package/src/__tests__/default-tui-cli-adapter-host-settings.test.ts +45 -0
- package/src/__tests__/effort-reaches-the-session.test.ts +41 -0
- package/src/__tests__/execution-workspace-switcher.test.tsx +147 -0
- package/src/__tests__/execution-workspace-view-model.test.ts +94 -0
- package/src/__tests__/external-prompt-notice.test.tsx +100 -0
- package/src/__tests__/fixtures/command-handoff-driver.tsx +111 -0
- package/src/__tests__/fixtures/fake-editor.sh +7 -0
- package/src/__tests__/fixtures/terminal-handoff-driver.tsx +90 -0
- package/src/__tests__/fork-attach.test.tsx +235 -0
- package/src/__tests__/helpers/terminal-profiles.ts +152 -0
- package/src/__tests__/helpers/vt-cursor-interpreter.ts +175 -0
- package/src/__tests__/history-clear-broadcast.test.ts +66 -0
- package/src/__tests__/history-search-overlay.test.tsx +329 -0
- package/src/__tests__/humanize-tool-name.test.ts +30 -0
- package/src/__tests__/input-area-bottom-border.test.tsx +45 -0
- package/src/__tests__/input-area-flow.test.ts +199 -0
- package/src/__tests__/input-area-focus-handoff.test.tsx +77 -0
- package/src/__tests__/interaction-channel-charter.test.ts +13 -0
- package/src/__tests__/key-hint-consistency.test.tsx +249 -0
- package/src/__tests__/key-hint-footer.test.tsx +69 -0
- package/src/__tests__/live-trace-reaches-session.test.ts +14 -0
- package/src/__tests__/message-list-rendering.test.tsx +473 -0
- package/src/__tests__/org-policy-reaches-the-channel.test.ts +77 -0
- package/src/__tests__/output-style-reaches-session.test.ts +35 -0
- package/src/__tests__/palette-consistency.test.ts +330 -0
- package/src/__tests__/permission-prompt-availability.test.tsx +23 -0
- package/src/__tests__/permission-prompt-peer.test.tsx +34 -0
- package/src/__tests__/product-display-name.test.tsx +79 -0
- package/src/__tests__/prompt-queue.test.tsx +260 -0
- package/src/__tests__/provider-definitions-reach-the-session.test.ts +75 -0
- package/src/__tests__/pty/ask-user-question.ptytest.ts +72 -0
- package/src/__tests__/pty/background-work-switcher.ptytest.ts +53 -0
- package/src/__tests__/pty/behavior-2437-git.ptytest.ts +342 -0
- package/src/__tests__/pty/fixtures/ask-user-question.jsonl +3 -0
- package/src/__tests__/pty/fixtures/replay-conversation.jsonl +4 -0
- package/src/__tests__/pty/fixtures/screen-005-ask-shapes.jsonl +5 -0
- package/src/__tests__/pty/fixtures/screen-005-permission.jsonl +3 -0
- package/src/__tests__/pty/fixtures/screen-006-colors.jsonl +3 -0
- package/src/__tests__/pty/fixtures/screen-2002-themes.jsonl +5 -0
- package/src/__tests__/pty/flag-tui.ptytest.ts +56 -0
- package/src/__tests__/pty/flow-2006-deep-link.ptytest.ts +291 -0
- package/src/__tests__/pty/ime-cursor-tmux.ptytest.ts +165 -0
- package/src/__tests__/pty/ime-cursor.ptytest.ts +186 -0
- package/src/__tests__/pty/isolated-home.ts +64 -0
- package/src/__tests__/pty/org-policy.ptytest.ts +71 -0
- package/src/__tests__/pty/provider-setup.ptytest.ts +61 -0
- package/src/__tests__/pty/pty-driver.ts +153 -0
- package/src/__tests__/pty/replay-conversation.ptytest.ts +68 -0
- package/src/__tests__/pty/screen-005-prompt-footers.ptytest.ts +134 -0
- package/src/__tests__/pty/screen-006-no-color.ptytest.ts +138 -0
- package/src/__tests__/pty/screen-010-scrollback.ptytest.ts +123 -0
- package/src/__tests__/pty/screen-1993-scrollback.ptytest.ts +124 -0
- package/src/__tests__/pty/screen-2002-themes.ptytest.ts +292 -0
- package/src/__tests__/pty/screen-2670-prepark.ptytest.ts +228 -0
- package/src/__tests__/pty/screen-reader-mode.ptytest.ts +228 -0
- package/src/__tests__/pty/settings-screen.ptytest.ts +49 -0
- package/src/__tests__/pty/spawn-pty.test.ts +120 -0
- package/src/__tests__/pty/spawn-pty.ts +229 -0
- package/src/__tests__/pty/terminal-handoff.ptytest.ts +79 -0
- package/src/__tests__/pty/tui-pty.ptytest.ts +65 -0
- package/src/__tests__/pty-driver-dispose.test.ts +43 -0
- package/src/__tests__/real-cursor-positioning.test.tsx +263 -0
- package/src/__tests__/rename-broadcast-persistence.test.tsx +140 -0
- package/src/__tests__/render-channel-options.test.ts +201 -0
- package/src/__tests__/render-lifecycle.test.ts +36 -0
- package/src/__tests__/render-markdown.test.ts +164 -0
- package/src/__tests__/rendered-markdown-styling.test.tsx +98 -0
- package/src/__tests__/safe-text-boundary.test.tsx +75 -0
- package/src/__tests__/screen-reader-input.test.tsx +106 -0
- package/src/__tests__/screen-reader-labels.test.ts +91 -0
- package/src/__tests__/screen-reader-menus.test.tsx +200 -0
- package/src/__tests__/screen-reader-pacing.test.ts +230 -0
- package/src/__tests__/screen-reader-prepark-echo.test.tsx +200 -0
- package/src/__tests__/screen-reader-render-options.test.ts +262 -0
- package/src/__tests__/screen-reader-stdout.test.ts +322 -0
- package/src/__tests__/screen-reader-turn-marks-port.test.tsx +85 -0
- package/src/__tests__/screen-reader-turn-signals.test.tsx +125 -0
- package/src/__tests__/sec-019-terminal-sanitizer.test.ts +276 -0
- package/src/__tests__/sec-019-tool-label-render.test.tsx +207 -0
- package/src/__tests__/selection-flow.test.ts +61 -0
- package/src/__tests__/session-event-notices.test.tsx +31 -0
- package/src/__tests__/session-init-poller.test.ts +102 -0
- package/src/__tests__/session-switch-channel.test.tsx +470 -0
- package/src/__tests__/shutdown-signal.test.ts +34 -0
- package/src/__tests__/status-activity.test.ts +71 -0
- package/src/__tests__/status-bar.test.tsx +282 -0
- package/src/__tests__/status-glyph.test.ts +30 -0
- package/src/__tests__/streaming-indicator.test.tsx +177 -0
- package/src/__tests__/terminal-capabilities.test.ts +153 -0
- package/src/__tests__/terminal-handoff-controller.test.ts +173 -0
- package/src/__tests__/terminal-handoff-pty-e2e.test.ts +77 -0
- package/src/__tests__/text-prompt-flow.test.ts +77 -0
- package/src/__tests__/theme-picker.test.tsx +345 -0
- package/src/__tests__/transport-boundary.test.ts +11 -0
- package/src/__tests__/tui-app-channel-port.test.ts +31 -0
- package/src/__tests__/tui-channel-init-failure.test.ts +62 -0
- package/src/__tests__/tui-channel-lifecycle-coordinator.test.ts +258 -0
- package/src/__tests__/tui-interaction-queues.test.ts +139 -0
- package/src/__tests__/tui-preset-capabilities.test.ts +33 -0
- package/src/__tests__/tui-state-manager.error-stall.test.ts +169 -0
- package/src/__tests__/tui-state-manager.test.ts +451 -0
- package/src/__tests__/ui-intent-rendering.test.tsx +291 -0
- package/src/__tests__/wave-text.test.tsx +133 -0
- package/src/app-banner.tsx +42 -0
- package/src/app-static-items.ts +35 -0
- package/src/app-view-model.ts +136 -0
- package/src/attention/__tests__/attention-coordinator.test.ts +166 -0
- package/src/attention/__tests__/attention-tracker.test.ts +103 -0
- package/src/attention/__tests__/countdown.test.ts +15 -0
- package/src/attention/__tests__/focus-input-filter.test.ts +92 -0
- package/src/attention/__tests__/interval-recap.test.ts +49 -0
- package/src/attention/attention-coordinator.ts +71 -0
- package/src/attention/attention-tracker.ts +124 -0
- package/src/attention/countdown.ts +24 -0
- package/src/attention/focus-input-filter.ts +131 -0
- package/src/attention/interval-recap.ts +148 -0
- package/src/attention/time-units.ts +6 -0
- package/src/attention-bell.ts +79 -0
- package/src/attributed-user-echo.ts +30 -0
- package/src/background-task-row-format.ts +99 -0
- package/src/command-interaction.ts +8 -0
- package/src/command-output-summary.ts +129 -0
- package/src/create-default-tui-cli-adapter.ts +45 -0
- package/src/execution-workspace-switcher-selection.ts +118 -0
- package/src/execution-workspace-view-model.ts +139 -0
- package/src/external-prompt-notice.tsx +38 -0
- package/src/flows/__tests__/background-focus-flow.test.ts +48 -0
- package/src/flows/__tests__/defer-submit.test.ts +87 -0
- package/src/flows/__tests__/real-cursor-flow.test.ts +124 -0
- package/src/flows/background-focus-flow.ts +44 -0
- package/src/flows/cjk-cursor-motion.ts +75 -0
- package/src/flows/cjk-text-deletion.ts +53 -0
- package/src/flows/cjk-text-input-flow.ts +248 -0
- package/src/flows/cjk-text-input-types.ts +24 -0
- package/src/flows/confirm-prompt-flow.ts +90 -0
- package/src/flows/defer-submit.ts +67 -0
- package/src/flows/fork-attach-flow.ts +62 -0
- package/src/flows/input-area-flow.ts +188 -0
- package/src/flows/permission-prompt-flow.ts +108 -0
- package/src/flows/real-cursor-flow.ts +83 -0
- package/src/flows/selection-flow.ts +205 -0
- package/src/flows/session-init-poller.ts +76 -0
- package/src/flows/text-prompt-flow.ts +93 -0
- package/src/history-search/__tests__/history-search-flow.test.ts +86 -0
- package/src/history-search/history-search-flow.ts +94 -0
- package/src/history-search/useHistorySearch.ts +336 -0
- package/src/hooks/__tests__/use-app-loop-escape.test.tsx +74 -0
- package/src/hooks/__tests__/use-plugin-callbacks.test.ts +18 -0
- package/src/hooks/__tests__/use-tui-channel-stability.test.tsx +100 -0
- package/src/hooks/__tests__/useAppThemeState.test.tsx +130 -0
- package/src/hooks/command-result-handler.ts +44 -0
- package/src/hooks/side-effects-types.ts +45 -0
- package/src/hooks/useAppController.ts +267 -0
- package/src/hooks/useAppInputBindings.ts +145 -0
- package/src/hooks/useAppInteractionState.ts +81 -0
- package/src/hooks/useAppLifecycleState.ts +81 -0
- package/src/hooks/useAppOverlays.ts +74 -0
- package/src/hooks/useAppScreenState.ts +81 -0
- package/src/hooks/useAppSubmissionState.ts +45 -0
- package/src/hooks/useAppThemeState.ts +157 -0
- package/src/hooks/useAppWorkspaceState.ts +149 -0
- package/src/hooks/useAppearanceSettings.ts +30 -0
- package/src/hooks/useAutocomplete.ts +101 -0
- package/src/hooks/useBackgroundPanel.ts +58 -0
- package/src/hooks/useCountdownTick.ts +24 -0
- package/src/hooks/useExecutionDetailPage.ts +69 -0
- package/src/hooks/useForkAttach.ts +60 -0
- package/src/hooks/useInputAreaHistorySearch.ts +72 -0
- package/src/hooks/useInputAreaKeys.ts +114 -0
- package/src/hooks/useNumberedSelection.ts +76 -0
- package/src/hooks/usePluginCallbacks.ts +38 -0
- package/src/hooks/usePluginScreenData.ts +85 -0
- package/src/hooks/useRealCursorPosition.ts +125 -0
- package/src/hooks/useRenderMarkdown.ts +38 -0
- package/src/hooks/useScreenReaderTurnSignals.ts +113 -0
- package/src/hooks/useSideEffects.ts +144 -0
- package/src/hooks/useStatusLineSettings.ts +50 -0
- package/src/hooks/useTerminalHandoffSuspension.ts +30 -0
- package/src/hooks/useTuiChannel.ts +129 -0
- package/src/humanize-tool-name.ts +51 -0
- package/src/index.ts +56 -0
- package/src/input-area-rules.tsx +71 -0
- package/src/json-value.ts +18 -0
- package/src/key-hint-footer.tsx +54 -0
- package/src/keybindings/__tests__/keybinding-registry.test.ts +228 -0
- package/src/keybindings/__tests__/node-keybindings-source.test.ts +111 -0
- package/src/keybindings/keybinding-catalogue.ts +171 -0
- package/src/keybindings/keybinding-registry.ts +303 -0
- package/src/keybindings/keybinding-syntax.ts +155 -0
- package/src/keybindings/keybindings-context.tsx +124 -0
- package/src/keybindings/node-keybindings-source.ts +189 -0
- package/src/numbered-list.tsx +113 -0
- package/src/plugin-tui-handlers.ts +163 -0
- package/src/product-display-name-context.tsx +30 -0
- package/src/render-markdown.ts +209 -0
- package/src/render.tsx +544 -0
- package/src/sanitize-terminal-text.ts +171 -0
- package/src/screen-reader-announcement.ts +52 -0
- package/src/screen-reader-context.tsx +32 -0
- package/src/screen-reader-labels.ts +64 -0
- package/src/screen-reader-pacing-context.tsx +47 -0
- package/src/screen-reader-pacing.ts +178 -0
- package/src/screen-reader-stdout.ts +328 -0
- package/src/shutdown-signal.ts +34 -0
- package/src/status-activity.ts +70 -0
- package/src/status-glyph.ts +78 -0
- package/src/terminal-capabilities-context.tsx +27 -0
- package/src/terminal-capabilities.ts +77 -0
- package/src/terminal-focus-reporting.ts +50 -0
- package/src/terminal-handoff-controller.ts +98 -0
- package/src/terminal-marks.ts +63 -0
- package/src/theme/__tests__/built-in-themes.test.ts +117 -0
- package/src/theme/__tests__/color-vision.test.ts +82 -0
- package/src/theme/__tests__/theme-context.test.tsx +146 -0
- package/src/theme/__tests__/theme-document.test.ts +222 -0
- package/src/theme/__tests__/theme-registry.test.ts +138 -0
- package/src/theme/__tests__/theme-styles.test.ts +233 -0
- package/src/theme/built-in-themes.ts +342 -0
- package/src/theme/color-vision.ts +171 -0
- package/src/theme/index.ts +45 -0
- package/src/theme/theme-context.tsx +81 -0
- package/src/theme/theme-contracts.ts +139 -0
- package/src/theme/theme-document.ts +278 -0
- package/src/theme/theme-registry.ts +127 -0
- package/src/theme/theme-styles.ts +177 -0
- package/src/tool-summary-status.ts +59 -0
- package/src/tui-app-channel-port.ts +83 -0
- package/src/tui-channel-lifecycle-coordinator.ts +149 -0
- package/src/tui-channel-options.ts +172 -0
- package/src/tui-cli-adapter-context.tsx +13 -0
- package/src/tui-cli-adapter.ts +24 -0
- package/src/tui-interaction-queues.ts +163 -0
- package/src/tui-session-binding.ts +52 -0
- package/src/tui-session-event-projector.ts +140 -0
- package/src/tui-session-events.ts +82 -0
- package/src/tui-session-notice-store.ts +43 -0
- package/src/tui-session-options.ts +118 -0
- package/src/tui-state-manager.ts +327 -0
- package/src/types.ts +19 -0
- package/src/use-terminal-title.ts +29 -0
- package/src/utils/__tests__/edit-diff.test.ts +426 -0
- package/src/utils/__tests__/paste-detection.test.ts +116 -0
- package/src/utils/__tests__/paste-labels.test.ts +46 -0
- package/src/utils/__tests__/tool-call-extractor.test.ts +227 -0
- package/src/utils/__tests__/tool-diff-summary.test.ts +104 -0
- package/src/utils/edit-diff.ts +151 -0
- package/src/utils/input-top-border.ts +19 -0
- package/src/utils/paste-labels.ts +9 -0
- package/src/utils/tool-call-extractor.ts +92 -0
- package/src/utils/tool-diff-summary.ts +75 -0
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PluginTUI — Main orchestrator component for plugin management.
|
|
3
|
+
* Manages a stack of screens: main, marketplace, installed plugins, etc.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import React, { useState, useCallback } from 'react';
|
|
7
|
+
|
|
8
|
+
import ConfirmPrompt from './ConfirmPrompt.js';
|
|
9
|
+
import { usePluginScreenData } from './hooks/usePluginScreenData.js';
|
|
10
|
+
import MenuSelect from './MenuSelect.js';
|
|
11
|
+
import {
|
|
12
|
+
handleMainSelect,
|
|
13
|
+
handleMarketplaceListSelect,
|
|
14
|
+
handleMarketplaceActionSelect,
|
|
15
|
+
handleMarketplaceBrowseSelect,
|
|
16
|
+
handleInstallScopeSelect,
|
|
17
|
+
handleInstalledListSelect,
|
|
18
|
+
handleInstalledActionSelect,
|
|
19
|
+
} from './plugin-tui-handlers.js';
|
|
20
|
+
import TextPrompt from './TextPrompt.js';
|
|
21
|
+
|
|
22
|
+
import type { IMenuSelectItem } from './MenuSelect.js';
|
|
23
|
+
import type { ICommandPluginAdapter } from '@robota-sdk/agent-interface-command';
|
|
24
|
+
|
|
25
|
+
type TScreenId =
|
|
26
|
+
| 'main'
|
|
27
|
+
| 'marketplace-list'
|
|
28
|
+
| 'marketplace-action'
|
|
29
|
+
| 'marketplace-browse'
|
|
30
|
+
| 'marketplace-install-scope'
|
|
31
|
+
| 'marketplace-add'
|
|
32
|
+
| 'installed-list'
|
|
33
|
+
| 'installed-action';
|
|
34
|
+
|
|
35
|
+
interface IMenuContext {
|
|
36
|
+
marketplace?: string;
|
|
37
|
+
pluginId?: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
interface IMenuState {
|
|
41
|
+
screen: TScreenId;
|
|
42
|
+
context?: IMenuContext;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
interface IConfirmState {
|
|
46
|
+
message: string;
|
|
47
|
+
onConfirm: () => void;
|
|
48
|
+
onCancel: () => void;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
interface IProps {
|
|
52
|
+
callbacks: ICommandPluginAdapter;
|
|
53
|
+
onClose: () => void;
|
|
54
|
+
addMessage?: (msg: { role: string; content: string }) => void;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export default function PluginTUI({ callbacks, onClose, addMessage }: IProps): React.ReactElement {
|
|
58
|
+
const [stack, setStack] = useState<IMenuState[]>([{ screen: 'main' }]);
|
|
59
|
+
const [confirm, setConfirm] = useState<IConfirmState | undefined>();
|
|
60
|
+
const [refreshCounter, setRefreshCounter] = useState(0);
|
|
61
|
+
|
|
62
|
+
const current = stack[stack.length - 1] ?? { screen: 'main' };
|
|
63
|
+
|
|
64
|
+
const push = useCallback((state: IMenuState) => {
|
|
65
|
+
setStack((prev) => [...prev, state]);
|
|
66
|
+
}, []);
|
|
67
|
+
|
|
68
|
+
const pop = useCallback(() => {
|
|
69
|
+
setStack((prev) => {
|
|
70
|
+
if (prev.length <= 1) {
|
|
71
|
+
onClose();
|
|
72
|
+
return prev;
|
|
73
|
+
}
|
|
74
|
+
return prev.slice(0, -1);
|
|
75
|
+
});
|
|
76
|
+
}, [onClose]);
|
|
77
|
+
|
|
78
|
+
const popN = useCallback(
|
|
79
|
+
(n: number) => {
|
|
80
|
+
setStack((prev) => {
|
|
81
|
+
const next = prev.slice(0, Math.max(1, prev.length - n));
|
|
82
|
+
if (next.length === 0) {
|
|
83
|
+
onClose();
|
|
84
|
+
return prev;
|
|
85
|
+
}
|
|
86
|
+
return next;
|
|
87
|
+
});
|
|
88
|
+
},
|
|
89
|
+
[onClose],
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
const notify = useCallback(
|
|
93
|
+
(content: string) => {
|
|
94
|
+
addMessage?.({ role: 'system', content });
|
|
95
|
+
},
|
|
96
|
+
[addMessage],
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
const refresh = useCallback(() => {
|
|
100
|
+
setRefreshCounter((c) => c + 1);
|
|
101
|
+
}, []);
|
|
102
|
+
|
|
103
|
+
const setConfirmNav = useCallback(
|
|
104
|
+
(state: IConfirmState | undefined) => setConfirm(state),
|
|
105
|
+
[setConfirm],
|
|
106
|
+
);
|
|
107
|
+
// nav.push accepts a loose { screen: string } shape to satisfy plugin-tui-handlers types;
|
|
108
|
+
// we cast screen to TScreenId which is safe because handlers only push valid screen names.
|
|
109
|
+
const pushNav = useCallback(
|
|
110
|
+
(state: { screen: string; context?: IMenuContext }) =>
|
|
111
|
+
push({ screen: state.screen as TScreenId, context: state.context }),
|
|
112
|
+
[push],
|
|
113
|
+
);
|
|
114
|
+
const nav = { push: pushNav, pop, popN, notify, setConfirm: setConfirmNav, refresh };
|
|
115
|
+
|
|
116
|
+
const { items, loading, error } = usePluginScreenData(
|
|
117
|
+
current.screen,
|
|
118
|
+
current.context?.marketplace,
|
|
119
|
+
callbacks,
|
|
120
|
+
refreshCounter,
|
|
121
|
+
stack.length,
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
const handleSelect = useCallback(
|
|
125
|
+
(value: string) => {
|
|
126
|
+
const screen = current.screen;
|
|
127
|
+
const ctx = current.context;
|
|
128
|
+
|
|
129
|
+
if (screen === 'main') handleMainSelect(value, nav);
|
|
130
|
+
else if (screen === 'marketplace-list') handleMarketplaceListSelect(value, nav);
|
|
131
|
+
else if (screen === 'marketplace-action')
|
|
132
|
+
handleMarketplaceActionSelect(value, ctx?.marketplace ?? '', callbacks, nav);
|
|
133
|
+
else if (screen === 'marketplace-browse')
|
|
134
|
+
handleMarketplaceBrowseSelect(value, ctx?.marketplace ?? '', items, nav);
|
|
135
|
+
else if (screen === 'marketplace-install-scope')
|
|
136
|
+
handleInstallScopeSelect(value, ctx?.pluginId ?? '', callbacks, nav);
|
|
137
|
+
else if (screen === 'installed-list') handleInstalledListSelect(value, callbacks, nav);
|
|
138
|
+
else if (screen === 'installed-action')
|
|
139
|
+
handleInstalledActionSelect(value, ctx?.pluginId ?? '', callbacks, nav);
|
|
140
|
+
},
|
|
141
|
+
[current, items, callbacks, push, pop, popN, notify, setConfirm, refresh],
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
const handleTextSubmit = useCallback(
|
|
145
|
+
(value: string) => {
|
|
146
|
+
if (current.screen === 'marketplace-add') {
|
|
147
|
+
callbacks
|
|
148
|
+
.marketplaceAdd(value)
|
|
149
|
+
.then((name) => {
|
|
150
|
+
notify(`Added marketplace "${name}" from ${value}.`);
|
|
151
|
+
pop();
|
|
152
|
+
})
|
|
153
|
+
.catch((err) => {
|
|
154
|
+
notify(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
155
|
+
pop();
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
[current.screen, callbacks, notify, pop],
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
// Confirm overlay intercepts everything
|
|
163
|
+
if (confirm) {
|
|
164
|
+
return (
|
|
165
|
+
<ConfirmPrompt
|
|
166
|
+
message={confirm.message}
|
|
167
|
+
onSelect={(index) => {
|
|
168
|
+
if (index === 0) confirm.onConfirm();
|
|
169
|
+
else confirm.onCancel();
|
|
170
|
+
}}
|
|
171
|
+
/>
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const screen = current.screen;
|
|
176
|
+
|
|
177
|
+
if (screen === 'marketplace-add') {
|
|
178
|
+
return (
|
|
179
|
+
<TextPrompt
|
|
180
|
+
title="Add Marketplace Source"
|
|
181
|
+
placeholder="owner/repo or git URL"
|
|
182
|
+
onSubmit={handleTextSubmit}
|
|
183
|
+
onCancel={pop}
|
|
184
|
+
validate={(v) => (!v.includes('/') ? 'Must be owner/repo or a git URL' : undefined)}
|
|
185
|
+
/>
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (screen === 'marketplace-action') {
|
|
190
|
+
return (
|
|
191
|
+
<MenuSelect
|
|
192
|
+
key={stack.length}
|
|
193
|
+
title={`Marketplace: ${current.context?.marketplace ?? ''}`}
|
|
194
|
+
items={[
|
|
195
|
+
{ label: 'Browse plugins', value: 'browse' },
|
|
196
|
+
{ label: 'Update', value: 'update' },
|
|
197
|
+
{ label: 'Remove', value: 'remove' },
|
|
198
|
+
]}
|
|
199
|
+
onSelect={handleSelect}
|
|
200
|
+
onBack={pop}
|
|
201
|
+
/>
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (screen === 'marketplace-install-scope') {
|
|
206
|
+
return (
|
|
207
|
+
<MenuSelect
|
|
208
|
+
key={stack.length}
|
|
209
|
+
title={`Install scope for "${current.context?.pluginId ?? ''}"`}
|
|
210
|
+
items={[
|
|
211
|
+
{ label: 'User scope', value: 'user' },
|
|
212
|
+
{ label: 'Project scope', value: 'project' },
|
|
213
|
+
]}
|
|
214
|
+
onSelect={handleSelect}
|
|
215
|
+
onBack={pop}
|
|
216
|
+
/>
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (screen === 'installed-action') {
|
|
221
|
+
return (
|
|
222
|
+
<MenuSelect
|
|
223
|
+
key={stack.length}
|
|
224
|
+
title={`Plugin: ${current.context?.pluginId ?? ''}`}
|
|
225
|
+
items={[{ label: 'Uninstall', value: 'uninstall' }]}
|
|
226
|
+
onSelect={handleSelect}
|
|
227
|
+
onBack={pop}
|
|
228
|
+
/>
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Screens with async items: main, marketplace-list, marketplace-browse, installed-list
|
|
233
|
+
const titleMap: Record<string, string> = {
|
|
234
|
+
main: 'Plugin Management',
|
|
235
|
+
'marketplace-list': 'Marketplace',
|
|
236
|
+
'marketplace-browse': `Browse: ${current.context?.marketplace ?? ''}`,
|
|
237
|
+
'installed-list': 'Installed Plugins',
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
const staticItemsMap: Record<string, IMenuSelectItem[]> = {
|
|
241
|
+
main: [
|
|
242
|
+
{ label: 'Marketplace', value: 'marketplace' },
|
|
243
|
+
{ label: 'Installed Plugins', value: 'installed' },
|
|
244
|
+
],
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
return (
|
|
248
|
+
<MenuSelect
|
|
249
|
+
key={`${screen}-${stack.length}-${refreshCounter}`}
|
|
250
|
+
title={titleMap[screen] ?? 'Plugin Management'}
|
|
251
|
+
items={staticItemsMap[screen] ?? items}
|
|
252
|
+
onSelect={handleSelect}
|
|
253
|
+
onBack={pop}
|
|
254
|
+
loading={loading}
|
|
255
|
+
error={error}
|
|
256
|
+
/>
|
|
257
|
+
);
|
|
258
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { OWNER_DRIVER_ID } from '@robota-sdk/agent-interface-session';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
|
|
4
|
+
import { Text } from './SafeText.js';
|
|
5
|
+
import { useProductDisplayName } from './product-display-name-context.js';
|
|
6
|
+
import { useScreenReader } from './screen-reader-context.js';
|
|
7
|
+
import { screenReaderLabelForRole } from './screen-reader-labels.js';
|
|
8
|
+
import { usePalette } from './theme/index.js';
|
|
9
|
+
|
|
10
|
+
import type { TUniversalMessage } from '@robota-sdk/agent-core';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Who a transcript line is attributed to, and in what colour. Extracted from MessageList when
|
|
14
|
+
* PEER-007 gave the `user` case a second thing to decide: the label stopped being a constant per
|
|
15
|
+
* role, so it is its own responsibility rather than four inline returns.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* PEER-007 (issue #1915): the operator's own turns stay `You:`; a turn driven by anyone else is
|
|
20
|
+
* labelled with WHO drove it, so a co-driven transcript can be read after the fact. `owner` is the
|
|
21
|
+
* operator, so it reads as `You:` too; anything else prints as itself (`peer:<session-id>`, `agent`).
|
|
22
|
+
*/
|
|
23
|
+
function driverLabel(driverId: string): string {
|
|
24
|
+
return driverId === OWNER_DRIVER_ID ? 'You' : driverId;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function RoleLabel({
|
|
28
|
+
role,
|
|
29
|
+
driverId,
|
|
30
|
+
}: {
|
|
31
|
+
role: TUniversalMessage['role'];
|
|
32
|
+
driverId?: string;
|
|
33
|
+
}): React.ReactElement {
|
|
34
|
+
const palette = usePalette();
|
|
35
|
+
const screenReader = useScreenReader();
|
|
36
|
+
const productDisplayName = useProductDisplayName();
|
|
37
|
+
if (screenReader) {
|
|
38
|
+
// CLI-2004: the label comes from the ROLE, never the vendor — `assistant:` under every
|
|
39
|
+
// agent-provider-*. No colour: in this mode the searchable word is the whole cue.
|
|
40
|
+
const label =
|
|
41
|
+
role === 'user' && driverId !== undefined && driverId !== OWNER_DRIVER_ID
|
|
42
|
+
? `${driverId}:`
|
|
43
|
+
: screenReaderLabelForRole(role);
|
|
44
|
+
return <Text>{label} </Text>;
|
|
45
|
+
}
|
|
46
|
+
switch (role) {
|
|
47
|
+
case 'user':
|
|
48
|
+
return (
|
|
49
|
+
<Text color={palette.text.success} bold>
|
|
50
|
+
{driverId !== undefined ? driverLabel(driverId) : 'You'}:{' '}
|
|
51
|
+
</Text>
|
|
52
|
+
);
|
|
53
|
+
case 'assistant':
|
|
54
|
+
return (
|
|
55
|
+
<Text color={palette.text.accent} bold>
|
|
56
|
+
{productDisplayName}:{' '}
|
|
57
|
+
</Text>
|
|
58
|
+
);
|
|
59
|
+
case 'system':
|
|
60
|
+
return (
|
|
61
|
+
<Text color={palette.text.warning} bold>
|
|
62
|
+
System:{' '}
|
|
63
|
+
</Text>
|
|
64
|
+
);
|
|
65
|
+
case 'tool':
|
|
66
|
+
return (
|
|
67
|
+
<Text color={palette.text.emphasis} bold>
|
|
68
|
+
Tool:{' '}
|
|
69
|
+
</Text>
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
}
|
package/src/SafeText.tsx
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `SafeText` — the ONE way a string reaches the terminal from this package (#2222).
|
|
3
|
+
*
|
|
4
|
+
* SEC-019 fixed every render site that was found to put untrusted text — model output, tool stdout,
|
|
5
|
+
* file contents, plugin-supplied names — on the terminal through Ink's `<Text>`. Six review rounds
|
|
6
|
+
* found three sites nobody had covered, because "sanitized" was a property of individual lines of
|
|
7
|
+
* code, and a site added later by someone who never read SEC-019 is covered by nothing.
|
|
8
|
+
*
|
|
9
|
+
* This component makes the boundary structural: every string child is passed through
|
|
10
|
+
* `sanitizeTerminalText` before Ink sees it, and the `tui-safe-text-boundary` scan refuses any other
|
|
11
|
+
* module in this package that imports `Text` from `ink` — plain, aliased, or through a namespace.
|
|
12
|
+
* A render site therefore CANNOT put a string on the terminal without passing through here. For the
|
|
13
|
+
* many constant labels this is a no-op; that is the point — the boundary is not "the sites judged
|
|
14
|
+
* to carry untrusted text", because that judgement is what already failed twice.
|
|
15
|
+
*
|
|
16
|
+
* This module is the only permitted importer of Ink's `Text`.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { Text as InkText } from 'ink';
|
|
20
|
+
import React from 'react';
|
|
21
|
+
|
|
22
|
+
import { sanitizeTerminalText } from './sanitize-terminal-text.js';
|
|
23
|
+
|
|
24
|
+
import type { ReactNode } from 'react';
|
|
25
|
+
|
|
26
|
+
type TInkTextProps = React.ComponentProps<typeof InkText>;
|
|
27
|
+
|
|
28
|
+
function sanitizeChildren(children: ReactNode): ReactNode {
|
|
29
|
+
if (typeof children === 'string') return sanitizeTerminalText(children);
|
|
30
|
+
if (typeof children === 'number' || typeof children === 'boolean') return children;
|
|
31
|
+
if (children === null || children === undefined) return children;
|
|
32
|
+
if (Array.isArray(children)) return children.map((child) => sanitizeChildren(child));
|
|
33
|
+
// A nested element (another <Text>, a <Box>) sanitizes its own string children on its own render.
|
|
34
|
+
return children;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Ink's `Text`, with every string child sanitized for the terminal. Same props, same output shape. */
|
|
38
|
+
export function SafeText(props: TInkTextProps): React.JSX.Element {
|
|
39
|
+
const { children, ...rest } = props;
|
|
40
|
+
return <InkText {...rest}>{sanitizeChildren(children)}</InkText>;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Ink's `Text` for a string THIS PACKAGE'S OWN RENDERER produced from already-sanitized input.
|
|
45
|
+
*
|
|
46
|
+
* `renderMarkdown` sanitizes the untrusted markdown BEFORE `marked-terminal` styles it, and the
|
|
47
|
+
* SGR colour in its output (the theme's diff pairs, heading emphasis) is the renderer's
|
|
48
|
+
* own, not the model's. Routing that output through `SafeText` would strip the styling the renderer
|
|
49
|
+
* just applied — the SCREEN-006 diff colours vanished exactly that way. The boundary still holds:
|
|
50
|
+
* the only string that reaches this component is one the renderer built from sanitized text, and
|
|
51
|
+
* the `tui-safe-text-boundary` scan keeps every other module off Ink's `Text`.
|
|
52
|
+
*
|
|
53
|
+
* PERMITTED here: a string this package's own renderer built from already-sanitized input — today
|
|
54
|
+
* that is exactly `renderMarkdown(...)`, which sanitizes before it styles. FORBIDDEN here: any
|
|
55
|
+
* string that has not passed a sanitizer — raw model output, tool stdout, file contents, plugin
|
|
56
|
+
* names, a template that interpolates one of those — which goes through `SafeText`/`Text` instead.
|
|
57
|
+
* The mirror rule holds too: `renderMarkdown(...)` must NOT be a child of `SafeText`/`Text`, whose
|
|
58
|
+
* sanitizer would strip the renderer's own SGR. The `tui-safe-text-boundary` scan enforces both
|
|
59
|
+
* directions, so neither is left to a reviewer noticing it.
|
|
60
|
+
*/
|
|
61
|
+
export function RenderedText(props: TInkTextProps): React.JSX.Element {
|
|
62
|
+
return <InkText {...props} />;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Call sites keep writing `<Text>`; the name resolves to the boundary rather than to Ink. */
|
|
66
|
+
export { SafeText as Text };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Box } from 'ink';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
|
|
4
|
+
import { Text } from './SafeText.js';
|
|
5
|
+
import { usePalette } from './theme/index.js';
|
|
6
|
+
|
|
7
|
+
import type { ITuiSessionEventNotice } from './tui-session-events.js';
|
|
8
|
+
|
|
9
|
+
export interface ISessionEventNoticesProps {
|
|
10
|
+
notices: readonly ITuiSessionEventNotice[];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export default function SessionEventNotices({
|
|
14
|
+
notices,
|
|
15
|
+
}: ISessionEventNoticesProps): React.ReactElement | null {
|
|
16
|
+
const palette = usePalette();
|
|
17
|
+
if (notices.length === 0) return null;
|
|
18
|
+
return (
|
|
19
|
+
<Box flexDirection="column" paddingX={1}>
|
|
20
|
+
{notices.map((notice) => (
|
|
21
|
+
<Text key={notice.id} color={palette.text.muted}>
|
|
22
|
+
{notice.message}
|
|
23
|
+
</Text>
|
|
24
|
+
))}
|
|
25
|
+
</Box>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session picker component for /resume command.
|
|
3
|
+
* Shows a list of sessions for the current cwd.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Box } from 'ink';
|
|
7
|
+
import React from 'react';
|
|
8
|
+
|
|
9
|
+
import { SELECTION_INDICATOR, SELECTION_INDICATOR_NONE } from './key-hint-footer.js';
|
|
10
|
+
import ListPicker from './ListPicker.js';
|
|
11
|
+
import { Text } from './SafeText.js';
|
|
12
|
+
import { usePalette } from './theme/index.js';
|
|
13
|
+
|
|
14
|
+
import type { IResumableSessionSummary } from '@robota-sdk/agent-interface-session';
|
|
15
|
+
|
|
16
|
+
const SESSION_ID_DISPLAY_LENGTH = 8;
|
|
17
|
+
const SESSION_PREVIEW_DISPLAY_LENGTH = 60;
|
|
18
|
+
|
|
19
|
+
interface IProps {
|
|
20
|
+
sessions: readonly IResumableSessionSummary[];
|
|
21
|
+
onSelect: (sessionId: string) => void;
|
|
22
|
+
onCancel: () => void;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export default function SessionPicker({
|
|
26
|
+
sessions,
|
|
27
|
+
onSelect,
|
|
28
|
+
onCancel,
|
|
29
|
+
}: IProps): React.ReactElement {
|
|
30
|
+
const palette = usePalette();
|
|
31
|
+
return (
|
|
32
|
+
<Box flexDirection="column" paddingX={1} marginBottom={1}>
|
|
33
|
+
<Text bold color={palette.text.accent}>
|
|
34
|
+
Select a session to resume:
|
|
35
|
+
</Text>
|
|
36
|
+
<ListPicker<IResumableSessionSummary>
|
|
37
|
+
items={[...sessions]}
|
|
38
|
+
renderItem={(session: IResumableSessionSummary, isSelected: boolean) => {
|
|
39
|
+
const preview = session.preview
|
|
40
|
+
? session.preview.slice(0, SESSION_PREVIEW_DISPLAY_LENGTH) +
|
|
41
|
+
(session.preview.length > SESSION_PREVIEW_DISPLAY_LENGTH ? '...' : '')
|
|
42
|
+
: '';
|
|
43
|
+
return (
|
|
44
|
+
<Text>
|
|
45
|
+
{isSelected ? SELECTION_INDICATOR : SELECTION_INDICATOR_NONE}
|
|
46
|
+
<Text bold>{session.name ?? session.id.slice(0, SESSION_ID_DISPLAY_LENGTH)}</Text>
|
|
47
|
+
{' '}
|
|
48
|
+
<Text dimColor>
|
|
49
|
+
{new Date(session.updatedAt).toLocaleString(undefined, {
|
|
50
|
+
month: 'short',
|
|
51
|
+
day: 'numeric',
|
|
52
|
+
hour: '2-digit',
|
|
53
|
+
minute: '2-digit',
|
|
54
|
+
})}
|
|
55
|
+
</Text>
|
|
56
|
+
{' '}
|
|
57
|
+
<Text dimColor>msgs: {session.messageCount}</Text>
|
|
58
|
+
{preview ? (
|
|
59
|
+
<>
|
|
60
|
+
{'\n '}
|
|
61
|
+
<Text dimColor>{preview}</Text>
|
|
62
|
+
</>
|
|
63
|
+
) : null}
|
|
64
|
+
</Text>
|
|
65
|
+
);
|
|
66
|
+
}}
|
|
67
|
+
onSelect={(session: IResumableSessionSummary) => onSelect(session.id)}
|
|
68
|
+
onCancel={onCancel}
|
|
69
|
+
/>
|
|
70
|
+
</Box>
|
|
71
|
+
);
|
|
72
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import React, { useMemo } from 'react';
|
|
2
|
+
|
|
3
|
+
import StatusBar from './StatusBar.js';
|
|
4
|
+
import { useTuiCliAdapter } from './tui-cli-adapter-context.js';
|
|
5
|
+
|
|
6
|
+
import type { TModelEffortSelection, TPermissionMode } from '@robota-sdk/agent-core';
|
|
7
|
+
import type { IStatusLineCommandSettings } from '@robota-sdk/agent-interface-command';
|
|
8
|
+
|
|
9
|
+
interface IProps {
|
|
10
|
+
cwd: string;
|
|
11
|
+
permissionMode: TPermissionMode;
|
|
12
|
+
modelId?: string;
|
|
13
|
+
providerType?: string | undefined;
|
|
14
|
+
sessionId: string;
|
|
15
|
+
isThinking: boolean;
|
|
16
|
+
activeToolCount: number;
|
|
17
|
+
activeBackgroundTaskCount: number;
|
|
18
|
+
hasPendingPrompt: boolean;
|
|
19
|
+
contextState: { percentage: number; usedTokens: number; maxTokens: number };
|
|
20
|
+
sessionName?: string;
|
|
21
|
+
settings: IStatusLineCommandSettings;
|
|
22
|
+
activeAgentLabel?: string;
|
|
23
|
+
activePresetId?: string;
|
|
24
|
+
effort?: TModelEffortSelection;
|
|
25
|
+
gitRefreshToken?: number;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export default function SessionStatusBar({
|
|
29
|
+
cwd,
|
|
30
|
+
permissionMode,
|
|
31
|
+
modelId,
|
|
32
|
+
providerType,
|
|
33
|
+
sessionId,
|
|
34
|
+
isThinking,
|
|
35
|
+
activeToolCount,
|
|
36
|
+
activeBackgroundTaskCount,
|
|
37
|
+
hasPendingPrompt,
|
|
38
|
+
contextState,
|
|
39
|
+
sessionName,
|
|
40
|
+
settings,
|
|
41
|
+
activeAgentLabel,
|
|
42
|
+
activePresetId,
|
|
43
|
+
effort,
|
|
44
|
+
gitRefreshToken,
|
|
45
|
+
}: IProps): React.ReactElement | null {
|
|
46
|
+
const cliAdapter = useTuiCliAdapter();
|
|
47
|
+
const gitBranch = useMemo(() => cliAdapter.getGitBranch(cwd), [cliAdapter, cwd, gitRefreshToken]);
|
|
48
|
+
const providerDisplayName = useMemo(
|
|
49
|
+
() =>
|
|
50
|
+
providerType !== undefined ? cliAdapter.getProviderDisplayName(providerType) : undefined,
|
|
51
|
+
[cliAdapter, providerType],
|
|
52
|
+
);
|
|
53
|
+
if (!settings.enabled) return null;
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<StatusBar
|
|
57
|
+
permissionMode={permissionMode}
|
|
58
|
+
modelName={modelId ?? ''}
|
|
59
|
+
providerDisplayName={providerDisplayName}
|
|
60
|
+
sessionId={sessionId}
|
|
61
|
+
isThinking={isThinking}
|
|
62
|
+
activeToolCount={activeToolCount}
|
|
63
|
+
activeBackgroundTaskCount={activeBackgroundTaskCount}
|
|
64
|
+
hasPendingPrompt={hasPendingPrompt}
|
|
65
|
+
contextPercentage={contextState.percentage}
|
|
66
|
+
contextUsedTokens={contextState.usedTokens}
|
|
67
|
+
contextMaxTokens={contextState.maxTokens}
|
|
68
|
+
sessionName={sessionName}
|
|
69
|
+
gitBranch={gitBranch}
|
|
70
|
+
showGitBranch={settings.gitBranch}
|
|
71
|
+
activeAgentLabel={activeAgentLabel}
|
|
72
|
+
activePresetId={activePresetId}
|
|
73
|
+
effort={effort}
|
|
74
|
+
/>
|
|
75
|
+
);
|
|
76
|
+
}
|