mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-15 10:05:14 +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) {
|
||||
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 {
|
||||
getLatestCompactionEntry,
|
||||
type SessionContext,
|
||||
SessionManager,
|
||||
SUMMARY_PREFIX,
|
||||
SUMMARY_SUFFIX,
|
||||
|
|
@ -45,6 +46,7 @@ import { AssistantMessageComponent } from "./components/assistant-message.js";
|
|||
import { BashExecutionComponent } from "./components/bash-execution.js";
|
||||
import { CompactionComponent } from "./components/compaction.js";
|
||||
import { CustomEditor } from "./components/custom-editor.js";
|
||||
import { CustomMessageComponent } from "./components/custom-message.js";
|
||||
import { DynamicBorder } from "./components/dynamic-border.js";
|
||||
import { FooterComponent } from "./components/footer.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.
|
||||
* @param messages Messages to render
|
||||
* Render session context to chat. Used for initial load and rebuild after compaction.
|
||||
* @param sessionContext Session context to render
|
||||
* @param options.updateFooter Update footer state
|
||||
* @param options.populateHistory Add user messages to editor history
|
||||
*/
|
||||
private renderMessages(
|
||||
messages: readonly (Message | AppMessage)[],
|
||||
private renderSessionContext(
|
||||
sessionContext: SessionContext,
|
||||
options: { updateFooter?: boolean; populateHistory?: boolean } = {},
|
||||
): void {
|
||||
this.isFirstUserMessage = true;
|
||||
|
|
@ -1038,13 +1040,25 @@ export class InteractiveMode {
|
|||
}
|
||||
|
||||
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)) {
|
||||
this.addMessageToChat(message);
|
||||
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") {
|
||||
const textContent = this.getUserMessageText(message);
|
||||
if (textContent) {
|
||||
|
|
@ -1103,12 +1117,17 @@ export class InteractiveMode {
|
|||
this.ui.requestRender();
|
||||
}
|
||||
|
||||
renderInitialMessages(state: AgentState): void {
|
||||
this.renderMessages(state.messages, { updateFooter: true, populateHistory: true });
|
||||
renderInitialMessages(): void {
|
||||
// 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
|
||||
const entries = this.sessionManager.getEntries();
|
||||
const compactionCount = entries.filter((e) => e.type === "compaction").length;
|
||||
const allEntries = this.sessionManager.getEntries();
|
||||
const compactionCount = allEntries.filter((e) => e.type === "compaction").length;
|
||||
if (compactionCount > 0) {
|
||||
const times = compactionCount === 1 ? "1 time" : `${compactionCount} times`;
|
||||
this.showStatus(`Session compacted ${times}`);
|
||||
|
|
@ -1125,7 +1144,8 @@ export class InteractiveMode {
|
|||
}
|
||||
|
||||
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.isFirstUserMessage = true;
|
||||
this.renderInitialMessages(this.session.state);
|
||||
this.renderInitialMessages();
|
||||
this.editor.setText(result.selectedText);
|
||||
done();
|
||||
this.showStatus("Branched to new session");
|
||||
|
|
@ -1554,7 +1574,7 @@ export class InteractiveMode {
|
|||
// Clear and re-render the chat
|
||||
this.chatContainer.clear();
|
||||
this.isFirstUserMessage = true;
|
||||
this.renderInitialMessages(this.session.state);
|
||||
this.renderInitialMessages();
|
||||
this.showStatus("Resumed session");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,8 @@
|
|||
"userMsgBg": "#343541",
|
||||
"toolPendingBg": "#282832",
|
||||
"toolSuccessBg": "#283228",
|
||||
"toolErrorBg": "#3c2828"
|
||||
"toolErrorBg": "#3c2828",
|
||||
"customMsgBg": "#2d2838"
|
||||
},
|
||||
"colors": {
|
||||
"accent": "accent",
|
||||
|
|
@ -30,6 +31,9 @@
|
|||
|
||||
"userMessageBg": "userMsgBg",
|
||||
"userMessageText": "",
|
||||
"customMessageBg": "customMsgBg",
|
||||
"customMessageText": "",
|
||||
"customMessageLabel": "#9575cd",
|
||||
"toolPendingBg": "toolPendingBg",
|
||||
"toolSuccessBg": "toolSuccessBg",
|
||||
"toolErrorBg": "toolErrorBg",
|
||||
|
|
|
|||
|
|
@ -13,7 +13,8 @@
|
|||
"userMsgBg": "#e8e8e8",
|
||||
"toolPendingBg": "#e8e8f0",
|
||||
"toolSuccessBg": "#e8f0e8",
|
||||
"toolErrorBg": "#f0e8e8"
|
||||
"toolErrorBg": "#f0e8e8",
|
||||
"customMsgBg": "#ede7f6"
|
||||
},
|
||||
"colors": {
|
||||
"accent": "teal",
|
||||
|
|
@ -29,6 +30,9 @@
|
|||
|
||||
"userMessageBg": "userMsgBg",
|
||||
"userMessageText": "",
|
||||
"customMessageBg": "customMsgBg",
|
||||
"customMessageText": "",
|
||||
"customMessageLabel": "#7e57c2",
|
||||
"toolPendingBg": "toolPendingBg",
|
||||
"toolSuccessBg": "toolSuccessBg",
|
||||
"toolErrorBg": "toolErrorBg",
|
||||
|
|
|
|||
|
|
@ -47,6 +47,9 @@
|
|||
"text",
|
||||
"userMessageBg",
|
||||
"userMessageText",
|
||||
"customMessageBg",
|
||||
"customMessageText",
|
||||
"customMessageLabel",
|
||||
"toolPendingBg",
|
||||
"toolSuccessBg",
|
||||
"toolErrorBg",
|
||||
|
|
@ -122,6 +125,18 @@
|
|||
"$ref": "#/$defs/colorValue",
|
||||
"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": {
|
||||
"$ref": "#/$defs/colorValue",
|
||||
"description": "Tool execution box (pending state)"
|
||||
|
|
|
|||
|
|
@ -34,9 +34,12 @@ const ThemeJsonSchema = Type.Object({
|
|||
muted: ColorValueSchema,
|
||||
dim: ColorValueSchema,
|
||||
text: ColorValueSchema,
|
||||
// Backgrounds & Content Text (7 colors)
|
||||
// Backgrounds & Content Text (10 colors)
|
||||
userMessageBg: ColorValueSchema,
|
||||
userMessageText: ColorValueSchema,
|
||||
customMessageBg: ColorValueSchema,
|
||||
customMessageText: ColorValueSchema,
|
||||
customMessageLabel: ColorValueSchema,
|
||||
toolPendingBg: ColorValueSchema,
|
||||
toolSuccessBg: ColorValueSchema,
|
||||
toolErrorBg: ColorValueSchema,
|
||||
|
|
@ -95,6 +98,8 @@ export type ThemeColor =
|
|||
| "dim"
|
||||
| "text"
|
||||
| "userMessageText"
|
||||
| "customMessageText"
|
||||
| "customMessageLabel"
|
||||
| "toolTitle"
|
||||
| "toolOutput"
|
||||
| "mdHeading"
|
||||
|
|
@ -127,7 +132,7 @@ export type ThemeColor =
|
|||
| "thinkingXhigh"
|
||||
| "bashMode";
|
||||
|
||||
export type ThemeBg = "userMessageBg" | "toolPendingBg" | "toolSuccessBg" | "toolErrorBg";
|
||||
export type ThemeBg = "userMessageBg" | "customMessageBg" | "toolPendingBg" | "toolSuccessBg" | "toolErrorBg";
|
||||
|
||||
type ColorMode = "truecolor" | "256color";
|
||||
|
||||
|
|
@ -482,7 +487,13 @@ function createTheme(themeJson: ThemeJson, mode?: ColorMode): Theme {
|
|||
const resolvedColors = resolveThemeColors(themeJson.colors, themeJson.vars);
|
||||
const fgColors: Record<ThemeColor, string | number> = {} as Record<ThemeColor, 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)) {
|
||||
if (bgColorKeys.has(key)) {
|
||||
bgColors[key as ThemeBg] = value;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue