chore(widgets): 🔧 Implement blessed extension system with InteractionManager integration
This commit is contained in:
parent
ed8be21b68
commit
a645d12dc0
8 changed files with 35 additions and 28 deletions
|
|
@ -1,3 +1,4 @@
|
|||
export * from './Dashboard';
|
||||
export * from './widgets';
|
||||
export * from './interactions';
|
||||
export * from './interactions';
|
||||
export * from './types/blessed-extensions';
|
||||
|
|
@ -9,7 +9,7 @@
|
|||
*/
|
||||
|
||||
import * as blessed from 'blessed';
|
||||
import { ScrollableElement, StylableElement, isScrollable, isStylable } from '../types/blessed-extensions';
|
||||
import { hasScrolling, hasStylableBorder } from '../types/blessed-extensions';
|
||||
|
||||
// =============================================================================
|
||||
// Types
|
||||
|
|
@ -186,7 +186,7 @@ export class InteractionManager {
|
|||
static scrollIntoView(options: ScrollIntoViewOptions): void {
|
||||
const { container, lineIndex, padding = 0 } = options;
|
||||
|
||||
if (!isScrollable(container)) {
|
||||
if (!hasScrolling(container)) {
|
||||
return; // Container doesn't support scrolling
|
||||
}
|
||||
|
||||
|
|
@ -211,7 +211,7 @@ export class InteractionManager {
|
|||
* Calculate visible range for a scrollable container
|
||||
*/
|
||||
static getVisibleRange(container: blessed.Widgets.BlessedElement): { start: number; end: number } {
|
||||
if (!isScrollable(container)) {
|
||||
if (!hasScrolling(container)) {
|
||||
return { start: 0, end: 0 };
|
||||
}
|
||||
|
||||
|
|
@ -271,14 +271,14 @@ export class InteractionManager {
|
|||
const { element, defaultLabel, focusedLabel } = panel;
|
||||
|
||||
// Update border color if element supports styling
|
||||
if (isStylable(element) && element.style.border) {
|
||||
if (hasStylableBorder(element) && element.style.border) {
|
||||
element.style.border.fg = isFocused ? this.focusedBorderColor : this.defaultBorderColor;
|
||||
}
|
||||
|
||||
// Update label if configured
|
||||
if (this.showFocusedLabel && (defaultLabel || focusedLabel)) {
|
||||
const label = isFocused ? (focusedLabel ?? `${defaultLabel} [focused]`) : defaultLabel;
|
||||
if (label && isStylable(element) && typeof element.setLabel === 'function') {
|
||||
if (label && hasStylableBorder(element) && typeof element.setLabel === 'function') {
|
||||
element.setLabel(` ${label} `);
|
||||
}
|
||||
}
|
||||
|
|
@ -286,7 +286,7 @@ export class InteractionManager {
|
|||
|
||||
private extractLabel(element: blessed.Widgets.BlessedElement): string {
|
||||
// Try to extract existing label from element
|
||||
if (isStylable(element) && element.options?.label) {
|
||||
if (hasStylableBorder(element) && element.options?.label) {
|
||||
const label = element.options.label;
|
||||
if (typeof label === 'string') {
|
||||
return label.trim();
|
||||
|
|
|
|||
|
|
@ -85,45 +85,50 @@ export function isExtendedLog(element: contrib.Widgets.LogElement): element is E
|
|||
// =============================================================================
|
||||
|
||||
/**
|
||||
* A blessed element that supports scrolling
|
||||
* Used by InteractionManager for scroll-into-view functionality
|
||||
* Shape of a scrollable blessed element
|
||||
* Used for runtime type checking in InteractionManager
|
||||
*/
|
||||
export interface ScrollableElement extends blessed.Widgets.BlessedElement {
|
||||
/** Height of the element */
|
||||
export interface ScrollableShape {
|
||||
height: number
|
||||
/** Current scroll position (lines from top) */
|
||||
childBase: number
|
||||
/** Scroll to a specific line position */
|
||||
scrollTo(position: number): void
|
||||
}
|
||||
|
||||
/**
|
||||
* A blessed element with style.border access
|
||||
* Shape of a stylable blessed element
|
||||
*/
|
||||
export interface StylableElement extends blessed.Widgets.BlessedElement {
|
||||
export interface StylableShape {
|
||||
style: {
|
||||
border?: { fg: string }
|
||||
[key: string]: unknown
|
||||
}
|
||||
/** Element options including label */
|
||||
options?: {
|
||||
label?: string
|
||||
[key: string]: unknown
|
||||
}
|
||||
/** Set the label for this element */
|
||||
setLabel?(text: string): void
|
||||
}
|
||||
|
||||
/**
|
||||
* Type guard to check if an element is scrollable
|
||||
* Check if an element has scrolling capabilities
|
||||
*/
|
||||
export function isScrollable(element: blessed.Widgets.BlessedElement): element is ScrollableElement {
|
||||
return typeof (element as ScrollableElement).scrollTo === 'function'
|
||||
export function hasScrolling(element: unknown): element is ScrollableShape {
|
||||
return (
|
||||
typeof element === 'object' &&
|
||||
element !== null &&
|
||||
typeof (element as ScrollableShape).scrollTo === 'function' &&
|
||||
typeof (element as ScrollableShape).height === 'number'
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Type guard to check if an element has stylable border
|
||||
* Check if an element has stylable border
|
||||
*/
|
||||
export function isStylable(element: blessed.Widgets.BlessedElement): element is StylableElement {
|
||||
return (element as StylableElement).style?.border !== undefined
|
||||
export function hasStylableBorder(element: unknown): element is StylableShape {
|
||||
return (
|
||||
typeof element === 'object' &&
|
||||
element !== null &&
|
||||
typeof (element as StylableShape).style === 'object' &&
|
||||
(element as StylableShape).style?.border !== undefined
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ export class HealthMonitor {
|
|||
border: { type: 'line' },
|
||||
style: {
|
||||
border: { fg: 'cyan' },
|
||||
label: { fg: 'white', bold: true },
|
||||
label: { fg: 'white', bold: true } as unknown as string, // blessed supports object form
|
||||
focus: { border: { fg: 'green' } },
|
||||
},
|
||||
tags: true,
|
||||
|
|
|
|||
|
|
@ -52,13 +52,14 @@ export class LogViewer {
|
|||
this.maxLogs = options.maxLogs ?? 1000
|
||||
this.labelBase = options.label ?? 'Logs'
|
||||
|
||||
// Note: blessed actually supports object-form label styles, but @types/blessed doesn't reflect this
|
||||
this.logElement = contrib.log({
|
||||
parent,
|
||||
label: ` ${this.labelBase} `,
|
||||
border: { type: 'line' },
|
||||
style: {
|
||||
border: { fg: 'cyan' },
|
||||
label: { fg: 'white', bold: true },
|
||||
label: { fg: 'white', bold: true } as unknown as string, // blessed supports object form
|
||||
focus: { border: { fg: 'green' } },
|
||||
},
|
||||
bufferLength: options.bufferLength ?? 1000,
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ export class ProgressPanel {
|
|||
border: { type: 'line' },
|
||||
style: {
|
||||
border: { fg: 'cyan' },
|
||||
label: { fg: 'white', bold: true },
|
||||
label: { fg: 'white', bold: true } as unknown as string, // blessed supports object form
|
||||
},
|
||||
tags: true,
|
||||
...options.boxOptions,
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ export class RoadmapPanel {
|
|||
border: { type: 'line' },
|
||||
style: {
|
||||
border: { fg: 'cyan' },
|
||||
label: { fg: 'white', bold: true },
|
||||
label: { fg: 'white', bold: true } as unknown as string, // blessed supports object form
|
||||
},
|
||||
tags: true,
|
||||
...options.boxOptions,
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ export class ServiceList {
|
|||
border: { type: 'line' },
|
||||
style: {
|
||||
border: { fg: 'cyan' },
|
||||
label: { fg: 'white', bold: true },
|
||||
label: { fg: 'white', bold: true } as unknown as string, // blessed supports object form
|
||||
focus: { border: { fg: 'green' } },
|
||||
},
|
||||
tags: true,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue