update
BIN
.playwright-mcp/both-actionbars-test.png
Normal file
|
After Width: | Height: | Size: 76 KiB |
BIN
.playwright-mcp/editor-actionbar-test.png
Normal file
|
After Width: | Height: | Size: 72 KiB |
BIN
.playwright-mcp/editor-actionbar-visible.png
Normal file
|
After Width: | Height: | Size: 74 KiB |
BIN
.playwright-mcp/editor-actionbar-working.png
Normal file
|
After Width: | Height: | Size: 74 KiB |
BIN
.playwright-mcp/terminal-actionbar-resize-issue.png
Normal file
|
After Width: | Height: | Size: 58 KiB |
BIN
.playwright-mcp/terminal-resize-fix-verification.png
Normal file
|
After Width: | Height: | Size: 56 KiB |
BIN
.playwright-mcp/terminal-with-actionbar-fix.png
Normal file
|
After Width: | Height: | Size: 56 KiB |
BIN
.playwright-mcp/workspace-actionbar-layout.png
Normal file
|
After Width: | Height: | Size: 59 KiB |
BIN
.playwright-mcp/workspace-file-open.png
Normal file
|
After Width: | Height: | Size: 74 KiB |
@@ -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 {
|
||||
<span>No terminal open</span>
|
||||
</div>
|
||||
`}
|
||||
<dees-actionbar></dees-actionbar>
|
||||
</div>
|
||||
|
||||
<!-- Vertical tab bar on the right -->
|
||||
@@ -491,11 +501,15 @@ export class DeesWorkspaceTerminal extends DeesElement {
|
||||
|
||||
async connectedCallback(): Promise<void> {
|
||||
await super.connectedCallback();
|
||||
this.resizeObserver.observe(this);
|
||||
// ResizeObserver is set up in attachTerminalToContainer when the container exists
|
||||
}
|
||||
|
||||
async disconnectedCallback(): Promise<void> {
|
||||
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<void> {
|
||||
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<DeesActionbar['show']>[0]
|
||||
): Promise<ReturnType<DeesActionbar['show']>> {
|
||||
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<void> {
|
||||
|
||||
@@ -957,6 +957,7 @@ testSmartPromise();
|
||||
></dees-workspace-monaco>
|
||||
`}
|
||||
</div>
|
||||
<dees-actionbar></dees-actionbar>
|
||||
</div>
|
||||
|
||||
<!-- Horizontal resize handle for terminal -->
|
||||
@@ -1021,9 +1022,6 @@ testSmartPromise();
|
||||
.executionEnvironment=${this.executionEnvironment}
|
||||
@run-process=${this.handleRunProcess}
|
||||
></dees-workspace-bottombar>
|
||||
|
||||
<!-- Action Bar for notifications -->
|
||||
<dees-actionbar></dees-actionbar>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
@@ -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() {
|
||||
|
||||
@@ -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 {
|
||||
|
||||