Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/app/components/chat/chat.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,7 @@ button {
flex-direction: column;
justify-content: center;
align-items: center;
background: var(--chat-card-background-color);

font-family: 'Google Sans', sans-serif;
font-weight: 400;
Expand Down
33 changes: 18 additions & 15 deletions src/app/components/chat/chat.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ import {TraceEventComponent} from '../trace-tab/trace-event/trace-event.componen
import {ViewImageDialogComponent} from '../view-image-dialog/view-image-dialog.component';

import {ChatMessagesInjectionToken} from './chat.component.i18n';
import {SnackbarType} from '../../core/constants/snackbar.constants';

const ROOT_AGENT = 'root_agent';
/** Query parameter for pre-filling user input. */
Expand Down Expand Up @@ -340,7 +341,7 @@ export class ChatComponent implements OnInit, AfterViewInit, OnDestroy {
this.streamChatService.onStreamClose().subscribe((closeReason) => {
const error =
'Please check server log for full details: \n' + closeReason;
this.openSnackBar(error, 'OK');
this.openSnackBar(error, 'OK', SnackbarType.ERROR);
});

// OAuth HACK: Opens oauth poup in a new window. If the oauth callback
Expand Down Expand Up @@ -406,7 +407,7 @@ export class ChatComponent implements OnInit, AfterViewInit, OnDestroy {

this.uiStateService.onNewMessagesLoadingFailed().subscribe(
(error: {message: string}) => {
this.openSnackBar(error.message, 'OK');
this.openSnackBar(error.message, 'OK', SnackbarType.ERROR);
});
}
});
Expand Down Expand Up @@ -579,7 +580,7 @@ export class ChatComponent implements OnInit, AfterViewInit, OnDestroy {
this.agentService.runSse(req).subscribe({
next: async (chunkJson: AdkEvent) => {
if (chunkJson.error) {
this.openSnackBar(chunkJson.error, 'OK');
this.openSnackBar(chunkJson.error, 'OK', SnackbarType.ERROR);
return;
}
if (chunkJson.content) {
Expand All @@ -603,7 +604,7 @@ export class ChatComponent implements OnInit, AfterViewInit, OnDestroy {
},
error: (err) => {
console.error('Send message error:', err);
this.openSnackBar(err, 'OK');
this.openSnackBar(err, 'OK', SnackbarType.ERROR);
},
complete: () => {
if (this.updatedSessionState()) {
Expand Down Expand Up @@ -1401,7 +1402,7 @@ export class ChatComponent implements OnInit, AfterViewInit, OnDestroy {

startAudioRecording() {
if (this.sessionHasUsedBidi.has(this.sessionId)) {
this.openSnackBar(BIDI_STREAMING_RESTART_WARNING, 'OK');
this.openSnackBar(BIDI_STREAMING_RESTART_WARNING, 'OK', SnackbarType.WARNING);
return;
}

Expand Down Expand Up @@ -1432,7 +1433,7 @@ export class ChatComponent implements OnInit, AfterViewInit, OnDestroy {

startVideoRecording() {
if (this.sessionHasUsedBidi.has(this.sessionId)) {
this.openSnackBar(BIDI_STREAMING_RESTART_WARNING, 'OK');
this.openSnackBar(BIDI_STREAMING_RESTART_WARNING, 'OK', SnackbarType.WARNING);
return;
}
const videoContainer = this.chatPanel()?.videoContainer;
Expand Down Expand Up @@ -1526,7 +1527,7 @@ export class ChatComponent implements OnInit, AfterViewInit, OnDestroy {

protected handleEvalNotInstalled(errorMsg: string) {
if (errorMsg) {
this.openSnackBar(errorMsg, 'OK');
this.openSnackBar(errorMsg, 'OK', SnackbarType.ERROR);
}
}

Expand Down Expand Up @@ -1797,7 +1798,7 @@ export class ChatComponent implements OnInit, AfterViewInit, OnDestroy {
this.appName, this.evalSetId, this.updatedEvalCase!.evalId,
this.updatedEvalCase!)
.subscribe((res) => {
this.openSnackBar('Eval case updated', 'OK');
this.openSnackBar('Eval case updated', 'OK', SnackbarType.SUCCESS);
this.resetEditEvalCaseVars();
});
}
Expand Down Expand Up @@ -1873,7 +1874,7 @@ export class ChatComponent implements OnInit, AfterViewInit, OnDestroy {
dialogRef.afterClosed().subscribe((result: boolean) => {
if (result) {
this.evalTab()?.deleteEvalCase(this.evalCase!.evalId);
this.openSnackBar('Eval case deleted', 'OK');
this.openSnackBar('Eval case deleted', 'OK', SnackbarType.SUCCESS);
}
});
}
Expand Down Expand Up @@ -2063,7 +2064,7 @@ export class ChatComponent implements OnInit, AfterViewInit, OnDestroy {
const app = params['app'];
if (apps && apps.length && app) {
if (!apps.includes(app)) {
this.openSnackBar(`Agent '${app}' not found`, 'OK');
this.openSnackBar(`Agent '${app}' not found`, 'OK', SnackbarType.ERROR);
return;
}

Expand Down Expand Up @@ -2193,8 +2194,10 @@ export class ChatComponent implements OnInit, AfterViewInit, OnDestroy {
return undefined; // Index out of bounds
}

openSnackBar(message: string, action: string) {
this._snackBar.open(message, action);
openSnackBar(message: string, action: string, type: SnackbarType = SnackbarType.INFO) {
this._snackBar.open(message, action, {
panelClass: ['custom-snackbar', `snackbar-${type}`],
});
}

private processThoughtText(text: string) {
Expand Down Expand Up @@ -2279,18 +2282,18 @@ export class ChatComponent implements OnInit, AfterViewInit, OnDestroy {
JSON.parse(e.target.result as string) as Session;
if (!sessionData.userId || !sessionData.appName ||
!sessionData.events) {
this.openSnackBar('Invalid session file format', 'OK');
this.openSnackBar('Invalid session file format', 'OK', SnackbarType.ERROR);
return;
}
this.sessionService
.importSession(
sessionData.userId, sessionData.appName, sessionData.events)
.subscribe((res) => {
this.openSnackBar('Session imported', 'OK');
this.openSnackBar('Session imported', 'OK', SnackbarType.SUCCESS);
this.sessionTab?.refreshSession();
});
} catch (error) {
this.openSnackBar('Error parsing session file', 'OK');
this.openSnackBar('Error parsing session file', 'OK', SnackbarType.ERROR);
}
}
};
Expand Down
14 changes: 14 additions & 0 deletions src/app/core/constants/snackbar.constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/**
* Copyright 2025 Google LLC
* Licensed under the Apache License, Version 2.0
*/

/** Snackbar message types */
export const SnackbarType = {
ERROR: 'error',
SUCCESS: 'success',
WARNING: 'warning',
INFO: 'info',
} as const;

export type SnackbarType = typeof SnackbarType[keyof typeof SnackbarType];
55 changes: 55 additions & 0 deletions src/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -969,3 +969,58 @@ html.light-theme {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3) !important;
max-width: 800px !important;
}

// Custom snackbar styling for error and success messages
.custom-snackbar {
.mdc-snackbar__surface {
background: var(--chat-side-drawer-background-color) !important;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4) !important;
}
.mdc-snackbar__label {
color: var(--chat-side-drawer-color) !important;
font-size: 14px;
}
.mat-mdc-button {
color: var(--mdc-text-button-label-text-color) !important;
font-weight: 500;
}
}

// Error snackbar - red background
.snackbar-error {
.mdc-snackbar__surface {
background: var(--chat-error-color) !important;
}
.mdc-snackbar__label {
color: white !important;
}
.mat-mdc-button {
color: white !important;
}
}

// Success snackbar - green background
.snackbar-success {
.mdc-snackbar__surface {
background: var(--chat-panel-eval-pass-color) !important;
}
.mdc-snackbar__label {
color: white !important;
}
.mat-mdc-button {
color: white !important;
}
}

// Warning snackbar - orange background
.snackbar-warning {
.mdc-snackbar__surface {
background: var(--chat-warning-color) !important;
}
.mdc-snackbar__label {
color: var(--chat-side-drawer-color) !important;
}
.mat-mdc-button {
color: var(--chat-side-drawer-color) !important;
}
}