diff --git a/.playwright-mcp/dees-workspace-bright-theme.png b/.playwright-mcp/dees-workspace-bright-theme.png new file mode 100644 index 0000000..03dd27a Binary files /dev/null and b/.playwright-mcp/dees-workspace-bright-theme.png differ diff --git a/.playwright-mcp/dees-workspace-dark-theme.png b/.playwright-mcp/dees-workspace-dark-theme.png new file mode 100644 index 0000000..82b7061 Binary files /dev/null and b/.playwright-mcp/dees-workspace-dark-theme.png differ diff --git a/changelog.md b/changelog.md index 83c9d56..a69d543 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,15 @@ # Changelog +## 2025-12-31 - 3.21.0 - feat(terminal) +add dynamic bright/dark theming for terminal components and terminal preview + +- Add bright/dark theme PNG assets under .playwright-mcp for previews. +- Replace hardcoded terminal background/colors with cssManager.bdTheme in workspace terminal and preview styles. +- Introduce getTerminalTheme helper to compute xterm theme for bright/dark modes. +- Subscribe to themeManager.themeObservable and apply updates to xterm (terminal.options.theme) so terminals update live on theme change. +- Remove hardcoded background property/CSS var and unused background accessor from workspace terminal. +- Ensure proper cleanup: unsubscribe theme subscriptions and dispose terminals in disconnectedCallback. + ## 2025-12-31 - 3.20.1 - fix(dees-workspace) fix demo wrapper and workspace layout; always render terminal preview diff --git a/ts_web/00_commitinfo_data.ts b/ts_web/00_commitinfo_data.ts index d728226..9c2bd38 100644 --- a/ts_web/00_commitinfo_data.ts +++ b/ts_web/00_commitinfo_data.ts @@ -3,6 +3,6 @@ */ export const commitinfo = { name: '@design.estate/dees-catalog', - version: '3.20.1', + version: '3.21.0', description: 'A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript.' } diff --git a/ts_web/elements/00group-simple/dees-simple-appdash/dees-simple-appdash.ts b/ts_web/elements/00group-simple/dees-simple-appdash/dees-simple-appdash.ts index d57955d..6c76418 100644 --- a/ts_web/elements/00group-simple/dees-simple-appdash/dees-simple-appdash.ts +++ b/ts_web/elements/00group-simple/dees-simple-appdash/dees-simple-appdash.ts @@ -417,7 +417,6 @@ export class DeesSimpleAppDash extends DeesElement { terminal.style.opacity = '0'; terminal.style.transform = 'translateY(8px) scale(0.99)'; terminal.style.transition = 'all 0.25s cubic-bezier(0.4, 0, 0.2, 1)'; - terminal.background = 'hsl(220 13% 8%)'; terminal.style.boxShadow = '0 25px 50px -12px rgb(0 0 0 / 0.5), 0 0 0 1px rgb(255 255 255 / 0.05)'; terminal.style.maxWidth = `calc(${maincontainer.clientWidth}px -240px)`; terminal.style.maxHeight = `calc(${maincontainer.clientHeight}px - 24px)`; diff --git a/ts_web/elements/00group-workspace/dees-workspace-terminal-preview/dees-workspace-terminal-preview.ts b/ts_web/elements/00group-workspace/dees-workspace-terminal-preview/dees-workspace-terminal-preview.ts index 5933e86..d6cf62d 100644 --- a/ts_web/elements/00group-workspace/dees-workspace-terminal-preview/dees-workspace-terminal-preview.ts +++ b/ts_web/elements/00group-workspace/dees-workspace-terminal-preview/dees-workspace-terminal-preview.ts @@ -55,6 +55,7 @@ export class DeesWorkspaceTerminalPreview extends DeesElement { private fitAddon: FitAddon | null = null; private lastLineCount: number = 0; private resizeObserver: ResizeObserver | null = null; + private terminalThemeSubscription: any = null; public static styles = [ themeDefaultStyles, @@ -69,8 +70,8 @@ export class DeesWorkspaceTerminalPreview extends DeesElement { height: 100%; border-radius: 8px; overflow: hidden; - background: #000000; - border: 1px solid hsl(0 0% 20%); + background: ${cssManager.bdTheme('#ffffff', '#000000')}; + border: 1px solid ${cssManager.bdTheme('hsl(0 0% 85%)', 'hsl(0 0% 20%)')}; display: flex; flex-direction: column; } @@ -80,20 +81,20 @@ export class DeesWorkspaceTerminalPreview extends DeesElement { align-items: center; gap: 8px; padding: 8px 12px; - background: hsl(0 0% 10%); + background: ${cssManager.bdTheme('hsl(0 0% 96%)', 'hsl(0 0% 10%)')}; font-size: 12px; font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Fira Code', monospace; - color: hsl(0 0% 60%); - border-bottom: 1px solid hsl(0 0% 20%); + color: ${cssManager.bdTheme('hsl(0 0% 40%)', 'hsl(0 0% 60%)')}; + border-bottom: 1px solid ${cssManager.bdTheme('hsl(0 0% 85%)', 'hsl(0 0% 20%)')}; flex-shrink: 0; } .terminal-header-icon { - color: hsl(0 0% 50%); + color: ${cssManager.bdTheme('hsl(0 0% 50%)', 'hsl(0 0% 50%)')}; } .terminal-header-command { - color: hsl(0 0% 80%); + color: ${cssManager.bdTheme('hsl(0 0% 20%)', 'hsl(0 0% 80%)')}; font-weight: 500; } @@ -148,8 +149,8 @@ export class DeesWorkspaceTerminalPreview extends DeesElement { } .xterm .composition-view { - background: #000000; - color: #fff; + background: ${cssManager.bdTheme('#ffffff', '#000000')}; + color: ${cssManager.bdTheme('#333333', '#ffffff')}; display: none; position: absolute; white-space: nowrap; @@ -161,7 +162,7 @@ export class DeesWorkspaceTerminalPreview extends DeesElement { } .xterm .xterm-viewport { - background-color: #000000; + background-color: ${cssManager.bdTheme('#ffffff', '#000000')}; overflow-y: scroll; cursor: default; position: absolute; @@ -243,16 +244,16 @@ export class DeesWorkspaceTerminalPreview extends DeesElement { } .xterm .xterm-viewport::-webkit-scrollbar-track { - background: hsl(0 0% 8%); + background: ${cssManager.bdTheme('hsl(0 0% 96%)', 'hsl(0 0% 8%)')}; } .xterm .xterm-viewport::-webkit-scrollbar-thumb { - background: hsl(0 0% 25%); + background: ${cssManager.bdTheme('hsl(0 0% 80%)', 'hsl(0 0% 25%)')}; border-radius: 4px; } .xterm .xterm-viewport::-webkit-scrollbar-thumb:hover { - background: hsl(0 0% 35%); + background: ${cssManager.bdTheme('hsl(0 0% 70%)', 'hsl(0 0% 35%)')}; } `, ]; @@ -271,6 +272,27 @@ export class DeesWorkspaceTerminalPreview extends DeesElement { `; } + /** + * Get terminal theme colors based on bright/dark mode + */ + private getTerminalTheme(isBright: boolean) { + return isBright + ? { + background: '#ffffff', + foreground: '#333333', + cursor: '#333333', + cursorAccent: '#ffffff', + selectionBackground: 'rgba(0, 0, 0, 0.2)', + } + : { + background: '#000000', + foreground: '#cccccc', + cursor: '#cccccc', + cursorAccent: '#000000', + selectionBackground: 'rgba(255, 255, 255, 0.2)', + }; + } + public async firstUpdated( _changedProperties: Map ): Promise { @@ -279,6 +301,10 @@ export class DeesWorkspaceTerminalPreview extends DeesElement { const container = this.shadowRoot?.getElementById('xterm-container'); if (!container) return; + // Get current theme from domtools + const domtoolsInstance = await this.domtoolsPromise; + const isBright = domtoolsInstance.themeManager.goBrightBoolean; + // Create xterm terminal in read-only mode this.terminal = new Terminal({ convertEol: true, @@ -286,13 +312,17 @@ export class DeesWorkspaceTerminalPreview extends DeesElement { disableStdin: true, fontSize: 12, fontFamily: "'SF Mono', 'Monaco', 'Inconsolata', 'Fira Code', monospace", - theme: { - background: '#000000', - foreground: '#cccccc', - }, + theme: this.getTerminalTheme(isBright), scrollback: 1000, }); + // Subscribe to theme changes + this.terminalThemeSubscription = domtoolsInstance.themeManager.themeObservable.subscribe((goBright: boolean) => { + if (this.terminal) { + this.terminal.options.theme = this.getTerminalTheme(goBright); + } + }); + this.fitAddon = new FitAddon(); this.terminal.loadAddon(this.fitAddon); this.terminal.open(container); @@ -334,6 +364,10 @@ export class DeesWorkspaceTerminalPreview extends DeesElement { this.resizeObserver.disconnect(); this.resizeObserver = null; } + if (this.terminalThemeSubscription) { + this.terminalThemeSubscription.unsubscribe(); + this.terminalThemeSubscription = null; + } if (this.terminal) { this.terminal.dispose(); this.terminal = null; 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 79b25f4..89a07d1 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 @@ -47,9 +47,6 @@ export class DeesWorkspaceTerminal extends DeesElement { @property() accessor environmentVariables: { [key: string]: string } = {}; - @property() - accessor background: string = '#000000'; - /** * Promise that resolves when the environment is ready. * @deprecated Use executionEnvironment directly @@ -57,6 +54,9 @@ export class DeesWorkspaceTerminal extends DeesElement { private environmentDeferred = new domtools.plugins.smartpromise.Deferred(); public environmentPromise = this.environmentDeferred.promise; + // Theme subscription for dynamic theme updates + private terminalThemeSubscription: any = null; + constructor() { super(); this.resizeObserver = new ResizeObserver((entries) => { @@ -72,10 +72,9 @@ export class DeesWorkspaceTerminal extends DeesElement { themeDefaultStyles, cssManager.defaultStyles, css` - /* TODO: Migrate hardcoded values to --dees-* CSS variables */ :host { padding: 20px; - background: var(--dees-terminal-background, #000000); + background: ${cssManager.bdTheme('#ffffff', '#000000')}; position: absolute; height: 100%; width: 100%; @@ -170,8 +169,8 @@ export class DeesWorkspaceTerminal extends DeesElement { .xterm .composition-view { /* TODO: Composition position got messed up somewhere */ - background: var(--dees-terminal-background, #000000); - color: #fff; + background: ${cssManager.bdTheme('#ffffff', '#000000')}; + color: ${cssManager.bdTheme('#333333', '#ffffff')}; display: none; position: absolute; white-space: nowrap; @@ -184,7 +183,7 @@ export class DeesWorkspaceTerminal extends DeesElement { .xterm .xterm-viewport { /* On OS X this is required in order for the scroll bar to appear fully opaque */ - background-color: var(--dees-terminal-background, #000000); + background-color: ${cssManager.bdTheme('#ffffff', '#000000')}; overflow-y: scroll; cursor: default; position: absolute; @@ -275,25 +274,51 @@ export class DeesWorkspaceTerminal extends DeesElement { private fitAddon: FitAddon; private terminal: Terminal | null = null; + /** + * Get terminal theme colors based on bright/dark mode + */ + private getTerminalTheme(isBright: boolean) { + return isBright + ? { + background: '#ffffff', + foreground: '#333333', + cursor: '#333333', + cursorAccent: '#ffffff', + selectionBackground: 'rgba(0, 0, 0, 0.2)', + } + : { + background: '#000000', + foreground: '#cccccc', + cursor: '#cccccc', + cursorAccent: '#000000', + selectionBackground: 'rgba(255, 255, 255, 0.2)', + }; + } + public async firstUpdated( _changedProperties: Map ): Promise { - const domtools = await this.domtoolsPromise; + const domtoolsInstance = await this.domtoolsPromise; super.firstUpdated(_changedProperties); - // Sync CSS variable with background property - this.style.setProperty('--dees-terminal-background', this.background); + // Get current theme + const isBright = domtoolsInstance.themeManager.goBrightBoolean; const container = this.shadowRoot.getElementById('container'); const term = new Terminal({ convertEol: true, cursorBlink: true, - theme: { - background: this.background, - }, + theme: this.getTerminalTheme(isBright), }); this.terminal = term; + + // Subscribe to theme changes + this.terminalThemeSubscription = domtoolsInstance.themeManager.themeObservable.subscribe((goBright: boolean) => { + if (this.terminal) { + this.terminal.options.theme = this.getTerminalTheme(goBright); + } + }); this.fitAddon = new FitAddon(); term.loadAddon(this.fitAddon); @@ -383,6 +408,14 @@ export class DeesWorkspaceTerminal extends DeesElement { async disconnectedCallback(): Promise { this.resizeObserver.unobserve(this); + if (this.terminalThemeSubscription) { + this.terminalThemeSubscription.unsubscribe(); + this.terminalThemeSubscription = null; + } + if (this.terminal) { + this.terminal.dispose(); + this.terminal = null; + } await super.disconnectedCallback(); }