@design.estate/dees-catalog 3.49.2 → 3.50.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (22) hide show
  1. package/dist_bundle/bundle.js +360 -56
  2. package/dist_ts_web/00_commitinfo_data.js +1 -1
  3. package/dist_ts_web/elements/00group-chart/dees-chart-area/component.js +24 -9
  4. package/dist_ts_web/elements/00group-chart/dees-chart-area/demo.js +22 -44
  5. package/dist_ts_web/elements/00group-chart/dees-chart-log/dees-chart-log.demo.js +2 -2
  6. package/dist_ts_web/elements/00group-layout/dees-dashboardgrid/dees-dashboardgrid.demo.js +3 -3
  7. package/dist_ts_web/elements/00group-simple/dees-simple-appdash/dees-simple-appdash.d.ts +25 -0
  8. package/dist_ts_web/elements/00group-simple/dees-simple-appdash/dees-simple-appdash.demo.js +39 -1
  9. package/dist_ts_web/elements/00group-simple/dees-simple-appdash/dees-simple-appdash.js +284 -6
  10. package/dist_ts_web/elements/00group-workspace/dees-workspace-terminal/dees-workspace-terminal.d.ts +1 -1
  11. package/dist_ts_web/elements/00group-workspace/dees-workspace-terminal/dees-workspace-terminal.js +4 -4
  12. package/dist_watch/bundle.js +358 -54
  13. package/dist_watch/bundle.js.map +3 -3
  14. package/package.json +1 -1
  15. package/ts_web/00_commitinfo_data.ts +1 -1
  16. package/ts_web/elements/00group-chart/dees-chart-area/component.ts +29 -13
  17. package/ts_web/elements/00group-chart/dees-chart-area/demo.ts +22 -44
  18. package/ts_web/elements/00group-chart/dees-chart-log/dees-chart-log.demo.ts +1 -1
  19. package/ts_web/elements/00group-layout/dees-dashboardgrid/dees-dashboardgrid.demo.ts +2 -2
  20. package/ts_web/elements/00group-simple/dees-simple-appdash/dees-simple-appdash.demo.ts +39 -1
  21. package/ts_web/elements/00group-simple/dees-simple-appdash/dees-simple-appdash.ts +296 -3
  22. package/ts_web/elements/00group-workspace/dees-workspace-terminal/dees-workspace-terminal.ts +4 -4
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@design.estate/dees-catalog",
3
- "version": "3.49.2",
3
+ "version": "3.50.2",
4
4
  "private": false,
5
5
  "description": "A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript.",
6
6
  "main": "dist_ts_web/index.js",
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@design.estate/dees-catalog',
6
- version: '3.49.2',
6
+ version: '3.50.2',
7
7
  description: 'A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript.'
8
8
  }
