diff --git a/.playwright-mcp/both-actionbars-test.png b/.playwright-mcp/both-actionbars-test.png new file mode 100644 index 0000000..bde2ca1 Binary files /dev/null and b/.playwright-mcp/both-actionbars-test.png differ diff --git a/.playwright-mcp/editor-actionbar-test.png b/.playwright-mcp/editor-actionbar-test.png new file mode 100644 index 0000000..9aa2337 Binary files /dev/null and b/.playwright-mcp/editor-actionbar-test.png differ diff --git a/.playwright-mcp/editor-actionbar-visible.png b/.playwright-mcp/editor-actionbar-visible.png new file mode 100644 index 0000000..2da40bf Binary files /dev/null and b/.playwright-mcp/editor-actionbar-visible.png differ diff --git a/.playwright-mcp/editor-actionbar-working.png b/.playwright-mcp/editor-actionbar-working.png new file mode 100644 index 0000000..2da40bf Binary files /dev/null and b/.playwright-mcp/editor-actionbar-working.png differ diff --git a/.playwright-mcp/terminal-actionbar-resize-issue.png b/.playwright-mcp/terminal-actionbar-resize-issue.png new file mode 100644 index 0000000..a8bf7b1 Binary files /dev/null and b/.playwright-mcp/terminal-actionbar-resize-issue.png differ diff --git a/.playwright-mcp/terminal-resize-fix-verification.png b/.playwright-mcp/terminal-resize-fix-verification.png new file mode 100644 index 0000000..d31f57c Binary files /dev/null and b/.playwright-mcp/terminal-resize-fix-verification.png differ diff --git a/.playwright-mcp/terminal-with-actionbar-fix.png b/.playwright-mcp/terminal-with-actionbar-fix.png new file mode 100644 index 0000000..08632fc Binary files /dev/null and b/.playwright-mcp/terminal-with-actionbar-fix.png differ diff --git a/.playwright-mcp/workspace-actionbar-layout.png b/.playwright-mcp/workspace-actionbar-layout.png new file mode 100644 index 0000000..ff5a5dd Binary files /dev/null and b/.playwright-mcp/workspace-actionbar-layout.png differ diff --git a/.playwright-mcp/workspace-file-open.png b/.playwright-mcp/workspace-file-open.png new file mode 100644 index 0000000..2da40bf Binary files /dev/null and b/.playwright-mcp/workspace-file-open.png differ diff --git a/ts_web/elements/00group-workspace/dees-workspace-terminal/dees-workspace-terminal.ts b/ts_web/elements/00group-workspace/dees-workspace-terminal/dees-workspace-terminal.ts index caf1d46..5637997 100644 --- a/ts_web/elements/00group-workspace/dees-workspace-terminal/dees-workspace-terminal.ts +++ b/ts_web/elements/00group-workspace/dees-workspace-terminal/dees-workspace-terminal.ts @@ -16,6 +16,8 @@ import { themeDefaultStyles } from '../../00theme.js'; import type { IExecutionEnvironment } from '../../00group-runtime/index.js'; import { WebContainerEnvironment } from '../../00group-runtime/index.js'; import '../../dees-icon/dees-icon.js'; +import '../../dees-actionbar/dees-actionbar.js'; +import type { DeesActionbar } from '../../dees-actionbar/dees-actionbar.js'; import { TerminalTabManager } from './terminal-tab-manager.js'; import type { ITerminalTab, @@ -79,6 +81,9 @@ export class DeesWorkspaceTerminal extends DeesElement { private terminalThemeSubscription: any = null; private isBright: boolean = false; + // Actionbar reference for terminal-context notifications + private terminalActionbar: DeesActionbar | null = null; + /** * Promise that resolves when the environment is ready. * @deprecated Use executionEnvironment directly @@ -120,17 +125,21 @@ export class DeesWorkspaceTerminal extends DeesElement { .terminal-content { flex: 1; - position: relative; + display: flex; + flex-direction: column; overflow: hidden; background: ${cssManager.bdTheme('#ffffff', '#000000')}; } #active-terminal-container { - position: absolute; - top: 20px; - left: 20px; - right: 20px; - bottom: 20px; + flex: 1; + position: relative; + min-height: 0; + margin: 20px; + } + + .terminal-content dees-actionbar { + flex-shrink: 0; } /* Tab bar on the right side */ @@ -426,6 +435,7 @@ export class DeesWorkspaceTerminal extends DeesElement { No terminal open `} + @@ -491,11 +501,15 @@ export class DeesWorkspaceTerminal extends DeesElement { async connectedCallback(): Promise { await super.connectedCallback(); - this.resizeObserver.observe(this); + // ResizeObserver is set up in attachTerminalToContainer when the container exists } async disconnectedCallback(): Promise { - this.resizeObserver.unobserve(this); + // Unobserve the terminal container + const container = this.shadowRoot?.getElementById('active-terminal-container'); + if (container) { + this.resizeObserver.unobserve(container); + } if (this.terminalThemeSubscription) { this.terminalThemeSubscription.unsubscribe(); this.terminalThemeSubscription = null; @@ -558,6 +572,10 @@ export class DeesWorkspaceTerminal extends DeesElement { const container = this.shadowRoot?.getElementById('active-terminal-container'); if (!container) return; + // Observe container for resize (handles actionbar appearing/disappearing) + // ResizeObserver.observe() is idempotent - safe to call multiple times + this.resizeObserver.observe(container); + // Clear container container.innerHTML = ''; @@ -656,6 +674,36 @@ export class DeesWorkspaceTerminal extends DeesElement { detail: { tabId, exitCode }, }) ); + + // Show actionbar to offer closing the tab (only if tab is closeable) + if (tab.closeable) { + this.showExitedTabActionbar(tabId, tab.label, exitCode); + } + } + + /** + * Show actionbar offering to close an exited tab + */ + private async showExitedTabActionbar(tabId: string, tabLabel: string, exitCode: number): Promise { + const isSuccess = exitCode === 0; + const result = await this.showActionbar({ + message: isSuccess + ? `"${tabLabel}" completed. Close tab?` + : `"${tabLabel}" exited (code ${exitCode}). Close tab?`, + type: isSuccess ? 'info' : 'warning', + icon: isSuccess ? 'lucide:checkCircle' : 'lucide:alertTriangle', + actions: [ + { id: 'close', label: 'Close Tab', primary: true }, + { id: 'keep', label: 'Keep Open' }, + ], + timeout: { duration: 10000, defaultActionId: 'close' }, + dismissible: true, + }); + + // Close tab if user clicked "Close Tab" or timeout triggered auto-close + if (result.actionId === 'close') { + this.closeTab(tabId); + } } // ========== Public API ========== @@ -816,6 +864,19 @@ export class DeesWorkspaceTerminal extends DeesElement { return true; } + /** + * Show an actionbar notification in the terminal panel context. + * Use this for terminal-related decisions (e.g., retry failed process, kill process, etc.) + */ + public async showActionbar( + options: Parameters[0] + ): Promise> { + if (!this.terminalActionbar) { + this.terminalActionbar = this.shadowRoot?.querySelector('dees-actionbar') as DeesActionbar; + } + return this.terminalActionbar?.show(options); + } + // ========== Utility Methods ========== public async waitForPrompt(term: Terminal, prompt: string): Promise { diff --git a/ts_web/elements/00group-workspace/dees-workspace/dees-workspace.ts b/ts_web/elements/00group-workspace/dees-workspace/dees-workspace.ts index c6a255b..296d6ee 100644 --- a/ts_web/elements/00group-workspace/dees-workspace/dees-workspace.ts +++ b/ts_web/elements/00group-workspace/dees-workspace/dees-workspace.ts @@ -957,6 +957,7 @@ testSmartPromise(); > `} + @@ -1021,9 +1022,6 @@ testSmartPromise(); .executionEnvironment=${this.executionEnvironment} @run-process=${this.handleRunProcess} > - - - `; } @@ -1056,9 +1054,6 @@ testSmartPromise(); this.currentFileTreeWidth = this.fileTreeWidth; this.currentTerminalHeight = this.terminalHeight; - // Get actionbar reference for file change notifications - this.actionbarElement = this.shadowRoot?.querySelector('dees-actionbar') as DeesActionbar; - if (this.executionEnvironment) { await this.initializeWorkspace(); } @@ -1068,6 +1063,11 @@ testSmartPromise(); if (changedProperties.has('executionEnvironment') && this.executionEnvironment) { await this.initializeWorkspace(); } + + // Capture actionbar reference when it becomes available (after initialization completes) + if (!this.actionbarElement) { + this.actionbarElement = this.shadowRoot?.querySelector('.editor-panel dees-actionbar') as DeesActionbar; + } } private async initializeWorkspace() { diff --git a/ts_web/elements/dees-actionbar/dees-actionbar.ts b/ts_web/elements/dees-actionbar/dees-actionbar.ts index 29e29d3..fa55ccb 100644 --- a/ts_web/elements/dees-actionbar/dees-actionbar.ts +++ b/ts_web/elements/dees-actionbar/dees-actionbar.ts @@ -130,12 +130,12 @@ export class DeesActionbar extends DeesElement { :host { display: block; overflow: hidden; - height: 0; - transition: height 0.2s ease-out; + max-height: 0; + transition: max-height 0.2s ease-out; } :host(.visible) { - height: auto; + max-height: 100px; /* Enough for actionbar content */ } .actionbar-item {