UNPKG

@qodalis/angular-cli

Version:

Angular wrapper for the @qodalis CLI terminal engine.

1,532 lines 148 kB
import * as i0 from '@angular/core';
import { InjectionToken, EventEmitter, ViewChild, Output, Input, Optional, Inject, ViewEncapsulation, Component, HostListener, ViewChildren, NgModule } from '@angular/core';
import * as i1 from '@angular/common';
import { CommonModule } from '@angular/common';
import { CliEngine } from '@qodalis/cli';
export { CliCommandExecutor, CliCommandHistory, CliEngine, CliPackageManagerService, CommandParser, ScriptLoaderService, getGreetingBasedOnTime, openLink } from '@qodalis/cli';
import { loadPanelPosition, savePanelPosition, derivePanelThemeStyles } from '@qodalis/cli-core';
import { BehaviorSubject, Subject, takeUntil, merge, interval, startWith, map, distinctUntilChanged } from 'rxjs';

const CliCommandProcessor_TOKEN = new InjectionToken('cli-processors');
const ICliUserSessionService_TOKEN = new InjectionToken('cli-user-session-service');
const ICliUsersStoreService_TOKEN = new InjectionToken('cli-users-store-service');
const ICliPingServerService_TOKEN = new InjectionToken('cli-ping-server-service');
const CliModule_TOKEN = new InjectionToken('cli-modules');

class CliComponent {
    constructor(diProcessors, diModules, pingServerService) {
        this.diProcessors = diProcessors;
        this.diModules = diModules;
        this.pingServerService = pingServerService;
        this.engineReady = new EventEmitter();
    }
    ngAfterViewInit() {
        const engineOptions = {
            ...(this.options ?? {}),
            ...(this.snapshot ? { snapshot: this.snapshot } : {}),
        };
        this.engine = new CliEngine(this.terminalDiv.nativeElement, engineOptions);
        // Identify the serving framework
        this.engine.registerService('cli-framework', 'Angular');
        // Bridge Angular DI services into the engine's service container
        if (this.pingServerService) {
            this.engine.registerService('cli-ping-server-service', this.pingServerService);
        }
        // Register processors provided via Angular DI (from resolveCommandProcessorProvider).
        // Exclude processors that already belong to a module — those will be
        // initialized as part of the module boot (which registers services first).
        if (this.diProcessors && this.diProcessors.length > 0) {
            const moduleProcessors = new Set((this.diModules ?? []).flatMap(m => m.processors ?? []));
            const standalone = this.diProcessors.filter(p => !moduleProcessors.has(p));
            if (standalone.length > 0) {
                this.engine.registerProcessors(standalone);
            }
        }
        // Register processors provided via @Input
        if (this.processors && this.processors.length > 0) {
            this.engine.registerProcessors(this.processors);
        }
        // Register modules provided via Angular DI
        if (this.diModules && this.diModules.length > 0) {
            this.engine.registerModules(this.diModules);
        }
        // Register modules provided via @Input
        if (this.modules && this.modules.length > 0) {
            this.engine.registerModules(this.modules);
        }
        this.engine.start().then(() => {
            this.engineReady.emit(this.engine);
        });
    }
    ngOnDestroy() {
        this.engine?.destroy();
    }
    focus() {
        this.engine?.focus();
    }
    getEngine() {
        return this.engine;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: CliComponent, deps: [{ token: CliCommandProcessor_TOKEN, optional: true }, { token: CliModule_TOKEN, optional: true }, { token: ICliPingServerService_TOKEN, optional: true }], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.20", type: CliComponent, isStandalone: false, selector: "cli", inputs: { options: "options", processors: "processors", modules: "modules", height: "height", snapshot: "snapshot" }, outputs: { engineReady: "engineReady" }, viewQueries: [{ propertyName: "terminalDiv", first: true, predicate: ["terminal"], descendants: true, static: true }], ngImport: i0, template: `<div
        #terminal
        [style.height]="height || '100%'"
        style="width: 100%;"
    ></div>`, isInline: true, encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: CliComponent, decorators: [{
            type: Component,
            args: [{ standalone: false, selector: 'cli', template: `<div
        #terminal
        [style.height]="height || '100%'"
        style="width: 100%;"
    ></div>`, encapsulation: ViewEncapsulation.None }]
        }], ctorParameters: () => [{ type: undefined, decorators: [{
                    type: Optional
                }, {
                    type: Inject,
                    args: [CliCommandProcessor_TOKEN]
                }] }, { type: undefined, decorators: [{
                    type: Optional
                }, {
                    type: Inject,
                    args: [CliModule_TOKEN]
                }] }, { type: undefined, decorators: [{
                    type: Optional
                }, {
                    type: Inject,
                    args: [ICliPingServerService_TOKEN]
                }] }], propDecorators: { options: [{
                type: Input
            }], processors: [{
                type: Input
            }], modules: [{
                type: Input
            }], height: [{
                type: Input
            }], snapshot: [{
                type: Input
            }], engineReady: [{
                type: Output
            }], terminalDiv: [{
                type: ViewChild,
                args: ['terminal', { static: true }]
            }] } });

const HEADER_HEIGHT = 60;
class CollapsableContentComponent {
    constructor() {
        this.previousPanelHeight = 600;
        this.panelHeight = 600;
        this.panelWidth = 400;
        this.previousPanelWidth = 400;
        this.isResizing = false;
        this.startY = 0;
        this.startX = 0;
        this.startHeight = 0;
        this.startWidth = 0;
        this.visible = true;
        this.isCollapsed = true;
        this.isMaximized = false;
        this.position = 'bottom';
        this.closable = true;
        this.resizable = true;
        this.hideable = true;
        this.hideAlignment = 'center';
        this.themeStyles = {};
        this.onToggle = new EventEmitter();
        this.onContentSizeChange = new EventEmitter();
        this.onClose = new EventEmitter();
        this.onHide = new EventEmitter();
        this.onPositionChange = new EventEmitter();
        this.isHidden = false;
        // Status bar inputs
        this.statusExecutionState = 'idle';
        this.statusLastCommand = null;
        this.statusServiceCount = { running: 0, total: 0 };
        this.statusServiceDetails = [];
        this.statusServerState = 'none';
        this.statusServerDetails = [];
        this.statusUptime = 0;
        this.notification = null;
        this.positionDropdownOpen = false;
        this.dropdownStyle = {};
        this.preHideCollapsed = true;
        this.servicesDropdownOpen = false;
        this.servicesDropdownStyle = {};
        this.serversDropdownOpen = false;
        this.serversDropdownStyle = {};
    }
    get isHorizontal() {
        return this.position === 'left' || this.position === 'right';
    }
    get showStatusIndicators() {
        return this.position === 'bottom' || this.position === 'top';
    }
    get connectedServerCount() {
        return this.statusServerDetails.filter(s => s.connected).length;
    }
    get totalServerCount() {
        return this.statusServerDetails.length;
    }
    toggleServicesDropdown(event) {
        event.stopPropagation();
        this.servicesDropdownOpen = !this.servicesDropdownOpen;
        if (this.servicesDropdownOpen) {
            const el = event.currentTarget;
            const rect = el.getBoundingClientRect();
            switch (this.position) {
                case 'bottom':
                    this.servicesDropdownStyle = {
                        bottom: (window.innerHeight - rect.top + 4) + 'px',
                        left: rect.left + 'px',
                    };
                    break;
                case 'top':
                    this.servicesDropdownStyle = {
                        top: (rect.bottom + 4) + 'px',
                        left: rect.left + 'px',
                    };
                    break;
                case 'left':
                    this.servicesDropdownStyle = {
                        top: rect.top + 'px',
                        left: (rect.right + 4) + 'px',
                    };
                    break;
                case 'right':
                    this.servicesDropdownStyle = {
                        top: rect.top + 'px',
                        right: (window.innerWidth - rect.left + 4) + 'px',
                    };
                    break;
                default:
                    this.servicesDropdownStyle = {
                        bottom: (window.innerHeight - rect.top + 4) + 'px',
                        left: rect.left + 'px',
                    };
            }
        }
    }
    toggleServersDropdown(event) {
        event.stopPropagation();
        this.serversDropdownOpen = !this.serversDropdownOpen;
        if (this.serversDropdownOpen) {
            const el = event.currentTarget;
            const rect = el.getBoundingClientRect();
            switch (this.position) {
                case 'bottom':
                    this.serversDropdownStyle = {
                        bottom: (window.innerHeight - rect.top + 4) + 'px',
                        left: rect.left + 'px',
                    };
                    break;
                case 'top':
                    this.serversDropdownStyle = {
                        top: (rect.bottom + 4) + 'px',
                        left: rect.left + 'px',
                    };
                    break;
                case 'left':
                    this.serversDropdownStyle = {
                        top: rect.top + 'px',
                        left: (rect.right + 4) + 'px',
                    };
                    break;
                case 'right':
                    this.serversDropdownStyle = {
                        top: rect.top + 'px',
                        right: (window.innerWidth - rect.left + 4) + 'px',
                    };
                    break;
                default:
                    this.serversDropdownStyle = {
                        bottom: (window.innerHeight - rect.top + 4) + 'px',
                        left: rect.left + 'px',
                    };
            }
        }
    }
    get formattedUptime() {
        const mins = Math.floor(this.statusUptime / 60000);
        if (mins < 60)
            return `${mins}m`;
        const hrs = Math.floor(mins / 60);
        return `${hrs}h${mins % 60}m`;
    }
    get wrapperStyle() {
        const size = this.isHorizontal
            ? { width: this.panelWidth + 'px' }
            : { height: this.panelHeight + 'px' };
        return { ...size, ...this.themeStyles };
    }
    toggleTerminal() {
        this.isCollapsed = !this.isCollapsed;
        this.onToggle.emit(this.isCollapsed);
    }
    closeTerminal() {
        this.visible = false;
        this.onClose.emit();
    }
    hideTerminal() {
        this.preHideCollapsed = this.isCollapsed;
        this.isHidden = true;
        this.onHide.emit();
    }
    unhideTerminal() {
        this.isHidden = false;
        this.isCollapsed = this.preHideCollapsed;
        this.onToggle.emit(this.isCollapsed);
    }
    togglePositionDropdown(event) {
        event.stopPropagation();
        this.positionDropdownOpen = !this.positionDropdownOpen;
        if (this.positionDropdownOpen) {
            const btn = event.currentTarget;
            const rect = btn.getBoundingClientRect();
            switch (this.position) {
                case 'bottom':
                    this.dropdownStyle = {
                        bottom: (window.innerHeight - rect.top + 4) + 'px',
                        right: (window.innerWidth - rect.right) + 'px',
                    };
                    break;
                case 'top':
                    this.dropdownStyle = {
                        top: (rect.bottom + 4) + 'px',
                        right: (window.innerWidth - rect.right) + 'px',
                    };
                    break;
                case 'left':
                    this.dropdownStyle = {
                        top: rect.top + 'px',
                        left: (rect.right + 4) + 'px',
                    };
                    break;
                case 'right':
                    this.dropdownStyle = {
                        top: rect.top + 'px',
                        right: (window.innerWidth - rect.left + 4) + 'px',
                    };
                    break;
            }
        }
    }
    selectPosition(pos) {
        this.positionDropdownOpen = false;
        this.onPositionChange.emit(pos);
    }
    closeDropdowns() {
        this.positionDropdownOpen = false;
        this.servicesDropdownOpen = false;
        this.serversDropdownOpen = false;
    }
    toggleMaximizationTerminal() {
        if (this.isHorizontal) {
            if (!this.isMaximized) {
                this.previousPanelWidth = this.panelWidth;
                this.panelWidth = window.innerWidth;
            }
            else {
                this.panelWidth = this.previousPanelWidth;
            }
        }
        else {
            if (!this.isMaximized) {
                this.previousPanelHeight = this.panelHeight;
                this.panelHeight = window.innerHeight;
            }
            else {
                this.panelHeight = this.previousPanelHeight;
            }
        }
        this.isMaximized = !this.isMaximized;
        this.updateTerminalSize();
    }
    onResizeStart(event) {
        if (!this.resizable)
            return;
        this.isResizing = true;
        if (this.isCollapsed) {
            this.toggleTerminal();
        }
        if (this.isHorizontal) {
            this.startX = event.clientX;
            this.startWidth = this.panelWidth;
        }
        else {
            this.startY = event.clientY;
            this.startHeight = this.panelHeight;
        }
        event.preventDefault();
    }
    onMouseMove(event) {
        if (!this.isResizing)
            return;
        if (this.isHorizontal) {
            const deltaX = this.position === 'left'
                ? event.clientX - this.startX
                : this.startX - event.clientX;
            let nextWidth = Math.max(100, this.startWidth + deltaX);
            if (nextWidth > window.innerWidth) {
                nextWidth = window.innerWidth;
            }
            this.panelWidth = nextWidth;
        }
        else {
            const deltaY = this.position === 'top'
                ? event.clientY - this.startY
                : this.startY - event.clientY;
            let nextHeight = Math.max(100, this.startHeight + deltaY);
            if (nextHeight > window.innerHeight) {
                nextHeight = window.innerHeight;
            }
            this.panelHeight = nextHeight;
        }
        this.updateTerminalSize();
    }
    onMouseUp() {
        this.isResizing = false;
    }
    /** Set collapsed state directly (called by parent CliPanelComponent). */
    setCollapsed(value) {
        this.isCollapsed = value;
        this.onToggle.emit(value);
    }
    /** Set maximized state directly (called by parent CliPanelComponent). */
    setMaximized(value) {
        if (value && !this.isMaximized) {
            if (this.isHorizontal) {
                this.previousPanelWidth = this.panelWidth;
                this.panelWidth = window.innerWidth;
            }
            else {
                this.previousPanelHeight = this.panelHeight;
                this.panelHeight = window.innerHeight;
            }
        }
        else if (!value && this.isMaximized) {
            if (this.isHorizontal) {
                this.panelWidth = this.previousPanelWidth;
            }
            else {
                this.panelHeight = this.previousPanelHeight;
            }
        }
        this.isMaximized = value;
        this.updateTerminalSize();
    }
    /** Programmatically set panel dimensions. */
    setDimensions(dims) {
        if (dims.height !== undefined) {
            this.panelHeight = dims.height;
            this.previousPanelHeight = dims.height;
        }
        if (dims.width !== undefined) {
            this.panelWidth = dims.width;
            this.previousPanelWidth = dims.width;
        }
        this.updateTerminalSize();
    }
    updateTerminalSize() {
        if (this.isHorizontal) {
            this.onContentSizeChange.emit(this.panelWidth - HEADER_HEIGHT);
        }
        else {
            this.onContentSizeChange.emit(this.panelHeight - HEADER_HEIGHT);
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: CollapsableContentComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.20", type: CollapsableContentComponent, isStandalone: false, selector: "collapsable-content", inputs: { visible: "visible", isCollapsed: "isCollapsed", isMaximized: "isMaximized", position: "position", closable: "closable", resizable: "resizable", hideable: "hideable", hideAlignment: "hideAlignment", themeStyles: "themeStyles", isHidden: "isHidden", statusExecutionState: "statusExecutionState", statusLastCommand: "statusLastCommand", statusServiceCount: "statusServiceCount", statusServiceDetails: "statusServiceDetails", statusServerState: "statusServerState", statusServerDetails: "statusServerDetails", statusUptime: "statusUptime", notification: "notification" }, outputs: { onToggle: "onToggle", onContentSizeChange: "onContentSizeChange", onClose: "onClose", onHide: "onHide", onPositionChange: "onPositionChange" }, host: { listeners: { "document:click": "closeDropdowns()", "document:mousemove": "onMouseMove($event)", "document:mouseup": "onMouseUp()" } }, ngImport: i0, template: "<!-- Hide tab (shown when panel is hidden) -->\n<button\n  *ngIf=\"visible && isHidden\"\n  class=\"cli-panel-hide-tab\"\n  [attr.data-position]=\"position\"\n  [attr.data-hide-align]=\"hideAlignment\"\n  [ngStyle]=\"themeStyles\"\n  title=\"Show CLI\"\n  (click)=\"unhideTerminal()\"\n>\n  <svg\n    class=\"cli-panel-hide-tab-icon\"\n    width=\"16\"\n    height=\"16\"\n    viewBox=\"0 0 24 24\"\n    fill=\"none\"\n    stroke=\"currentColor\"\n    stroke-width=\"2\"\n    stroke-linecap=\"round\"\n    stroke-linejoin=\"round\"\n  >\n    <polyline points=\"4 17 10 11 4 5\" />\n    <line x1=\"12\" y1=\"19\" x2=\"20\" y2=\"19\" />\n  </svg>\n  <span class=\"cli-panel-hide-tab-label\">CLI</span>\n  <svg\n    class=\"cli-panel-hide-tab-arrow\"\n    width=\"14\"\n    height=\"14\"\n    viewBox=\"0 0 24 24\"\n    fill=\"none\"\n    stroke=\"currentColor\"\n    stroke-width=\"2.5\"\n    stroke-linecap=\"round\"\n    stroke-linejoin=\"round\"\n  >\n    <!-- bottom: chevron up -->\n    <polyline *ngIf=\"position === 'bottom'\" points=\"18 15 12 9 6 15\" />\n    <!-- top: chevron down -->\n    <polyline *ngIf=\"position === 'top'\" points=\"6 9 12 15 18 9\" />\n    <!-- left: chevron right -->\n    <polyline *ngIf=\"position === 'left'\" points=\"9 18 15 12 9 6\" />\n    <!-- right: chevron left -->\n    <polyline *ngIf=\"position === 'right'\" points=\"15 6 9 12 15 18\" />\n  </svg>\n</button>\n\n<!-- Main panel (kept in DOM when hidden to preserve terminal state) -->\n<div\n  *ngIf=\"visible\"\n  class=\"terminal-wrapper\"\n  [style.display]=\"isHidden ? 'none' : ''\"\n  [class.collapsed]=\"isCollapsed\"\n  [class.maximized]=\"isMaximized\"\n  [class.resizing]=\"isResizing\"\n  [attr.data-position]=\"position\"\n  [attr.data-resizable]=\"resizable\"\n  [attr.data-closable]=\"closable\"\n  [attr.data-hideable]=\"hideable\"\n  [ngStyle]=\"wrapperStyle\"\n>\n  <div class=\"terminal-header\">\n    <div class=\"resize-bar\" (mousedown)=\"onResizeStart($event)\">\n      <div class=\"resize-grip\"></div>\n    </div>\n    <div class=\"header-content\">\n      <p class=\"terminal-title\">\n        <svg\n          class=\"title-icon\"\n          width=\"22\"\n          height=\"22\"\n          viewBox=\"0 0 24 24\"\n          fill=\"none\"\n          stroke=\"currentColor\"\n          stroke-width=\"2\"\n          stroke-linecap=\"round\"\n          stroke-linejoin=\"round\"\n        >\n          <polyline points=\"4 17 10 11 4 5\" />\n          <line x1=\"12\" y1=\"19\" x2=\"20\" y2=\"19\" />\n        </svg>\n        CLI\n      </p>\n      <!-- Status indicators (bottom/top positions only) -->\n      <div class=\"status-indicators\" *ngIf=\"showStatusIndicators\">\n          <!-- Execution state -->\n          <span class=\"status-item\" [class.status-running]=\"statusExecutionState === 'running'\">\n              <span class=\"status-dot\" [class.dot-idle]=\"statusExecutionState === 'idle'\" [class.dot-running]=\"statusExecutionState === 'running'\"></span>\n              <span class=\"status-label\">{{ statusExecutionState }}</span>\n          </span>\n\n          <!-- Background services (only if any exist) -->\n          <span class=\"status-item status-clickable\" *ngIf=\"statusServiceCount.total > 0\" (click)=\"toggleServicesDropdown($event)\">\n              <span class=\"status-icon\">&#9881;</span>\n              <span class=\"status-label\">{{ statusServiceCount.running }}/{{ statusServiceCount.total }} services</span>\n          </span>\n\n          <!-- Last command -->\n          <span class=\"status-item\" *ngIf=\"statusLastCommand\">\n              <span class=\"status-icon\" [class.status-success]=\"statusLastCommand.success\" [class.status-error]=\"!statusLastCommand.success\">\n                  {{ statusLastCommand.success ? '&#10003;' : '&#10005;' }}\n              </span>\n              <span class=\"status-label\">{{ statusLastCommand.name }}</span>\n          </span>\n\n          <!-- Server connection -->\n          <span class=\"status-item status-clickable\" *ngIf=\"statusServerState !== 'none'\" (click)=\"toggleServersDropdown($event)\">\n              <span class=\"status-dot\" [class.dot-idle]=\"statusServerState === 'connected'\" [class.dot-error]=\"statusServerState === 'disconnected'\"></span>\n              <span class=\"status-label\">{{ connectedServerCount }}/{{ totalServerCount }} servers</span>\n          </span>\n\n          <!-- Uptime -->\n          <span class=\"status-item status-muted\" *ngIf=\"statusUptime > 0\">\n              <span class=\"status-icon\">&uarr;</span>\n              <span class=\"status-label\">{{ formattedUptime }}</span>\n          </span>\n\n          <!-- Custom processor notification -->\n          <span class=\"status-item status-text\" [ngClass]=\"'level-' + notification?.level\" *ngIf=\"notification\">\n              <span class=\"status-label\">{{ notification.message }}</span>\n          </span>\n      </div>\n\n      <!-- Compact status indicators (left/right positions \u2014 CSS controls visibility) -->\n      <div class=\"status-indicators-compact\">\n          <!-- Execution state -->\n          <span class=\"compact-item\" [title]=\"statusExecutionState\">\n              <span class=\"status-dot\" [class.dot-idle]=\"statusExecutionState === 'idle'\" [class.dot-running]=\"statusExecutionState === 'running'\"></span>\n          </span>\n\n          <!-- Background services -->\n          <span class=\"compact-item status-clickable\" *ngIf=\"statusServiceCount.total > 0\" [title]=\"statusServiceCount.running + '/' + statusServiceCount.total + ' services'\" (click)=\"toggleServicesDropdown($event)\">\n              <span class=\"status-icon\">&#9881;</span>\n          </span>\n\n          <!-- Last command -->\n          <span class=\"compact-item\" *ngIf=\"statusLastCommand\" [title]=\"(statusLastCommand.success ? '\u2713 ' : '\u2717 ') + statusLastCommand.name\">\n              <span class=\"status-icon\" [class.status-success]=\"statusLastCommand.success\" [class.status-error]=\"!statusLastCommand.success\">\n                  {{ statusLastCommand.success ? '&#10003;' : '&#10005;' }}\n              </span>\n          </span>\n\n          <!-- Server connection -->\n          <span class=\"compact-item status-clickable\" *ngIf=\"statusServerState !== 'none'\" [title]=\"connectedServerCount + '/' + totalServerCount + ' servers'\" (click)=\"toggleServersDropdown($event)\">\n              <span class=\"status-dot\" [class.dot-idle]=\"statusServerState === 'connected'\" [class.dot-error]=\"statusServerState === 'disconnected'\"></span>\n          </span>\n\n          <!-- Uptime -->\n          <span class=\"compact-item status-muted\" *ngIf=\"statusUptime > 0\" [title]=\"formattedUptime\">\n              <span class=\"status-icon\">&uarr;</span>\n          </span>\n\n          <!-- Notification -->\n          <span class=\"compact-item\" *ngIf=\"notification\" [title]=\"notification.message\">\n              <span class=\"compact-dot\" [ngClass]=\"'level-' + notification.level\"></span>\n          </span>\n      </div>\n\n      <div class=\"action-buttons\">\n        <!-- Position dropdown -->\n        <div class=\"panel-btn-position-wrapper\" (click)=\"$event.stopPropagation()\">\n          <button\n            class=\"panel-btn panel-btn-position\"\n            title=\"Move panel\"\n            (click)=\"togglePositionDropdown($event)\"\n          >\n            <svg\n              width=\"20\"\n              height=\"20\"\n              viewBox=\"0 0 24 24\"\n              fill=\"none\"\n              stroke=\"currentColor\"\n              stroke-width=\"1.8\"\n              stroke-linecap=\"round\"\n              stroke-linejoin=\"round\"\n            >\n              <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\" />\n              <!-- bottom -->\n              <rect *ngIf=\"position === 'bottom'\" x=\"4\" y=\"15\" width=\"16\" height=\"5\" rx=\"1\" fill=\"currentColor\" stroke=\"none\" opacity=\"0.5\" />\n              <!-- top -->\n              <rect *ngIf=\"position === 'top'\" x=\"4\" y=\"4\" width=\"16\" height=\"5\" rx=\"1\" fill=\"currentColor\" stroke=\"none\" opacity=\"0.5\" />\n              <!-- left -->\n              <rect *ngIf=\"position === 'left'\" x=\"4\" y=\"4\" width=\"5\" height=\"16\" rx=\"1\" fill=\"currentColor\" stroke=\"none\" opacity=\"0.5\" />\n              <!-- right -->\n              <rect *ngIf=\"position === 'right'\" x=\"15\" y=\"4\" width=\"5\" height=\"16\" rx=\"1\" fill=\"currentColor\" stroke=\"none\" opacity=\"0.5\" />\n            </svg>\n          </button>\n        </div>\n\n        <!-- Hide button -->\n        <button\n          *ngIf=\"hideable\"\n          class=\"panel-btn panel-btn-hide\"\n          title=\"Hide\"\n          (click)=\"hideTerminal()\"\n        >\n          <svg\n            width=\"20\"\n            height=\"20\"\n            viewBox=\"0 0 24 24\"\n            fill=\"none\"\n            stroke=\"currentColor\"\n            stroke-width=\"1.8\"\n            stroke-linecap=\"round\"\n            stroke-linejoin=\"round\"\n          >\n            <line x1=\"5\" y1=\"18\" x2=\"19\" y2=\"18\" />\n            <polyline points=\"9 14 12 17 15 14\" />\n          </svg>\n        </button>\n\n        <button\n          class=\"panel-btn\"\n          [title]=\"!isMaximized ? 'Maximize' : 'Restore'\"\n          [disabled]=\"isCollapsed\"\n          (click)=\"toggleMaximizationTerminal()\"\n        >\n          <svg\n            *ngIf=\"!isMaximized\"\n            width=\"20\"\n            height=\"20\"\n            viewBox=\"0 0 24 24\"\n            fill=\"none\"\n            stroke=\"currentColor\"\n            stroke-width=\"1.8\"\n            stroke-linecap=\"round\"\n            stroke-linejoin=\"round\"\n          >\n            <polyline points=\"15 3 21 3 21 9\" />\n            <polyline points=\"9 21 3 21 3 15\" />\n            <line x1=\"21\" y1=\"3\" x2=\"14\" y2=\"10\" />\n            <line x1=\"3\" y1=\"21\" x2=\"10\" y2=\"14\" />\n          </svg>\n          <svg\n            *ngIf=\"isMaximized\"\n            width=\"20\"\n            height=\"20\"\n            viewBox=\"0 0 24 24\"\n            fill=\"none\"\n            stroke=\"currentColor\"\n            stroke-width=\"1.8\"\n            stroke-linecap=\"round\"\n            stroke-linejoin=\"round\"\n          >\n            <polyline points=\"4 14 10 14 10 20\" />\n            <polyline points=\"20 10 14 10 14 4\" />\n            <line x1=\"14\" y1=\"10\" x2=\"21\" y2=\"3\" />\n            <line x1=\"3\" y1=\"21\" x2=\"10\" y2=\"14\" />\n          </svg>\n        </button>\n\n        <button\n          class=\"panel-btn\"\n          [title]=\"isCollapsed ? 'Expand' : 'Collapse'\"\n          (click)=\"toggleTerminal()\"\n        >\n          <svg\n            width=\"20\"\n            height=\"20\"\n            viewBox=\"0 0 24 24\"\n            fill=\"none\"\n            stroke=\"currentColor\"\n            stroke-width=\"1.8\"\n            stroke-linecap=\"round\"\n            stroke-linejoin=\"round\"\n          >\n            <polyline *ngIf=\"position === 'bottom' && isCollapsed\" points=\"18 15 12 9 6 15\" />\n            <polyline *ngIf=\"position === 'bottom' && !isCollapsed\" points=\"6 9 12 15 18 9\" />\n            <polyline *ngIf=\"position === 'top' && isCollapsed\" points=\"6 9 12 15 18 9\" />\n            <polyline *ngIf=\"position === 'top' && !isCollapsed\" points=\"18 15 12 9 6 15\" />\n            <polyline *ngIf=\"position === 'left' && isCollapsed\" points=\"9 18 15 12 9 6\" />\n            <polyline *ngIf=\"position === 'left' && !isCollapsed\" points=\"15 6 9 12 15 18\" />\n            <polyline *ngIf=\"position === 'right' && isCollapsed\" points=\"15 6 9 12 15 18\" />\n            <polyline *ngIf=\"position === 'right' && !isCollapsed\" points=\"9 18 15 12 9 6\" />\n          </svg>\n        </button>\n\n        <button class=\"panel-btn panel-btn-close\" title=\"Close\" (click)=\"closeTerminal()\">\n          <svg\n            width=\"20\"\n            height=\"20\"\n            viewBox=\"0 0 24 24\"\n            fill=\"none\"\n            stroke=\"currentColor\"\n            stroke-width=\"1.8\"\n            stroke-linecap=\"round\"\n            stroke-linejoin=\"round\"\n          >\n            <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n            <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n          </svg>\n        </button>\n      </div>\n    </div>\n    <!-- Ambient glow line -->\n    <div class=\"glow-line\" [class.glow-active]=\"statusExecutionState === 'running'\"></div>\n  </div>\n  <div class=\"terminal-content\" [style.display]=\"isCollapsed ? 'none' : ''\">\n    <ng-content></ng-content>\n  </div>\n</div>\n\n<!-- Services dropdown portaled outside terminal-wrapper -->\n<div class=\"services-dropdown\" *ngIf=\"servicesDropdownOpen\" [ngStyle]=\"servicesDropdownStyle\" (click)=\"$event.stopPropagation()\">\n  <div class=\"services-dropdown-header\">Background Services</div>\n  <div class=\"services-dropdown-list\">\n    <div class=\"services-dropdown-item\" *ngFor=\"let svc of statusServiceDetails\">\n      <span class=\"svc-dot\" [class.svc-running]=\"svc.status === 'running'\" [class.svc-stopped]=\"svc.status !== 'running'\"></span>\n      <div class=\"svc-info\">\n        <span class=\"svc-name\">{{ svc.name }}</span>\n        <span class=\"svc-desc\" *ngIf=\"svc.description\">{{ svc.description }}</span>\n      </div>\n      <span class=\"svc-status\">{{ svc.status }}</span>\n    </div>\n  </div>\n  <div class=\"services-dropdown-empty\" *ngIf=\"statusServiceDetails.length === 0\">\n    No services registered\n  </div>\n</div>\n\n<!-- Servers dropdown portaled outside terminal-wrapper -->\n<div class=\"services-dropdown\" *ngIf=\"serversDropdownOpen\" [ngStyle]=\"serversDropdownStyle\" (click)=\"$event.stopPropagation()\">\n  <div class=\"services-dropdown-header\">Server Connections</div>\n  <div class=\"services-dropdown-list\">\n    <div class=\"services-dropdown-item\" *ngFor=\"let srv of statusServerDetails\">\n      <span class=\"svc-dot\" [class.svc-running]=\"srv.connected\" [class.svc-stopped]=\"!srv.connected\"></span>\n      <div class=\"svc-info\">\n        <span class=\"svc-name\">{{ srv.name }}</span>\n        <span class=\"svc-desc\" *ngIf=\"srv.url\">{{ srv.url }}</span>\n      </div>\n      <div class=\"srv-meta\">\n        <span class=\"svc-status\">{{ srv.connected ? 'connected' : 'disconnected' }}</span>\n        <span class=\"svc-desc\" *ngIf=\"srv.apiVersion\">v{{ srv.apiVersion }}</span>\n        <span class=\"svc-desc\" *ngIf=\"srv.commandCount\">{{ srv.commandCount }} cmds</span>\n      </div>\n    </div>\n  </div>\n  <div class=\"services-dropdown-empty\" *ngIf=\"statusServerDetails.length === 0\">\n    No servers configured\n  </div>\n</div>\n\n<!-- Position dropdown portaled outside terminal-wrapper to escape transform stacking context -->\n<div class=\"position-dropdown\" *ngIf=\"positionDropdownOpen\" [ngStyle]=\"dropdownStyle\" (click)=\"$event.stopPropagation()\">\n  <button\n    class=\"position-dropdown-item\"\n    [class.active]=\"position === 'bottom'\"\n    (click)=\"selectPosition('bottom')\"\n  >\n    <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n      <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\" />\n      <rect x=\"4\" y=\"15\" width=\"16\" height=\"5\" rx=\"1\" fill=\"currentColor\" stroke=\"none\" opacity=\"0.6\" />\n    </svg>\n    Bottom\n  </button>\n  <button\n    class=\"position-dropdown-item\"\n    [class.active]=\"position === 'top'\"\n    (click)=\"selectPosition('top')\"\n  >\n    <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n      <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\" />\n      <rect x=\"4\" y=\"4\" width=\"16\" height=\"5\" rx=\"1\" fill=\"currentColor\" stroke=\"none\" opacity=\"0.6\" />\n    </svg>\n    Top\n  </button>\n  <button\n    class=\"position-dropdown-item\"\n    [class.active]=\"position === 'left'\"\n    (click)=\"selectPosition('left')\"\n  >\n    <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n      <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\" />\n      <rect x=\"4\" y=\"4\" width=\"5\" height=\"16\" rx=\"1\" fill=\"currentColor\" stroke=\"none\" opacity=\"0.6\" />\n    </svg>\n    Left\n  </button>\n  <button\n    class=\"position-dropdown-item\"\n    [class.active]=\"position === 'right'\"\n    (click)=\"selectPosition('right')\"\n  >\n    <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n      <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\" />\n      <rect x=\"15\" y=\"4\" width=\"5\" height=\"16\" rx=\"1\" fill=\"currentColor\" stroke=\"none\" opacity=\"0.6\" />\n    </svg>\n    Right\n  </button>\n</div>\n", styles: [".terminal-wrapper{position:fixed;bottom:0;left:0;right:0;transform:translateY(0);transition:transform .3s cubic-bezier(.4,0,.2,1);background-color:var(--cli-panel-bg, #111827);color:var(--cli-panel-text, rgba(255, 255, 255, .87));border-top:1px solid var(--cli-panel-border, #374151);display:flex;flex-direction:column;z-index:1000;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif;box-shadow:0 -4px 20px #00000040}.terminal-wrapper.collapsed{transform:translateY(calc(100% - 60px))}.terminal-wrapper.resizing{transition:none;-webkit-user-select:none;user-select:none}.terminal-header{background-color:var(--cli-panel-header-bg, #1f2937);display:flex;flex-direction:column;position:relative;flex-shrink:0}.terminal-header .resize-bar{position:absolute;top:-6px;left:0;right:0;height:12px;cursor:ns-resize;display:flex;align-items:center;justify-content:center;z-index:2}.terminal-header .resize-bar .resize-grip{width:32px;height:4px;border-radius:2px;background:var(--cli-panel-text-secondary, rgba(255, 255, 255, .3));opacity:0;transition:opacity .2s ease}.terminal-header .resize-bar:hover .resize-grip{opacity:1}.terminal-header .header-content{height:60px;display:flex;flex-direction:row;gap:1rem;align-items:center;border-bottom:1px solid var(--cli-panel-border, #374151);padding:0 1rem}.terminal-header .header-content .terminal-title{font-size:.95rem;display:flex;gap:.5rem;justify-content:center;align-items:center;margin:0;font-weight:600;color:var(--cli-panel-text, rgba(255, 255, 255, .87))}.terminal-header .header-content .terminal-title .title-icon{color:var(--cli-panel-accent, #818cf8)}.terminal-header .header-content .action-buttons{display:flex;flex-direction:row;gap:.35rem;margin-left:auto}.panel-btn{appearance:none;background:transparent;border:none;color:var(--cli-panel-text-secondary, rgba(255, 255, 255, .6));cursor:pointer;width:40px;height:40px;border-radius:50%;display:inline-flex;align-items:center;justify-content:center;padding:0;transition:background-color .2s ease,color .2s ease}.panel-btn svg{flex-shrink:0}.panel-btn:hover:not(:disabled){color:var(--cli-panel-text, rgba(255, 255, 255, .87));background-color:var(--cli-btn-hover-bg, rgba(129, 140, 248, .12))}.panel-btn:active:not(:disabled){background-color:var(--cli-btn-active-bg, rgba(129, 140, 248, .2))}.panel-btn:disabled{opacity:.3;cursor:not-allowed}.terminal-content{flex:1;min-height:0;overflow:hidden;display:flex;flex-direction:column}.terminal-wrapper[data-position=top]{bottom:auto;top:0;border-top:none;border-bottom:1px solid var(--cli-panel-border, #374151);box-shadow:0 4px 20px #00000040;flex-direction:column-reverse}.terminal-wrapper[data-position=top].collapsed{transform:translateY(calc(-100% + 60px))}.terminal-wrapper[data-position=top] .terminal-header .header-content{border-bottom:none;border-top:1px solid var(--cli-panel-border, #374151)}.terminal-wrapper[data-position=top] .terminal-header .resize-bar{top:auto;bottom:-6px;cursor:ns-resize}.terminal-wrapper[data-position=left]{inset:0 auto 0 0;width:auto;height:100vh!important;border-top:none;border-right:1px solid var(--cli-panel-border, #374151);box-shadow:4px 0 20px #00000040;flex-direction:row-reverse}.terminal-wrapper[data-position=left].collapsed{transform:translate(calc(-100% + 60px))}.terminal-wrapper[data-position=left] .terminal-header{width:60px;flex-shrink:0;flex-direction:column}.terminal-wrapper[data-position=left] .terminal-header .header-content{height:auto;flex:1;flex-direction:column;padding:1rem 0;border-bottom:none;border-left:1px solid var(--cli-panel-border, #374151)}.terminal-wrapper[data-position=left] .terminal-header .header-content .terminal-title{writing-mode:vertical-rl;text-orientation:mixed}.terminal-wrapper[data-position=left] .terminal-header .header-content .action-buttons{flex-direction:column;margin-left:0;margin-top:auto}.terminal-wrapper[data-position=left] .terminal-header .resize-bar{position:absolute;inset:0 -6px 0 auto;width:12px;height:auto;cursor:ew-resize}.terminal-wrapper[data-position=left] .terminal-header .resize-bar .resize-grip{width:4px;height:32px}.terminal-wrapper[data-position=left] .terminal-content{flex:1;min-width:0}.terminal-wrapper[data-position=right]{inset:0 0 0 auto;width:auto;height:100vh!important;border-top:none;border-left:1px solid var(--cli-panel-border, #374151);box-shadow:-4px 0 20px #00000040;flex-direction:row}.terminal-wrapper[data-position=right].collapsed{transform:translate(calc(100% - 60px))}.terminal-wrapper[data-position=right] .terminal-header{width:60px;flex-shrink:0;flex-direction:column}.terminal-wrapper[data-position=right] .terminal-header .header-content{height:auto;flex:1;flex-direction:column;padding:1rem 0;border-bottom:none;border-right:1px solid var(--cli-panel-border, #374151)}.terminal-wrapper[data-position=right] .terminal-header .header-content .terminal-title{writing-mode:vertical-rl;text-orientation:mixed}.terminal-wrapper[data-position=right] .terminal-header .header-content .action-buttons{flex-direction:column;margin-left:0;margin-top:auto}.terminal-wrapper[data-position=right] .terminal-header .resize-bar{position:absolute;inset:0 auto 0 -6px;width:12px;height:auto;cursor:ew-resize}.terminal-wrapper[data-position=right] .terminal-header .resize-bar .resize-grip{width:4px;height:32px}.terminal-wrapper[data-position=right] .terminal-content{flex:1;min-width:0}.terminal-wrapper[data-resizable=false] .resize-bar,.terminal-wrapper[data-closable=false] .panel-btn-close{display:none}.cli-panel-hide-tab{position:fixed;z-index:1000;display:flex;align-items:center;gap:6px;background-color:var(--cli-panel-header-bg, #1f2937);border:1px solid var(--cli-panel-border, #374151);color:var(--cli-panel-text-secondary, rgba(255, 255, 255, .6));cursor:pointer;padding:6px 12px;appearance:none;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif;font-size:.8rem;font-weight:600;transition:transform .2s ease,opacity .2s ease,color .2s ease,background-color .2s ease,box-shadow .2s ease;box-shadow:0 2px 8px #0000004d;opacity:.85}.cli-panel-hide-tab:hover{transform:scale(1.05);opacity:1;color:var(--cli-panel-text, rgba(255, 255, 255, .87));background-color:var(--cli-panel-bg, #111827);box-shadow:0 4px 16px #0006}.cli-panel-hide-tab:active{transform:scale(.97)}.cli-panel-hide-tab svg{flex-shrink:0}.cli-panel-hide-tab .cli-panel-hide-tab-icon{color:var(--cli-panel-accent, #818cf8)}.cli-panel-hide-tab .cli-panel-hide-tab-label{letter-spacing:.03em}.cli-panel-hide-tab[data-position=bottom]{bottom:0;border-bottom:none;border-radius:8px 8px 0 0}.cli-panel-hide-tab[data-position=bottom][data-hide-align=start]{left:16px}.cli-panel-hide-tab[data-position=bottom][data-hide-align=center]{left:50%;transform:translate(-50%)}.cli-panel-hide-tab[data-position=bottom][data-hide-align=center]:hover{transform:translate(-50%) scale(1.05)}.cli-panel-hide-tab[data-position=bottom][data-hide-align=end]{right:16px}.cli-panel-hide-tab[data-position=top]{top:0;border-top:none;border-radius:0 0 8px 8px}.cli-panel-hide-tab[data-position=top][data-hide-align=start]{left:16px}.cli-panel-hide-tab[data-position=top][data-hide-align=center]{left:50%;transform:translate(-50%)}.cli-panel-hide-tab[data-position=top][data-hide-align=center]:hover{transform:translate(-50%) scale(1.05)}.cli-panel-hide-tab[data-position=top][data-hide-align=end]{right:16px}.cli-panel-hide-tab[data-position=left]{left:0;border-left:none;border-radius:0 8px 8px 0}.cli-panel-hide-tab[data-position=left][data-hide-align=start]{top:16px}.cli-panel-hide-tab[data-position=left][data-hide-align=center]{top:50%;transform:translateY(-50%)}.cli-panel-hide-tab[data-position=left][data-hide-align=center]:hover{transform:translateY(-50%) scale(1.05)}.cli-panel-hide-tab[data-position=left][data-hide-align=end]{bottom:16px}.cli-panel-hide-tab[data-position=right]{right:0;border-right:none;border-radius:8px 0 0 8px}.cli-panel-hide-tab[data-position=right][data-hide-align=start]{top:16px}.cli-panel-hide-tab[data-position=right][data-hide-align=center]{top:50%;transform:translateY(-50%)}.cli-panel-hide-tab[data-position=right][data-hide-align=center]:hover{transform:translateY(-50%) scale(1.05)}.cli-panel-hide-tab[data-position=right][data-hide-align=end]{bottom:16px}@keyframes cli-hide-tab-pulse{0%,to{transform:translate(0)}50%{transform:translate(3px)}}@keyframes cli-hide-tab-pulse-vertical{0%,to{transform:translateY(0)}50%{transform:translateY(3px)}}.cli-panel-hide-tab:hover .cli-panel-hide-tab-arrow{animation:cli-hide-tab-pulse .6s ease infinite}.cli-panel-hide-tab[data-position=left]:hover .cli-panel-hide-tab-arrow,.cli-panel-hide-tab[data-position=right]:hover .cli-panel-hide-tab-arrow{animation:cli-hide-tab-pulse-vertical .6s ease infinite}.panel-btn-position-wrapper{position:relative}.position-dropdown{position:fixed;background-color:var(--cli-panel-header-bg, #1f2937);border:1px solid var(--cli-panel-border, #374151);border-radius:6px;box-shadow:0 8px 24px #0006;z-index:1100;min-width:120px;overflow:hidden;display:flex;flex-direction:column}.position-dropdown-item{appearance:none;background:transparent;border:none;color:var(--cli-panel-text-secondary, rgba(255, 255, 255, .6));cursor:pointer;display:flex;align-items:center;gap:8px;padding:8px 12px;font-size:.85rem;font-family:inherit;width:100%;text-align:left;transition:background-color .15s ease,color .15s ease}.position-dropdown-item:hover{background-color:var(--cli-btn-hover-bg, rgba(129, 140, 248, .12));color:var(--cli-panel-text, rgba(255, 255, 255, .87))}.position-dropdown-item.active{color:var(--cli-panel-accent, #818cf8)}.position-dropdown-item svg{flex-shrink:0}.terminal-wrapper[data-hideable=false] .panel-btn-hide{display:none}.status-indicators{display:flex;align-items:center;gap:14px;margin-left:24px;font-size:12px;overflow:hidden;white-space:nowrap}.status-item{display:flex;align-items:center;gap:5px;color:var(--cli-panel-text-secondary, rgba(255, 255, 255, .6))}.status-dot{width:6px;height:6px;border-radius:50%;flex-shrink:0;background:var(--cli-status-neutral, #6b7280)}.status-dot.dot-idle{background:var(--cli-status-idle, #3fb950)}.status-dot.dot-running{background:var(--cli-status-running, #f0883e);animation:statusPulse 1.5s ease-in-out infinite}.status-dot.dot-error{background:var(--cli-status-error, #f85149)}.status-icon{flex-shrink:0}.status-icon.status-success{color:var(--cli-status-idle, #3fb950)}.status-icon.status-error{color:var(--cli-status-error, #f85149)}.status-label{overflow:hidden;text-overflow:ellipsis}.status-muted{color:var(--cli-panel-text-secondary, rgba(255, 255, 255, .4))}.status-text{color:var(--cli-panel-accent, #818cf8);max-width:200px}.status-text .status-label{overflow:hidden;text-overflow:ellipsis}.status-running{color:var(--cli-status-running, #f0883e)}.status-clickable{cursor:pointer;border-radius:4px;padding:2px 6px;margin:-2px -6px;transition:background-color .15s ease}.status-clickable:hover{background-color:var(--cli-btn-hover-bg, rgba(129, 140, 248, .12))}.services-dropdown{position:fixed;background-color:var(--cli-panel-header-bg, #1f2937);border:1px solid var(--cli-panel-border, #374151);border-radius:8px;box-shadow:0 8px 24px #0006;z-index:1100;min-width:260px;max-width:380px;overflow:hidden;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif}.services-dropdown-header{padding:10px 14px 8px;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--cli-panel-text-secondary, rgba(255, 255, 255, .5));border-bottom:1px solid var(--cli-panel-border, #374151)}.services-dropdown-list{max-height:240px;overflow-y:auto}.services-dropdown-list::-webkit-scrollbar{width:6px}.services-dropdown-list::-webkit-scrollbar-track{background:transparent}.services-dropdown-list::-webkit-scrollbar-thumb{background:var(--cli-panel-text-secondary, rgba(255, 255, 255, .2));border-radius:3px}.services-dropdown-list::-webkit-scrollbar-thumb:hover{background:var(--cli-panel-text-secondary, rgba(255, 255, 255, .35))}.services-dropdown-list{scrollbar-width:thin;scrollbar-color:var(--cli-panel-text-secondary, rgba(255, 255, 255, .2)) transparent}.services-dropdown-item{display:flex;align-items:flex-start;gap:10px;padding:10px 14px;border-bottom:1px solid rgba(255,255,255,.04);transition:background-color .15s ease}.services-dropdown-item:last-child{border-bottom:none}.services-dropdown-item:hover{background-color:var(--cli-btn-hover-bg, rgba(129, 140, 248, .08))}.svc-dot{width:7px;height:7px;border-radius:50%;flex-shrink:0;margin-top:5px}.svc-dot.svc-running{background:var(--cli-status-idle, #3fb950)}.svc-dot.svc-stopped{background:var(--cli-panel-text-secondary, rgba(255, 255, 255, .3))}.svc-info{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}.svc-name{font-size:13px;font-weight:500;color:var(--cli-panel-text, rgba(255, 255, 255, .87));overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.svc-desc{font-size:11px;color:var(--cli-panel-text-secondary, rgba(255, 255, 255, .5));overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.svc-status{font-size:11px;flex-shrink:0;color:var(--cli-panel-text-secondary, rgba(255, 255, 255, .5));margin-top:1px}.srv-meta{display:flex;flex-direction:column;align-items:flex-end;gap:2px;flex-shrink:0}.services-dropdown-empty{padding:16px 14px;font-size:12px;color:var(--cli-panel-text-secondary, rgba(255, 255, 255, .4));text-align:center}.glow-line{position:absolute;bottom:0;left:0;right:0;height:2px;background:linear-gradient(90deg,transparent,var(--cli-status-glow, var(--cli-panel-accent, #818cf8)),#a78bfa,var(--cli-status-glow, var(--cli-panel-accent, #818cf8)),transparent);background-size:200% 100%;opacity:.4;animation:glowShift 8s ease-in-out infinite;transition:opacity .6s ease;pointer-events:none}.glow-line.glow-active{opacity:.8;animation-duration:3s}.status-indicators-compact{display:none}.terminal-wrapper[data-position=left] .status-indicators-compact,.terminal-wrapper[data-position=right] .status-indicators-compact{display:flex;flex-direction:column;align-items:center;gap:10px;padding:12px 0}.compact-item{display:flex;align-items:center;justify-content:center;width:20px;height:20px;cursor:default}.compact-item.status-clickable{cursor:pointer;border-radius:4px}.compact-item.status-clickable:hover{background-color:var(--cli-btn-hover-bg, rgba(129, 140, 248, .12))}.compact-dot{width:6px;height:6px;border-radius:50%;flex-shrink:0}.compact-dot.level-info{background:var(--cli-panel-accent, #818cf8)}.compact-dot.level-success{background:var(--cli-status-idle, #3fb950)}.compact-dot.level-warn{background:var(--cli-status-running, #f0883e)}.compact-dot.level-error{background:var(--cli-status-error, #f85149)}.terminal-wrapper[data-position=left] .glow-line{inset:0 0 auto auto;width:2px;height:100%;background:linear-gradient(180deg,transparent,var(--cli-status-glow, var(--cli-panel-accent, #818cf8)),#a78bfa,var(--cli-status-glow, var(--cli-panel-accent, #818cf8)),transparent);background-size:100% 200%}.terminal-wrapper[data-position=right] .glow-line{inset:0 auto auto 0;width:2px;height:100%;background:linear-gradient(180deg,transparent,var(--cli-status-glow, var(--cli-panel-accent, #818cf8)),#a78bfa,var(--cli-status-glow, var(--cli-panel-accent, #818cf8)),transparent);background-size:100% 200%}@keyframes glowShift{0%{background-position:200% center}to{background-position:-200% center}}@keyframes statusPulse{0%,to{opacity:1}50%{opacity:.3}}@media(prefers-reduced-motion:reduce){.glow-line,.status-dot{animation:none!important}}\n"], dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: CollapsableContentComponent, decorators: [{
            type: Component,
            args: [{ standalone: false, selector: 'collapsable-content', template: "<!-- Hide tab (shown when panel is hidden) -->\n<button\n  *ngIf=\"visible && isHidden\"\n  class=\"cli-panel-hide-tab\"\n  [attr.data-position]=\"position\"\n  [attr.data-hide-align]=\"hideAlignment\"\n  [ngStyle]=\"themeStyles\"\n  title=\"Show CLI\"\n  (click)=\"unhideTerminal()\"\n>\n  <svg\n    class=\"cli-panel-hide-tab-icon\"\n    width=\"16\"\n    height=\"16\"\n    viewBox=\"0 0 24 24\"\n    fill=\"none\"\n    stroke=\"currentColor\"\n    stroke-width=\"2\"\n    stroke-linecap=\"round\"\n    stroke-linejoin=\"round\"\n  >\n    <polyline points=\"4 17 10 11 4 5\" />\n    <line x1=\"12\" y1=\"19\" x2=\"20\" y2=\"19\" />\n  </svg>\n  <span class=\"cli-panel-hide-tab-label\">CLI</span>\n  <svg\n    class=\"cli-panel-hide-tab-arrow\"\n    width=\"14\"\n    height=\"14\"\n    viewBox=\"0 0 24 24\"\n    fill=\"none\"\n    stroke=\"currentColor\"\n    stroke-width=\"2.5\"\n    stroke-linecap=\"round\"\n    stroke-linejoin=\"round\"\n  >\n    <!-- bottom: chevron up -->\n    <polyline *ngIf=\"position === 'bottom'\" points=\"18 15 12 9 6 15\" />\n    <!-- top: chevron down -->\n    <polyline *ngIf=\"position === 'top'\" points=\"6 9 12 15 18 9\" />\n    <!-- left: chevron right -->\n    <polyline *ngIf=\"position === 'left'\" points=\"9 18 15 12 9 6\" />\n    <!-- right: chevron left -->\n    <polyline *ngIf=\"position === 'right'\" points=\"15 6 9 12 15 18\" />\n  </svg>\n</button>\n\n<!-- Main panel (kept in DOM when hidden to preserve terminal state) -->\n<div\n  *ngIf=\"visible\"\n  class=\"terminal-wrapper\"\n  [style.display]=\"isHidden ? 'none' : ''\"\n  [class.collapsed]=\"isCollapsed\"\n  [class.maximized]=\"isMaximized\"\n  [class.resizing]=\"isResizing\"\n  [attr.data-position]=\"position\"\n  [attr.data-resizable]=\"resizable\"\n  [attr.data-closable]=\"closable\"\n  [attr.data-hideable]=\"hideable\"\n  [ngStyle]=\"wrapperStyle\"\n>\n  <div class=\"terminal-header\">\n    <div class=\"resize-bar\" (mousedown)=\"onResizeStart($event)\">\n      <div class=\"resize-grip\"></div>\n    </div>\n    <div class=\"header-content\">\n      <p class=\"terminal-title\">\n        <svg\n          class=\"title-icon\"\n          width=\"22\"\n          height=\"22\"\n          viewBox=\"0 0 24 24\"\n          fill=\"none\"\n          stroke=\"currentColor\"\n          stroke-width=\"2\"\n          stroke-linecap=\"round\"\n          stroke-linejoin=\"round\"\n        >\n          <polyline points=\"4 17 10 11 4 5\" />\n          <line x1=\"12\" y1=\"19\" x2=\"20\" y2=\"19\" />\n        </svg>\n        CLI\n      </p>\n      <!-- Status indicators (bottom/top positions only) -->\n      <div class=\"status-indicators\" *ngIf=\"showStatusIndicators\">\n          <!-- Execution state -->\n          <span class=\"status-item\" [class.status-running]=\"statusExecutionState === 'running'\">\n              <span class=\"status-dot\" [class.dot-idle]=\"statusExecutionState === 'idle'\" [class.dot-running]=\"statusExecutionState === 'running'\"></span>\n              <span class=\"status-label\">{{ statusExecutionState }}</span>\n          </span>\n\n          <!-- Background services (only if any exist) -->\n          <span class=\"status-item status-clickable\" *ngIf=\"statusServiceCount.total > 0\" (click)=\"toggleServicesDropdown($event)\">\n              <span class=\"status-icon\">&#9881;</span>\n              <span class=\"status-label\">{{ statusServiceCount.running }}/{{ statusServiceCount.total }} services</span>\n          </span>\n\n          <!-- Last command -->\n          <span class=\"status-item\" *ngIf=\"statusLastCommand\">\n              <span class=\"status-icon\" [class.status-success]=\"statusLastCommand.success\" [class.status-error]=\"!statusLastCommand.success\">\n                  {{ statusLastCommand.success ? '&#10003;' : '&#10005;' }}\n              </span>\n              <span class=\"status-label\">{{ statusLastCommand.name }}</span>\n          </span>\n\n          <!-- Server connection -->\n          <span class=\"status-item status-clickable\" *ngIf=\"statusServerState !== 'none'\" (click)=\"toggleServersDropdown($event)\">\n              <span class=\"status-dot\" [class.dot-idle]=\"statusServerState === 'connected'\" [class.dot-error]=\"statusServerState === 'disconnected'\"></span>\n              <span class=\"status-label\">{{ connectedServerCount }}/{{ totalServerCount }} servers</span>\n          </span>\n\n          <!-- Uptime -->\n          <span class=\"status-item status-muted\" *ngIf=\"statusUptime > 0\">\n              <span class=\"status-icon\">&uarr;</span>\n              <span class=\"status-label\">{{ formattedUptime }}</span>\n          </span>\n\n          <!-- Custom processor notification -->\n          <span class=\"status-item status-text\" [ngClass]=\"'level-' + notification?.level\" *ngIf=\"notification\">\n              <span class=\"status-label\">{{ notification.message }}</span>\n          </span>\n      </div>\n\n      <!-- Compact status indicators (left/right positions \u2014 CSS controls visibility) -->\n      <div class=\"status-indicators-compact\">\n          <!-- Execution state -->\n          <span class=\"compact-item\" [title]=\"statusExecutionState\">\n              <span class=\"status-dot\" [class.dot-idle]=\"statusExecutionState === 'idle'\" [class.dot-running]=\"statusExecutionState === 'running'\"></span>\n          </span>\n\n          <!-- Background services -->\n          <span class=\"compact-item status-clickable\" *ngIf=\"statusServiceCount.total > 0\" [title]=\"statusServiceCount.running + '/' + statusServiceCount.total + ' services'\" (click)=\"toggleServicesDropdown($event)\">\n              <span class=\"status-icon\">&#9881;</span>\n          </span>\n\n          <!-- Last command -->\n          <span class=\"compact-item\" *ngIf=\"statusLastCommand\" [title]=\"(statusLastCommand.success ? '\u2713 ' : '\u2717 ') + statusLastCommand.name\">\n              <span class=\"status-icon\" [class.status-success]=\"statusLastCommand.success\" [class.status-error]=\"!statusLastCommand.success\">\n                  {{ statusLastCommand.success ? '&#10003;' : '&#10005;' }}\n              </span>\n          </span>\n\n          <!-- Server connection -->\n          <span class=\"compact-item status-clickable\" *ngIf=\"statusServerState !== 'none'\" [title]=\"connectedServerCount + '/' + totalServerCount + ' servers'\" (click)=\"toggleServersDropdown($event)\">\n              <span class=\"status-dot\" [class.dot-idle]=\"statusServerState === 'connected'\" [class.dot-error]=\"statusServerState === 'disconnected'\"></span>\n          </span>\n\n          <!-- Uptime -->\n          <span class=\"compact-item status-muted\" *ngIf=\"statusUptime > 0\" [title]=\"formattedUptime\">\n              <span class=\"status-icon\">&uarr;</span>\n          </span>\n\n          <!-- Notification -->\n          <span class=\"compact-item\" *ngIf=\"notification\" [title]=\"notification.message\">\n              <span class=\"compact-dot\" [ngClass]=\"'level-' + notification.level\"></span>\n          </span>\n      </div>\n\n      <div class=\"action-buttons\">\n        <!-- Position dropdown -->\n        <div class=\"panel-btn-position-wrapper\" (click)=\"$event.stopPropagation()\">\n          <button\n            class=\"panel-btn panel-btn-position\"\n            title=\"Move panel\"\n            (click)=\"togglePositionDropdown($event)\"\n          >\n            <svg\n              width=\"20\"\n              height=\"20\"\n              viewBox=\"0 0 24 24\"\n              fill=\"none\"\n              stroke=\"currentColor\"\n              stroke-width=\"1.8\"\n              stroke-linecap=\"round\"\n              stroke-linejoin=\"round\"\n            >\n              <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\" />\n              <!-- bottom -->\n              <rect *ngIf=\"position === 'bottom'\" x=\"4\" y=\"15\" width=\"16\" height=\"5\" rx=\"1\" fill=\"currentColor\" stroke=\"none\" opacity=\"0.5\" />\n              <!-- top -->\n              <rect *ngIf=\"position === 'top'\" x=\"4\" y=\"4\" width=\"16\" height=\"5\" rx=\"1\" fill=\"currentColor\" stroke=\"none\" opacity=\"0.5\" />\n              <!-- left -->\n              <rect *ngIf=\"position === 'left'\" x=\"4\" y=\"4\" width=\"5\" height=\"16\" rx=\"1\" fill=\"currentColor\" stroke=\"none\" opacity=\"0.5\" />\n              <!-- right -->\n              <rect *ngIf=\"position === 'right'\" x=\"15\" y=\"4\" width=\"5\" height=\"16\" rx=\"1\" fill=\"currentColor\" stroke=\"none\" opacity=\"0.5\" />\n            </svg>\n          </button>\n        </div>\n\n        <!-- Hide button -->\n        <button\n          *ngIf=\"hideable\"\n          class=\"panel-btn panel-btn-hide\"\n          title=\"Hide\"\n          (click)=\"hideTerminal()\"\n        >\n          <svg\n            width=\"20\"\n            height=\"20\"\n            viewBox=\"0 0 24 24\"\n            fill=\"none\"\n            stroke=\"currentColor\"\n            stroke-width=\"1.8\"\n            stroke-linecap=\"round\"\n            stroke-linejoin=\"round\"\n          >\n            <line x1=\"5\" y1=\"18\" x2=\"19\" y2=\"18\" />\n            <polyline points=\"9 14 12 17 15 14\" />\n          </svg>\n        </button>\n\n        <button\n          class=\"panel-btn\"\n          [title]=\"!isMaximized ? 'Maximize' : 'Restore'\"\n          [disabled]=\"isCollapsed\"\n          (click)=\"toggleMaximizationTerminal()\"\n        >\n          <svg\n            *ngIf=\"!isMaximized\"\n            width=\"20\"\n            height=\"20\"\n            viewBox=\"0 0 24 24\"\n            fill=\"none\"\n            stroke=\"currentColor\"\n            stroke-width=\"1.8\"\n            stroke-linecap=\"round\"\n            stroke-linejoin=\"round\"\n          >\n            <polyline points=\"15 3 21 3 21 9\" />\n            <polyline points=\"9 21 3 21 3 15\" />\n            <line x1=\"21\" y1=\"3\" x2=\"14\" y2=\"10\" />\n            <line x1=\"3\" y1=\"21\" x2=\"10\" y2=\"14\" />\n          </svg>\n          <svg\n            *ngIf=\"isMaximized\"\n            width=\"20\"\n            height=\"20\"\n            viewBox=\"0 0 24 24\"\n            fill=\"none\"\n            stroke=\"currentColor\"\n            stroke-width=\"1.8\"\n            stroke-linecap=\"round\"\n            stroke-linejoin=\"round\"\n          >\n            <polyline points=\"4 14 10 14 10 20\" />\n            <polyline points=\"20 10 14 10 14 4\" />\n            <line x1=\"14\" y1=\"10\" x2=\"21\" y2=\"3\" />\n            <line x1=\"3\" y1=\"21\" x2=\"10\" y2=\"14\" />\n          </svg>\n        </button>\n\n        <button\n          class=\"panel-btn\"\n          [title]=\"isCollapsed ? 'Expand' : 'Collapse'\"\n          (click)=\"toggleTerminal()\"\n        >\n          <svg\n            width=\"20\"\n            height=\"20\"\n            viewBox=\"0 0 24 24\"\n            fill=\"none\"\n            stroke=\"currentColor\"\n            stroke-width=\"1.8\"\n            stroke-linecap=\"round\"\n            stroke-linejoin=\"round\"\n          >\n            <polyline *ngIf=\"position === 'bottom' && isCollapsed\" points=\"18 15 12 9 6 15\" />\n            <polyline *ngIf=\"position === 'bottom' && !isCollapsed\" points=\"6 9 12 15 18 9\" />\n            <polyline *ngIf=\"position === 'top' && isCollapsed\" points=\"6 9 12 15 18 9\" />\n            <polyline *ngIf=\"position === 'top' && !isCollapsed\" points=\"18 15 12 9 6 15\" />\n            <polyline *ngIf=\"position === 'left' && isCollapsed\" points=\"9 18 15 12 9 6\" />\n            <polyline *ngIf=\"position === 'left' && !isCollapsed\" points=\"15 6 9 12 15 18\" />\n            <polyline *ngIf=\"position === 'right' && isCollapsed\" points=\"15 6 9 12 15 18\" />\n            <polyline *ngIf=\"position === 'right' && !isCollapsed\" points=\"9 18 15 12 9 6\" />\n          </svg>\n        </button>\n\n        <button class=\"panel-btn panel-btn-close\" title=\"Close\" (click)=\"closeTerminal()\">\n          <svg\n            width=\"20\"\n            height=\"20\"\n            viewBox=\"0 0 24 24\"\n            fill=\"none\"\n            stroke=\"currentColor\"\n            stroke-width=\"1.8\"\n            stroke-linecap=\"round\"\n            stroke-linejoin=\"round\"\n          >\n            <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n            <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n          </svg>\n        </button>\n      </div>\n    </div>\n    <!-- Ambient glow line -->\n    <div class=\"glow-line\" [class.glow-active]=\"statusExecutionState === 'running'\"></div>\n  </div>\n  <div class=\"terminal-content\" [style.display]=\"isCollapsed ? 'none' : ''\">\n    <ng-content></ng-content>\n  </div>\n</div>\n\n<!-- Services dropdown portaled outside terminal-wrapper -->\n<div class=\"services-dropdown\" *ngIf=\"servicesDropdownOpen\" [ngStyle]=\"servicesDropdownStyle\" (click)=\"$event.stopPropagation()\">\n  <div class=\"services-dropdown-header\">Background Services</div>\n  <div class=\"services-dropdown-list\">\n    <div class=\"services-dropdown-item\" *ngFor=\"let svc of statusServiceDetails\">\n      <span class=\"svc-dot\" [class.svc-running]=\"svc.status === 'running'\" [class.svc-stopped]=\"svc.status !== 'running'\"></span>\n      <div class=\"svc-info\">\n        <span class=\"svc-name\">{{ svc.name }}</span>\n        <span class=\"svc-desc\" *ngIf=\"svc.description\">{{ svc.description }}</span>\n      </div>\n      <span class=\"svc-status\">{{ svc.status }}</span>\n    </div>\n  </div>\n  <div class=\"services-dropdown-empty\" *ngIf=\"statusServiceDetails.length === 0\">\n    No services registered\n  </div>\n</div>\n\n<!-- Servers dropdown portaled outside terminal-wrapper -->\n<div class=\"services-dropdown\" *ngIf=\"serversDropdownOpen\" [ngStyle]=\"serversDropdownStyle\" (click)=\"$event.stopPropagation()\">\n  <div class=\"services-dropdown-header\">Server Connections</div>\n  <div class=\"services-dropdown-list\">\n    <div class=\"services-dropdown-item\" *ngFor=\"let srv of statusServerDetails\">\n      <span class=\"svc-dot\" [class.svc-running]=\"srv.connected\" [class.svc-stopped]=\"!srv.connected\"></span>\n      <div class=\"svc-info\">\n        <span class=\"svc-name\">{{ srv.name }}</span>\n        <span class=\"svc-desc\" *ngIf=\"srv.url\">{{ srv.url }}</span>\n      </div>\n      <div class=\"srv-meta\">\n        <span class=\"svc-status\">{{ srv.connected ? 'connected' : 'disconnected' }}</span>\n        <span class=\"svc-desc\" *ngIf=\"srv.apiVersion\">v{{ srv.apiVersion }}</span>\n        <span class=\"svc-desc\" *ngIf=\"srv.commandCount\">{{ srv.commandCount }} cmds</span>\n      </div>\n    </div>\n  </div>\n  <div class=\"services-dropdown-empty\" *ngIf=\"statusServerDetails.length === 0\">\n    No servers configured\n  </div>\n</div>\n\n<!-- Position dropdown portaled outside terminal-wrapper to escape transform stacking context -->\n<div class=\"position-dropdown\" *ngIf=\"positionDropdownOpen\" [ngStyle]=\"dropdownStyle\" (click)=\"$event.stopPropagation()\">\n  <button\n    class=\"position-dropdown-item\"\n    [class.active]=\"position === 'bottom'\"\n    (click)=\"selectPosition('bottom')\"\n  >\n    <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n      <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\" />\n      <rect x=\"4\" y=\"15\" width=\"16\" height=\"5\" rx=\"1\" fill=\"currentColor\" stroke=\"none\" opacity=\"0.6\" />\n    </svg>\n    Bottom\n  </button>\n  <button\n    class=\"position-dropdown-item\"\n    [class.active]=\"position === 'top'\"\n    (click)=\"selectPosition('top')\"\n  >\n    <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n      <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\" />\n      <rect x=\"4\" y=\"4\" width=\"16\" height=\"5\" rx=\"1\" fill=\"currentColor\" stroke=\"none\" opacity=\"0.6\" />\n    </svg>\n    Top\n  </button>\n  <button\n    class=\"position-dropdown-item\"\n    [class.active]=\"position === 'left'\"\n    (click)=\"selectPosition('left')\"\n  >\n    <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n      <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\" />\n      <rect x=\"4\" y=\"4\" width=\"5\" height=\"16\" rx=\"1\" fill=\"currentColor\" stroke=\"none\" opacity=\"0.6\" />\n    </svg>\n    Left\n  </button>\n  <button\n    class=\"position-dropdown-item\"\n    [class.active]=\"position === 'right'\"\n    (click)=\"selectPosition('right')\"\n  >\n    <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n      <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\" />\n      <rect x=\"15\" y=\"4\" width=\"5\" height=\"16\" rx=\"1\" fill=\"currentColor\" stroke=\"none\" opacity=\"0.6\" />\n    </svg>\n    Right\n  </button>\n</div>\n", styles: [".terminal-wrapper{position:fixed;bottom:0;left:0;right:0;transform:translateY(0);transition:transform .3s cubic-bezier(.4,0,.2,1);background-color:var(--cli-panel-bg, #111827);color:var(--cli-panel-text, rgba(255, 255, 255, .87));border-top:1px solid var(--cli-panel-border, #374151);display:flex;flex-direction:column;z-index:1000;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif;box-shadow:0 -4px 20px #00000040}.terminal-wrapper.collapsed{transform:translateY(calc(100% - 60px))}.terminal-wrapper.resizing{transition:none;-webkit-user-select:none;user-select:none}.terminal-header{background-color:var(--cli-panel-header-bg, #1f2937);display:flex;flex-direction:column;position:relative;flex-shrink:0}.terminal-header .resize-bar{position:absolute;top:-6px;left:0;right:0;height:12px;cursor:ns-resize;display:flex;align-items:center;justify-content:center;z-index:2}.terminal-header .resize-bar .resize-grip{width:32px;height:4px;border-radius:2px;background:var(--cli-panel-text-secondary, rgba(255, 255, 255, .3));opacity:0;transition:opacity .2s ease}.terminal-header .resize-bar:hover .resize-grip{opacity:1}.terminal-header .header-content{height:60px;display:flex;flex-direction:row;gap:1rem;align-items:center;border-bottom:1px solid var(--cli-panel-border, #374151);padding:0 1rem}.terminal-header .header-content .terminal-title{font-size:.95rem;display:flex;gap:.5rem;justify-content:center;align-items:center;margin:0;font-weight:600;color:var(--cli-panel-text, rgba(255, 255, 255, .87))}.terminal-header .header-content .terminal-title .title-icon{color:var(--cli-panel-accent, #818cf8)}.terminal-header .header-content .action-buttons{display:flex;flex-direction:row;gap:.35rem;margin-left:auto}.panel-btn{appearance:none;background:transparent;border:none;color:var(--cli-panel-text-secondary, rgba(255, 255, 255, .6));cursor:pointer;width:40px;height:40px;border-radius:50%;display:inline-flex;align-items:center;justify-content:center;padding:0;transition:background-color .2s ease,color .2s ease}.panel-btn svg{flex-shrink:0}.panel-btn:hover:not(:disabled){color:var(--cli-panel-text, rgba(255, 255, 255, .87));background-color:var(--cli-btn-hover-bg, rgba(129, 140, 248, .12))}.panel-btn:active:not(:disabled){background-color:var(--cli-btn-active-bg, rgba(129, 140, 248, .2))}.panel-btn:disabled{opacity:.3;cursor:not-allowed}.terminal-content{flex:1;min-height:0;overflow:hidden;display:flex;flex-direction:column}.terminal-wrapper[data-position=top]{bottom:auto;top:0;border-top:none;border-bottom:1px solid var(--cli-panel-border, #374151);box-shadow:0 4px 20px #00000040;flex-direction:column-reverse}.terminal-wrapper[data-position=top].collapsed{transform:translateY(calc(-100% + 60px))}.terminal-wrapper[data-position=top] .terminal-header .header-content{border-bottom:none;border-top:1px solid var(--cli-panel-border, #374151)}.terminal-wrapper[data-position=top] .terminal-header .resize-bar{top:auto;bottom:-6px;cursor:ns-resize}.terminal-wrapper[data-position=left]{inset:0 auto 0 0;width:auto;height:100vh!important;border-top:none;border-right:1px solid var(--cli-panel-border, #374151);box-shadow:4px 0 20px #00000040;flex-direction:row-reverse}.terminal-wrapper[data-position=left].collapsed{transform:translate(calc(-100% + 60px))}.terminal-wrapper[data-position=left] .terminal-header{width:60px;flex-shrink:0;flex-direction:column}.terminal-wrapper[data-position=left] .terminal-header .header-content{height:auto;flex:1;flex-direction:column;padding:1rem 0;border-bottom:none;border-left:1px solid var(--cli-panel-border, #374151)}.terminal-wrapper[data-position=left] .terminal-header .header-content .terminal-title{writing-mode:vertical-rl;text-orientation:mixed}.terminal-wrapper[data-position=left] .terminal-header .header-content .action-buttons{flex-direction:column;margin-left:0;margin-top:auto}.terminal-wrapper[data-position=left] .terminal-header .resize-bar{position:absolute;inset:0 -6px 0 auto;width:12px;height:auto;cursor:ew-resize}.terminal-wrapper[data-position=left] .terminal-header .resize-bar .resize-grip{width:4px;height:32px}.terminal-wrapper[data-position=left] .terminal-content{flex:1;min-width:0}.terminal-wrapper[data-position=right]{inset:0 0 0 auto;width:auto;height:100vh!important;border-top:none;border-left:1px solid var(--cli-panel-border, #374151);box-shadow:-4px 0 20px #00000040;flex-direction:row}.terminal-wrapper[data-position=right].collapsed{transform:translate(calc(100% - 60px))}.terminal-wrapper[data-position=right] .terminal-header{width:60px;flex-shrink:0;flex-direction:column}.terminal-wrapper[data-position=right] .terminal-header .header-content{height:auto;flex:1;flex-direction:column;padding:1rem 0;border-bottom:none;border-right:1px solid var(--cli-panel-border, #374151)}.terminal-wrapper[data-position=right] .terminal-header .header-content .terminal-title{writing-mode:vertical-rl;text-orientation:mixed}.terminal-wrapper[data-position=right] .terminal-header .header-content .action-buttons{flex-direction:column;margin-left:0;margin-top:auto}.terminal-wrapper[data-position=right] .terminal-header .resize-bar{position:absolute;inset:0 auto 0 -6px;width:12px;height:auto;cursor:ew-resize}.terminal-wrapper[data-position=right] .terminal-header .resize-bar .resize-grip{width:4px;height:32px}.terminal-wrapper[data-position=right] .terminal-content{flex:1;min-width:0}.terminal-wrapper[data-resizable=false] .resize-bar,.terminal-wrapper[data-closable=false] .panel-btn-close{display:none}.cli-panel-hide-tab{position:fixed;z-index:1000;display:flex;align-items:center;gap:6px;background-color:var(--cli-panel-header-bg, #1f2937);border:1px solid var(--cli-panel-border, #374151);color:var(--cli-panel-text-secondary, rgba(255, 255, 255, .6));cursor:pointer;padding:6px 12px;appearance:none;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif;font-size:.8rem;font-weight:600;transition:transform .2s ease,opacity .2s ease,color .2s ease,background-color .2s ease,box-shadow .2s ease;box-shadow:0 2px 8px #0000004d;opacity:.85}.cli-panel-hide-tab:hover{transform:scale(1.05);opacity:1;color:var(--cli-panel-text, rgba(255, 255, 255, .87));background-color:var(--cli-panel-bg, #111827);box-shadow:0 4px 16px #0006}.cli-panel-hide-tab:active{transform:scale(.97)}.cli-panel-hide-tab svg{flex-shrink:0}.cli-panel-hide-tab .cli-panel-hide-tab-icon{color:var(--cli-panel-accent, #818cf8)}.cli-panel-hide-tab .cli-panel-hide-tab-label{letter-spacing:.03em}.cli-panel-hide-tab[data-position=bottom]{bottom:0;border-bottom:none;border-radius:8px 8px 0 0}.cli-panel-hide-tab[data-position=bottom][data-hide-align=start]{left:16px}.cli-panel-hide-tab[data-position=bottom][data-hide-align=center]{left:50%;transform:translate(-50%)}.cli-panel-hide-tab[data-position=bottom][data-hide-align=center]:hover{transform:translate(-50%) scale(1.05)}.cli-panel-hide-tab[data-position=bottom][data-hide-align=end]{right:16px}.cli-panel-hide-tab[data-position=top]{top:0;border-top:none;border-radius:0 0 8px 8px}.cli-panel-hide-tab[data-position=top][data-hide-align=start]{left:16px}.cli-panel-hide-tab[data-position=top][data-hide-align=center]{left:50%;transform:translate(-50%)}.cli-panel-hide-tab[data-position=top][data-hide-align=center]:hover{transform:translate(-50%) scale(1.05)}.cli-panel-hide-tab[data-position=top][data-hide-align=end]{right:16px}.cli-panel-hide-tab[data-position=left]{left:0;border-left:none;border-radius:0 8px 8px 0}.cli-panel-hide-tab[data-position=left][data-hide-align=start]{top:16px}.cli-panel-hide-tab[data-position=left][data-hide-align=center]{top:50%;transform:translateY(-50%)}.cli-panel-hide-tab[data-position=left][data-hide-align=center]:hover{transform:translateY(-50%) scale(1.05)}.cli-panel-hide-tab[data-position=left][data-hide-align=end]{bottom:16px}.cli-panel-hide-tab[data-position=right]{right:0;border-right:none;border-radius:8px 0 0 8px}.cli-panel-hide-tab[data-position=right][data-hide-align=start]{top:16px}.cli-panel-hide-tab[data-position=right][data-hide-align=center]{top:50%;transform:translateY(-50%)}.cli-panel-hide-tab[data-position=right][data-hide-align=center]:hover{transform:translateY(-50%) scale(1.05)}.cli-panel-hide-tab[data-position=right][data-hide-align=end]{bottom:16px}@keyframes cli-hide-tab-pulse{0%,to{transform:translate(0)}50%{transform:translate(3px)}}@keyframes cli-hide-tab-pulse-vertical{0%,to{transform:translateY(0)}50%{transform:translateY(3px)}}.cli-panel-hide-tab:hover .cli-panel-hide-tab-arrow{animation:cli-hide-tab-pulse .6s ease infinite}.cli-panel-hide-tab[data-position=left]:hover .cli-panel-hide-tab-arrow,.cli-panel-hide-tab[data-position=right]:hover .cli-panel-hide-tab-arrow{animation:cli-hide-tab-pulse-vertical .6s ease infinite}.panel-btn-position-wrapper{position:relative}.position-dropdown{position:fixed;background-color:var(--cli-panel-header-bg, #1f2937);border:1px solid var(--cli-panel-border, #374151);border-radius:6px;box-shadow:0 8px 24px #0006;z-index:1100;min-width:120px;overflow:hidden;display:flex;flex-direction:column}.position-dropdown-item{appearance:none;background:transparent;border:none;color:var(--cli-panel-text-secondary, rgba(255, 255, 255, .6));cursor:pointer;display:flex;align-items:center;gap:8px;padding:8px 12px;font-size:.85rem;font-family:inherit;width:100%;text-align:left;transition:background-color .15s ease,color .15s ease}.position-dropdown-item:hover{background-color:var(--cli-btn-hover-bg, rgba(129, 140, 248, .12));color:var(--cli-panel-text, rgba(255, 255, 255, .87))}.position-dropdown-item.active{color:var(--cli-panel-accent, #818cf8)}.position-dropdown-item svg{flex-shrink:0}.terminal-wrapper[data-hideable=false] .panel-btn-hide{display:none}.status-indicators{display:flex;align-items:center;gap:14px;margin-left:24px;font-size:12px;overflow:hidden;white-space:nowrap}.status-item{display:flex;align-items:center;gap:5px;color:var(--cli-panel-text-secondary, rgba(255, 255, 255, .6))}.status-dot{width:6px;height:6px;border-radius:50%;flex-shrink:0;background:var(--cli-status-neutral, #6b7280)}.status-dot.dot-idle{background:var(--cli-status-idle, #3fb950)}.status-dot.dot-running{background:var(--cli-status-running, #f0883e);animation:statusPulse 1.5s ease-in-out infinite}.status-dot.dot-error{background:var(--cli-status-error, #f85149)}.status-icon{flex-shrink:0}.status-icon.status-success{color:var(--cli-status-idle, #3fb950)}.status-icon.status-error{color:var(--cli-status-error, #f85149)}.status-label{overflow:hidden;text-overflow:ellipsis}.status-muted{color:var(--cli-panel-text-secondary, rgba(255, 255, 255, .4))}.status-text{color:var(--cli-panel-accent, #818cf8);max-width:200px}.status-text .status-label{overflow:hidden;text-overflow:ellipsis}.status-running{color:var(--cli-status-running, #f0883e)}.status-clickable{cursor:pointer;border-radius:4px;padding:2px 6px;margin:-2px -6px;transition:background-color .15s ease}.status-clickable:hover{background-color:var(--cli-btn-hover-bg, rgba(129, 140, 248, .12))}.services-dropdown{position:fixed;background-color:var(--cli-panel-header-bg, #1f2937);border:1px solid var(--cli-panel-border, #374151);border-radius:8px;box-shadow:0 8px 24px #0006;z-index:1100;min-width:260px;max-width:380px;overflow:hidden;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif}.services-dropdown-header{padding:10px 14px 8px;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--cli-panel-text-secondary, rgba(255, 255, 255, .5));border-bottom:1px solid var(--cli-panel-border, #374151)}.services-dropdown-list{max-height:240px;overflow-y:auto}.services-dropdown-list::-webkit-scrollbar{width:6px}.services-dropdown-list::-webkit-scrollbar-track{background:transparent}.services-dropdown-list::-webkit-scrollbar-thumb{background:var(--cli-panel-text-secondary, rgba(255, 255, 255, .2));border-radius:3px}.services-dropdown-list::-webkit-scrollbar-thumb:hover{background:var(--cli-panel-text-secondary, rgba(255, 255, 255, .35))}.services-dropdown-list{scrollbar-width:thin;scrollbar-color:var(--cli-panel-text-secondary, rgba(255, 255, 255, .2)) transparent}.services-dropdown-item{display:flex;align-items:flex-start;gap:10px;padding:10px 14px;border-bottom:1px solid rgba(255,255,255,.04);transition:background-color .15s ease}.services-dropdown-item:last-child{border-bottom:none}.services-dropdown-item:hover{background-color:var(--cli-btn-hover-bg, rgba(129, 140, 248, .08))}.svc-dot{width:7px;height:7px;border-radius:50%;flex-shrink:0;margin-top:5px}.svc-dot.svc-running{background:var(--cli-status-idle, #3fb950)}.svc-dot.svc-stopped{background:var(--cli-panel-text-secondary, rgba(255, 255, 255, .3))}.svc-info{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}.svc-name{font-size:13px;font-weight:500;color:var(--cli-panel-text, rgba(255, 255, 255, .87));overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.svc-desc{font-size:11px;color:var(--cli-panel-text-secondary, rgba(255, 255, 255, .5));overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.svc-status{font-size:11px;flex-shrink:0;color:var(--cli-panel-text-secondary, rgba(255, 255, 255, .5));margin-top:1px}.srv-meta{display:flex;flex-direction:column;align-items:flex-end;gap:2px;flex-shrink:0}.services-dropdown-empty{padding:16px 14px;font-size:12px;color:var(--cli-panel-text-secondary, rgba(255, 255, 255, .4));text-align:center}.glow-line{position:absolute;bottom:0;left:0;right:0;height:2px;background:linear-gradient(90deg,transparent,var(--cli-status-glow, var(--cli-panel-accent, #818cf8)),#a78bfa,var(--cli-status-glow, var(--cli-panel-accent, #818cf8)),transparent);background-size:200% 100%;opacity:.4;animation:glowShift 8s ease-in-out infinite;transition:opacity .6s ease;pointer-events:none}.glow-line.glow-active{opacity:.8;animation-duration:3s}.status-indicators-compact{display:none}.terminal-wrapper[data-position=left] .status-indicators-compact,.terminal-wrapper[data-position=right] .status-indicators-compact{display:flex;flex-direction:column;align-items:center;gap:10px;padding:12px 0}.compact-item{display:flex;align-items:center;justify-content:center;width:20px;height:20px;cursor:default}.compact-item.status-clickable{cursor:pointer;border-radius:4px}.compact-item.status-clickable:hover{background-color:var(--cli-btn-hover-bg, rgba(129, 140, 248, .12))}.compact-dot{width:6px;height:6px;border-radius:50%;flex-shrink:0}.compact-dot.level-info{background:var(--cli-panel-accent, #818cf8)}.compact-dot.level-success{background:var(--cli-status-idle, #3fb950)}.compact-dot.level-warn{background:var(--cli-status-running, #f0883e)}.compact-dot.level-error{background:var(--cli-status-error, #f85149)}.terminal-wrapper[data-position=left] .glow-line{inset:0 0 auto auto;width:2px;height:100%;background:linear-gradient(180deg,transparent,var(--cli-status-glow, var(--cli-panel-accent, #818cf8)),#a78bfa,var(--cli-status-glow, var(--cli-panel-accent, #818cf8)),transparent);background-size:100% 200%}.terminal-wrapper[data-position=right] .glow-line{inset:0 auto auto 0;width:2px;height:100%;background:linear-gradient(180deg,transparent,var(--cli-status-glow, var(--cli-panel-accent, #818cf8)),#a78bfa,var(--cli-status-glow, var(--cli-panel-accent, #818cf8)),transparent);background-size:100% 200%}@keyframes glowShift{0%{background-position:200% center}to{background-position:-200% center}}@keyframes statusPulse{0%,to{opacity:1}50%{opacity:.3}}@media(prefers-reduced-motion:reduce){.glow-line,.status-dot{animation:none!important}}\n"] }]
        }], propDecorators: { visible: [{
                type: Input
            }], isCollapsed: [{
                type: Input
            }], isMaximized: [{
                type: Input
            }], position: [{
                type: Input
            }], closable: [{
                type: Input
            }], resizable: [{
                type: Input
            }], hideable: [{
                type: Input
            }], hideAlignment: [{
                type: Input
            }], themeStyles: [{
                type: Input
            }], onToggle: [{
                type: Output
            }], onContentSizeChange: [{
                type: Output
            }], onClose: [{
                type: Output
            }], onHide: [{
                type: Output
            }], onPositionChange: [{
                type: Output
            }], isHidden: [{
                type: Input
            }], statusExecutionState: [{
                type: Input
            }], statusLastCommand: [{
                type: Input
            }], statusServiceCount: [{
                type: Input
            }], statusServiceDetails: [{
                type: Input
            }], statusServerState: [{
                type: Input
            }], statusServerDetails: [{
                type: Input
            }], statusUptime: [{
                type: Input
            }], notification: [{
                type: Input
            }], closeDropdowns: [{
                type: HostListener,
                args: ['document:click']
            }], onMouseMove: [{
                type: HostListener,
                args: ['document:mousemove', ['$event']]
            }], onMouseUp: [{
                type: HostListener,
                args: ['document:mouseup']
            }] } });

/** Slow poll for uptime display only. */
const UPTIME_POLL_MS = 2000;
/** Fast poll to catch execution state changes. */
const EXEC_POLL_MS = 300;
const DEFAULT_TAB_STATUS = {
    executionState: 'idle',
    lastCommandStatus: null,
    lastCommandName: null,
    notification: null,
};
const DEFAULT_GLOBAL_STATUS = {
    runningServiceCount: 0,
    totalServiceCount: 0,
    serviceDetails: [],
    serverConnectionState: 'none',
    serverDetails: [],
    uptime: 0,
};
class CliPanelStatusService {
    constructor() {
        this.tabs = new Map();
        this.globalStatus$ = new BehaviorSubject(DEFAULT_GLOBAL_STATUS);
        this.destroy$ = new Subject();
        /** Push to trigger an immediate global status recalculation. */
        this.globalRefresh$ = new Subject();
        /** Cleanup functions for background-service event listeners. */
        this.bgUnsubscribes = [];
        this.globalPollingStarted = false;
    }
    /**
     * Register an engine for a specific pane within a tab.
     */
    registerEngine(tabId, paneId, engine) {
        let entry = this.tabs.get(tabId);
        if (!entry) {
            entry = {
                engines: new Map(),
                status$: new BehaviorSubject(DEFAULT_TAB_STATUS),
                destroy$: new Subject(),
                refresh$: new Subject(),
            };
            this.tabs.set(tabId, entry);
            this.startPollingTab(entry);
        }
        entry.engines.set(paneId, engine);
        // Listen to notifier changes for immediate tab refresh
        const context = engine.getContext();
        if (context?.notifier?.change$) {
            context.notifier.change$.pipe(takeUntil(entry.destroy$), takeUntil(this.destroy$)).subscribe(() => {
                entry.refresh$.next();
            });
        }
        // Listen to background service events for immediate global refresh
        this.listenToBackgroundServices(engine);
        if (!this.globalPollingStarted) {
            this.globalPollingStarted = true;
            this.startGlobalPolling();
        }
    }
    /**
     * Unregister all engines for a tab (called on tab close).
     */
    unregisterTab(tabId) {
        const entry = this.tabs.get(tabId);
        if (entry) {
            entry.destroy$.next();
            entry.destroy$.complete();
            this.tabs.delete(tabId);
        }
    }
    /**
     * Set the active tab for global status derivation.
     */
    setActiveTab(tabId) {
        this.activeTabId = tabId;
        this.globalRefresh$.next();
    }
    /**
     * Observable of per-tab status.
     */
    getTabStatus$(tabId) {
        const entry = this.tabs.get(tabId);
        return entry ? entry.status$.asObservable() : new BehaviorSubject(DEFAULT_TAB_STATUS).asObservable();
    }
    /**
     * Observable of global status (from active tab).
     */
    getGlobalStatus$() {
        return this.globalStatus$.asObservable();
    }
    /**
     * Clean up all subscriptions.
     */
    destroy() {
        this.destroy$.next();
        this.destroy$.complete();
        for (const entry of this.tabs.values()) {
            entry.destroy$.next();
            entry.destroy$.complete();
        }
        this.tabs.clear();
        for (const unsub of this.bgUnsubscribes) {
            unsub();
        }
        this.bgUnsubscribes.length = 0;
    }
    /**
     * Hook into an engine's background service events so that
     * service start/stop/status-change triggers an immediate refresh.
     */
    listenToBackgroundServices(engine) {
        const context = engine.getContext();
        if (!context?.backgroundServices)
            return;
        try {
            const unsub = context.backgroundServices.on(() => {
                this.globalRefresh$.next();
            });
            this.bgUnsubscribes.push(unsub);
        }
        catch { /* not available */ }
    }
    startPollingTab(entry) {
        merge(interval(EXEC_POLL_MS).pipe(startWith(0)), entry.refresh$).pipe(map(() => this.computeTabStatus(entry)), distinctUntilChanged((a, b) => JSON.stringify(a) === JSON.stringify(b)), takeUntil(entry.destroy$), takeUntil(this.destroy$)).subscribe(status => {
            entry.status$.next(status);
        });
    }
    startGlobalPolling() {
        // Merge: slow timer for uptime ticking + immediate refresh signals
        merge(interval(UPTIME_POLL_MS).pipe(startWith(0)), this.globalRefresh$).pipe(map(() => this.computeGlobalStatus()), distinctUntilChanged((a, b) => JSON.stringify(a) === JSON.stringify(b)), takeUntil(this.destroy$)).subscribe(status => {
            this.globalStatus$.next(status);
        });
    }
    computeTabStatus(entry) {
        let running = false;
        let latestResult;
        let notification = null;
        for (const engine of entry.engines.values()) {
            const context = engine.getContext();
            if (!context)
                continue;
            // Check if a command is currently executing or a context processor
            // (raw/fullscreen mode, e.g. games) is active
            if (context.isExecuting || context.contextProcessor) {
                running = true;
            }
            // Check last command result
            const result = context.lastCommandResult;
            if (result) {
                latestResult = result;
            }
            // Pick up notification from processors
            const notif = context.notifier?.current;
            if (notif) {
                notification = notif;
            }
        }
        return {
            executionState: running ? 'running' : 'idle',
            lastCommandStatus: latestResult ? (latestResult.success ? 'success' : 'error') : null,
            lastCommandName: latestResult?.command ?? null,
            notification,
        };
    }
    computeGlobalStatus() {
        if (this.activeTabId == null)
            return DEFAULT_GLOBAL_STATUS;
        const entry = this.tabs.get(this.activeTabId);
        if (!entry)
            return DEFAULT_GLOBAL_STATUS;
        // Use the first engine in the active tab for global data
        const engine = entry.engines.values().next().value;
        if (!engine)
            return DEFAULT_GLOBAL_STATUS;
        const context = engine.getContext();
        if (!context)
            return DEFAULT_GLOBAL_STATUS;
        // Background services
        let runningServiceCount = 0;
        let totalServiceCount = 0;
        let serviceDetails = [];
        try {
            const services = context.backgroundServices?.list() ?? [];
            totalServiceCount = services.length;
            runningServiceCount = services.filter((s) => s.status === 'running').length;
            serviceDetails = services.map((s) => ({
                name: s.name,
                status: s.status,
                description: s.description,
            }));
        }
        catch { /* not available */ }
        // Server connection
        let serverConnectionState = 'none';
        const serverDetails = [];
        try {
            const serverManager = context.services?.get?.('cli-server-manager');
            if (serverManager?.connections?.size > 0) {
                let anyConnected = false;
                for (const [name, conn] of serverManager.connections) {
                    if (conn.connected) {
                        anyConnected = true;
                    }
                    serverDetails.push({
                        name,
                        url: conn.config?.url ?? '',
                        connected: !!conn.connected,
                        apiVersion: conn.connected ? conn.apiVersion : undefined,
                        commandCount: conn.connected ? conn.commands?.length : undefined,
                    });
                }
                serverConnectionState = anyConnected ? 'connected' : 'disconnected';
            }
            else {
                // Fall back to configured servers from engine options
                const configuredServers = engine.options?.servers;
                if (Array.isArray(configuredServers) && configuredServers.length > 0) {
                    for (const srv of configuredServers) {
                        if (srv.enabled === false)
                            continue;
                        serverDetails.push({
                            name: srv.name,
                            url: srv.url ?? '',
                            connected: false,
                        });
                    }
                    serverConnectionState = 'disconnected';
                }
            }
        }
        catch { /* not available */ }
        // Uptime
        const uptime = engine.startedAt ? Date.now() - engine.startedAt : 0;
        return { runningServiceCount, totalServiceCount, serviceDetails, serverConnectionState, serverDetails, uptime };
    }
}

/**
 * A component that displays the CLI on the bottom of page.
 */
class CliPanelComponent {
    get resolvedCollapsed() {
        return this.collapsed !== undefined ? this.collapsed : this._internalCollapsed;
    }
    get resolvedHidden() {
        return this.hidden !== undefined ? this.hidden : this._internalHidden;
    }
    get resolvedMaximized() {
        return this.maximized !== undefined ? this.maximized : this._internalMaximized;
    }
    get resolvedActiveTabId() {
        return this.activeTabId !== undefined ? this.activeTabId : this._internalActiveTabId;
    }
    get resolvedHeight() {
        return this.height !== undefined ? this.height : this._internalHeight;
    }
    get resolvedWidth() {
        return this.width !== undefined ? this.width : this._internalWidth;
    }
    get resolvedPosition() {
        return this.position !== undefined ? this.position : this.currentPosition;
    }
    static { this.MIN_PANE_WIDTH_PERCENT = 10; }
    constructor(elementRef) {
        this.elementRef = elementRef;
        this.onClose = new EventEmitter();
        this.collapsedChange = new EventEmitter();
        this.hiddenChange = new EventEmitter();
        this.maximizedChange = new EventEmitter();
        this.activeTabIdChange = new EventEmitter();
        this.positionChange = new EventEmitter();
        this.heightChange = new EventEmitter();
        this.widthChange = new EventEmitter();
        // ── Structural events ──
        this.onTabAdded = new EventEmitter();
        this.onTabClosed = new EventEmitter();
        this.onPaneSplit = new EventEmitter();
        this.onPaneClosed = new EventEmitter();
        this.currentPosition = 'bottom';
        this.themeStyles = {};
        this.visible = true;
        this.tabs = [];
        this.tabStatuses = {};
        this.activePaneId = 0;
        this._internalActiveTabId = 0;
        this._internalCollapsed = true;
        this._internalHidden = false;
        this._internalMaximized = false;
        this._internalHeight = 600;
        this._internalWidth = 400;
        this.nextTabId = 1;
        this.nextPaneId = 1;
        this.statusService = new CliPanelStatusService();
        this.destroy$ = new Subject();
        this.globalStatus = {
            runningServiceCount: 0,
            totalServiceCount: 0,
            serviceDetails: [],
            serverConnectionState: 'none',
            serverDetails: [],
            uptime: 0,
        };
        this.contextMenu = {
            visible: false,
            x: 0,
            y: 0,
            tabId: 0,
        };
        this.paneResizing = false;
        this.paneResizeTabId = 0;
        this.paneResizeDividerIndex = 0;
        this.paneResizeStartX = 0;
        this.paneResizeStartWidths = [];
        this.paneResizeContainerWidth = 0;
        this.terminalHeight = '100%';
        this.initialized = false;
    }
    ngOnInit() {
        this.currentPosition = loadPanelPosition() ?? this.options?.position ?? 'bottom';
        if (this.options?.isHidden != null) {
            this._internalHidden = this.options.isHidden;
        }
        if (this.options?.isCollapsed != null) {
            this._internalCollapsed = this.options.isCollapsed;
        }
        this.statusService.getGlobalStatus$().pipe(takeUntil(this.destroy$)).subscribe(status => {
            this.globalStatus = status;
        });
    }
    ngOnDestroy() {
        this.themeObserver?.disconnect();
        this.statusService.destroy();
        this.destroy$.next();
        this.destroy$.complete();
    }
    onDocumentClick(event) {
        if (this.contextMenu.visible) {
            this.closeContextMenu();
        }
    }
    onEscapeKey() {
        if (this.contextMenu.visible) {
            this.closeContextMenu();
        }
        this.cancelAllEditing();
    }
    onPositionChange(position) {
        this.currentPosition = position;
        savePanelPosition(this.currentPosition);
        this.positionChange.emit(position);
    }
    onToggle($event) {
        this._internalCollapsed = $event;
        this.collapsedChange.emit($event);
        if (!$event && !this.initialized) {
            this.initialized = true;
            this.addTab();
            this.setupThemeSync();
        }
    }
    onContentSizeChange(_event) {
        // Terminal height is handled via CSS flex layout
    }
    // --- Tab management ---
    addTab(title) {
        const pane = { id: this.nextPaneId++, widthPercent: 100 };
        const tabId = this.nextTabId++;
        const tab = {
            id: tabId,
            title: title ?? `Terminal ${tabId}`,
            isEditing: false,
            panes: [pane],
        };
        this.tabs.push(tab);
        this._internalActiveTabId = tabId;
        this.activeTabIdChange.emit(tabId);
        this.activePaneId = pane.id;
        this.statusService.setActiveTab(tabId);
        this.onTabAdded.emit({ tabId });
        return tabId;
    }
    closeTab(id) {
        const index = this.tabs.findIndex((t) => t.id === id);
        if (index === -1)
            return;
        this.tabs.splice(index, 1);
        this.onTabClosed.emit({ tabId: id });
        this.statusService.unregisterTab(id);
        delete this.tabStatuses[id];
        if (this.tabs.length === 0) {
            this.initialized = false;
            this.collapsableContent?.toggleTerminal();
            return;
        }
        if (this.resolvedActiveTabId === id) {
            const nextIndex = Math.min(index, this.tabs.length - 1);
            this.selectTab(this.tabs[nextIndex].id);
        }
    }
    selectTab(id) {
        this._internalActiveTabId = id;
        this.activeTabIdChange.emit(id);
        this.statusService.setActiveTab(id);
        const tab = this.findTab(id);
        if (tab && tab.panes.length > 0) {
            this.activePaneId = tab.panes[0].id;
        }
        setTimeout(() => {
            this.focusActiveTerminal();
        });
    }
    trackByTabId(_index, tab) {
        return tab.id;
    }
    getTabDotClass(tabId) {
        const status = this.tabStatuses[tabId];
        if (!status)
            return 'dot-idle';
        if (status.executionState === 'running')
            return 'dot-running';
        if (status.lastCommandStatus === 'error')
            return 'dot-error';
        return 'dot-idle';
    }
    onPaneEngineReady(tabId, paneId, engine) {
        this.statusService.registerEngine(tabId, paneId, engine);
        this.statusService.getTabStatus$(tabId).pipe(takeUntil(this.destroy$)).subscribe(status => {
            this.tabStatuses[tabId] = status;
        });
    }
    trackByPaneId(_index, pane) {
        return pane.id;
    }
    // --- Inline rename ---
    startRename(tab) {
        this.cancelAllEditing();
        tab.isEditing = true;
        setTimeout(() => {
            const input = this.elementRef.nativeElement.querySelector('.tab-rename-input');
            if (input) {
                input.focus();
                input.select();
            }
        });
    }
    onTabDoubleClick(tab) {
        this.startRename(tab);
    }
    commitRename(tab, value) {
        const trimmed = value.trim();
        if (trimmed) {
            tab.title = trimmed;
        }
        tab.isEditing = false;
    }
    onRenameKeydown(event, tab) {
        if (event.key === 'Enter') {
            this.commitRename(tab, event.target.value);
        }
        else if (event.key === 'Escape') {
            tab.isEditing = false;
        }
    }
    onRenameBlur(event, tab) {
        if (tab.isEditing) {
            this.commitRename(tab, event.target.value);
        }
    }
    // --- Context menu ---
    onTabContextMenu(event, tab) {
        event.preventDefault();
        event.stopPropagation();
        this.contextMenu = {
            visible: true,
            x: event.clientX,
            y: event.clientY,
            tabId: tab.id,
        };
    }
    closeContextMenu() {
        this.contextMenu = { ...this.contextMenu, visible: false };
    }
    contextMenuRename() {
        const tab = this.findTab(this.contextMenu.tabId);
        this.closeContextMenu();
        if (tab) {
            this.startRename(tab);
        }
    }
    contextMenuDuplicate() {
        const sourceTab = this.findTab(this.contextMenu.tabId);
        this.closeContextMenu();
        if (sourceTab) {
            const snapshot = this.getEngineForTab(sourceTab)?.snapshot();
            const pane = {
                id: this.nextPaneId++,
                widthPercent: 100,
                snapshot,
            };
            const tab = {
                id: this.nextTabId++,
                title: `${sourceTab.title} (copy)`,
                isEditing: false,
                panes: [pane],
            };
            const sourceIndex = this.tabs.indexOf(sourceTab);
            this.tabs.splice(sourceIndex + 1, 0, tab);
            this._internalActiveTabId = tab.id;
            this.activeTabIdChange.emit(tab.id);
            this.activePaneId = pane.id;
        }
    }
    contextMenuClose() {
        const id = this.contextMenu.tabId;
        this.closeContextMenu();
        this.closeTab(id);
    }
    contextMenuCloseOthers() {
        const id = this.contextMenu.tabId;
        this.closeContextMenu();
        this.tabs = this.tabs.filter((t) => t.id === id);
        this._internalActiveTabId = id;
        this.activeTabIdChange.emit(id);
    }
    contextMenuCloseToTheRight() {
        const id = this.contextMenu.tabId;
        this.closeContextMenu();
        const index = this.tabs.findIndex((t) => t.id === id);
        if (index === -1)
            return;
        this.tabs = this.tabs.slice(0, index + 1);
        if (!this.tabs.find((t) => t.id === this.resolvedActiveTabId)) {
            this._internalActiveTabId = id;
            this.activeTabIdChange.emit(id);
        }
    }
    contextMenuCloseAll() {
        this.closeContextMenu();
        this.tabs = [];
        this.initialized = false;
        this.collapsableContent?.toggleTerminal();
    }
    // --- Split / close pane ---
    splitPane(tabId) {
        const targetTabId = tabId ?? this.resolvedActiveTabId;
        const tab = this.findTab(targetTabId);
        if (!tab)
            return -1;
        const paneId = this.nextPaneId++;
        const newPane = { id: paneId, widthPercent: 0 };
        tab.panes.push(newPane);
        const evenWidth = 100 / tab.panes.length;
        tab.panes.forEach((p) => (p.widthPercent = evenWidth));
        this.normalizePaneWidths(tab.panes);
        this.activePaneId = paneId;
        this.onPaneSplit.emit({ paneId, tabId: targetTabId });
        return paneId;
    }
    closePane(paneId) {
        for (const tab of this.tabs) {
            const idx = tab.panes.findIndex((p) => p.id === paneId);
            if (idx === -1)
                continue;
            if (tab.panes.length <= 1) {
                this.closeTab(tab.id);
                return;
            }
            // Destroy engine at the flat index
            let flatIndex = 0;
            for (const t of this.tabs) {
                for (const p of t.panes) {
                    if (p.id === paneId) {
                        const engine = this.cliComponents?.toArray()[flatIndex]?.getEngine();
                        if (engine)
                            engine.destroy();
                    }
                    flatIndex++;
                }
            }
            tab.panes.splice(idx, 1);
            this.normalizePaneWidths(tab.panes);
            if (this.activePaneId === paneId) {
                this.activePaneId = tab.panes[Math.min(idx, tab.panes.length - 1)].id;
            }
            this.onPaneClosed.emit({ paneId });
            setTimeout(() => this.focusActiveTerminal());
            return;
        }
    }
    contextMenuSplitRight() {
        const tabId = this.contextMenu.tabId;
        this.closeContextMenu();
        this.splitPane(tabId);
    }
    // --- Pane resize ---
    onPaneResizeStart(event, tabId, dividerIndex) {
        event.preventDefault();
        const tab = this.findTab(tabId);
        if (!tab)
            return;
        this.paneResizing = true;
        this.paneResizeTabId = tabId;
        this.paneResizeDividerIndex = dividerIndex;
        this.paneResizeStartX = event.clientX;
        this.paneResizeStartWidths = tab.panes.map((p) => p.widthPercent);
        const container = event.target.closest('.terminal-panes-container');
        this.paneResizeContainerWidth = container ? container.clientWidth : 1;
        document.body.classList.add('cli-pane-resizing');
    }
    onPaneResizeMove(event) {
        if (!this.paneResizing)
            return;
        const tab = this.findTab(this.paneResizeTabId);
        if (!tab)
            return;
        const deltaX = event.clientX - this.paneResizeStartX;
        const deltaPct = (deltaX / this.paneResizeContainerWidth) * 100;
        const i = this.paneResizeDividerIndex;
        const minW = CliPanelComponent.MIN_PANE_WIDTH_PERCENT;
        let leftWidth = this.paneResizeStartWidths[i] + deltaPct;
        let rightWidth = this.paneResizeStartWidths[i + 1] - deltaPct;
        if (leftWidth < minW) {
            leftWidth = minW;
            rightWidth =
                this.paneResizeStartWidths[i] +
                    this.paneResizeStartWidths[i + 1] -
                    minW;
        }
        if (rightWidth < minW) {
            rightWidth = minW;
            leftWidth =
                this.paneResizeStartWidths[i] +
                    this.paneResizeStartWidths[i + 1] -
                    minW;
        }
        tab.panes[i].widthPercent = leftWidth;
        tab.panes[i + 1].widthPercent = rightWidth;
    }
    onPaneResizeEnd() {
        if (!this.paneResizing)
            return;
        this.paneResizing = false;
        document.body.classList.remove('cli-pane-resizing');
    }
    // --- Pane focus ---
    onPaneClick(tabId, paneId) {
        this.activePaneId = paneId;
        if (this.resolvedActiveTabId !== tabId) {
            this._internalActiveTabId = tabId;
            this.activeTabIdChange.emit(tabId);
        }
        setTimeout(() => this.focusPane(tabId, paneId));
    }
    // --- ICliPanelRef methods ---
    open() {
        if (!this.visible) {
            this.visible = true;
        }
        if (this.resolvedHidden) {
            this._internalHidden = false;
            this.hiddenChange.emit(false);
        }
        if (this.resolvedCollapsed) {
            this._internalCollapsed = false;
            this.collapsedChange.emit(false);
            this.collapsableContent?.setCollapsed(false);
            if (!this.initialized) {
                this.initialized = true;
                this.addTab();
                this.setupThemeSync();
            }
        }
    }
    collapse() {
        if (!this.resolvedCollapsed) {
            this._internalCollapsed = true;
            this.collapsedChange.emit(true);
            this.collapsableContent?.setCollapsed(true);
        }
    }
    toggleCollapse() {
        if (this.resolvedCollapsed) {
            this.open();
        }
        else {
            this.collapse();
        }
    }
    hide() {
        if (!this.resolvedHidden) {
            this._internalHidden = true;
            this.hiddenChange.emit(true);
            this.collapsableContent?.hideTerminal();
        }
    }
    unhide() {
        if (this.resolvedHidden) {
            this._internalHidden = false;
            this.hiddenChange.emit(false);
            this.collapsableContent?.unhideTerminal();
        }
    }
    toggleHide() {
        if (this.resolvedHidden) {
            this.unhide();
        }
        else {
            this.hide();
        }
    }
    close() {
        this.visible = false;
        this.onClose.emit();
    }
    maximize() {
        if (!this.resolvedMaximized) {
            this._internalMaximized = true;
            this.maximizedChange.emit(true);
            this.collapsableContent?.setMaximized(true);
        }
    }
    restore() {
        if (this.resolvedMaximized) {
            this._internalMaximized = false;
            this.maximizedChange.emit(false);
            this.collapsableContent?.setMaximized(false);
        }
    }
    toggleMaximize() {
        if (this.resolvedMaximized) {
            this.restore();
        }
        else {
            this.maximize();
        }
    }
    resize(dimensions) {
        if (dimensions.height !== undefined) {
            this._internalHeight = dimensions.height;
            this.heightChange.emit(dimensions.height);
        }
        if (dimensions.width !== undefined) {
            this._internalWidth = dimensions.width;
            this.widthChange.emit(dimensions.width);
        }
        this.collapsableContent?.setDimensions(dimensions);
    }
    setPosition(pos) {
        this.currentPosition = pos;
        savePanelPosition(pos);
        this.positionChange.emit(pos);
    }
    renameTab(tabId, title) {
        const tab = this.findTab(tabId);
        if (tab) {
            tab.title = title;
        }
    }
    getEngine(paneId) {
        const targetPaneId = paneId ?? this.activePaneId;
        let flatIndex = 0;
        for (const tab of this.tabs) {
            for (const pane of tab.panes) {
                if (pane.id === targetPaneId) {
                    const components = this.cliComponents?.toArray();
                    return components?.[flatIndex]?.getEngine();
                }
                flatIndex++;
            }
        }
        return undefined;
    }
    getState() {
        return {
            collapsed: this.resolvedCollapsed,
            hidden: this.resolvedHidden,
            maximized: this.resolvedMaximized,
            position: this.resolvedPosition,
            height: this.resolvedHeight,
            width: this.resolvedWidth,
            activeTabId: this.resolvedActiveTabId,
            activePaneId: this.activePaneId,
            tabs: this.tabs.map((t) => ({
                id: t.id,
                title: t.title,
                panes: t.panes.map((p) => ({ id: p.id, widthPercent: p.widthPercent })),
            })),
        };
    }
    // --- Helpers ---
    findTab(id) {
        return this.tabs.find((t) => t.id === id);
    }
    getEngineForTab(tab) {
        if (!this.cliComponents)
            return undefined;
        let flatIndex = 0;
        for (const t of this.tabs) {
            for (const _pane of t.panes) {
                if (t.id === tab.id) {
                    const component = this.cliComponents.toArray()[flatIndex];
                    return component?.getEngine();
                }
                flatIndex++;
            }
        }
        return undefined;
    }
    cancelAllEditing() {
        this.tabs.forEach((t) => (t.isEditing = false));
    }
    focusActiveTerminal() {
        this.focusPane(this.resolvedActiveTabId, this.activePaneId);
    }
    focusPane(tabId, paneId) {
        if (!this.cliComponents)
            return;
        let flatIndex = 0;
        for (const tab of this.tabs) {
            for (const pane of tab.panes) {
                if (tab.id === tabId && pane.id === paneId) {
                    const component = this.cliComponents.toArray()[flatIndex];
                    component?.focus();
                    return;
                }
                flatIndex++;
            }
        }
    }
    normalizePaneWidths(panes) {
        const total = panes.reduce((s, p) => s + p.widthPercent, 0);
        if (total === 0)
            return;
        const scale = 100 / total;
        panes.forEach((p) => (p.widthPercent = p.widthPercent * scale));
    }
    setupThemeSync() {
        if (!this.options?.syncTheme)
            return;
        // Wait for the first terminal to render, then observe style changes
        setTimeout(() => {
            this.syncThemeFromEngine();
            const container = this.elementRef.nativeElement.querySelector('.terminal-container');
            if (!container)
                return;
            this.themeObserver?.disconnect();
            this.themeObserver = new MutationObserver(() => {
                this.syncThemeFromEngine();
            });
            this.themeObserver.observe(container, {
                attributes: true,
                attributeFilter: ['style'],
            });
        }, 200);
    }
    syncThemeFromEngine() {
        if (!this.cliComponents)
            return;
        const first = this.cliComponents.first;
        if (!first)
            return;
        const engine = first.getEngine();
        if (!engine)
            return;
        const theme = engine.getTerminal().options.theme;
        if (!theme)
            return;
        this.themeStyles = derivePanelThemeStyles(theme);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: CliPanelComponent, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.20", type: CliPanelComponent, isStandalone: false, selector: "cli-panel", inputs: { options: "options", modules: "modules", processors: "processors", collapsed: "collapsed", hidden: "hidden", maximized: "maximized", activeTabId: "activeTabId", position: "position", height: "height", width: "width" }, outputs: { onClose: "onClose", collapsedChange: "collapsedChange", hiddenChange: "hiddenChange", maximizedChange: "maximizedChange", activeTabIdChange: "activeTabIdChange", positionChange: "positionChange", heightChange: "heightChange", widthChange: "widthChange", onTabAdded: "onTabAdded", onTabClosed: "onTabClosed", onPaneSplit: "onPaneSplit", onPaneClosed: "onPaneClosed" }, host: { listeners: { "document:click": "onDocumentClick($event)", "document:keydown.escape": "onEscapeKey()", "document:mousemove": "onPaneResizeMove($event)", "document:mouseup": "onPaneResizeEnd()" } }, viewQueries: [{ propertyName: "collapsableContent", first: true, predicate: CollapsableContentComponent, descendants: true }, { propertyName: "cliComponents", predicate: CliComponent, descendants: true }], ngImport: i0, template: "<collapsable-content\n  [isCollapsed]=\"resolvedCollapsed\"\n  [isHidden]=\"resolvedHidden\"\n  [isMaximized]=\"resolvedMaximized\"\n  [position]=\"resolvedPosition\"\n  [closable]=\"options?.closable ?? true\"\n  [resizable]=\"options?.resizable ?? true\"\n  [hideable]=\"options?.hideable ?? true\"\n  [hideAlignment]=\"options?.hideAlignment ?? 'center'\"\n  [themeStyles]=\"themeStyles\"\n  [statusExecutionState]=\"tabStatuses[resolvedActiveTabId]?.executionState || 'idle'\"\n  [statusLastCommand]=\"tabStatuses[resolvedActiveTabId]?.lastCommandStatus ? { name: tabStatuses[resolvedActiveTabId]?.lastCommandName || '', success: tabStatuses[resolvedActiveTabId]?.lastCommandStatus === 'success' } : null\"\n  [statusServiceCount]=\"{ running: globalStatus.runningServiceCount, total: globalStatus.totalServiceCount }\"\n  [statusServiceDetails]=\"globalStatus.serviceDetails\"\n  [statusServerState]=\"globalStatus.serverConnectionState\"\n  [statusServerDetails]=\"globalStatus.serverDetails\"\n  [statusUptime]=\"globalStatus.uptime\"\n  [notification]=\"tabStatuses[resolvedActiveTabId]?.notification || null\"\n  (onToggle)=\"onToggle($event)\"\n  (onContentSizeChange)=\"onContentSizeChange($event)\"\n  (onPositionChange)=\"onPositionChange($event)\"\n  (onClose)=\"onClose.emit()\"\n  *ngIf=\"visible\"\n>\n  <ng-container *ngIf=\"initialized\">\n    <div class=\"terminal-tabs\">\n      <ul class=\"tab-list\">\n        <li\n          *ngFor=\"let tab of tabs; trackBy: trackByTabId\"\n          class=\"tab\"\n          [class.active]=\"tab.id === resolvedActiveTabId\"\n          (click)=\"selectTab(tab.id)\"\n          (dblclick)=\"onTabDoubleClick(tab)\"\n          (contextmenu)=\"onTabContextMenu($event, tab)\"\n        >\n          <input\n            *ngIf=\"tab.isEditing\"\n            class=\"tab-rename-input\"\n            type=\"text\"\n            [value]=\"tab.title\"\n            (keydown)=\"onRenameKeydown($event, tab)\"\n            (blur)=\"onRenameBlur($event, tab)\"\n            (click)=\"$event.stopPropagation()\"\n            (dblclick)=\"$event.stopPropagation()\"\n            #renameInput\n          />\n          <span *ngIf=\"!tab.isEditing\" class=\"tab-dot\" [ngClass]=\"getTabDotClass(tab.id)\"></span>\n          <span *ngIf=\"!tab.isEditing\" class=\"tab-title\">{{ tab.title }}</span>\n          <button\n            *ngIf=\"!tab.isEditing\"\n            class=\"close-btn\"\n            title=\"Close tab\"\n            (click)=\"closeTab(tab.id); $event.stopPropagation()\"\n          >\n            &times;\n          </button>\n        </li>\n      </ul>\n      <button class=\"add-tab\" title=\"New terminal\" (click)=\"addTab()\">+</button>\n    </div>\n    <div class=\"terminal-instances\">\n      <div\n        *ngFor=\"let tab of tabs; trackBy: trackByTabId\"\n        class=\"terminal-instance\"\n        [hidden]=\"tab.id !== resolvedActiveTabId\"\n      >\n        <div\n          class=\"terminal-panes-container\"\n          [attr.data-tab-id]=\"tab.id\"\n          [class.resizing]=\"paneResizing\"\n        >\n          <ng-container\n            *ngFor=\"\n              let pane of tab.panes;\n              let i = index;\n              trackBy: trackByPaneId\n            \"\n          >\n            <div\n              *ngIf=\"i > 0\"\n              class=\"pane-divider\"\n              (mousedown)=\"onPaneResizeStart($event, tab.id, i - 1)\"\n            >\n              <div class=\"pane-divider-grip\"></div>\n            </div>\n            <div\n              class=\"terminal-pane\"\n              [class.active-pane]=\"\n                pane.id === activePaneId && tab.id === resolvedActiveTabId\n              \"\n              [style.flex]=\"pane.widthPercent + ' 1 0'\"\n              (click)=\"onPaneClick(tab.id, pane.id)\"\n            >\n              <button\n                *ngIf=\"tab.panes.length > 1\"\n                class=\"pane-close-btn\"\n                (click)=\"closePane(pane.id); $event.stopPropagation()\"\n                title=\"Close pane\"\n              >\n                &times;\n              </button>\n              <cli\n                [options]=\"options\"\n                [height]=\"terminalHeight\"\n                [modules]=\"modules\"\n                [processors]=\"processors\"\n                [snapshot]=\"pane.snapshot\"\n                (engineReady)=\"onPaneEngineReady(tab.id, pane.id, $event)\"\n              />\n            </div>\n          </ng-container>\n        </div>\n      </div>\n    </div>\n  </ng-container>\n</collapsable-content>\n\n<!-- Context menu rendered outside collapsable-content to escape transform/overflow clipping -->\n<div\n  class=\"tab-context-menu\"\n  *ngIf=\"contextMenu.visible\"\n  [style.left.px]=\"contextMenu.x\"\n  [style.top.px]=\"contextMenu.y\"\n  (click)=\"$event.stopPropagation()\"\n>\n  <button class=\"context-menu-item\" (click)=\"contextMenuRename()\">\n    Rename\n  </button>\n  <button class=\"context-menu-item\" (click)=\"contextMenuDuplicate()\">\n    Duplicate\n  </button>\n  <button class=\"context-menu-item\" (click)=\"contextMenuSplitRight()\">\n    Split Right\n  </button>\n  <div class=\"context-menu-separator\"></div>\n  <button class=\"context-menu-item\" (click)=\"contextMenuClose()\">Close</button>\n  <button\n    class=\"context-menu-item\"\n    (click)=\"contextMenuCloseOthers()\"\n    [disabled]=\"tabs.length <= 1\"\n  >\n    Close Others\n  </button>\n  <button class=\"context-menu-item\" (click)=\"contextMenuCloseToTheRight()\">\n    Close to the Right\n  </button>\n  <div class=\"context-menu-separator\"></div>\n  <button class=\"context-menu-item destructive\" (click)=\"contextMenuCloseAll()\">\n    Close All\n  </button>\n</div>\n", styles: [".terminal-tabs{background-color:var(--cli-panel-header-bg, #1f2937);display:flex;align-items:center;height:38px;padding:0 8px;gap:4px;border-bottom:1px solid var(--cli-panel-border, #374151);overflow-x:auto;overflow-y:hidden;flex-shrink:0;scrollbar-width:thin;scrollbar-color:var(--cli-panel-text-secondary, rgba(255, 255, 255, .3)) transparent}.terminal-tabs::-webkit-scrollbar{height:2px}.terminal-tabs::-webkit-scrollbar-thumb{background:var(--cli-panel-text-secondary, rgba(255, 255, 255, .3));border-radius:1px}.terminal-tabs .tab-list{list-style:none;margin:0;padding:0;display:flex;gap:2px;align-items:stretch;height:100%}.terminal-tabs .tab{display:flex;align-items:center;gap:6px;padding:0 12px;cursor:pointer;background:transparent;border:none;border-top:2px solid transparent;color:var(--cli-panel-text-secondary, rgba(255, 255, 255, .6));font-size:.8rem;font-family:inherit;white-space:nowrap;transition:background-color .15s ease,color .15s ease,border-color .15s ease;height:100%}.terminal-tabs .tab:hover:not(.active){background-color:var(--cli-btn-hover-bg, rgba(129, 140, 248, .12));color:var(--cli-panel-text, rgba(255, 255, 255, .87))}.terminal-tabs .tab.active{background-color:var(--cli-panel-bg, #111827);color:var(--cli-panel-text, rgba(255, 255, 255, .87));border-top-color:var(--cli-panel-accent, #818cf8)}.terminal-tabs .tab .tab-title{pointer-events:none}.terminal-tabs .tab .tab-rename-input{appearance:none;background:var(--cli-panel-bg, #111827);border:1px solid var(--cli-panel-accent, #818cf8);color:var(--cli-panel-text, rgba(255, 255, 255, .87));font-size:.8rem;font-family:inherit;padding:2px 6px;border-radius:3px;outline:none;width:120px;height:22px}.terminal-tabs .tab .close-btn{appearance:none;background:none;border:none;color:var(--cli-panel-text-secondary, rgba(255, 255, 255, .4));cursor:pointer;font-size:1rem;line-height:1;padding:0 2px;border-radius:3px;transition:background-color .15s ease,color .15s ease}.terminal-tabs .tab .close-btn:hover{color:var(--cli-panel-text, rgba(255, 255, 255, .87));background-color:#ffffff1a}.terminal-tabs .add-tab{appearance:none;background:transparent;border:1px dashed var(--cli-panel-text-secondary, rgba(255, 255, 255, .3));color:var(--cli-panel-text-secondary, rgba(255, 255, 255, .5));cursor:pointer;font-size:1rem;line-height:1;width:28px;height:24px;border-radius:4px;display:flex;align-items:center;justify-content:center;flex-shrink:0;transition:background-color .15s ease,color .15s ease,border-color .15s ease}.terminal-tabs .add-tab:hover{background-color:var(--cli-btn-hover-bg, rgba(129, 140, 248, .12));color:var(--cli-panel-text, rgba(255, 255, 255, .87));border-color:var(--cli-panel-text, rgba(255, 255, 255, .5))}.tab-context-menu{position:fixed;z-index:1100;background-color:var(--cli-panel-header-bg, #1f2937);border:1px solid var(--cli-panel-border, #374151);border-radius:6px;padding:4px 0;min-width:180px;box-shadow:0 4px 16px #0006;font-family:inherit}.tab-context-menu .context-menu-item{appearance:none;background:transparent;border:none;color:var(--cli-panel-text, rgba(255, 255, 255, .87));cursor:pointer;display:block;width:100%;text-align:left;padding:6px 14px;font-size:.82rem;font-family:inherit;transition:background-color .1s ease}.tab-context-menu .context-menu-item:hover:not(:disabled){background-color:var(--cli-btn-hover-bg, rgba(129, 140, 248, .12))}.tab-context-menu .context-menu-item:disabled{color:var(--cli-panel-text-secondary, rgba(255, 255, 255, .3));cursor:not-allowed}.tab-context-menu .context-menu-item.destructive{color:#f87171}.tab-context-menu .context-menu-item.destructive:hover:not(:disabled){background-color:#f871711f}.tab-context-menu .context-menu-separator{height:1px;background-color:var(--cli-panel-border, #374151);margin:4px 0}.terminal-instances{flex:1;min-height:0;overflow:hidden}.terminal-instance{height:100%}.terminal-panes-container{display:flex;height:100%}.terminal-panes-container.resizing{-webkit-user-select:none;user-select:none;cursor:ew-resize}.terminal-pane{position:relative;height:100%;min-width:0;overflow:hidden}.terminal-pane cli{display:block;height:100%}.terminal-pane.active-pane{outline:1px solid var(--cli-panel-accent, #818cf8);outline-offset:-1px}.terminal-pane:hover .pane-close-btn{opacity:1}.pane-close-btn{appearance:none;position:absolute;top:4px;right:4px;z-index:10;background:var(--cli-panel-header-bg, #1f2937);border:1px solid var(--cli-panel-border, #374151);color:var(--cli-panel-text-secondary, rgba(255, 255, 255, .6));cursor:pointer;font-size:.9rem;line-height:1;width:22px;height:22px;border-radius:3px;display:flex;align-items:center;justify-content:center;opacity:0;transition:opacity .15s ease,background-color .15s ease,color .15s ease}.pane-close-btn:hover{color:var(--cli-panel-text, rgba(255, 255, 255, .87));background-color:#ffffff1a}.pane-divider{width:6px;cursor:ew-resize;display:flex;align-items:center;justify-content:center;flex-shrink:0;background:transparent;transition:background-color .15s ease}.pane-divider:hover{background-color:var(--cli-panel-accent, #818cf8)}.pane-divider .pane-divider-grip{width:2px;height:24px;border-radius:1px;background-color:var(--cli-panel-text-secondary, rgba(255, 255, 255, .3));transition:background-color .15s ease}.pane-divider:hover .pane-divider-grip{background-color:var(--cli-panel-text, rgba(255, 255, 255, .87))}.tab-dot{width:7px;height:7px;border-radius:50%;flex-shrink:0;background:var(--cli-status-neutral, #6b7280)}.tab-dot.dot-idle{background:var(--cli-status-idle, #3fb950)}.tab-dot.dot-running{background:var(--cli-status-running, #f0883e);animation:statusPulse 1.5s ease-in-out infinite}.tab-dot.dot-error{background:var(--cli-status-error, #f85149)}.tab-dot.dot-neutral{background:var(--cli-status-neutral, #6b7280)}@keyframes statusPulse{0%,to{opacity:1}50%{opacity:.3}}@media(prefers-reduced-motion:reduce){.tab-dot{animation:none!important}}\n"], dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: CliComponent, selector: "cli", inputs: ["options", "processors", "modules", "height", "snapshot"], outputs: ["engineReady"] }, { kind: "component", type: CollapsableContentComponent, selector: "collapsable-content", inputs: ["visible", "isCollapsed", "isMaximized", "position", "closable", "resizable", "hideable", "hideAlignment", "themeStyles", "isHidden", "statusExecutionState", "statusLastCommand", "statusServiceCount", "statusServiceDetails", "statusServerState", "statusServerDetails", "statusUptime", "notification"], outputs: ["onToggle", "onContentSizeChange", "onClose", "onHide", "onPositionChange"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: CliPanelComponent, decorators: [{
            type: Component,
            args: [{ standalone: false, selector: 'cli-panel', template: "<collapsable-content\n  [isCollapsed]=\"resolvedCollapsed\"\n  [isHidden]=\"resolvedHidden\"\n  [isMaximized]=\"resolvedMaximized\"\n  [position]=\"resolvedPosition\"\n  [closable]=\"options?.closable ?? true\"\n  [resizable]=\"options?.resizable ?? true\"\n  [hideable]=\"options?.hideable ?? true\"\n  [hideAlignment]=\"options?.hideAlignment ?? 'center'\"\n  [themeStyles]=\"themeStyles\"\n  [statusExecutionState]=\"tabStatuses[resolvedActiveTabId]?.executionState || 'idle'\"\n  [statusLastCommand]=\"tabStatuses[resolvedActiveTabId]?.lastCommandStatus ? { name: tabStatuses[resolvedActiveTabId]?.lastCommandName || '', success: tabStatuses[resolvedActiveTabId]?.lastCommandStatus === 'success' } : null\"\n  [statusServiceCount]=\"{ running: globalStatus.runningServiceCount, total: globalStatus.totalServiceCount }\"\n  [statusServiceDetails]=\"globalStatus.serviceDetails\"\n  [statusServerState]=\"globalStatus.serverConnectionState\"\n  [statusServerDetails]=\"globalStatus.serverDetails\"\n  [statusUptime]=\"globalStatus.uptime\"\n  [notification]=\"tabStatuses[resolvedActiveTabId]?.notification || null\"\n  (onToggle)=\"onToggle($event)\"\n  (onContentSizeChange)=\"onContentSizeChange($event)\"\n  (onPositionChange)=\"onPositionChange($event)\"\n  (onClose)=\"onClose.emit()\"\n  *ngIf=\"visible\"\n>\n  <ng-container *ngIf=\"initialized\">\n    <div class=\"terminal-tabs\">\n      <ul class=\"tab-list\">\n        <li\n          *ngFor=\"let tab of tabs; trackBy: trackByTabId\"\n          class=\"tab\"\n          [class.active]=\"tab.id === resolvedActiveTabId\"\n          (click)=\"selectTab(tab.id)\"\n          (dblclick)=\"onTabDoubleClick(tab)\"\n          (contextmenu)=\"onTabContextMenu($event, tab)\"\n        >\n          <input\n            *ngIf=\"tab.isEditing\"\n            class=\"tab-rename-input\"\n            type=\"text\"\n            [value]=\"tab.title\"\n            (keydown)=\"onRenameKeydown($event, tab)\"\n            (blur)=\"onRenameBlur($event, tab)\"\n            (click)=\"$event.stopPropagation()\"\n            (dblclick)=\"$event.stopPropagation()\"\n            #renameInput\n          />\n          <span *ngIf=\"!tab.isEditing\" class=\"tab-dot\" [ngClass]=\"getTabDotClass(tab.id)\"></span>\n          <span *ngIf=\"!tab.isEditing\" class=\"tab-title\">{{ tab.title }}</span>\n          <button\n            *ngIf=\"!tab.isEditing\"\n            class=\"close-btn\"\n            title=\"Close tab\"\n            (click)=\"closeTab(tab.id); $event.stopPropagation()\"\n          >\n            &times;\n          </button>\n        </li>\n      </ul>\n      <button class=\"add-tab\" title=\"New terminal\" (click)=\"addTab()\">+</button>\n    </div>\n    <div class=\"terminal-instances\">\n      <div\n        *ngFor=\"let tab of tabs; trackBy: trackByTabId\"\n        class=\"terminal-instance\"\n        [hidden]=\"tab.id !== resolvedActiveTabId\"\n      >\n        <div\n          class=\"terminal-panes-container\"\n          [attr.data-tab-id]=\"tab.id\"\n          [class.resizing]=\"paneResizing\"\n        >\n          <ng-container\n            *ngFor=\"\n              let pane of tab.panes;\n              let i = index;\n              trackBy: trackByPaneId\n            \"\n          >\n            <div\n              *ngIf=\"i > 0\"\n              class=\"pane-divider\"\n              (mousedown)=\"onPaneResizeStart($event, tab.id, i - 1)\"\n            >\n              <div class=\"pane-divider-grip\"></div>\n            </div>\n            <div\n              class=\"terminal-pane\"\n              [class.active-pane]=\"\n                pane.id === activePaneId && tab.id === resolvedActiveTabId\n              \"\n              [style.flex]=\"pane.widthPercent + ' 1 0'\"\n              (click)=\"onPaneClick(tab.id, pane.id)\"\n            >\n              <button\n                *ngIf=\"tab.panes.length > 1\"\n                class=\"pane-close-btn\"\n                (click)=\"closePane(pane.id); $event.stopPropagation()\"\n                title=\"Close pane\"\n              >\n                &times;\n              </button>\n              <cli\n                [options]=\"options\"\n                [height]=\"terminalHeight\"\n                [modules]=\"modules\"\n                [processors]=\"processors\"\n                [snapshot]=\"pane.snapshot\"\n                (engineReady)=\"onPaneEngineReady(tab.id, pane.id, $event)\"\n              />\n            </div>\n          </ng-container>\n        </div>\n      </div>\n    </div>\n  </ng-container>\n</collapsable-content>\n\n<!-- Context menu rendered outside collapsable-content to escape transform/overflow clipping -->\n<div\n  class=\"tab-context-menu\"\n  *ngIf=\"contextMenu.visible\"\n  [style.left.px]=\"contextMenu.x\"\n  [style.top.px]=\"contextMenu.y\"\n  (click)=\"$event.stopPropagation()\"\n>\n  <button class=\"context-menu-item\" (click)=\"contextMenuRename()\">\n    Rename\n  </button>\n  <button class=\"context-menu-item\" (click)=\"contextMenuDuplicate()\">\n    Duplicate\n  </button>\n  <button class=\"context-menu-item\" (click)=\"contextMenuSplitRight()\">\n    Split Right\n  </button>\n  <div class=\"context-menu-separator\"></div>\n  <button class=\"context-menu-item\" (click)=\"contextMenuClose()\">Close</button>\n  <button\n    class=\"context-menu-item\"\n    (click)=\"contextMenuCloseOthers()\"\n    [disabled]=\"tabs.length <= 1\"\n  >\n    Close Others\n  </button>\n  <button class=\"context-menu-item\" (click)=\"contextMenuCloseToTheRight()\">\n    Close to the Right\n  </button>\n  <div class=\"context-menu-separator\"></div>\n  <button class=\"context-menu-item destructive\" (click)=\"contextMenuCloseAll()\">\n    Close All\n  </button>\n</div>\n", styles: [".terminal-tabs{background-color:var(--cli-panel-header-bg, #1f2937);display:flex;align-items:center;height:38px;padding:0 8px;gap:4px;border-bottom:1px solid var(--cli-panel-border, #374151);overflow-x:auto;overflow-y:hidden;flex-shrink:0;scrollbar-width:thin;scrollbar-color:var(--cli-panel-text-secondary, rgba(255, 255, 255, .3)) transparent}.terminal-tabs::-webkit-scrollbar{height:2px}.terminal-tabs::-webkit-scrollbar-thumb{background:var(--cli-panel-text-secondary, rgba(255, 255, 255, .3));border-radius:1px}.terminal-tabs .tab-list{list-style:none;margin:0;padding:0;display:flex;gap:2px;align-items:stretch;height:100%}.terminal-tabs .tab{display:flex;align-items:center;gap:6px;padding:0 12px;cursor:pointer;background:transparent;border:none;border-top:2px solid transparent;color:var(--cli-panel-text-secondary, rgba(255, 255, 255, .6));font-size:.8rem;font-family:inherit;white-space:nowrap;transition:background-color .15s ease,color .15s ease,border-color .15s ease;height:100%}.terminal-tabs .tab:hover:not(.active){background-color:var(--cli-btn-hover-bg, rgba(129, 140, 248, .12));color:var(--cli-panel-text, rgba(255, 255, 255, .87))}.terminal-tabs .tab.active{background-color:var(--cli-panel-bg, #111827);color:var(--cli-panel-text, rgba(255, 255, 255, .87));border-top-color:var(--cli-panel-accent, #818cf8)}.terminal-tabs .tab .tab-title{pointer-events:none}.terminal-tabs .tab .tab-rename-input{appearance:none;background:var(--cli-panel-bg, #111827);border:1px solid var(--cli-panel-accent, #818cf8);color:var(--cli-panel-text, rgba(255, 255, 255, .87));font-size:.8rem;font-family:inherit;padding:2px 6px;border-radius:3px;outline:none;width:120px;height:22px}.terminal-tabs .tab .close-btn{appearance:none;background:none;border:none;color:var(--cli-panel-text-secondary, rgba(255, 255, 255, .4));cursor:pointer;font-size:1rem;line-height:1;padding:0 2px;border-radius:3px;transition:background-color .15s ease,color .15s ease}.terminal-tabs .tab .close-btn:hover{color:var(--cli-panel-text, rgba(255, 255, 255, .87));background-color:#ffffff1a}.terminal-tabs .add-tab{appearance:none;background:transparent;border:1px dashed var(--cli-panel-text-secondary, rgba(255, 255, 255, .3));color:var(--cli-panel-text-secondary, rgba(255, 255, 255, .5));cursor:pointer;font-size:1rem;line-height:1;width:28px;height:24px;border-radius:4px;display:flex;align-items:center;justify-content:center;flex-shrink:0;transition:background-color .15s ease,color .15s ease,border-color .15s ease}.terminal-tabs .add-tab:hover{background-color:var(--cli-btn-hover-bg, rgba(129, 140, 248, .12));color:var(--cli-panel-text, rgba(255, 255, 255, .87));border-color:var(--cli-panel-text, rgba(255, 255, 255, .5))}.tab-context-menu{position:fixed;z-index:1100;background-color:var(--cli-panel-header-bg, #1f2937);border:1px solid var(--cli-panel-border, #374151);border-radius:6px;padding:4px 0;min-width:180px;box-shadow:0 4px 16px #0006;font-family:inherit}.tab-context-menu .context-menu-item{appearance:none;background:transparent;border:none;color:var(--cli-panel-text, rgba(255, 255, 255, .87));cursor:pointer;display:block;width:100%;text-align:left;padding:6px 14px;font-size:.82rem;font-family:inherit;transition:background-color .1s ease}.tab-context-menu .context-menu-item:hover:not(:disabled){background-color:var(--cli-btn-hover-bg, rgba(129, 140, 248, .12))}.tab-context-menu .context-menu-item:disabled{color:var(--cli-panel-text-secondary, rgba(255, 255, 255, .3));cursor:not-allowed}.tab-context-menu .context-menu-item.destructive{color:#f87171}.tab-context-menu .context-menu-item.destructive:hover:not(:disabled){background-color:#f871711f}.tab-context-menu .context-menu-separator{height:1px;background-color:var(--cli-panel-border, #374151);margin:4px 0}.terminal-instances{flex:1;min-height:0;overflow:hidden}.terminal-instance{height:100%}.terminal-panes-container{display:flex;height:100%}.terminal-panes-container.resizing{-webkit-user-select:none;user-select:none;cursor:ew-resize}.terminal-pane{position:relative;height:100%;min-width:0;overflow:hidden}.terminal-pane cli{display:block;height:100%}.terminal-pane.active-pane{outline:1px solid var(--cli-panel-accent, #818cf8);outline-offset:-1px}.terminal-pane:hover .pane-close-btn{opacity:1}.pane-close-btn{appearance:none;position:absolute;top:4px;right:4px;z-index:10;background:var(--cli-panel-header-bg, #1f2937);border:1px solid var(--cli-panel-border, #374151);color:var(--cli-panel-text-secondary, rgba(255, 255, 255, .6));cursor:pointer;font-size:.9rem;line-height:1;width:22px;height:22px;border-radius:3px;display:flex;align-items:center;justify-content:center;opacity:0;transition:opacity .15s ease,background-color .15s ease,color .15s ease}.pane-close-btn:hover{color:var(--cli-panel-text, rgba(255, 255, 255, .87));background-color:#ffffff1a}.pane-divider{width:6px;cursor:ew-resize;display:flex;align-items:center;justify-content:center;flex-shrink:0;background:transparent;transition:background-color .15s ease}.pane-divider:hover{background-color:var(--cli-panel-accent, #818cf8)}.pane-divider .pane-divider-grip{width:2px;height:24px;border-radius:1px;background-color:var(--cli-panel-text-secondary, rgba(255, 255, 255, .3));transition:background-color .15s ease}.pane-divider:hover .pane-divider-grip{background-color:var(--cli-panel-text, rgba(255, 255, 255, .87))}.tab-dot{width:7px;height:7px;border-radius:50%;flex-shrink:0;background:var(--cli-status-neutral, #6b7280)}.tab-dot.dot-idle{background:var(--cli-status-idle, #3fb950)}.tab-dot.dot-running{background:var(--cli-status-running, #f0883e);animation:statusPulse 1.5s ease-in-out infinite}.tab-dot.dot-error{background:var(--cli-status-error, #f85149)}.tab-dot.dot-neutral{background:var(--cli-status-neutral, #6b7280)}@keyframes statusPulse{0%,to{opacity:1}50%{opacity:.3}}@media(prefers-reduced-motion:reduce){.tab-dot{animation:none!important}}\n"] }]
        }], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { options: [{
                type: Input
            }], modules: [{
                type: Input
            }], processors: [{
                type: Input
            }], onClose: [{
                type: Output
            }], collapsed: [{
                type: Input
            }], collapsedChange: [{
                type: Output
            }], hidden: [{
                type: Input
            }], hiddenChange: [{
                type: Output
            }], maximized: [{
                type: Input
            }], maximizedChange: [{
                type: Output
            }], activeTabId: [{
                type: Input
            }], activeTabIdChange: [{
                type: Output
            }], position: [{
                type: Input
            }], positionChange: [{
                type: Output
            }], height: [{
                type: Input
            }], heightChange: [{
                type: Output
            }], width: [{
                type: Input
            }], widthChange: [{
                type: Output
            }], onTabAdded: [{
                type: Output
            }], onTabClosed: [{
                type: Output
            }], onPaneSplit: [{
                type: Output
            }], onPaneClosed: [{
                type: Output
            }], collapsableContent: [{
                type: ViewChild,
                args: [CollapsableContentComponent]
            }], cliComponents: [{
                type: ViewChildren,
                args: [CliComponent]
            }], onDocumentClick: [{
                type: HostListener,
                args: ['document:click', ['$event']]
            }], onEscapeKey: [{
                type: HostListener,
                args: ['document:keydown.escape']
            }], onPaneResizeMove: [{
                type: HostListener,
                args: ['document:mousemove', ['$event']]
            }], onPaneResizeEnd: [{
                type: HostListener,
                args: ['document:mouseup']
            }] } });

class CliDefaultPingServerService {
    ping() {
        // Simulate a server ping
        return new Promise((resolve) => setTimeout(resolve, 2000));
    }
}

/**
 * Angular DI providers for services that the framework-agnostic
 * processors in @qodalis/cli need. The CliComponent bridges these
 * into the engine's service container automatically.
 */
const resolveCliProviders = () => {
    return [
        {
            useClass: CliDefaultPingServerService,
            provide: ICliPingServerService_TOKEN,
        },
    ];
};

class CliModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: CliModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.20", ngImport: i0, type: CliModule, declarations: [CliComponent,
            CollapsableContentComponent,
            CliPanelComponent], imports: [CommonModule], exports: [CliPanelComponent, CliComponent] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: CliModule, providers: [resolveCliProviders()], imports: [CommonModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.20", ngImport: i0, type: CliModule, decorators: [{
            type: NgModule,
            args: [{
                    declarations: [
                        CliComponent,
                        CollapsableContentComponent,
                        CliPanelComponent,
                    ],
                    imports: [CommonModule],
                    providers: [resolveCliProviders()],
                    exports: [CliPanelComponent, CliComponent],
                }]
        }] });

const resolveCliProvider = (token, provider) => ({
    provide: token,
    useExisting: provider,
    multi: true,
});
/**
 * @deprecated Use resolveCliModuleProvider() instead.
 */
const resolveCommandProcessorProvider = (provider) => [
    provider,
    resolveCliProvider(CliCommandProcessor_TOKEN, provider),
];
/**
 * Resolve an ICliModule into Angular providers.
 * Registers the module via CliModule_TOKEN and also registers processors
 * individually via CliCommandProcessor_TOKEN for backward compatibility.
 */
const resolveCliModuleProvider = (module) => {
    const providers = [
        {
            provide: CliModule_TOKEN,
            useValue: module,
            multi: true,
        },
    ];
    if (module.processors) {
        for (const processor of module.processors) {
            providers.push({
                provide: CliCommandProcessor_TOKEN,
                useValue: processor,
                multi: true,
            });
        }
    }
    return providers;
};

// Re-exports from @qodalis/cli for backwards compatibility

/*
 * Public API Surface of @qodalis/angular-cli
 */

/**
 * Generated bundle index. Do not edit.
 */

export { CliCommandProcessor_TOKEN, CliComponent, CliDefaultPingServerService, CliModule, CliModule_TOKEN, CliPanelComponent, ICliPingServerService_TOKEN, ICliUserSessionService_TOKEN, ICliUsersStoreService_TOKEN, resolveCliModuleProvider, resolveCliProvider, resolveCliProviders, resolveCommandProcessorProvider };
//# sourceMappingURL=qodalis-angular-cli.mjs.map