@oh-my-pi/pi-coding-agent 15.9.67 → 15.10.1
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 +136 -0
- package/dist/types/cli/args.d.ts +1 -1
- package/dist/types/cli/dry-balance-cli.d.ts +15 -1
- package/dist/types/cli/gallery-cli.d.ts +43 -0
- package/dist/types/cli/gallery-fixtures/agentic.d.ts +2 -0
- package/dist/types/cli/gallery-fixtures/codeintel.d.ts +3 -0
- package/dist/types/cli/gallery-fixtures/edit.d.ts +3 -0
- package/dist/types/cli/gallery-fixtures/fs.d.ts +2 -0
- package/dist/types/cli/gallery-fixtures/index.d.ts +4 -0
- package/dist/types/cli/gallery-fixtures/interaction.d.ts +3 -0
- package/dist/types/cli/gallery-fixtures/memory.d.ts +2 -0
- package/dist/types/cli/gallery-fixtures/misc.d.ts +3 -0
- package/dist/types/cli/gallery-fixtures/search.d.ts +3 -0
- package/dist/types/cli/gallery-fixtures/shell.d.ts +3 -0
- package/dist/types/cli/gallery-fixtures/types.d.ts +44 -0
- package/dist/types/cli/gallery-fixtures/web.d.ts +2 -0
- package/dist/types/cli/gallery-screenshot.d.ts +35 -0
- package/dist/types/commands/gallery.d.ts +47 -0
- package/dist/types/commit/analysis/conventional.d.ts +2 -2
- package/dist/types/commit/analysis/summary.d.ts +2 -2
- package/dist/types/commit/changelog/generate.d.ts +2 -2
- package/dist/types/commit/changelog/index.d.ts +2 -2
- package/dist/types/commit/map-reduce/index.d.ts +3 -3
- package/dist/types/commit/map-reduce/map-phase.d.ts +2 -2
- package/dist/types/commit/map-reduce/reduce-phase.d.ts +2 -2
- package/dist/types/commit/model-selection.d.ts +10 -4
- package/dist/types/config/api-key-resolver.d.ts +34 -0
- package/dist/types/config/keybindings.d.ts +6 -1
- package/dist/types/config/model-id-affixes.d.ts +2 -0
- package/dist/types/config/model-registry.d.ts +25 -2
- package/dist/types/config/settings-schema.d.ts +41 -6
- package/dist/types/dap/config.d.ts +14 -1
- package/dist/types/dap/types.d.ts +10 -0
- package/dist/types/extensibility/plugins/marketplace-auto-update.d.ts +8 -0
- package/dist/types/lsp/types.d.ts +10 -0
- package/dist/types/lsp/utils.d.ts +3 -2
- package/dist/types/main.d.ts +3 -2
- package/dist/types/memory-backend/index.d.ts +2 -1
- package/dist/types/memory-backend/resolve.d.ts +1 -1
- package/dist/types/memory-backend/types.d.ts +1 -1
- package/dist/types/modes/components/chat-block.d.ts +64 -0
- package/dist/types/modes/components/custom-editor.d.ts +5 -1
- package/dist/types/modes/components/overlay-box.d.ts +17 -0
- package/dist/types/modes/components/plan-review-overlay.d.ts +59 -0
- package/dist/types/modes/components/plan-toc.d.ts +41 -0
- package/dist/types/modes/components/read-tool-group.d.ts +2 -0
- package/dist/types/modes/components/tool-execution.d.ts +18 -0
- package/dist/types/modes/components/transcript-container.d.ts +11 -0
- package/dist/types/modes/controllers/command-controller.d.ts +1 -0
- package/dist/types/modes/controllers/event-controller.d.ts +0 -1
- package/dist/types/modes/controllers/extension-ui-controller.d.ts +0 -1
- package/dist/types/modes/controllers/input-controller.d.ts +1 -1
- package/dist/types/modes/controllers/selector-controller.d.ts +1 -1
- package/dist/types/modes/controllers/streaming-reveal.d.ts +22 -0
- package/dist/types/modes/controllers/tan-command-controller.d.ts +6 -0
- package/dist/types/modes/index.d.ts +5 -4
- package/dist/types/modes/interactive-mode.d.ts +16 -6
- package/dist/types/modes/setup-version.d.ts +11 -0
- package/dist/types/modes/setup-wizard/index.d.ts +2 -1
- package/dist/types/modes/setup-wizard/scenes/web-search.d.ts +2 -1
- package/dist/types/modes/theme/theme.d.ts +1 -1
- package/dist/types/modes/types.d.ts +19 -6
- package/dist/types/modes/utils/copy-targets.d.ts +21 -1
- package/dist/types/plan-mode/approved-plan.d.ts +27 -8
- package/dist/types/plan-mode/plan-protection.d.ts +4 -4
- package/dist/types/sdk.d.ts +3 -1
- package/dist/types/session/agent-session.d.ts +21 -0
- package/dist/types/session/messages.d.ts +12 -0
- package/dist/types/session/session-manager.d.ts +3 -1
- package/dist/types/slash-commands/types.d.ts +4 -6
- package/dist/types/task/executor.d.ts +14 -0
- package/dist/types/task/index.d.ts +1 -0
- package/dist/types/task/render.d.ts +3 -2
- package/dist/types/telemetry-export.d.ts +1 -1
- package/dist/types/tools/archive-reader.d.ts +5 -0
- package/dist/types/tools/ast-edit.d.ts +3 -0
- package/dist/types/tools/ast-grep.d.ts +3 -0
- package/dist/types/tools/bash.d.ts +1 -0
- package/dist/types/tools/eval-render.d.ts +1 -8
- package/dist/types/tools/fetch.d.ts +15 -7
- package/dist/types/tools/find.d.ts +8 -4
- package/dist/types/tools/grouped-file-output.d.ts +95 -12
- package/dist/types/tools/memory-render.d.ts +4 -1
- package/dist/types/tools/plan-mode-guard.d.ts +8 -9
- package/dist/types/tools/render-utils.d.ts +13 -9
- package/dist/types/tools/renderers.d.ts +16 -2
- package/dist/types/tools/search.d.ts +5 -1
- package/dist/types/tools/sqlite-reader.d.ts +1 -0
- package/dist/types/tools/todo.d.ts +3 -2
- package/dist/types/tools/write.d.ts +5 -0
- package/dist/types/tui/output-block.d.ts +16 -4
- package/dist/types/tui/status-line.d.ts +3 -0
- package/dist/types/utils/enhanced-paste.d.ts +20 -0
- package/dist/types/web/scrapers/github.d.ts +22 -0
- package/dist/types/web/search/providers/kimi.d.ts +1 -1
- package/dist/types/web/search/providers/perplexity.d.ts +8 -1
- package/dist/types/web/search/types.d.ts +1 -1
- package/package.json +9 -9
- package/scripts/dev-launch +42 -0
- package/scripts/dev-launch-preload.ts +19 -0
- package/src/auto-thinking/classifier.ts +5 -1
- package/src/cli/args.ts +2 -2
- package/src/cli/dry-balance-cli.ts +52 -17
- package/src/cli/gallery-cli.ts +226 -0
- package/src/cli/gallery-fixtures/agentic.ts +292 -0
- package/src/cli/gallery-fixtures/codeintel.ts +188 -0
- package/src/cli/gallery-fixtures/edit.ts +194 -0
- package/src/cli/gallery-fixtures/fs.ts +153 -0
- package/src/cli/gallery-fixtures/index.ts +40 -0
- package/src/cli/gallery-fixtures/interaction.ts +49 -0
- package/src/cli/gallery-fixtures/memory.ts +81 -0
- package/src/cli/gallery-fixtures/misc.ts +250 -0
- package/src/cli/gallery-fixtures/search.ts +213 -0
- package/src/cli/gallery-fixtures/shell.ts +167 -0
- package/src/cli/gallery-fixtures/types.ts +41 -0
- package/src/cli/gallery-fixtures/web.ts +158 -0
- package/src/cli/gallery-screenshot.ts +279 -0
- package/src/cli-commands.ts +1 -0
- package/src/commands/gallery.ts +52 -0
- package/src/commands/launch.ts +1 -1
- package/src/commit/analysis/conventional.ts +2 -2
- package/src/commit/analysis/summary.ts +2 -2
- package/src/commit/changelog/generate.ts +2 -2
- package/src/commit/changelog/index.ts +2 -2
- package/src/commit/map-reduce/index.ts +3 -3
- package/src/commit/map-reduce/map-phase.ts +2 -2
- package/src/commit/map-reduce/reduce-phase.ts +2 -2
- package/src/commit/model-selection.ts +33 -9
- package/src/commit/pipeline.ts +4 -4
- package/src/config/api-key-resolver.ts +58 -0
- package/src/config/keybindings.ts +15 -6
- package/src/config/model-equivalence.ts +35 -12
- package/src/config/model-id-affixes.ts +39 -22
- package/src/config/model-registry.ts +41 -18
- package/src/config/settings-schema.ts +28 -5
- package/src/config/settings.ts +31 -2
- package/src/dap/client.ts +14 -16
- package/src/dap/config.ts +41 -2
- package/src/dap/defaults.json +1 -0
- package/src/dap/session.ts +1 -0
- package/src/dap/types.ts +10 -0
- package/src/debug/index.ts +40 -54
- package/src/edit/renderer.ts +111 -119
- package/src/eval/__tests__/agent-bridge.test.ts +75 -32
- package/src/eval/__tests__/llm-bridge.test.ts +90 -31
- package/src/eval/agent-bridge.ts +34 -7
- package/src/eval/llm-bridge.ts +8 -3
- package/src/extensibility/extensions/runner.ts +1 -0
- package/src/extensibility/plugins/doctor.ts +0 -1
- package/src/extensibility/plugins/marketplace-auto-update.ts +49 -0
- package/src/goals/tools/goal-tool.ts +37 -27
- package/src/internal-urls/docs-index.generated.ts +10 -10
- package/src/lsp/client.ts +104 -55
- package/src/lsp/types.ts +10 -0
- package/src/lsp/utils.ts +3 -2
- package/src/main.ts +53 -56
- package/src/memories/index.ts +12 -5
- package/src/memory-backend/index.ts +13 -1
- package/src/memory-backend/resolve.ts +3 -5
- package/src/memory-backend/types.ts +1 -1
- package/src/mnemopi/backend.ts +5 -1
- package/src/modes/acp/acp-agent.ts +33 -26
- package/src/modes/components/assistant-message.ts +2 -9
- package/src/modes/components/chat-block.ts +111 -0
- package/src/modes/components/copy-selector.ts +1 -44
- package/src/modes/components/custom-editor.ts +33 -1
- package/src/modes/components/custom-message.ts +1 -3
- package/src/modes/components/execution-shared.ts +1 -2
- package/src/modes/components/hook-message.ts +1 -3
- package/src/modes/components/overlay-box.ts +108 -0
- package/src/modes/components/plan-review-overlay.ts +799 -0
- package/src/modes/components/plan-toc.ts +138 -0
- package/src/modes/components/read-tool-group.ts +20 -4
- package/src/modes/components/skill-message.ts +0 -1
- package/src/modes/components/status-line.ts +3 -5
- package/src/modes/components/tips.txt +1 -0
- package/src/modes/components/todo-reminder.ts +0 -2
- package/src/modes/components/tool-execution.ts +115 -90
- package/src/modes/components/transcript-container.ts +84 -24
- package/src/modes/components/user-message.ts +1 -2
- package/src/modes/controllers/command-controller-shared.ts +7 -6
- package/src/modes/controllers/command-controller.ts +70 -57
- package/src/modes/controllers/event-controller.ts +41 -40
- package/src/modes/controllers/extension-ui-controller.ts +10 -73
- package/src/modes/controllers/input-controller.ts +135 -122
- package/src/modes/controllers/mcp-command-controller.ts +69 -60
- package/src/modes/controllers/selector-controller.ts +25 -27
- package/src/modes/controllers/streaming-reveal.ts +212 -0
- package/src/modes/controllers/tan-command-controller.ts +173 -0
- package/src/modes/index.ts +5 -4
- package/src/modes/interactive-mode.ts +171 -82
- package/src/modes/setup-version.ts +11 -0
- package/src/modes/setup-wizard/index.ts +3 -2
- package/src/modes/setup-wizard/scenes/web-search.ts +3 -2
- package/src/modes/setup-wizard/wizard-overlay.ts +1 -1
- package/src/modes/theme/theme-schema.json +1 -1
- package/src/modes/theme/theme.ts +8 -4
- package/src/modes/types.ts +19 -8
- package/src/modes/utils/context-usage.ts +10 -6
- package/src/modes/utils/copy-targets.ts +133 -27
- package/src/modes/utils/hotkeys-markdown.ts +1 -0
- package/src/modes/utils/ui-helpers.ts +44 -46
- package/src/plan-mode/approved-plan.ts +66 -43
- package/src/plan-mode/plan-protection.ts +4 -4
- package/src/prompts/system/background-tan-dispatch.md +8 -0
- package/src/prompts/system/plan-mode-active.md +67 -58
- package/src/prompts/system/plan-mode-approved.md +1 -1
- package/src/sdk.ts +32 -60
- package/src/session/agent-session.ts +89 -13
- package/src/session/messages.ts +26 -0
- package/src/session/session-manager.ts +13 -5
- package/src/slash-commands/builtin-registry.ts +37 -10
- package/src/slash-commands/helpers/usage-report.ts +2 -0
- package/src/slash-commands/types.ts +4 -6
- package/src/task/executor.ts +25 -4
- package/src/task/index.ts +4 -0
- package/src/task/render.ts +212 -148
- package/src/telemetry-export.ts +25 -7
- package/src/tools/archive-reader.ts +64 -0
- package/src/tools/ask.ts +119 -164
- package/src/tools/ast-edit.ts +98 -71
- package/src/tools/ast-grep.ts +37 -43
- package/src/tools/bash.ts +50 -6
- package/src/tools/debug.ts +20 -8
- package/src/tools/eval-backends.ts +6 -17
- package/src/tools/eval-render.ts +21 -18
- package/src/tools/eval.ts +5 -4
- package/src/tools/fetch.ts +391 -91
- package/src/tools/find.ts +44 -30
- package/src/tools/gh-renderer.ts +81 -42
- package/src/tools/grouped-file-output.ts +272 -48
- package/src/tools/image-gen.ts +150 -103
- package/src/tools/inspect-image-renderer.ts +63 -41
- package/src/tools/inspect-image.ts +8 -1
- package/src/tools/job.ts +3 -4
- package/src/tools/memory-render.ts +4 -1
- package/src/tools/plan-mode-guard.ts +21 -39
- package/src/tools/read.ts +23 -16
- package/src/tools/render-utils.ts +38 -40
- package/src/tools/renderers.ts +16 -1
- package/src/tools/report-tool-issue.ts +1 -1
- package/src/tools/resolve.ts +14 -0
- package/src/tools/search-tool-bm25.ts +36 -23
- package/src/tools/search.ts +189 -95
- package/src/tools/sqlite-reader.ts +9 -12
- package/src/tools/todo.ts +138 -59
- package/src/tools/write.ts +100 -60
- package/src/tui/output-block.ts +60 -13
- package/src/tui/status-line.ts +5 -1
- package/src/utils/commit-message-generator.ts +9 -1
- package/src/utils/enhanced-paste.ts +202 -0
- package/src/utils/title-generator.ts +2 -1
- package/src/web/scrapers/github.ts +255 -3
- package/src/web/scrapers/youtube.ts +3 -2
- package/src/web/search/providers/anthropic.ts +25 -19
- package/src/web/search/providers/exa.ts +11 -3
- package/src/web/search/providers/kimi.ts +28 -17
- package/src/web/search/providers/parallel.ts +35 -24
- package/src/web/search/providers/perplexity.ts +199 -51
- package/src/web/search/providers/synthetic.ts +8 -6
- package/src/web/search/providers/tavily.ts +9 -8
- package/src/web/search/providers/zai.ts +8 -6
- package/src/web/search/render.ts +39 -54
- package/src/web/search/types.ts +5 -1
- package/dist/types/eval/__tests__/shared-executors.test.d.ts +0 -1
- package/src/eval/__tests__/shared-executors.test.ts +0 -609
package/src/modes/types.ts
CHANGED
|
@@ -14,7 +14,7 @@ import type {
|
|
|
14
14
|
import type { CompactOptions } from "../extensibility/extensions/types";
|
|
15
15
|
import type { MCPManager } from "../mcp";
|
|
16
16
|
import type { PlanApprovalDetails } from "../plan-mode/approved-plan";
|
|
17
|
-
import type { AgentSession
|
|
17
|
+
import type { AgentSession } from "../session/agent-session";
|
|
18
18
|
import type { HistoryStorage } from "../session/history-storage";
|
|
19
19
|
import type { SessionContext, SessionManager } from "../session/session-manager";
|
|
20
20
|
import type { ShakeMode } from "../session/shake-types";
|
|
@@ -63,6 +63,7 @@ export type TodoPhase = {
|
|
|
63
63
|
|
|
64
64
|
export interface InteractiveModeInitOptions {
|
|
65
65
|
suppressWelcomeIntro?: boolean;
|
|
66
|
+
clearInitialTerminalHistory?: boolean;
|
|
66
67
|
}
|
|
67
68
|
|
|
68
69
|
export type InteractiveSelectorDialogOptions = ExtensionUIDialogOptions & Pick<HookSelectorOptions, "disabledIndices">;
|
|
@@ -95,7 +96,6 @@ export interface InteractiveModeContext {
|
|
|
95
96
|
|
|
96
97
|
// State
|
|
97
98
|
isInitialized: boolean;
|
|
98
|
-
isBackgrounded: boolean;
|
|
99
99
|
isBashMode: boolean;
|
|
100
100
|
toolOutputExpanded: boolean;
|
|
101
101
|
todoExpanded: boolean;
|
|
@@ -149,15 +149,25 @@ export interface InteractiveModeContext {
|
|
|
149
149
|
// Extension UI integration
|
|
150
150
|
setToolUIContext(uiContext: ExtensionUIContext, hasUI: boolean): void;
|
|
151
151
|
initializeHookRunner(uiContext: ExtensionUIContext, hasUI: boolean): void;
|
|
152
|
-
createBackgroundUiContext(): ExtensionUIContext;
|
|
153
152
|
setEditorComponent(
|
|
154
153
|
factory: ((tui: TUI, theme: EditorTheme, keybindings: KeybindingsManager) => CustomEditor) | undefined,
|
|
155
154
|
): void;
|
|
156
155
|
|
|
157
|
-
// Event handling
|
|
158
|
-
handleBackgroundEvent(event: AgentSessionEvent): Promise<void>;
|
|
159
|
-
|
|
160
156
|
// UI helpers
|
|
157
|
+
/**
|
|
158
|
+
* Mount transcript content and repaint once. The single sink for "show this in
|
|
159
|
+
* chat": producers build and return a `Component` (or a `ChatBlock` carrying
|
|
160
|
+
* its own lifecycle) and hand it here instead of touching `chatContainer` /
|
|
161
|
+
* `ui.requestRender()` directly. `ChatBlock`s are mounted (their `onMount`
|
|
162
|
+
* runs) so their timers/subscriptions start.
|
|
163
|
+
*/
|
|
164
|
+
present(content: Component | readonly Component[]): void;
|
|
165
|
+
/**
|
|
166
|
+
* Dispose every live block in the transcript (stopping timers/subscriptions)
|
|
167
|
+
* and clear it. Used before a full rebuild so animated/streaming blocks do not
|
|
168
|
+
* leak.
|
|
169
|
+
*/
|
|
170
|
+
resetTranscript(): void;
|
|
161
171
|
showStatus(message: string, options?: { dim?: boolean }): void;
|
|
162
172
|
showError(message: string): void;
|
|
163
173
|
showPinnedError(message: string): void;
|
|
@@ -233,6 +243,7 @@ export interface InteractiveModeContext {
|
|
|
233
243
|
handleDumpCommand(): void;
|
|
234
244
|
handleDebugTranscriptCommand(): Promise<void>;
|
|
235
245
|
handleClearCommand(): Promise<void>;
|
|
246
|
+
handleFreshCommand(): Promise<void>;
|
|
236
247
|
handleDropCommand(): Promise<void>;
|
|
237
248
|
handleForkCommand(): Promise<void>;
|
|
238
249
|
handleBashCommand(command: string, excludeFromContext?: boolean): Promise<void>;
|
|
@@ -269,7 +280,7 @@ export interface InteractiveModeContext {
|
|
|
269
280
|
handleSessionDeleteCommand(): Promise<void>;
|
|
270
281
|
showOAuthSelector(mode: "login" | "logout", providerId?: string): Promise<void>;
|
|
271
282
|
showHookConfirm(title: string, message: string): Promise<boolean>;
|
|
272
|
-
showDebugSelector(): void
|
|
283
|
+
showDebugSelector(): Promise<void>;
|
|
273
284
|
showSessionObserver(): void;
|
|
274
285
|
resetObserverRegistry(): void;
|
|
275
286
|
|
|
@@ -278,9 +289,9 @@ export interface InteractiveModeContext {
|
|
|
278
289
|
handleCtrlD(): void;
|
|
279
290
|
handleCtrlZ(): void;
|
|
280
291
|
handleDequeue(): void;
|
|
281
|
-
handleBackgroundCommand(): void;
|
|
282
292
|
handleImagePaste(): Promise<boolean>;
|
|
283
293
|
handleBtwCommand(question: string): Promise<void>;
|
|
294
|
+
handleTanCommand(work: string): Promise<void>;
|
|
284
295
|
hasActiveBtw(): boolean;
|
|
285
296
|
handleBtwEscape(): boolean;
|
|
286
297
|
handleOmfgCommand(complaint: string): Promise<void>;
|
|
@@ -37,6 +37,9 @@ export interface ContextBreakdown {
|
|
|
37
37
|
freeTokens: number;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
const EMPTY_STRING_PARTS: readonly string[] = [];
|
|
41
|
+
const EMPTY_TOOLS: ReadonlyArray<Pick<Tool, "name" | "description" | "parameters">> = [];
|
|
42
|
+
|
|
40
43
|
export function estimateSkillsTokens(skills: readonly Skill[]): number {
|
|
41
44
|
const fragments: string[] = [];
|
|
42
45
|
for (const skill of skills) {
|
|
@@ -75,15 +78,16 @@ export function estimateToolSchemaTokens(
|
|
|
75
78
|
* messages walked incrementally as new entries append.
|
|
76
79
|
*/
|
|
77
80
|
export function computeNonMessageTokens(session: AgentSession): number {
|
|
78
|
-
const
|
|
79
|
-
|
|
81
|
+
const systemPromptParts = session.systemPrompt ?? EMPTY_STRING_PARTS;
|
|
82
|
+
const tools = session.agent?.state?.tools ?? EMPTY_TOOLS;
|
|
83
|
+
return countTokens(systemPromptParts) + estimateToolSchemaTokens(tools);
|
|
80
84
|
}
|
|
81
85
|
|
|
82
86
|
/**
|
|
83
|
-
* Shared helper for the four non-message token totals
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
+
* Shared helper for the four non-message token totals used by
|
|
88
|
+
* `computeContextBreakdown` (/context panel). Keep this category split stable:
|
|
89
|
+
* the status-line fast path intentionally uses the equivalent collapsed total
|
|
90
|
+
* in `computeNonMessageTokens`.
|
|
87
91
|
*/
|
|
88
92
|
function computeNonMessageBreakdown(session: AgentSession): {
|
|
89
93
|
skillsTokens: number;
|
|
@@ -9,6 +9,15 @@ export interface CodeBlock {
|
|
|
9
9
|
code: string;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
/** A blockquote block: a maximal run of `>`-prefixed lines from markdown. */
|
|
13
|
+
export interface QuoteBlock {
|
|
14
|
+
/** Block body with each line's `>` marker (and one optional space) removed. */
|
|
15
|
+
text: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** A drillable block within an assistant message, in document order. */
|
|
19
|
+
export type MessageBlock = ({ kind: "code" } & CodeBlock) | ({ kind: "quote" } & QuoteBlock);
|
|
20
|
+
|
|
12
21
|
/** A runnable command found in the transcript. */
|
|
13
22
|
export interface LastCommand {
|
|
14
23
|
kind: "bash" | "eval";
|
|
@@ -23,7 +32,7 @@ export interface LastCommand {
|
|
|
23
32
|
* `children` to drill into.
|
|
24
33
|
*/
|
|
25
34
|
export interface CopyTarget {
|
|
26
|
-
/** Stable
|
|
35
|
+
/** Stable id (e.g. "msg:1", "msg:1:code:0", "msg:1:quote:0", "msg:1:all", "cmd:1"). */
|
|
27
36
|
id: string;
|
|
28
37
|
label: string;
|
|
29
38
|
/** Dim annotation: line/block counts, language, or tool name. */
|
|
@@ -49,17 +58,73 @@ export interface CopySource {
|
|
|
49
58
|
/** Cap on how many recent assistant messages the picker lists. */
|
|
50
59
|
const MAX_MESSAGES = 50;
|
|
51
60
|
|
|
52
|
-
const
|
|
61
|
+
const OPEN_FENCE_RE = /^```([^\n]*)$/;
|
|
62
|
+
const CLOSE_FENCE_RE = /^```/;
|
|
63
|
+
const QUOTE_LINE_RE = /^>(.*)$/;
|
|
53
64
|
|
|
54
|
-
/**
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
65
|
+
/**
|
|
66
|
+
* Split assistant markdown into drillable blocks — fenced code and `>`-quoted
|
|
67
|
+
* runs — in document order. Fences mask their bodies, so a `>` line inside a
|
|
68
|
+
* code block is never mistaken for a quote. An unclosed fence is treated as
|
|
69
|
+
* ordinary text, matching the fenced-block grammar.
|
|
70
|
+
*/
|
|
71
|
+
export function extractBlocks(text: string): MessageBlock[] {
|
|
72
|
+
const blocks: MessageBlock[] = [];
|
|
73
|
+
const lines = text.split("\n");
|
|
74
|
+
let quote: string[] | undefined;
|
|
75
|
+
const flushQuote = () => {
|
|
76
|
+
if (quote) {
|
|
77
|
+
blocks.push({ kind: "quote", text: quote.join("\n") });
|
|
78
|
+
quote = undefined;
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
for (let i = 0; i < lines.length; i++) {
|
|
83
|
+
const line = lines[i]!;
|
|
84
|
+
const open = OPEN_FENCE_RE.exec(line);
|
|
85
|
+
if (open) {
|
|
86
|
+
let close = -1;
|
|
87
|
+
for (let k = i + 1; k < lines.length; k++) {
|
|
88
|
+
if (CLOSE_FENCE_RE.test(lines[k]!)) {
|
|
89
|
+
close = k;
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (close !== -1) {
|
|
94
|
+
flushQuote();
|
|
95
|
+
blocks.push({ kind: "code", lang: open[1].trim(), code: lines.slice(i + 1, close).join("\n") });
|
|
96
|
+
i = close;
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const quoted = QUOTE_LINE_RE.exec(line);
|
|
102
|
+
if (quoted) {
|
|
103
|
+
// Strip the `>` marker plus one optional following space.
|
|
104
|
+
quote ??= [];
|
|
105
|
+
quote.push(quoted[1].startsWith(" ") ? quoted[1].slice(1) : quoted[1]);
|
|
106
|
+
} else {
|
|
107
|
+
flushQuote();
|
|
108
|
+
}
|
|
59
109
|
}
|
|
110
|
+
flushQuote();
|
|
60
111
|
return blocks;
|
|
61
112
|
}
|
|
62
113
|
|
|
114
|
+
/** Extract fenced code blocks from assistant markdown, in document order. */
|
|
115
|
+
export function extractCodeBlocks(text: string): CodeBlock[] {
|
|
116
|
+
return extractBlocks(text)
|
|
117
|
+
.filter((b): b is { kind: "code" } & CodeBlock => b.kind === "code")
|
|
118
|
+
.map(b => ({ lang: b.lang, code: b.code }));
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** Extract `>`-quoted blocks from assistant markdown, in document order. */
|
|
122
|
+
export function extractQuoteBlocks(text: string): QuoteBlock[] {
|
|
123
|
+
return extractBlocks(text)
|
|
124
|
+
.filter((b): b is { kind: "quote" } & QuoteBlock => b.kind === "quote")
|
|
125
|
+
.map(b => ({ text: b.text }));
|
|
126
|
+
}
|
|
127
|
+
|
|
63
128
|
function extractEvalCode(args: unknown): { code: string; language: string } | undefined {
|
|
64
129
|
if (!args || typeof args !== "object") return undefined;
|
|
65
130
|
const cells = (args as { cells?: unknown }).cells;
|
|
@@ -136,42 +201,83 @@ function firstLine(text: string): string {
|
|
|
136
201
|
return text.trim().replace(/\s+/g, " ");
|
|
137
202
|
}
|
|
138
203
|
|
|
139
|
-
/**
|
|
140
|
-
|
|
204
|
+
/** "<n> lines · <c> code · <q> quote" — omitting block kinds that are absent. */
|
|
205
|
+
function blockSummaryHint(text: string, codeCount: number, quoteCount: number): string {
|
|
206
|
+
const parts = [pluralLines(text)];
|
|
207
|
+
if (codeCount > 0) parts.push(`${codeCount} code`);
|
|
208
|
+
if (quoteCount > 0) parts.push(`${quoteCount} quote`);
|
|
209
|
+
return parts.join(" · ");
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/** Build the target node for one assistant message: a leaf when it has no
|
|
213
|
+
* drillable blocks, otherwise a group exposing the full message plus each
|
|
214
|
+
* fenced code block and `>`-quoted block (de-prefixed) as a child target. */
|
|
141
215
|
function messageTarget(text: string, rank: number): CopyTarget {
|
|
142
216
|
const id = `msg:${rank}`;
|
|
143
217
|
const label = firstLine(text);
|
|
144
|
-
const blocks =
|
|
145
|
-
const hint = blocks.length > 0 ? `${pluralLines(text)} · ${blocks.length} code` : pluralLines(text);
|
|
218
|
+
const blocks = extractBlocks(text);
|
|
146
219
|
const messageCopy = rank === 1 ? "Copied last message to clipboard" : "Copied message to clipboard";
|
|
147
220
|
|
|
148
221
|
if (blocks.length === 0) {
|
|
149
|
-
return { id, label, hint, preview: text, content: text, copyMessage: messageCopy };
|
|
222
|
+
return { id, label, hint: pluralLines(text), preview: text, content: text, copyMessage: messageCopy };
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// The message node itself copies the full message; each block is a child
|
|
226
|
+
// copy target you can drill into, kept in document order.
|
|
227
|
+
const children: CopyTarget[] = [];
|
|
228
|
+
const codeBlocks: CodeBlock[] = [];
|
|
229
|
+
const quoteBlocks: QuoteBlock[] = [];
|
|
230
|
+
for (const block of blocks) {
|
|
231
|
+
if (block.kind === "code") {
|
|
232
|
+
const j = codeBlocks.length;
|
|
233
|
+
codeBlocks.push(block);
|
|
234
|
+
children.push({
|
|
235
|
+
id: `${id}:code:${j}`,
|
|
236
|
+
label: `Block ${j + 1}`,
|
|
237
|
+
hint: blockHint(block),
|
|
238
|
+
preview: block.code,
|
|
239
|
+
language: block.lang || undefined,
|
|
240
|
+
content: block.code,
|
|
241
|
+
copyMessage: `Copied code block ${j + 1} to clipboard`,
|
|
242
|
+
});
|
|
243
|
+
} else {
|
|
244
|
+
const j = quoteBlocks.length;
|
|
245
|
+
quoteBlocks.push(block);
|
|
246
|
+
children.push({
|
|
247
|
+
id: `${id}:quote:${j}`,
|
|
248
|
+
label: `Quote ${j + 1}`,
|
|
249
|
+
hint: pluralLines(block.text),
|
|
250
|
+
preview: block.text,
|
|
251
|
+
content: block.text,
|
|
252
|
+
copyMessage: `Copied quote block ${j + 1} to clipboard`,
|
|
253
|
+
});
|
|
254
|
+
}
|
|
150
255
|
}
|
|
151
256
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
const children: CopyTarget[] = blocks.map((block, j) => ({
|
|
155
|
-
id: `${id}:code:${j}`,
|
|
156
|
-
label: `Block ${j + 1}`,
|
|
157
|
-
hint: blockHint(block),
|
|
158
|
-
preview: block.code,
|
|
159
|
-
language: block.lang || undefined,
|
|
160
|
-
content: block.code,
|
|
161
|
-
copyMessage: `Copied code block ${j + 1} to clipboard`,
|
|
162
|
-
}));
|
|
163
|
-
if (blocks.length > 1) {
|
|
164
|
-
const combined = blocks.map(b => b.code).join("\n\n");
|
|
257
|
+
if (codeBlocks.length > 1) {
|
|
258
|
+
const combined = codeBlocks.map(b => b.code).join("\n\n");
|
|
165
259
|
children.push({
|
|
166
260
|
id: `${id}:all`,
|
|
167
|
-
label: `All ${
|
|
261
|
+
label: `All ${codeBlocks.length} blocks`,
|
|
262
|
+
hint: pluralLines(combined),
|
|
263
|
+
preview: combined,
|
|
264
|
+
content: combined,
|
|
265
|
+
copyMessage: `Copied ${codeBlocks.length} code blocks to clipboard`,
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
if (quoteBlocks.length > 1) {
|
|
269
|
+
const combined = quoteBlocks.map(b => b.text).join("\n\n");
|
|
270
|
+
children.push({
|
|
271
|
+
id: `${id}:all-quotes`,
|
|
272
|
+
label: `All ${quoteBlocks.length} quotes`,
|
|
168
273
|
hint: pluralLines(combined),
|
|
169
274
|
preview: combined,
|
|
170
275
|
content: combined,
|
|
171
|
-
copyMessage: `Copied ${
|
|
276
|
+
copyMessage: `Copied ${quoteBlocks.length} quote blocks to clipboard`,
|
|
172
277
|
});
|
|
173
278
|
}
|
|
174
279
|
|
|
280
|
+
const hint = blockSummaryHint(text, codeBlocks.length, quoteBlocks.length);
|
|
175
281
|
return { id, label, hint, preview: text, content: text, copyMessage: messageCopy, children };
|
|
176
282
|
}
|
|
177
283
|
|
|
@@ -37,6 +37,7 @@ export function buildHotkeysMarkdown(bindings: HotkeysMarkdownBindings): string
|
|
|
37
37
|
`| \`${appKey(bindings, "app.clear")}\` | Clear editor (first) / exit (second) |`,
|
|
38
38
|
`| \`${appKey(bindings, "app.exit")}\` | Exit (when editor is empty) |`,
|
|
39
39
|
`| \`${appKey(bindings, "app.suspend")}\` | Suspend to background |`,
|
|
40
|
+
`| \`${appKey(bindings, "app.display.reset")}\` | Reset terminal display |`,
|
|
40
41
|
`| \`${appKey(bindings, "app.thinking.cycle")}\` | Cycle thinking level |`,
|
|
41
42
|
`| \`${appKey(bindings, "app.model.cycleForward")}\` | Cycle role models (slow/default/smol) |`,
|
|
42
43
|
`| \`${appKey(bindings, "app.model.cycleBackward")}\` | Cycle role models (backward) |`,
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
} from "../../modes/components/read-tool-group";
|
|
18
18
|
import { SkillMessageComponent } from "../../modes/components/skill-message";
|
|
19
19
|
import { ToolExecutionComponent } from "../../modes/components/tool-execution";
|
|
20
|
+
import { TranscriptBlock } from "../../modes/components/transcript-container";
|
|
20
21
|
import { UserMessageComponent } from "../../modes/components/user-message";
|
|
21
22
|
import { materializeImageReferenceLinksSync } from "../../modes/image-references";
|
|
22
23
|
import { theme } from "../../modes/theme/theme";
|
|
@@ -24,6 +25,7 @@ import type { CompactionQueuedMessage, InteractiveModeContext } from "../../mode
|
|
|
24
25
|
import {
|
|
25
26
|
type CustomMessage,
|
|
26
27
|
isSilentAbort,
|
|
28
|
+
resolveAbortLabel,
|
|
27
29
|
SKILL_PROMPT_MESSAGE_TYPE,
|
|
28
30
|
type SkillPromptDetails,
|
|
29
31
|
} from "../../session/messages";
|
|
@@ -73,9 +75,6 @@ export class UiHelpers {
|
|
|
73
75
|
* we update the previous status line instead of appending new ones to avoid log spam.
|
|
74
76
|
*/
|
|
75
77
|
showStatus(message: string, options?: { dim?: boolean }): void {
|
|
76
|
-
if (this.ctx.isBackgrounded) {
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
78
|
const children = this.ctx.chatContainer.children;
|
|
80
79
|
const last = children.length > 0 ? children[children.length - 1] : undefined;
|
|
81
80
|
const secondLast = children.length > 1 ? children[children.length - 2] : undefined;
|
|
@@ -90,11 +89,9 @@ export class UiHelpers {
|
|
|
90
89
|
|
|
91
90
|
const spacer = new Spacer(1);
|
|
92
91
|
const text = new Text(rendered, 1, 0);
|
|
93
|
-
this.ctx.
|
|
94
|
-
this.ctx.chatContainer.addChild(text);
|
|
92
|
+
this.ctx.present([spacer, text]);
|
|
95
93
|
this.ctx.lastStatusSpacer = spacer;
|
|
96
94
|
this.ctx.lastStatusText = text;
|
|
97
|
-
this.ctx.ui.requestRender();
|
|
98
95
|
}
|
|
99
96
|
|
|
100
97
|
addMessageToChat(
|
|
@@ -153,6 +150,7 @@ export class UiHelpers {
|
|
|
153
150
|
durationMs: details?.durationMs,
|
|
154
151
|
},
|
|
155
152
|
];
|
|
153
|
+
const block = new TranscriptBlock();
|
|
156
154
|
for (const job of jobs) {
|
|
157
155
|
const jobId = job.jobId ?? "unknown";
|
|
158
156
|
const typeLabel = job.type ? `[${job.type}]` : "[job]";
|
|
@@ -165,8 +163,9 @@ export class UiHelpers {
|
|
|
165
163
|
]
|
|
166
164
|
.filter(Boolean)
|
|
167
165
|
.join(" ");
|
|
168
|
-
|
|
166
|
+
block.addChild(new Text(line, 1, 0));
|
|
169
167
|
}
|
|
168
|
+
this.ctx.chatContainer.addChild(block);
|
|
170
169
|
break;
|
|
171
170
|
}
|
|
172
171
|
if (message.customType === SKILL_PROMPT_MESSAGE_TYPE) {
|
|
@@ -206,19 +205,18 @@ export class UiHelpers {
|
|
|
206
205
|
body = details?.body ?? "";
|
|
207
206
|
arrow = `${from} ⇨ ${to}`;
|
|
208
207
|
}
|
|
209
|
-
const
|
|
208
|
+
const block = new TranscriptBlock();
|
|
210
209
|
const header = `${theme.fg("accent", `[IRC] ${arrow}`)}`;
|
|
211
210
|
const headerComponent = new Text(header, 1, 0);
|
|
212
|
-
|
|
213
|
-
components.push(headerComponent);
|
|
211
|
+
block.addChild(headerComponent);
|
|
214
212
|
if (body) {
|
|
215
213
|
for (const line of body.split("\n")) {
|
|
216
214
|
const lineComponent = new Text(theme.fg("muted", ` ${line}`), 0, 0);
|
|
217
|
-
|
|
218
|
-
components.push(lineComponent);
|
|
215
|
+
block.addChild(lineComponent);
|
|
219
216
|
}
|
|
220
217
|
}
|
|
221
|
-
|
|
218
|
+
this.ctx.chatContainer.addChild(block);
|
|
219
|
+
return [block];
|
|
222
220
|
}
|
|
223
221
|
const renderer = this.ctx.session.extensionRunner?.getMessageRenderer(message.customType);
|
|
224
222
|
// Both HookMessage and CustomMessage have the same structure, cast for compatibility
|
|
@@ -229,14 +227,12 @@ export class UiHelpers {
|
|
|
229
227
|
break;
|
|
230
228
|
}
|
|
231
229
|
case "compactionSummary": {
|
|
232
|
-
this.ctx.chatContainer.addChild(new Spacer(1));
|
|
233
230
|
const component = new CompactionSummaryMessageComponent(message);
|
|
234
231
|
component.setExpanded(this.ctx.toolOutputExpanded);
|
|
235
232
|
this.ctx.chatContainer.addChild(component);
|
|
236
233
|
break;
|
|
237
234
|
}
|
|
238
235
|
case "branchSummary": {
|
|
239
|
-
this.ctx.chatContainer.addChild(new Spacer(1));
|
|
240
236
|
const component = new BranchSummaryMessageComponent(message);
|
|
241
237
|
component.setExpanded(this.ctx.toolOutputExpanded);
|
|
242
238
|
this.ctx.chatContainer.addChild(component);
|
|
@@ -244,6 +240,7 @@ export class UiHelpers {
|
|
|
244
240
|
}
|
|
245
241
|
case "fileMention": {
|
|
246
242
|
// Render compact file mention display
|
|
243
|
+
const block = new TranscriptBlock();
|
|
247
244
|
for (const file of message.files) {
|
|
248
245
|
let suffix: string;
|
|
249
246
|
if (file.skippedReason === "tooLarge") {
|
|
@@ -260,8 +257,9 @@ export class UiHelpers {
|
|
|
260
257
|
"accent",
|
|
261
258
|
file.path,
|
|
262
259
|
)} ${theme.fg("dim", suffix)}`;
|
|
263
|
-
|
|
260
|
+
block.addChild(new Text(text, 0, 0));
|
|
264
261
|
}
|
|
262
|
+
if (block.children.length > 0) this.ctx.chatContainer.addChild(block);
|
|
265
263
|
break;
|
|
266
264
|
}
|
|
267
265
|
case "user":
|
|
@@ -338,18 +336,21 @@ export class UiHelpers {
|
|
|
338
336
|
if (assistantComponent) {
|
|
339
337
|
assistantComponent.setUsageInfo(message.usage);
|
|
340
338
|
}
|
|
341
|
-
|
|
339
|
+
const hasVisibleAssistantContent = message.content.some(
|
|
340
|
+
content =>
|
|
341
|
+
(content.type === "text" && content.text.trim().length > 0) ||
|
|
342
|
+
(content.type === "thinking" && content.thinking.trim().length > 0),
|
|
343
|
+
);
|
|
344
|
+
if (hasVisibleAssistantContent) {
|
|
345
|
+
readGroup?.finalize();
|
|
346
|
+
readGroup = null;
|
|
347
|
+
}
|
|
342
348
|
const isAbortedSilently = message.stopReason === "aborted" && isSilentAbort(message.errorMessage);
|
|
343
349
|
const hasErrorStop =
|
|
344
350
|
!isAbortedSilently && (message.stopReason === "aborted" || message.stopReason === "error");
|
|
345
351
|
const errorMessage = hasErrorStop
|
|
346
352
|
? message.stopReason === "aborted"
|
|
347
|
-
? (
|
|
348
|
-
const retryAttempt = this.ctx.session.retryAttempt;
|
|
349
|
-
return retryAttempt > 0
|
|
350
|
-
? `Aborted after ${retryAttempt} retry attempt${retryAttempt > 1 ? "s" : ""}`
|
|
351
|
-
: "Operation aborted";
|
|
352
|
-
})()
|
|
353
|
+
? resolveAbortLabel(message.errorMessage, this.ctx.session.retryAttempt)
|
|
353
354
|
: message.errorMessage || "Error"
|
|
354
355
|
: null;
|
|
355
356
|
|
|
@@ -391,6 +392,7 @@ export class UiHelpers {
|
|
|
391
392
|
continue;
|
|
392
393
|
}
|
|
393
394
|
|
|
395
|
+
readGroup?.finalize();
|
|
394
396
|
readGroup = null;
|
|
395
397
|
const tool = this.ctx.session.getToolByName(content.name);
|
|
396
398
|
const renderArgs =
|
|
@@ -478,6 +480,10 @@ export class UiHelpers {
|
|
|
478
480
|
}
|
|
479
481
|
}
|
|
480
482
|
|
|
483
|
+
// The trailing read run has no following break to close it; finalize so the
|
|
484
|
+
// rebuilt group commits to native scrollback like every other historical block.
|
|
485
|
+
readGroup?.finalize();
|
|
486
|
+
|
|
481
487
|
// Render deferred messages (compaction summaries) at the bottom so they're visible
|
|
482
488
|
for (const message of deferredMessages) {
|
|
483
489
|
this.ctx.addMessageToChat(message, options);
|
|
@@ -490,8 +496,15 @@ export class UiHelpers {
|
|
|
490
496
|
renderInitialMessages(prebuiltContext?: SessionContext, options: RenderInitialMessagesOptions = {}): void {
|
|
491
497
|
// This path is used to rebuild the visible chat transcript (e.g. after custom/debug UI).
|
|
492
498
|
// Clear existing rendered chat first to avoid duplicating the full session in the container.
|
|
499
|
+
// On a non-preserving rebuild the existing blocks are discarded for good, so
|
|
500
|
+
// dispose them (stopping any live timers/subscriptions) before clearing. When
|
|
501
|
+
// preserving, the same instances are re-added below, so detach without dispose.
|
|
493
502
|
const preservedChatChildren = options.preserveExistingChat ? this.ctx.chatContainer.children : undefined;
|
|
494
|
-
|
|
503
|
+
if (preservedChatChildren) {
|
|
504
|
+
this.ctx.chatContainer.clear();
|
|
505
|
+
} else {
|
|
506
|
+
this.ctx.resetTranscript();
|
|
507
|
+
}
|
|
495
508
|
this.ctx.pendingMessagesContainer.clear();
|
|
496
509
|
this.ctx.pendingBashComponents = [];
|
|
497
510
|
this.ctx.pendingPythonComponents = [];
|
|
@@ -527,9 +540,6 @@ export class UiHelpers {
|
|
|
527
540
|
}
|
|
528
541
|
|
|
529
542
|
clearEditor(): void {
|
|
530
|
-
if (this.ctx.isBackgrounded) {
|
|
531
|
-
return;
|
|
532
|
-
}
|
|
533
543
|
this.ctx.editor.setText("");
|
|
534
544
|
this.ctx.pendingImages = [];
|
|
535
545
|
this.ctx.pendingImageLinks = [];
|
|
@@ -538,29 +548,17 @@ export class UiHelpers {
|
|
|
538
548
|
}
|
|
539
549
|
|
|
540
550
|
showError(errorMessage: string): void {
|
|
541
|
-
|
|
542
|
-
process.stderr.write(`Error: ${errorMessage}\n`);
|
|
543
|
-
return;
|
|
544
|
-
}
|
|
545
|
-
this.ctx.chatContainer.addChild(new Spacer(1));
|
|
546
|
-
this.ctx.chatContainer.addChild(new Text(theme.fg("error", `Error: ${errorMessage}`), 1, 0));
|
|
547
|
-
this.ctx.ui.requestRender();
|
|
551
|
+
this.ctx.present([new Spacer(1), new Text(theme.fg("error", `Error: ${errorMessage}`), 1, 0)]);
|
|
548
552
|
}
|
|
549
553
|
|
|
550
554
|
showWarning(warningMessage: string): void {
|
|
551
|
-
|
|
552
|
-
process.stderr.write(`Warning: ${warningMessage}\n`);
|
|
553
|
-
return;
|
|
554
|
-
}
|
|
555
|
-
this.ctx.chatContainer.addChild(new Spacer(1));
|
|
556
|
-
this.ctx.chatContainer.addChild(new Text(theme.fg("warning", `Warning: ${warningMessage}`), 1, 0));
|
|
557
|
-
this.ctx.ui.requestRender();
|
|
555
|
+
this.ctx.present([new Spacer(1), new Text(theme.fg("warning", `Warning: ${warningMessage}`), 1, 0)]);
|
|
558
556
|
}
|
|
559
557
|
|
|
560
558
|
showNewVersionNotification(newVersion: string): void {
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
559
|
+
const block = new TranscriptBlock();
|
|
560
|
+
block.addChild(new DynamicBorder(text => theme.fg("warning", text)));
|
|
561
|
+
block.addChild(
|
|
564
562
|
new Text(
|
|
565
563
|
theme.bold(theme.fg("warning", "Update Available")) +
|
|
566
564
|
"\n" +
|
|
@@ -570,8 +568,8 @@ export class UiHelpers {
|
|
|
570
568
|
0,
|
|
571
569
|
),
|
|
572
570
|
);
|
|
573
|
-
|
|
574
|
-
this.ctx.
|
|
571
|
+
block.addChild(new DynamicBorder(text => theme.fg("warning", text)));
|
|
572
|
+
this.ctx.present(block);
|
|
575
573
|
}
|
|
576
574
|
|
|
577
575
|
updatePendingMessagesDisplay(): void {
|