Files
oh-my-pi/packages/coding-agent/src/modes/interactive/components/ttsr-notification.ts
T
can1357 9b7596c4aa feat(coding-agent): added theme system with 30+ presets and status line editor
- Added 30+ built-in color themes and comprehensive symbol customization via theme presets and overrides.
- Added status line segment editor with presets, separators, and customizable left/right segments.
- Added Biome CLI-based linter client as alternative to LSP for diagnostics and formatting.
- Added 20MB file size limit for image files to prevent memory issues during serialization.
- Replaced hardcoded Unicode symbols throughout codebase with theme-configurable symbol system.
2026-01-05 00:57:16 +01:00

83 lines
2.3 KiB
TypeScript

import { Box, Container, Spacer, Text } from "@oh-my-pi/pi-tui";
import type { Rule } from "../../../capability/rule";
import { theme } from "../theme/theme";
/**
* Component that renders a TTSR (Time Traveling Stream Rules) notification.
* Shows when a rule violation is detected and the stream is being rewound.
*/
export class TtsrNotificationComponent extends Container {
private rules: Rule[];
private box: Box;
private _expanded = false;
constructor(rules: Rule[]) {
super();
this.rules = rules;
this.addChild(new Spacer(1));
// Use inverse warning color for yellow background effect
this.box = new Box(1, 1, (t) => theme.inverse(theme.fg("warning", t)));
this.addChild(this.box);
this.rebuild();
}
setExpanded(expanded: boolean): void {
if (this._expanded !== expanded) {
this._expanded = expanded;
this.rebuild();
}
}
isExpanded(): boolean {
return this._expanded;
}
private rebuild(): void {
this.box.clear();
// Build header: warning symbol + rule name + rewind icon
const ruleNames = this.rules.map((r) => theme.bold(r.name)).join(", ");
const label = this.rules.length === 1 ? "rule" : "rules";
const header = `${theme.icon.warning} Injecting ${label}: ${ruleNames}`;
// Create header with rewind icon on the right
const rewindIcon = theme.icon.rewind;
this.box.addChild(new Text(`${header} ${rewindIcon}`, 0, 0));
// Show description(s) - italic and truncated
for (const rule of this.rules) {
const desc = rule.description || rule.content;
if (desc) {
this.box.addChild(new Spacer(1));
let displayText = desc.trim();
if (!this._expanded) {
// Truncate to first 2 lines
const lines = displayText.split("\n");
if (lines.length > 2) {
displayText = `${lines.slice(0, 2).join("\n")}${theme.format.ellipsis}`;
}
}
// Use italic for subtle distinction (fg colors conflict with inverse)
this.box.addChild(new Text(theme.italic(displayText), 0, 0));
}
}
// Show expand hint if collapsed and there's more content
if (!this._expanded) {
const hasMoreContent = this.rules.some((r) => {
const desc = r.description || r.content;
return desc && desc.split("\n").length > 2;
});
if (hasMoreContent) {
this.box.addChild(new Text(theme.italic(" (ctrl+o to expand)"), 0, 0));
}
}
}
}