@@ -468,16 +468,16 @@ export class DeesChartArea extends DeesElement {
468
468
  if (!this.chart) {
469
469
  return;
470
470
  }
471
-
471
+
472
472
  try {
473
473
  // Store the new data first
474
474
  this.internalChartData = newSeries;
475
-
475
+
476
476
  // Handle rolling window if enabled
477
477
  if (this.rollingWindow > 0 && this.realtimeMode) {
478
478
  const now = Date.now();
479
479
  const cutoffTime = now - this.rollingWindow;
480
-
480
+
481
481
  // Filter data to only include points within the rolling window
482
482
  const filteredSeries = newSeries.map(series => ({
483
483
  name: series.name,
@@ -488,25 +488,41 @@ export class DeesChartArea extends DeesElement {
488
488
  return false;
489
489
  })
490
490
  }));
491
-
491
+
492
492
  // Only update if we have data
493
493
  if (filteredSeries.some(s => s.data.length > 0)) {
494
- // Handle y-axis scaling first
494
+ // Build a single options update with series, x-axis window, and y-axis
495
+ const isDark = !this.goBright;
496
+ const options: ApexCharts.ApexOptions = {
497
+ series: filteredSeries,
498
+ xaxis: {
499
+ min: cutoffTime,
500
+ max: now,
501
+ labels: {
502
+ format: 'HH:mm:ss',
503
+ datetimeUTC: false,
504
+ style: {
505
+ colors: [isDark ? 'hsl(0 0% 63.9%)' : 'hsl(0 0% 20%)'],
506
+ fontSize: '12px',
507
+ fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
508
+ fontWeight: '400',
509
+ },
510
+ },
511
+ tickAmount: 6,
512
+ },
513
+ };
514
+
515
+ // Handle y-axis scaling
495
516
  if (this.yAxisScaling === 'dynamic') {
496
517
  const allValues = filteredSeries.flatMap(s => (s.data as any[]).map(d => d.y));
497
518
  if (allValues.length > 0) {
498
519
  const maxValue = Math.max(...allValues);
499
520
  const dynamicMax = Math.ceil(maxValue * 1.1);
500
- await this.chart.updateOptions({
501
- yaxis: {
502
- min: 0,
503
- max: dynamicMax
504
- }
505
- }, false, false);
521
+ options.yaxis = { min: 0, max: dynamicMax };
506
522
  }
507
523
  }
508
-
509
- await this.chart.updateSeries(filteredSeries, false);
524
+
525
+ await this.chart.updateOptions(options, false, false);
510
526
  }
511
527
  } else {
512
528
  await this.chart.updateSeries(newSeries, animate);
@@ -310,16 +310,31 @@ export const demoFunc = () => {
310
310
  connectionsLastUpdate = 0;
311
311
  };
312
312
 
313
+ // Update button states based on current dataset
314
+ const updateButtonStates = () => {
315
+ const allButtons = elementArg.querySelectorAll('dees-button');
316
+ allButtons.forEach((button: any) => {
317
+ const text = button.text?.trim();
318
+ if (text === 'System Usage') {
319
+ button.type = currentDataset === 'system' ? 'highlighted' : 'normal';
320
+ } else if (text === 'Network Traffic') {
321
+ button.type = currentDataset === 'network' ? 'highlighted' : 'normal';
322
+ } else if (text === 'Sales Data') {
323
+ button.type = currentDataset === 'sales' ? 'highlighted' : 'normal';
324
+ }
325
+ });
326
+ };
327
+
313
328
  // Wire up button click handlers
314
329
  const buttons = elementArg.querySelectorAll('dees-button');
315
- buttons.forEach(button => {
316
- const text = button.textContent?.trim();
330
+ buttons.forEach((button: any) => {
331
+ const text = button.text?.trim();
317
332
  if (text === 'System Usage') {
318
- button.addEventListener('click', () => switchDataset('system'));
333
+ button.addEventListener('click', () => { switchDataset('system'); updateButtonStates(); });
319
334
  } else if (text === 'Network Traffic') {
320
- button.addEventListener('click', () => switchDataset('network'));
335
+ button.addEventListener('click', () => { switchDataset('network'); updateButtonStates(); });
321
336
  } else if (text === 'Sales Data') {
322
- button.addEventListener('click', () => switchDataset('sales'));
337
+ button.addEventListener('click', () => { switchDataset('sales'); updateButtonStates(); });
323
338
  } else if (text === 'Start Live') {
324
339
  button.addEventListener('click', () => startRealtime());
325
340
  } else if (text === 'Stop Live') {
@@ -328,56 +343,19 @@ export const demoFunc = () => {
328
343
  button.addEventListener('click', () => randomizeData());
329
344
  }
330
345
  });
331
-
332
- // Update button states based on current dataset
333
- const updateButtonStates = () => {
334
- const buttons = elementArg.querySelectorAll('dees-button');
335
- buttons.forEach(button => {
336
- const text = button.textContent?.trim();
337
- if (text === 'System Usage') {
338
- button.type = currentDataset === 'system' ? 'highlighted' : 'normal';
339
- } else if (text === 'Network Traffic') {
340
- button.type = currentDataset === 'network' ? 'highlighted' : 'normal';
341
- } else if (text === 'Sales Data') {
342
- button.type = currentDataset === 'sales' ? 'highlighted' : 'normal';
343
- }
344
- });
345
- };
346
-
346
+
347
347
  // Configure main chart with rolling window
348
348
  chartElement.rollingWindow = TIME_WINDOW;
349
349
  chartElement.realtimeMode = false; // Will be enabled when starting live updates
350
350
  chartElement.yAxisScaling = 'percentage'; // Initial system dataset uses percentage
351
351
  chartElement.yAxisMax = 100;
352
352
  chartElement.autoScrollInterval = 1000; // Auto-scroll every second
353
-
353
+
354
354
  // Set initial time window
355
355
  setTimeout(() => {
356
356
  chartElement.updateTimeWindow();
357
357
  }, 100);
358
358
 
359
- // Update button states when dataset changes
360
- const originalSwitchDataset = switchDataset;
361
- const switchDatasetWithButtonUpdate = (name: string) => {
362
- originalSwitchDataset(name);
363
- updateButtonStates();
364
- };
365
-
366
- // Replace switchDataset with the one that updates buttons
367
- buttons.forEach(button => {
368
- const text = button.textContent?.trim();
369
- if (text === 'System Usage') {
370
- button.removeEventListener('click', () => switchDataset('system'));
371
- button.addEventListener('click', () => switchDatasetWithButtonUpdate('system'));
372
- } else if (text === 'Network Traffic') {
373
- button.removeEventListener('click', () => switchDataset('network'));
374
- button.addEventListener('click', () => switchDatasetWithButtonUpdate('network'));
375
- } else if (text === 'Sales Data') {
376
- button.removeEventListener('click', () => switchDataset('sales'));
377
- button.addEventListener('click', () => switchDatasetWithButtonUpdate('sales'));
378
- }
379
- });
380
-
381
359
  // Initialize connections chart with data
382
360
  if (connectionsChartElement) {
383
361
  const initialConnectionsData = generateInitialData(previousValues.connections, 30, UPDATE_INTERVAL);
@@ -174,7 +174,7 @@ export const demoFunc = () => {
174
174
  // Wire up button click handlers
175
175
  const buttons = elementArg.querySelectorAll('dees-button');
176
176
  buttons.forEach(button => {
177
- const text = button.textContent?.trim();
177
+ const text = (button as any).text?.trim();
178
178
  switch (text) {
179
179
  case 'Add Structured Log':
180
180
  button.addEventListener('click', () => generateRandomLog());
@@ -103,7 +103,7 @@ export const demoFunc = () => {
103
103
 
104
104
  const buttons = elementArg.querySelectorAll('dees-button');
105
105
  buttons.forEach(button => {
106
- const text = button.textContent?.trim();
106
+ const text = (button as any).text?.trim();
107
107
 
108
108
  switch (text) {
109
109
  case 'Toggle Animation':
@@ -147,7 +147,7 @@ export const demoFunc = () => {
147
147
  case 'Toggle Edit Mode':
148
148
  button.addEventListener('click', () => {
149
149
  grid.editable = !grid.editable;
150
- button.textContent = grid.editable ? 'Lock Grid' : 'Unlock Grid';
150
+ (button as any).text = grid.editable ? 'Lock Grid' : 'Unlock Grid';
151
151
  });
152
152
  break;
153
153
  case 'Reset Layout':
@@ -1,5 +1,5 @@
1
1
  import { html, DeesElement, customElement, css, cssManager } from '@design.estate/dees-element';
2
- import type { IView } from './dees-simple-appdash.js';
2
+ import type { IView, IGlobalMessage } from './dees-simple-appdash.js';
3
3
  import '../../00group-form/dees-form/dees-form.js';
4
4
  import '../../00group-input/dees-input-text/dees-input-text.js';
5
5
  import '../../00group-input/dees-input-checkbox/dees-input-checkbox.js';
@@ -263,6 +263,44 @@ export const demoFunc = () => html`
263
263
  <dees-simple-appdash
264
264
  name="My Application"
265
265
  terminalSetupCommand="echo 'Welcome to the terminal!'"
266
+ .globalMessages=${[
267
+ {
268
+ id: 'update',
269
+ type: 'info',
270
+ message: 'A new version (v3.50.0) is available with performance improvements and bug fixes.',
271
+ dismissible: true,
272
+ actions: [
273
+ {
274
+ name: 'Update Now',
275
+ iconName: 'lucide:download',
276
+ action: () => alert('Updating...'),
277
+ },
278
+ {
279
+ name: 'Release Notes',
280
+ action: () => alert('Opening release notes...'),
281
+ },
282
+ ],
283
+ },
284
+ {
285
+ id: 'maintenance',
286
+ type: 'warning',
287
+ message: 'Scheduled maintenance window: April 5, 2026 02:00–06:00 UTC. Some services may be temporarily unavailable.',
288
+ dismissible: true,
289
+ },
290
+ {
291
+ id: 'critical',
292
+ type: 'error',
293
+ message: 'Your SSL certificate expires in 3 days. Renew now to avoid service disruption.',
294
+ dismissible: false,
295
+ actions: [
296
+ {
297
+ name: 'Renew Certificate',
298
+ iconName: 'lucide:shieldCheck',
299
+ action: () => alert('Renewing certificate...'),
300
+ },
301
+ ],
302
+ },
303
+ ] as IGlobalMessage[]}
266
304
  .viewTabs=${[
267
305
  {
268
306
  name: 'Dashboard',
@@ -29,6 +29,23 @@ export interface IView {
29
29
  element: DeesElement['constructor']['prototype'];
30
30
  }
31
31
 
32
+ export type TGlobalMessageType = 'info' | 'success' | 'warning' | 'error';
33
+
34
+ export interface IGlobalMessageAction {
35
+ name: string;
36
+ iconName?: string;
37
+ action: () => void | Promise<void>;
38
+ }
39
+
40
+ export interface IGlobalMessage {
41
+ id: string;
42
+ type: TGlobalMessageType;
43
+ message: string;
44
+ dismissible?: boolean;
45
+ icon?: string;
46
+ actions?: IGlobalMessageAction[];
47
+ }
48
+
32
49
  @customElement('dees-simple-appdash')
33
50
  export class DeesSimpleAppDash extends DeesElement {
34
51
  // STATIC
@@ -45,9 +62,15 @@ export class DeesSimpleAppDash extends DeesElement {
45
62
  @property({ type: String })
46
63
  accessor terminalSetupCommand: string = `echo "Terminal ready"`;
47
64
 
65
+ @property({ type: Array })
66
+ accessor globalMessages: IGlobalMessage[] = [];
67
+
48
68
  @state()
49
69
  accessor selectedView!: IView;
50
70
 
71
+ @state()
72
+ accessor _activeMessages: IGlobalMessage[] = [];
73
+
51
74
 
52
75
  public static styles = [
53
76
  themeDefaultStyles,
@@ -327,6 +350,170 @@ export class DeesSimpleAppDash extends DeesElement {
327
350
  .control.status-terminal dees-icon {
328
351
  color: hsl(45 90% 55%);
329
352
  }
353
+
354
+ /* Global Message Banners */
355
+ .messageBannerArea {
356
+ position: absolute;
357
+ top: 0;
358
+ left: 240px;
359
+ right: 0;
360
+ z-index: 3;
361
+ display: flex;
362
+ flex-direction: column;
363
+ transition: height 0.25s cubic-bezier(0.4, 0, 0.2, 1);
364
+ }
365
+
366
+ .messageBanner {
367
+ display: flex;
368
+ align-items: center;
369
+ gap: 12px;
370
+ padding: 10px 16px;
371
+ font-size: 13px;
372
+ font-family: 'Geist Sans', sans-serif;
373
+ font-weight: 500;
374
+ border-bottom: 1px solid ${cssManager.bdTheme('hsl(0 0% 91%)', 'hsl(0 0% 13%)')};
375
+ animation: bannerSlideDown 0.25s cubic-bezier(0.4, 0, 0.2, 1) forwards;
376
+ }
377
+
378
+ .messageBanner.dismissing {
379
+ animation: bannerSlideUp 0.2s cubic-bezier(0.4, 0, 0.2, 1) forwards;
380
+ }
381
+
382
+ @keyframes bannerSlideDown {
383
+ from {
384
+ opacity: 0;
385
+ transform: translateY(-100%);
386
+ }
387
+ to {
388
+ opacity: 1;
389
+ transform: translateY(0);
390
+ }
391
+ }
392
+
393
+ @keyframes bannerSlideUp {
394
+ from {
395
+ opacity: 1;
396
+ transform: translateY(0);
397
+ }
398
+ to {
399
+ opacity: 0;
400
+ transform: translateY(-100%);
401
+ }
402
+ }
403
+
404
+ .messageBanner dees-icon {
405
+ font-size: 16px;
406
+ flex-shrink: 0;
407
+ }
408
+
409
+ .messageBanner-text {
410
+ flex: 1;
411
+ overflow: hidden;
412
+ text-overflow: ellipsis;
413
+ white-space: nowrap;
414
+ }
415
+
416
+ .messageBanner-actions {
417
+ display: flex;
418
+ align-items: center;
419
+ gap: 6px;
420
+ flex-shrink: 0;
421
+ }
422
+
423
+ .messageBanner-action {
424
+ display: flex;
425
+ align-items: center;
426
+ gap: 6px;
427
+ padding: 4px 12px;
428
+ border-radius: 4px;
429
+ cursor: default;
430
+ font-size: 12px;
431
+ font-weight: 600;
432
+ letter-spacing: 0.01em;
433
+ transition: all 0.15s ease;
434
+ white-space: nowrap;
435
+ background: ${cssManager.bdTheme('hsl(0 0% 0% / 0.08)', 'hsl(0 0% 100% / 0.1)')};
436
+ color: inherit;
437
+ }
438
+
439
+ .messageBanner-action:hover {
440
+ background: ${cssManager.bdTheme('hsl(0 0% 0% / 0.15)', 'hsl(0 0% 100% / 0.18)')};
441
+ }
442
+
443
+ .messageBanner-action:active {
444
+ transform: scale(0.97);
445
+ }
446
+
447
+ .messageBanner-action dees-icon {
448
+ font-size: 13px;
449
+ }
450
+
451
+ .messageBanner-dismiss {
452
+ flex-shrink: 0;
453
+ display: flex;
454
+ align-items: center;
455
+ justify-content: center;
456
+ width: 24px;
457
+ height: 24px;
458
+ border-radius: 4px;
459
+ cursor: default;
460
+ opacity: 0.5;
461
+ transition: all 0.15s ease;
462
+ }
463
+
464
+ .messageBanner-dismiss:hover {
465
+ opacity: 1;
466
+ background: hsl(0 0% 0% / 0.1);
467
+ }
468
+
469
+ /* Message type: info */
470
+ .messageBanner-info {
471
+ background: ${cssManager.bdTheme('hsl(210 100% 97%)', 'hsl(210 50% 10%)')};
472
+ color: ${cssManager.bdTheme('hsl(210 70% 30%)', 'hsl(210 70% 80%)')};
473
+ border-left: 3px solid #0084ff;
474
+ }
475
+ .messageBanner-info dees-icon {
476
+ color: #0084ff;
477
+ }
478
+
479
+ /* Message type: success */
480
+ .messageBanner-success {
481
+ background: ${cssManager.bdTheme('hsl(142 70% 97%)', 'hsl(142 30% 10%)')};
482
+ color: ${cssManager.bdTheme('hsl(142 50% 25%)', 'hsl(142 50% 80%)')};
483
+ border-left: 3px solid #22c55e;
484
+ }
485
+ .messageBanner-success dees-icon {
486
+ color: #22c55e;
487
+ }
488
+
489
+ /* Message type: warning */
490
+ .messageBanner-warning {
491
+ background: ${cssManager.bdTheme('hsl(38 90% 97%)', 'hsl(38 40% 10%)')};
492
+ color: ${cssManager.bdTheme('hsl(38 60% 25%)', 'hsl(38 60% 80%)')};
493
+ border-left: 3px solid #f59e0b;
494
+ }
495
+ .messageBanner-warning dees-icon {
496
+ color: #f59e0b;
497
+ }
498
+
499
+ /* Message type: error */
500
+ .messageBanner-error {
501
+ background: ${cssManager.bdTheme('hsl(0 70% 97%)', 'hsl(0 40% 10%)')};
502
+ color: ${cssManager.bdTheme('hsl(0 60% 30%)', 'hsl(0 60% 80%)')};
503
+ border-left: 3px solid #ef4444;
504
+ }
505
+ .messageBanner-error dees-icon {
506
+ color: #ef4444;
507
+ }
508
+
509
+ .messageBanner-dismiss:hover {
510
+ background: ${cssManager.bdTheme('hsl(0 0% 0% / 0.08)', 'hsl(0 0% 100% / 0.1)')};
511
+ }
512
+
513
+ .appcontent {
514
+ top: var(--banner-area-height, 0px);
515
+ height: calc(100% - 24px - var(--banner-area-height, 0px));
516
+ }
330
517
  `,
331
518
  ];
332
519
 
@@ -369,6 +556,34 @@ export class DeesSimpleAppDash extends DeesElement {
369
556
  </div>
370
557
  </div>
371
558
  </div>
559
+ ${this._activeMessages.length > 0 ? html`
560
+ <div class="messageBannerArea">
561
+ ${this._activeMessages.map(msg => html`
562
+ <div
563
+ class="messageBanner messageBanner-${msg.type}"
564
+ data-message-id="${msg.id}"
565
+ >
566
+ <dees-icon .icon="${this.getMessageIcon(msg)}"></dees-icon>
567
+ <span class="messageBanner-text">${msg.message}</span>
568
+ ${msg.actions?.length ? html`
569
+ <div class="messageBanner-actions">
570
+ ${msg.actions.map(a => html`
571
+ <div class="messageBanner-action" @click=${() => a.action()}>
572
+ ${a.iconName ? html`<dees-icon .icon="${a.iconName.includes(':') ? a.iconName : `lucide:${a.iconName}`}"></dees-icon>` : ''}
573
+ <span>${a.name}</span>
574
+ </div>
575
+ `)}
576
+ </div>
577
+ ` : ''}
578
+ ${msg.dismissible !== false ? html`
579
+ <div class="messageBanner-dismiss" @click=${() => this.removeMessage(msg.id)}>
580
+ <dees-icon .icon="${'lucide:x'}"></dees-icon>
581
+ </div>
582
+ ` : ''}
583
+ </div>
584
+ `)}
585
+ </div>
586
+ ` : ''}
372
587
  <div class="appcontent">
373
588
  <!-- Content goes here -->
374
589
  </div>
@@ -394,6 +609,86 @@ export class DeesSimpleAppDash extends DeesElement {
394
609
  await this.loadView(viewToLoad);
395
610
  }
396
611
  }
612
+
613
+ public willUpdate(changedProperties: Map<string, unknown>) {
614
+ if (changedProperties.has('globalMessages')) {
615
+ // Sync globalMessages property into _activeMessages
616
+ // Keep any messages added via addMessage() that aren't in globalMessages
617
+ const propertyIds = new Set(this.globalMessages.map(m => m.id));
618
+ const existingIds = new Set(this._activeMessages.map(m => m.id));
619
+
620
+ // Add new messages from property that aren't already active
621
+ const newMessages = this.globalMessages.filter(m => !existingIds.has(m.id));
622
+
623
+ // Keep messages added via API (those not in globalMessages are kept as-is)
624
+ // Remove messages that were in the previous globalMessages but are no longer
625
+ const previousGlobalMessages = (changedProperties.get('globalMessages') as IGlobalMessage[]) || [];
626
+ const previousIds = new Set(previousGlobalMessages.map(m => m.id));
627
+ const removedIds = new Set([...previousIds].filter(id => !propertyIds.has(id)));
628
+
629
+ this._activeMessages = [
630
+ ...this._activeMessages.filter(m => !removedIds.has(m.id)),
631
+ ...newMessages,
632
+ ];
633
+ }
634
+ }
635
+
636
+ public updated(changedProperties: Map<string, unknown>) {
637
+ super.updated(changedProperties);
638
+ this.updateBannerOffset();
639
+ }
640
+
641
+ private updateBannerOffset() {
642
+ requestAnimationFrame(() => {
643
+ const bannerArea = this.shadowRoot?.querySelector('.messageBannerArea') as HTMLElement;
644
+ const maincontainer = this.shadowRoot?.querySelector('.maincontainer') as HTMLElement;
645
+ const height = bannerArea ? bannerArea.offsetHeight : 0;
646
+ maincontainer?.style.setProperty('--banner-area-height', `${height}px`);
647
+ });
648
+ }
649
+
650
+ private getMessageIcon(msg: IGlobalMessage): string {
651
+ if (msg.icon) return msg.icon;
652
+ const defaults: Record<TGlobalMessageType, string> = {
653
+ info: 'lucide:info',
654
+ success: 'lucide:circleCheck',
655
+ warning: 'lucide:triangleAlert',
656
+ error: 'lucide:circleX',
657
+ };
658
+ return defaults[msg.type];
659
+ }
660
+
661
+ public addMessage(message: Omit<IGlobalMessage, 'id'> & { id?: string }): string {
662
+ const id = message.id || `msg-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
663
+ const fullMessage: IGlobalMessage = {
664
+ dismissible: true,
665
+ ...message,
666
+ id,
667
+ };
668
+ this._activeMessages = [...this._activeMessages, fullMessage];
669
+ return id;
670
+ }
671
+
672
+ public removeMessage(id: string): void {
673
+ const bannerEl = this.shadowRoot?.querySelector(`[data-message-id="${id}"]`) as HTMLElement;
674
+ if (bannerEl) {
675
+ bannerEl.classList.add('dismissing');
676
+ bannerEl.addEventListener('animationend', () => {
677
+ this._activeMessages = this._activeMessages.filter(m => m.id !== id);
678
+ this.dispatchEvent(new CustomEvent('message-dismiss', {
679
+ detail: { id },
680
+ bubbles: true,
681
+ composed: true,
682
+ }));
683
+ }, { once: true });
684
+ } else {
685
+ this._activeMessages = this._activeMessages.filter(m => m.id !== id);
686
+ }
687
+ }
688
+
689
+ public clearMessages(): void {
690
+ this._activeMessages = [];
691
+ }
397
692
 
398
693
  public currentTerminal: DeesWorkspaceTerminal | null = null;
399
694
  public async launchTerminal() {
@@ -412,7 +707,7 @@ export class DeesSimpleAppDash extends DeesElement {
412
707
  maincontainer.appendChild(terminal);
413
708
  terminal.style.position = 'absolute';
414
709
  terminal.style.zIndex = '10';
415
- terminal.style.top = '0px';
710
+ terminal.style.top = 'var(--banner-area-height, 0px)';
416
711
  terminal.style.left = '240px';
417
712
  terminal.style.right = '0px';
418
713
  terminal.style.bottom = '24px';
@@ -420,8 +715,6 @@ export class DeesSimpleAppDash extends DeesElement {
420
715
  terminal.style.transform = 'translateY(8px) scale(0.99)';
421
716
  terminal.style.transition = 'all 0.25s cubic-bezier(0.4, 0, 0.2, 1)';
422
717
  terminal.style.boxShadow = '0 25px 50px -12px rgb(0 0 0 / 0.5), 0 0 0 1px rgb(255 255 255 / 0.05)';
423
- terminal.style.maxWidth = `calc(${maincontainer.clientWidth}px -240px)`;
424
- terminal.style.maxHeight = `calc(${maincontainer.clientHeight}px - 24px)`;
425
718
 
426
719
  // Add close button to terminal
427
720
  terminal.addEventListener('close', () => this.closeTerminal());
@@ -106,11 +106,11 @@ export class DeesWorkspaceTerminal extends DeesElement {
106
106
  css`
107
107
  :host {
108
108
  background: ${cssManager.bdTheme('#ffffff', '#000000')};
109
- position: absolute;
110
- height: 100%;
111
- width: 100%;
112
109
  display: flex;
113
110
  flex-direction: row;
111
+ box-sizing: border-box;
112
+ height: 100%;
113
+ width: 100%;
114
114
  }
115
115
 
116
116
  * {
@@ -596,7 +596,7 @@ export class DeesWorkspaceTerminal extends DeesElement {
596
596
  tab.terminal.focus();
597
597
  }
598
598
 
599
- private handleResize(): void {
599
+ public handleResize(): void {
600
600
  if (this.activeTabId) {
601
601
  const tab = this.tabManager.getTab(this.activeTabId);
602
602
  if (tab) {