81 lines
2.2 KiB
TypeScript
81 lines
2.2 KiB
TypeScript
import { Box, Container, Spacer, Text } from "@oh-my-pi/pi-tui";
|
|
import type { Rule } from "../../capability/rule";
|
|
import { theme } from "../../modes/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 {
|
|
#box: Box;
|
|
#expanded = false;
|
|
|
|
constructor(private readonly rules: Rule[]) {
|
|
super();
|
|
|
|
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;
|
|
}
|
|
|
|
#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")}…`;
|
|
}
|
|
}
|
|
|
|
// 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));
|
|
}
|
|
}
|
|
}
|
|
}
|