mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-20 12:04:35 +00:00
Add TUI rendering for CustomMessageEntry
- Add CustomMessageComponent with purple-tinted styling - Add theme colors: customMessageBg, customMessageText, customMessageLabel - Rename renderMessages to renderSessionContext taking SessionContext directly - renderInitialMessages now gets context from sessionManager - Skip rendering for display: false entries
This commit is contained in:
parent
beb804cda0
commit
7b94ddf36b
6 changed files with 72 additions and 18 deletions
|
|
@ -77,7 +77,7 @@ async function runInteractiveMode(
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
mode.renderInitialMessages(session.state);
|
mode.renderInitialMessages();
|
||||||
|
|
||||||
if (migratedProviders.length > 0) {
|
if (migratedProviders.length > 0) {
|
||||||
mode.showWarning(`Migrated credentials to auth.json: ${migratedProviders.join(", ")}`);
|
mode.showWarning(`Migrated credentials to auth.json: ${migratedProviders.join(", ")}`);
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ import type { HookUIContext } from "../../core/hooks/index.js";
|
||||||
import { isBashExecutionMessage } from "../../core/messages.js";
|
import { isBashExecutionMessage } from "../../core/messages.js";
|
||||||
import {
|
import {
|
||||||
getLatestCompactionEntry,
|
getLatestCompactionEntry,
|
||||||
|
type SessionContext,
|
||||||
SessionManager,
|
SessionManager,
|
||||||
SUMMARY_PREFIX,
|
SUMMARY_PREFIX,
|
||||||
SUMMARY_SUFFIX,
|
SUMMARY_SUFFIX,
|
||||||
|
|
@ -45,6 +46,7 @@ import { AssistantMessageComponent } from "./components/assistant-message.js";
|
||||||
import { BashExecutionComponent } from "./components/bash-execution.js";
|
import { BashExecutionComponent } from "./components/bash-execution.js";
|
||||||
import { CompactionComponent } from "./components/compaction.js";
|
import { CompactionComponent } from "./components/compaction.js";
|
||||||
import { CustomEditor } from "./components/custom-editor.js";
|
import { CustomEditor } from "./components/custom-editor.js";
|
||||||
|
import { CustomMessageComponent } from "./components/custom-message.js";
|
||||||
import { DynamicBorder } from "./components/dynamic-border.js";
|
import { DynamicBorder } from "./components/dynamic-border.js";
|
||||||
import { FooterComponent } from "./components/footer.js";
|
import { FooterComponent } from "./components/footer.js";
|
||||||
import { HookInputComponent } from "./components/hook-input.js";
|
import { HookInputComponent } from "./components/hook-input.js";
|
||||||
|
|
@ -1020,13 +1022,13 @@ export class InteractiveMode {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render messages to chat. Used for initial load and rebuild after compaction.
|
* Render session context to chat. Used for initial load and rebuild after compaction.
|
||||||
* @param messages Messages to render
|
* @param sessionContext Session context to render
|
||||||
* @param options.updateFooter Update footer state
|
* @param options.updateFooter Update footer state
|
||||||
* @param options.populateHistory Add user messages to editor history
|
* @param options.populateHistory Add user messages to editor history
|
||||||
*/
|
*/
|
||||||
private renderMessages(
|
private renderSessionContext(
|
||||||
messages: readonly (Message | AppMessage)[],
|
sessionContext: SessionContext,
|
||||||
options: { updateFooter?: boolean; populateHistory?: boolean } = {},
|
options: { updateFooter?: boolean; populateHistory?: boolean } = {},
|
||||||
): void {
|
): void {
|
||||||
this.isFirstUserMessage = true;
|
this.isFirstUserMessage = true;
|
||||||
|
|
@ -1038,13 +1040,25 @@ export class InteractiveMode {
|
||||||
}
|
}
|
||||||
|
|
||||||
const compactionEntry = getLatestCompactionEntry(this.sessionManager.getEntries());
|
const compactionEntry = getLatestCompactionEntry(this.sessionManager.getEntries());
|
||||||
|
const entries = sessionContext.entries;
|
||||||
|
|
||||||
|
for (let i = 0; i < sessionContext.messages.length; i++) {
|
||||||
|
const message = sessionContext.messages[i];
|
||||||
|
const entry = entries?.[i];
|
||||||
|
|
||||||
for (const message of messages) {
|
|
||||||
if (isBashExecutionMessage(message)) {
|
if (isBashExecutionMessage(message)) {
|
||||||
this.addMessageToChat(message);
|
this.addMessageToChat(message);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if this is a custom_message entry
|
||||||
|
if (entry?.type === "custom_message") {
|
||||||
|
if (entry.display) {
|
||||||
|
this.chatContainer.addChild(new CustomMessageComponent(entry));
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (message.role === "user") {
|
if (message.role === "user") {
|
||||||
const textContent = this.getUserMessageText(message);
|
const textContent = this.getUserMessageText(message);
|
||||||
if (textContent) {
|
if (textContent) {
|
||||||
|
|
@ -1103,12 +1117,17 @@ export class InteractiveMode {
|
||||||
this.ui.requestRender();
|
this.ui.requestRender();
|
||||||
}
|
}
|
||||||
|
|
||||||
renderInitialMessages(state: AgentState): void {
|
renderInitialMessages(): void {
|
||||||
this.renderMessages(state.messages, { updateFooter: true, populateHistory: true });
|
// Get aligned messages and entries from session context
|
||||||
|
const context = this.sessionManager.buildSessionContext();
|
||||||
|
this.renderSessionContext(context, {
|
||||||
|
updateFooter: true,
|
||||||
|
populateHistory: true,
|
||||||
|
});
|
||||||
|
|
||||||
// Show compaction info if session was compacted
|
// Show compaction info if session was compacted
|
||||||
const entries = this.sessionManager.getEntries();
|
const allEntries = this.sessionManager.getEntries();
|
||||||
const compactionCount = entries.filter((e) => e.type === "compaction").length;
|
const compactionCount = allEntries.filter((e) => e.type === "compaction").length;
|
||||||
if (compactionCount > 0) {
|
if (compactionCount > 0) {
|
||||||
const times = compactionCount === 1 ? "1 time" : `${compactionCount} times`;
|
const times = compactionCount === 1 ? "1 time" : `${compactionCount} times`;
|
||||||
this.showStatus(`Session compacted ${times}`);
|
this.showStatus(`Session compacted ${times}`);
|
||||||
|
|
@ -1125,7 +1144,8 @@ export class InteractiveMode {
|
||||||
}
|
}
|
||||||
|
|
||||||
private rebuildChatFromMessages(): void {
|
private rebuildChatFromMessages(): void {
|
||||||
this.renderMessages(this.session.messages);
|
const context = this.sessionManager.buildSessionContext();
|
||||||
|
this.renderSessionContext(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
|
|
@ -1500,7 +1520,7 @@ export class InteractiveMode {
|
||||||
|
|
||||||
this.chatContainer.clear();
|
this.chatContainer.clear();
|
||||||
this.isFirstUserMessage = true;
|
this.isFirstUserMessage = true;
|
||||||
this.renderInitialMessages(this.session.state);
|
this.renderInitialMessages();
|
||||||
this.editor.setText(result.selectedText);
|
this.editor.setText(result.selectedText);
|
||||||
done();
|
done();
|
||||||
this.showStatus("Branched to new session");
|
this.showStatus("Branched to new session");
|
||||||
|
|
@ -1554,7 +1574,7 @@ export class InteractiveMode {
|
||||||
// Clear and re-render the chat
|
// Clear and re-render the chat
|
||||||
this.chatContainer.clear();
|
this.chatContainer.clear();
|
||||||
this.isFirstUserMessage = true;
|
this.isFirstUserMessage = true;
|
||||||
this.renderInitialMessages(this.session.state);
|
this.renderInitialMessages();
|
||||||
this.showStatus("Resumed session");
|
this.showStatus("Resumed session");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,8 @@
|
||||||
"userMsgBg": "#343541",
|
"userMsgBg": "#343541",
|
||||||
"toolPendingBg": "#282832",
|
"toolPendingBg": "#282832",
|
||||||
"toolSuccessBg": "#283228",
|
"toolSuccessBg": "#283228",
|
||||||
"toolErrorBg": "#3c2828"
|
"toolErrorBg": "#3c2828",
|
||||||
|
"customMsgBg": "#2d2838"
|
||||||
},
|
},
|
||||||
"colors": {
|
"colors": {
|
||||||
"accent": "accent",
|
"accent": "accent",
|
||||||
|
|
@ -30,6 +31,9 @@
|
||||||
|
|
||||||
"userMessageBg": "userMsgBg",
|
"userMessageBg": "userMsgBg",
|
||||||
"userMessageText": "",
|
"userMessageText": "",
|
||||||
|
"customMessageBg": "customMsgBg",
|
||||||
|
"customMessageText": "",
|
||||||
|
"customMessageLabel": "#9575cd",
|
||||||
"toolPendingBg": "toolPendingBg",
|
"toolPendingBg": "toolPendingBg",
|
||||||
"toolSuccessBg": "toolSuccessBg",
|
"toolSuccessBg": "toolSuccessBg",
|
||||||
"toolErrorBg": "toolErrorBg",
|
"toolErrorBg": "toolErrorBg",
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,8 @@
|
||||||
"userMsgBg": "#e8e8e8",
|
"userMsgBg": "#e8e8e8",
|
||||||
"toolPendingBg": "#e8e8f0",
|
"toolPendingBg": "#e8e8f0",
|
||||||
"toolSuccessBg": "#e8f0e8",
|
"toolSuccessBg": "#e8f0e8",
|
||||||
"toolErrorBg": "#f0e8e8"
|
"toolErrorBg": "#f0e8e8",
|
||||||
|
"customMsgBg": "#ede7f6"
|
||||||
},
|
},
|
||||||
"colors": {
|
"colors": {
|
||||||
"accent": "teal",
|
"accent": "teal",
|
||||||
|
|
@ -29,6 +30,9 @@
|
||||||
|
|
||||||
"userMessageBg": "userMsgBg",
|
"userMessageBg": "userMsgBg",
|
||||||
"userMessageText": "",
|
"userMessageText": "",
|
||||||
|
"customMessageBg": "customMsgBg",
|
||||||
|
"customMessageText": "",
|
||||||
|
"customMessageLabel": "#7e57c2",
|
||||||
"toolPendingBg": "toolPendingBg",
|
"toolPendingBg": "toolPendingBg",
|
||||||
"toolSuccessBg": "toolSuccessBg",
|
"toolSuccessBg": "toolSuccessBg",
|
||||||
"toolErrorBg": "toolErrorBg",
|
"toolErrorBg": "toolErrorBg",
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,9 @@
|
||||||
"text",
|
"text",
|
||||||
"userMessageBg",
|
"userMessageBg",
|
||||||
"userMessageText",
|
"userMessageText",
|
||||||
|
"customMessageBg",
|
||||||
|
"customMessageText",
|
||||||
|
"customMessageLabel",
|
||||||
"toolPendingBg",
|
"toolPendingBg",
|
||||||
"toolSuccessBg",
|
"toolSuccessBg",
|
||||||
"toolErrorBg",
|
"toolErrorBg",
|
||||||
|
|
@ -122,6 +125,18 @@
|
||||||
"$ref": "#/$defs/colorValue",
|
"$ref": "#/$defs/colorValue",
|
||||||
"description": "User message text color"
|
"description": "User message text color"
|
||||||
},
|
},
|
||||||
|
"customMessageBg": {
|
||||||
|
"$ref": "#/$defs/colorValue",
|
||||||
|
"description": "Custom message background (hook-injected messages)"
|
||||||
|
},
|
||||||
|
"customMessageText": {
|
||||||
|
"$ref": "#/$defs/colorValue",
|
||||||
|
"description": "Custom message text color"
|
||||||
|
},
|
||||||
|
"customMessageLabel": {
|
||||||
|
"$ref": "#/$defs/colorValue",
|
||||||
|
"description": "Custom message type label color"
|
||||||
|
},
|
||||||
"toolPendingBg": {
|
"toolPendingBg": {
|
||||||
"$ref": "#/$defs/colorValue",
|
"$ref": "#/$defs/colorValue",
|
||||||
"description": "Tool execution box (pending state)"
|
"description": "Tool execution box (pending state)"
|
||||||
|
|
|
||||||
|
|
@ -34,9 +34,12 @@ const ThemeJsonSchema = Type.Object({
|
||||||
muted: ColorValueSchema,
|
muted: ColorValueSchema,
|
||||||
dim: ColorValueSchema,
|
dim: ColorValueSchema,
|
||||||
text: ColorValueSchema,
|
text: ColorValueSchema,
|
||||||
// Backgrounds & Content Text (7 colors)
|
// Backgrounds & Content Text (10 colors)
|
||||||
userMessageBg: ColorValueSchema,
|
userMessageBg: ColorValueSchema,
|
||||||
userMessageText: ColorValueSchema,
|
userMessageText: ColorValueSchema,
|
||||||
|
customMessageBg: ColorValueSchema,
|
||||||
|
customMessageText: ColorValueSchema,
|
||||||
|
customMessageLabel: ColorValueSchema,
|
||||||
toolPendingBg: ColorValueSchema,
|
toolPendingBg: ColorValueSchema,
|
||||||
toolSuccessBg: ColorValueSchema,
|
toolSuccessBg: ColorValueSchema,
|
||||||
toolErrorBg: ColorValueSchema,
|
toolErrorBg: ColorValueSchema,
|
||||||
|
|
@ -95,6 +98,8 @@ export type ThemeColor =
|
||||||
| "dim"
|
| "dim"
|
||||||
| "text"
|
| "text"
|
||||||
| "userMessageText"
|
| "userMessageText"
|
||||||
|
| "customMessageText"
|
||||||
|
| "customMessageLabel"
|
||||||
| "toolTitle"
|
| "toolTitle"
|
||||||
| "toolOutput"
|
| "toolOutput"
|
||||||
| "mdHeading"
|
| "mdHeading"
|
||||||
|
|
@ -127,7 +132,7 @@ export type ThemeColor =
|
||||||
| "thinkingXhigh"
|
| "thinkingXhigh"
|
||||||
| "bashMode";
|
| "bashMode";
|
||||||
|
|
||||||
export type ThemeBg = "userMessageBg" | "toolPendingBg" | "toolSuccessBg" | "toolErrorBg";
|
export type ThemeBg = "userMessageBg" | "customMessageBg" | "toolPendingBg" | "toolSuccessBg" | "toolErrorBg";
|
||||||
|
|
||||||
type ColorMode = "truecolor" | "256color";
|
type ColorMode = "truecolor" | "256color";
|
||||||
|
|
||||||
|
|
@ -482,7 +487,13 @@ function createTheme(themeJson: ThemeJson, mode?: ColorMode): Theme {
|
||||||
const resolvedColors = resolveThemeColors(themeJson.colors, themeJson.vars);
|
const resolvedColors = resolveThemeColors(themeJson.colors, themeJson.vars);
|
||||||
const fgColors: Record<ThemeColor, string | number> = {} as Record<ThemeColor, string | number>;
|
const fgColors: Record<ThemeColor, string | number> = {} as Record<ThemeColor, string | number>;
|
||||||
const bgColors: Record<ThemeBg, string | number> = {} as Record<ThemeBg, string | number>;
|
const bgColors: Record<ThemeBg, string | number> = {} as Record<ThemeBg, string | number>;
|
||||||
const bgColorKeys: Set<string> = new Set(["userMessageBg", "toolPendingBg", "toolSuccessBg", "toolErrorBg"]);
|
const bgColorKeys: Set<string> = new Set([
|
||||||
|
"userMessageBg",
|
||||||
|
"customMessageBg",
|
||||||
|
"toolPendingBg",
|
||||||
|
"toolSuccessBg",
|
||||||
|
"toolErrorBg",
|
||||||
|
]);
|
||||||
for (const [key, value] of Object.entries(resolvedColors)) {
|
for (const [key, value] of Object.entries(resolvedColors)) {
|
||||||
if (bgColorKeys.has(key)) {
|
if (bgColorKeys.has(key)) {
|
||||||
bgColors[key as ThemeBg] = value;
|
bgColors[key as ThemeBg] = value;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue