@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,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Screen-specific selection handlers for PluginTUI.
|
|
3
|
+
* Extracted to keep PluginTUI.tsx under 300 lines.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { IMenuSelectItem } from './MenuSelect.js';
|
|
7
|
+
import type { ICommandPluginAdapter } from '@robota-sdk/agent-interface-command';
|
|
8
|
+
|
|
9
|
+
interface IConfirmState {
|
|
10
|
+
message: string;
|
|
11
|
+
onConfirm: () => void;
|
|
12
|
+
onCancel: () => void;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface IMenuContext {
|
|
16
|
+
marketplace?: string;
|
|
17
|
+
pluginId?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface INavActions {
|
|
21
|
+
push: (state: { screen: string; context?: IMenuContext }) => void;
|
|
22
|
+
pop: () => void;
|
|
23
|
+
popN: (n: number) => void;
|
|
24
|
+
notify: (content: string) => void;
|
|
25
|
+
setConfirm: (state: IConfirmState | undefined) => void;
|
|
26
|
+
refresh: () => void;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function handleMainSelect(value: string, nav: Pick<INavActions, 'push'>): void {
|
|
30
|
+
if (value === 'marketplace') {
|
|
31
|
+
nav.push({ screen: 'marketplace-list' });
|
|
32
|
+
} else if (value === 'installed') {
|
|
33
|
+
nav.push({ screen: 'installed-list' });
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function handleMarketplaceListSelect(value: string, nav: Pick<INavActions, 'push'>): void {
|
|
38
|
+
if (value === '__add__') {
|
|
39
|
+
nav.push({ screen: 'marketplace-add' });
|
|
40
|
+
} else {
|
|
41
|
+
nav.push({ screen: 'marketplace-action', context: { marketplace: value } });
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function handleMarketplaceActionSelect(
|
|
46
|
+
value: string,
|
|
47
|
+
marketplace: string,
|
|
48
|
+
callbacks: ICommandPluginAdapter,
|
|
49
|
+
nav: Pick<INavActions, 'push' | 'pop' | 'popN' | 'notify' | 'setConfirm'>,
|
|
50
|
+
): void {
|
|
51
|
+
if (value === 'browse') {
|
|
52
|
+
nav.push({ screen: 'marketplace-browse', context: { marketplace } });
|
|
53
|
+
} else if (value === 'update') {
|
|
54
|
+
callbacks
|
|
55
|
+
.marketplaceUpdate(marketplace)
|
|
56
|
+
.then(() => {
|
|
57
|
+
nav.notify(`Updated marketplace "${marketplace}".`);
|
|
58
|
+
nav.pop();
|
|
59
|
+
})
|
|
60
|
+
.catch((err) => {
|
|
61
|
+
nav.notify(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
62
|
+
});
|
|
63
|
+
} else if (value === 'remove') {
|
|
64
|
+
nav.setConfirm({
|
|
65
|
+
message: `Remove marketplace "${marketplace}" and all its plugins?`,
|
|
66
|
+
onConfirm: () => {
|
|
67
|
+
nav.setConfirm(undefined);
|
|
68
|
+
callbacks
|
|
69
|
+
.marketplaceRemove(marketplace)
|
|
70
|
+
.then(() => {
|
|
71
|
+
nav.notify(`Removed marketplace "${marketplace}".`);
|
|
72
|
+
nav.popN(2);
|
|
73
|
+
})
|
|
74
|
+
.catch((err) => {
|
|
75
|
+
nav.notify(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
76
|
+
});
|
|
77
|
+
},
|
|
78
|
+
onCancel: () => nav.setConfirm(undefined),
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function handleMarketplaceBrowseSelect(
|
|
84
|
+
value: string,
|
|
85
|
+
marketplace: string,
|
|
86
|
+
items: IMenuSelectItem[],
|
|
87
|
+
nav: Pick<INavActions, 'push'>,
|
|
88
|
+
): void {
|
|
89
|
+
const fullId = `${value}@${marketplace}`;
|
|
90
|
+
const item = items.find((i) => i.value === value);
|
|
91
|
+
if (item?.hint === 'installed') {
|
|
92
|
+
nav.push({ screen: 'installed-action', context: { pluginId: fullId } });
|
|
93
|
+
} else {
|
|
94
|
+
nav.push({ screen: 'marketplace-install-scope', context: { marketplace, pluginId: fullId } });
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function handleInstallScopeSelect(
|
|
99
|
+
value: string,
|
|
100
|
+
pluginId: string,
|
|
101
|
+
callbacks: ICommandPluginAdapter,
|
|
102
|
+
nav: Pick<INavActions, 'popN' | 'notify'>,
|
|
103
|
+
): void {
|
|
104
|
+
const scope = value as 'user' | 'project';
|
|
105
|
+
callbacks
|
|
106
|
+
.install(pluginId, scope)
|
|
107
|
+
.then(() => {
|
|
108
|
+
nav.notify(`Installed plugin "${pluginId}" (${scope} scope).`);
|
|
109
|
+
nav.popN(2);
|
|
110
|
+
})
|
|
111
|
+
.catch((err) => {
|
|
112
|
+
nav.notify(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function handleInstalledListSelect(
|
|
117
|
+
value: string,
|
|
118
|
+
callbacks: ICommandPluginAdapter,
|
|
119
|
+
nav: Pick<INavActions, 'notify' | 'setConfirm' | 'refresh'>,
|
|
120
|
+
): void {
|
|
121
|
+
nav.setConfirm({
|
|
122
|
+
message: `Uninstall plugin "${value}"?`,
|
|
123
|
+
onConfirm: () => {
|
|
124
|
+
nav.setConfirm(undefined);
|
|
125
|
+
callbacks
|
|
126
|
+
.uninstall(value)
|
|
127
|
+
.then(() => {
|
|
128
|
+
nav.notify(`Uninstalled plugin "${value}".`);
|
|
129
|
+
nav.refresh();
|
|
130
|
+
})
|
|
131
|
+
.catch((err) => {
|
|
132
|
+
nav.notify(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
133
|
+
});
|
|
134
|
+
},
|
|
135
|
+
onCancel: () => nav.setConfirm(undefined),
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export function handleInstalledActionSelect(
|
|
140
|
+
value: string,
|
|
141
|
+
pluginId: string,
|
|
142
|
+
callbacks: ICommandPluginAdapter,
|
|
143
|
+
nav: Pick<INavActions, 'popN' | 'notify' | 'setConfirm'>,
|
|
144
|
+
): void {
|
|
145
|
+
if (value === 'uninstall') {
|
|
146
|
+
nav.setConfirm({
|
|
147
|
+
message: `Uninstall plugin "${pluginId}"?`,
|
|
148
|
+
onConfirm: () => {
|
|
149
|
+
nav.setConfirm(undefined);
|
|
150
|
+
callbacks
|
|
151
|
+
.uninstall(pluginId)
|
|
152
|
+
.then(() => {
|
|
153
|
+
nav.notify(`Uninstalled plugin "${pluginId}".`);
|
|
154
|
+
nav.popN(2);
|
|
155
|
+
})
|
|
156
|
+
.catch((err) => {
|
|
157
|
+
nav.notify(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
158
|
+
});
|
|
159
|
+
},
|
|
160
|
+
onCancel: () => nav.setConfirm(undefined),
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import React, { createContext, useContext } from 'react';
|
|
2
|
+
|
|
3
|
+
const ProductDisplayNameContext = createContext('Assistant');
|
|
4
|
+
const ModelCommandToolPrefixContext = createContext<string | undefined>(undefined);
|
|
5
|
+
|
|
6
|
+
export function ProductDisplayNameProvider({
|
|
7
|
+
name,
|
|
8
|
+
modelCommandToolPrefix,
|
|
9
|
+
children,
|
|
10
|
+
}: {
|
|
11
|
+
name?: string;
|
|
12
|
+
modelCommandToolPrefix?: string;
|
|
13
|
+
children: React.ReactNode;
|
|
14
|
+
}): React.ReactElement {
|
|
15
|
+
return (
|
|
16
|
+
<ProductDisplayNameContext.Provider value={name?.trim() || 'Assistant'}>
|
|
17
|
+
<ModelCommandToolPrefixContext.Provider value={modelCommandToolPrefix}>
|
|
18
|
+
{children}
|
|
19
|
+
</ModelCommandToolPrefixContext.Provider>
|
|
20
|
+
</ProductDisplayNameContext.Provider>
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function useProductDisplayName(): string {
|
|
25
|
+
return useContext(ProductDisplayNameContext);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function useModelCommandToolPrefix(): string | undefined {
|
|
29
|
+
return useContext(ModelCommandToolPrefixContext);
|
|
30
|
+
}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { marked } from 'marked';
|
|
2
|
+
// @ts-expect-error — marked-terminal has no type declarations
|
|
3
|
+
import TerminalRenderer from 'marked-terminal';
|
|
4
|
+
|
|
5
|
+
import { sanitizeTerminalText } from './sanitize-terminal-text.js';
|
|
6
|
+
import { isInteractiveColorTerminal } from './terminal-capabilities.js';
|
|
7
|
+
import {
|
|
8
|
+
diffRowStyles,
|
|
9
|
+
markdownRendererOptions,
|
|
10
|
+
resolveTheme,
|
|
11
|
+
syntaxHighlightTheme,
|
|
12
|
+
} from './theme/index.js';
|
|
13
|
+
|
|
14
|
+
import type { IDiffRowStyles, ITuiTheme } from './theme/index.js';
|
|
15
|
+
import type { Renderer } from 'marked';
|
|
16
|
+
|
|
17
|
+
const CODE_BLOCK_INDENT = ' ';
|
|
18
|
+
|
|
19
|
+
export interface IRenderMarkdownOptions {
|
|
20
|
+
color?: boolean;
|
|
21
|
+
codeBlockWidth?: number;
|
|
22
|
+
/**
|
|
23
|
+
* SCREEN-2002: the resolved theme. Absent ⇒ the `dark` built-in, which is byte-identical to what
|
|
24
|
+
* this renderer produced before themes existed.
|
|
25
|
+
*/
|
|
26
|
+
theme?: ITuiTheme;
|
|
27
|
+
/** SCREEN-2002: `false` renders code blocks as plain indented text, with no highlight SGR. */
|
|
28
|
+
syntaxHighlighting?: boolean;
|
|
29
|
+
/**
|
|
30
|
+
* CLI-2004: flatten tables to `Header: value` sentences. A box-drawn grid is unreadable aloud —
|
|
31
|
+
* a reader announces the rules, not the relationship between a heading and its cell.
|
|
32
|
+
*/
|
|
33
|
+
screenReader?: boolean;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
interface IHighlightOptions {
|
|
37
|
+
ignoreIllegals?: boolean;
|
|
38
|
+
/** The cli-highlight theme marked-terminal forwards; absent ⇒ its red/green defaults. */
|
|
39
|
+
theme?: Record<string, (text: string) => string>;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
type TTerminalRendererConstructor = new (
|
|
43
|
+
/** `marked-terminal`'s style options: one chalk instance per token it colours. */
|
|
44
|
+
options?: Record<string, (text: string) => string>,
|
|
45
|
+
highlightOptions?: IHighlightOptions,
|
|
46
|
+
) => Renderer;
|
|
47
|
+
|
|
48
|
+
const TerminalRendererConstructor = TerminalRenderer as TTerminalRendererConstructor;
|
|
49
|
+
|
|
50
|
+
function shouldUseColor(option: boolean | undefined): boolean {
|
|
51
|
+
if (option !== undefined) {
|
|
52
|
+
return option;
|
|
53
|
+
}
|
|
54
|
+
return isInteractiveColorTerminal();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function isDiffLanguage(language: string | undefined): boolean {
|
|
58
|
+
return language?.trim().toLowerCase() === 'diff';
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function styleAddedOrRemovedDiffRow(
|
|
62
|
+
line: string,
|
|
63
|
+
rowWidth: number,
|
|
64
|
+
color: boolean,
|
|
65
|
+
styles: IDiffRowStyles,
|
|
66
|
+
): string {
|
|
67
|
+
const row = `${CODE_BLOCK_INDENT}${line}`.padEnd(rowWidth);
|
|
68
|
+
if (!color) {
|
|
69
|
+
return row.trimEnd();
|
|
70
|
+
}
|
|
71
|
+
if (line.startsWith('+')) return styles.added(row);
|
|
72
|
+
if (line.startsWith('-')) return styles.removed(row);
|
|
73
|
+
return row.trimEnd();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function colorizeDiffLine(
|
|
77
|
+
line: string,
|
|
78
|
+
color: boolean,
|
|
79
|
+
rowWidth: number,
|
|
80
|
+
styles: IDiffRowStyles,
|
|
81
|
+
): string {
|
|
82
|
+
if (line.startsWith('+') || line.startsWith('-')) {
|
|
83
|
+
return styleAddedOrRemovedDiffRow(line, rowWidth, color, styles);
|
|
84
|
+
}
|
|
85
|
+
const row = `${CODE_BLOCK_INDENT}${line}`;
|
|
86
|
+
if (!color) {
|
|
87
|
+
return row;
|
|
88
|
+
}
|
|
89
|
+
if (line.startsWith('@@')) return styles.hunk(row);
|
|
90
|
+
if (line.startsWith('diff ') || line.startsWith('index ')) return styles.meta(row);
|
|
91
|
+
return row;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function resolveDiffRowWidth(lines: readonly string[], requestedWidth: number | undefined): number {
|
|
95
|
+
const minimumWidth = lines.reduce(
|
|
96
|
+
(maxWidth, line) => Math.max(maxWidth, CODE_BLOCK_INDENT.length + line.length),
|
|
97
|
+
0,
|
|
98
|
+
);
|
|
99
|
+
if (requestedWidth === undefined) {
|
|
100
|
+
return minimumWidth;
|
|
101
|
+
}
|
|
102
|
+
return Math.max(minimumWidth, requestedWidth);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function renderDiffCodeBlock(
|
|
106
|
+
code: string,
|
|
107
|
+
color: boolean,
|
|
108
|
+
codeBlockWidth: number | undefined,
|
|
109
|
+
styles: IDiffRowStyles,
|
|
110
|
+
): string {
|
|
111
|
+
const lines = code.split('\n');
|
|
112
|
+
const rowWidth = resolveDiffRowWidth(lines, codeBlockWidth);
|
|
113
|
+
const body = lines.map((line) => colorizeDiffLine(line, color, rowWidth, styles)).join('\n');
|
|
114
|
+
return `${body}\n\n`;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* CLI-2004 — flatten a table to `Header: value` lines.
|
|
119
|
+
*
|
|
120
|
+
* The cell and row hooks tag their output with separators marked's own renderers never emit, so the
|
|
121
|
+
* table hook can recover the grid it was given. Reconstructing it from the RENDERED strings is the
|
|
122
|
+
* alternative, and those already carry the SGR the inline renderers added.
|
|
123
|
+
*/
|
|
124
|
+
const CELL_MARK = '\u0000';
|
|
125
|
+
const ROW_MARK = '\u0001';
|
|
126
|
+
|
|
127
|
+
function splitTableCells(row: string): string[] {
|
|
128
|
+
const cells = row.split(ROW_MARK).join('').split(CELL_MARK);
|
|
129
|
+
// The trailing separator leaves one empty tail entry; the cells themselves may legitimately be ''.
|
|
130
|
+
return cells.slice(0, -1);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function installTableFlattening(renderer: Renderer): void {
|
|
134
|
+
renderer.tablecell = (content: string): string => `${content}${CELL_MARK}`;
|
|
135
|
+
renderer.tablerow = (content: string): string => `${content}${ROW_MARK}`;
|
|
136
|
+
renderer.table = (header: string, body: string): string => {
|
|
137
|
+
const headers = splitTableCells(header);
|
|
138
|
+
const rows = body
|
|
139
|
+
.split(ROW_MARK)
|
|
140
|
+
.filter((row) => row.length > 0)
|
|
141
|
+
.map((row) => splitTableCells(`${row}${ROW_MARK}`));
|
|
142
|
+
if (headers.length === 0 || rows.length === 0) return '';
|
|
143
|
+
const blocks = rows.map((cells) =>
|
|
144
|
+
headers.map((head, index) => `${head}: ${cells[index] ?? ''}`).join('\n'),
|
|
145
|
+
);
|
|
146
|
+
// A blank line between rows: the row boundary is the only structure left to hear.
|
|
147
|
+
return `${blocks.join('\n\n')}\n\n`;
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function createTerminalRenderer(
|
|
152
|
+
color: boolean,
|
|
153
|
+
codeBlockWidth: number | undefined,
|
|
154
|
+
screenReader: boolean,
|
|
155
|
+
theme: ITuiTheme,
|
|
156
|
+
syntaxHighlighting: boolean,
|
|
157
|
+
): Renderer {
|
|
158
|
+
// SCREEN-2002: both dependencies are told what to colour with. Left to their own defaults, a
|
|
159
|
+
// daltonized theme would still get green/red code blocks from cli-highlight's own theme.
|
|
160
|
+
const renderer = new TerminalRendererConstructor(markdownRendererOptions(theme.markdown), {
|
|
161
|
+
ignoreIllegals: true,
|
|
162
|
+
theme: syntaxHighlightTheme(theme.syntax),
|
|
163
|
+
});
|
|
164
|
+
const renderCode = renderer.code.bind(renderer);
|
|
165
|
+
const styles = diffRowStyles(theme);
|
|
166
|
+
|
|
167
|
+
renderer.code = (code: string, language: string | undefined, escaped: boolean): string => {
|
|
168
|
+
if (isDiffLanguage(language)) {
|
|
169
|
+
return renderDiffCodeBlock(code, color, codeBlockWidth, styles);
|
|
170
|
+
}
|
|
171
|
+
// Highlighting off: the block renders as plain indented text, the shape the diff path uses.
|
|
172
|
+
if (!syntaxHighlighting) {
|
|
173
|
+
return `${code
|
|
174
|
+
.split('\n')
|
|
175
|
+
.map((line) => `${CODE_BLOCK_INDENT}${line}`)
|
|
176
|
+
.join('\n')}\n\n`;
|
|
177
|
+
}
|
|
178
|
+
return renderCode(code, language, escaped);
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
if (screenReader) installTableFlattening(renderer);
|
|
182
|
+
|
|
183
|
+
return renderer;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Render markdown to a terminal-formatted string with colors, bold, etc.
|
|
188
|
+
* Returns the rendered string (may include ANSI escape codes).
|
|
189
|
+
*/
|
|
190
|
+
export function renderMarkdown(md: string, options: IRenderMarkdownOptions = {}): string {
|
|
191
|
+
// SEC-019 (issue #2022): the untrusted string is sanitized BEFORE parsing, never after. The
|
|
192
|
+
// renderer ADDS ANSI — colours, bold, code-block framing — so filtering its output would strip the
|
|
193
|
+
// repository's own presentation along with the attacker's. Filtering its input removes what
|
|
194
|
+
// arrived from outside and leaves what is generated after.
|
|
195
|
+
//
|
|
196
|
+
// This is the choke point every render path shares (MessageList, StreamingIndicator,
|
|
197
|
+
// ToolDiffBlock), which is why it is here rather than at each of the three.
|
|
198
|
+
const safe = sanitizeTerminalText(md);
|
|
199
|
+
const result = marked.parse(safe, {
|
|
200
|
+
renderer: createTerminalRenderer(
|
|
201
|
+
shouldUseColor(options.color),
|
|
202
|
+
options.codeBlockWidth,
|
|
203
|
+
options.screenReader === true,
|
|
204
|
+
resolveTheme(options.theme),
|
|
205
|
+
options.syntaxHighlighting !== false,
|
|
206
|
+
),
|
|
207
|
+
});
|
|
208
|
+
return typeof result === 'string' ? result.trimEnd() : safe;
|
|
209
|
+
}
|