chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 13:30:30 +08:00
commit 914fea506e
2793 changed files with 802106 additions and 0 deletions
@@ -0,0 +1,41 @@
/**
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {NgModule} from '@angular/core';
import {RouterModule, Routes} from '@angular/router';
import {MainComponent} from './components/main/main.component';
import {LoginComponent} from './components/login/login.component';
import {AuthGuard} from './services/login/auth.guard';
import {SearchResultsComponent} from './components/main/search-results/search-results.component';
import {ManageSearchApplicationComponent} from './components/search-application/manage-search-application/manage-search-application.component';
const routes: Routes = [
{path: '', component: MainComponent, canActivate: [AuthGuard]},
{path: 'login', component: LoginComponent},
{path: 'search', component: SearchResultsComponent, canActivate: [AuthGuard]},
// TODO: Add new AddNewAgentComponent
{
path: 'manage-config',
component: ManageSearchApplicationComponent,
canActivate: [AuthGuard],
},
];
@NgModule({
imports: [RouterModule.forRoot(routes, {onSameUrlNavigation: 'reload'})],
exports: [RouterModule],
})
export class AppRoutingModule {}
@@ -0,0 +1,18 @@
<!--
Copyright 2025 Google LLC
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<router-outlet></router-outlet>
<app-footer *ngIf="showHeader"></app-footer>
@@ -0,0 +1,98 @@
/**
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
::ng-deep .tour-step {
background: white;
}
::ng-deep .tour-top{
margin: -1%;
}
::ng-deep .tour-left{
margin-left: -1%;
}
::ng-deep .tour-top-right{
margin-top: -1%;
}
::ng-deep .tour-bottom-right{
margin-top: 1%;
}
::ng-deep .tour-bottom-left{
margin-top: 1%;
}
::ng-deep .tour-bottom{
margin: 1%;
}
::ng-deep ngx-guided-tour .tour-content{
font-size: 17px !important;
padding-bottom: 20px !important;
}
::ng-deep ngx-guided-tour .tour-title{
font-size: 22px !important;
padding-bottom: 0 !important;
}
::ng-deep ngx-guided-tour .tour-step .tour-buttons .skip-button{
font-weight: 500 !important;
font-size: 17px !important;
line-height: 1.7 !important;
}
::ng-deep ngx-guided-tour .tour-step{
border-radius: 20px !important;
}
::ng-deep ngx-guided-tour .tour-step .tour-buttons .back-button{
font-weight: 500 !important;
font-size: 17px !important;
line-height: 1.7 !important;
}
::ng-deep ngx-guided-tour .tour-step .tour-buttons .next-button{
background: #4285F4;
padding-left: 16px !important;
padding-right: 16px !important;
padding-top: 6px !important;
padding-bottom: 6px !important;
font-size: 16px !important;
color: white;
font-weight: 500;
border-radius: 15px !important;
}
::ng-deep ngx-guided-tour .tour-step.tour-top .tour-block{
margin-bottom: 0 !important;
}
::ng-deep ngx-guided-tour .tour-step.tour-bottom-left .tour-block{
margin-top: 0 !important;
}
::ng-deep pre{
background: #485f84 !important;
}
div {
font-family: "Google Sans", sans-serif !important;
}
@@ -0,0 +1,49 @@
/**
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {TestBed} from '@angular/core/testing';
import {RouterTestingModule} from '@angular/router/testing';
import {AppComponent} from './app.component';
describe('AppComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [RouterTestingModule],
declarations: [AppComponent],
}).compileComponents();
});
it('should create the app', () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app).toBeTruthy();
});
it("should have as title 'pac-assist-ui'", () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app.title).toEqual('pac-assist-ui');
});
it('should render title', () => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.nativeElement as HTMLElement;
expect(compiled.querySelector('.content span')?.textContent).toContain(
'pac-assist-ui app is running!'
);
});
});
@@ -0,0 +1,55 @@
/**
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {Component} from '@angular/core';
import {Router, NavigationEnd, Event as NavigationEvent} from '@angular/router';
import {UserService} from './services/user/user.service';
import {AuthService} from './services/login/auth.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
})
export class AppComponent {
title = 'pac-assist-ui';
showHeader = true;
userInfo: any;
constructor(
private router: Router,
private userService: UserService,
public authService: AuthService
) {
this.router.events.subscribe((event: NavigationEvent) => {
if (event instanceof NavigationEnd) {
if (
event.url === '/login' ||
event.url === '/login/e2e' ||
(event.url.includes('login') && event.url.includes('email')) ||
(event.url.includes('login') && event.url.includes('tos')) ||
event.url.includes('reset-password') ||
event.url.includes('support-ticket')
) {
this.showHeader = false;
} else {
this.userInfo = this.userService.getUserDetails();
this.showHeader = true;
}
}
});
}
}
@@ -0,0 +1,167 @@
/**
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {NgModule} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import {AppRoutingModule} from './app-routing.module';
import {AppComponent} from './app.component';
import {HeaderComponent} from './components/header/header.component';
import {FooterComponent} from './components/footer/footer.component';
import {MainComponent} from './components/main/main.component';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
import {MatIconModule} from '@angular/material/icon';
import {MatAutocompleteModule} from '@angular/material/autocomplete';
import {MatButtonModule} from '@angular/material/button';
import {MatToolbarModule} from '@angular/material/toolbar';
import {HttpClientModule} from '@angular/common/http';
import {MatInputModule} from '@angular/material/input';
import {NgFor, PathLocationStrategy} from '@angular/common';
import {MatSelectModule} from '@angular/material/select';
import {MatFormFieldModule} from '@angular/material/form-field';
import {FormsModule, ReactiveFormsModule} from '@angular/forms';
import {MatGridListModule} from '@angular/material/grid-list';
import {MatCardModule} from '@angular/material/card';
import {MatProgressSpinnerModule} from '@angular/material/progress-spinner';
import {MatTooltipModule} from '@angular/material/tooltip';
import {LoginComponent} from './components/login/login.component';
import {initializeApp, provideFirebaseApp} from '@angular/fire/app';
import {
provideAnalytics,
getAnalytics,
ScreenTrackingService,
UserTrackingService,
} from '@angular/fire/analytics';
import {environment} from '../environments/environment';
import {provideAuth, getAuth} from '@angular/fire/auth';
import {MatMenuModule} from '@angular/material/menu';
import {MatDividerModule} from '@angular/material/divider';
import {MatChipsModule} from '@angular/material/chips';
import {LocationStrategy} from '@angular/common';
import {MarkdownModule} from 'ngx-markdown';
import {MatDialogModule} from '@angular/material/dialog';
import {CdkAccordionModule} from '@angular/cdk/accordion';
import {MatExpansionModule} from '@angular/material/expansion';
import {IonicRatingModule} from 'ionic-emoji-rating';
import {MatListModule} from '@angular/material/list';
import {MatTabsModule} from '@angular/material/tabs';
import {MatTableModule} from '@angular/material/table';
import {MatCheckboxModule} from '@angular/material/checkbox';
import {MatSlideToggleModule} from '@angular/material/slide-toggle';
import {MatButtonToggleModule} from '@angular/material/button-toggle';
import {MatSnackBarModule} from '@angular/material/snack-bar';
import {MatSortModule} from '@angular/material/sort';
import {MatPaginatorModule} from '@angular/material/paginator';
import {MatSidenavModule} from '@angular/material/sidenav';
import {NgIdleModule} from '@ng-idle/core';
import {ClipboardModule} from '@angular/cdk/clipboard';
import {MatProgressBarModule} from '@angular/material/progress-bar';
import {ManageSearchApplicationComponent} from './components/search-application/manage-search-application/manage-search-application.component';
import {SearchApplicationFormComponent} from './components/search-application/search-application-form/search-application-form.component';
import {PdfViewerModule} from 'ng2-pdf-viewer';
import 'prismjs';
import 'prismjs/components/prism-typescript.min.js';
import 'prismjs/plugins/line-numbers/prism-line-numbers.js';
import 'prismjs/plugins/line-highlight/prism-line-highlight.js';
import {FlexLayoutModule} from '@angular/flex-layout';
import {MatSliderModule} from '@angular/material/slider';
import {MatStepperModule} from '@angular/material/stepper';
import {ChatInputComponent} from './components/main/chat-input/chat-input.component';
import {SearchResultsComponent} from './components/main/search-results/search-results.component';
import {ToastMessageComponent} from './components/toast-message/toast-message.component';
import {TruncatePipe} from './pipes/truncate.pipe';
@NgModule({
declarations: [
AppComponent,
HeaderComponent,
FooterComponent,
MainComponent,
LoginComponent,
ChatInputComponent,
SearchResultsComponent,
ToastMessageComponent,
SearchApplicationFormComponent,
ManageSearchApplicationComponent,
TruncatePipe,
],
imports: [
BrowserModule,
AppRoutingModule,
BrowserAnimationsModule,
MatSnackBarModule,
MatPaginatorModule,
MatSortModule,
MatSliderModule,
MatToolbarModule,
MatButtonModule,
MatIconModule,
HttpClientModule,
FormsModule,
MatFormFieldModule,
MatSelectModule,
NgFor,
MatInputModule,
MatGridListModule,
MatCardModule,
MatTooltipModule,
MatProgressSpinnerModule,
MatMenuModule,
MatDividerModule,
MatChipsModule,
ReactiveFormsModule,
MatMenuModule,
MatDialogModule,
CdkAccordionModule,
IonicRatingModule,
MarkdownModule.forRoot(),
MatExpansionModule,
MatListModule,
MatTabsModule,
MatCheckboxModule,
MatTableModule,
MatSlideToggleModule,
MatButtonToggleModule,
MatSidenavModule,
MatAutocompleteModule,
environment.requiredLogin === 'True'
? [
provideFirebaseApp(() => initializeApp(environment.firebase)),
provideAuth(() => getAuth()),
]
: [],
environment.requiredLogin === 'True'
? [provideAnalytics(() => getAnalytics())]
: [],
FlexLayoutModule,
NgIdleModule.forRoot(),
ClipboardModule,
MatStepperModule,
MatProgressBarModule,
PdfViewerModule,
],
providers: [
{provide: LocationStrategy, useClass: PathLocationStrategy},
environment.requiredLogin === 'True'
? [
ScreenTrackingService, // Automatically track screen views
UserTrackingService, // Automatically track user interactions
]
: [],
],
bootstrap: [AppComponent],
})
export class AppModule {}
@@ -0,0 +1,23 @@
<!--
Copyright 2025 Google LLC
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<p>
<mat-toolbar class="container footer" color="primary">
<div class="links-weight">
<span>Powered by <span class="genAI">Vertex AI</span></span>
</div>
</mat-toolbar>
</p>
@@ -0,0 +1,71 @@
/**
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
.container {
padding-left: 4rem;
padding-right: 4rem;
}
.footer {
position: fixed;
bottom: 0;
}
.genAI {
background: linear-gradient(86.95deg, #34A853 -65.34%, #FBBC05 -12.75%, #EA4335 25.15%, #1A73E8 103.88%);
-webkit-background-clip: text;
-moz-background-clip: text;
background-clip: text;
color: transparent;
}
:host ::ng-deep .mat-toolbar.mat-primary {
background-color: transparent;
color: #504747;
box-shadow: none;
justify-content: center;
}
.line-spacer {
flex: 1 1 auto;
}
.links-weight {
margin-left: 10px;
margin-right: 10px;
font-size: 14px;
font-weight: 400;
cursor: pointer;
}
.links-bold {
margin-right: 10px;
color: #5f6368;
font-family: 'Google Sans', sans-serif !important;
.blue{
color: #4285f4;
}
.red{
color: #ea4335;
}
.yellow{
color: #fbbc04;
}
.green{
color: #34a853;
}
}
@@ -0,0 +1,38 @@
/**
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {ComponentFixture, TestBed} from '@angular/core/testing';
import {FooterComponent} from './footer.component';
describe('FooterComponent', () => {
let component: FooterComponent;
let fixture: ComponentFixture<FooterComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [FooterComponent],
}).compileComponents();
fixture = TestBed.createComponent(FooterComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,24 @@
/**
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {Component} from '@angular/core';
@Component({
selector: 'app-footer',
templateUrl: './footer.component.html',
styleUrls: ['./footer.component.scss'],
})
export class FooterComponent {}
@@ -0,0 +1,66 @@
<!--
Copyright 2025 Google LLC
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<mat-progress-bar *ngIf="showLoading" mode="indeterminate"></mat-progress-bar>
<nav class="navbar navbar-expand-lg navbar-light bg-light top-nav fixed-top">
<div class="parent-container">
<div class="left-heading" [matTooltip]="'Home'" [routerLink]="'/'">
<span class="logo-text gradient-text">{{headerTitle}}</span>
</div>
<app-chat-input (emitSearch)="this.searchTerm($event)" *ngIf="this.isSearchRoute()"></app-chat-input>
<div class="right-menu-items">
<div *ngIf="authService.isUserLoggedIn()" class="profile-container" [matMenuTriggerFor]="userAccountMenu">
<div class="circle" *ngIf="_UserService.getUserDetails().photoURL && requiredLogin === 'True'"><img referrerpolicy="no-referrer"
[src]="_UserService.getUserDetails().photoURL" class="user-icon" /> </div>
<div class="circle bypassLoginLogo" *ngIf="requiredLogin !== 'True'"><mat-icon>person</mat-icon></div>
</div>
</div>
</div>
<mat-menu #userAccountMenu class="profile-menu" [overlapTrigger]="false" yPosition="below">
<div class="card-inside-right">
<div class="name">
<span class="sub-title">
{{ requiredLogin === 'True' ? _UserService.getUserDetails().email : 'Guest'}}
</span>
<div *ngIf="requiredLogin === 'True' && _UserService.getUserDetails().photoURL ">
<img [src]="_UserService.getUserDetails().photoURL" class="user-icon container-circle" />
</div>
<div *ngIf="requiredLogin !== 'True'" class="user-icon container-circle bypassMenuContainer">
<mat-icon class="bypassMenuLogo">person</mat-icon>
</div>
</div>
<div class="action-item-container">
<br>
<div class="logout" (click)="goToManageConfig()">
<div class="logout-container">
<span class="material-symbols-rounded logo">settings</span>
<div class="text">
<span class="sub-title">Manage AgentBuilder Config</span>
</div>
</div>
</div>
<div class="logout logout-button" (click)="logout()" *ngIf="requiredLogin === 'True'">
<div class="logout-container">
<span class="material-symbols-rounded logo logout-button-text">exit_to_app</span>
<div class="text">
<span class="sub-title logout-button-text">Logout</span>
</div>
</div>
</div>
</div>
</div>
</mat-menu>
</nav>
@@ -0,0 +1,644 @@
/**
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
.container {
margin: auto;
}
.logo-text {
font-family: Google Sans,Helvetica Neue,sans-serif;
font-style:normal;
font-size: 22px;
line-height: 128.7%;
letter-spacing: normal;
color: #2d2c2cbf;
}
.gradient-text {
font-family: Google Sans,Helvetica Neue,sans-serif;
font-style:normal;
font-size: 30px;
line-height: 128.7%;
letter-spacing: normal;
background: linear-gradient(89.9deg, rgba(66, 133, 244, 0.95) 21.17%, #A488F5 44.34%, rgba(234, 67, 53, 0.88) 81.26%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
text-fill-color: transparent;
}
.links-pointer {
cursor: pointer;
}
.logo-icon {
height: 35px;
width: 125px;
cursor: pointer;
}
// .profile-container {
// margin-left: 1%;
// font-family: 'Google Sans', sans-serif !important;
// .circle {
// display: flex;
// justify-content: center;
// width: 40px;
// height: 40px;
// border: 5px solid rgb(255, 255, 255);
// border-radius: 60%;
// overflow: hidden;
// }
// .circle img {
// width: 100%;
// height: 100%;
// object-fit: cover;
// }
// }
.card-inside-right {
display: flex;
flex-direction: column;
align-items: flex-start;
color: #3E4245;
font-size: 1rem;
font-weight: 400;
font-family: 'Google Sans', sans-serif !important;
border-radius: 5px;
letter-spacing: 0.4px;
.container-circle {
width: 100%;
height: 100%;
object-fit: cover;
border-radius: 50px;
padding-top: 4%;
padding-bottom: 4%;
}
.name {
display: flex;
align-items: center;
font-size: 1rem;
color: #3E4245;
font-weight: 500;
padding: 1.3em 0.4em;
flex-direction: column;
gap: 10px;
background: #DDE3EA !important;
width: -webkit-fill-available;;
.sub-title {
color: #3E4245;
}
margin: auto;
}
.company {
display: flex;
align-items: center;
padding: 0.3em 1em;
width: -webkit-fill-available;;
.logo {
padding-right: 2%;
color: #5F6368;
}
.text {
min-width: 100px;
.sub-title {
font-size: 0.85rem;
margin-bottom: 10px;
color: #3E4245;
}
}
margin-bottom: 10px;
}
.logout {
background: #DDE3EA;
border-radius: 20px;
font-size: 1rem;
font-weight: 500;
font-family: 'Google Sans', sans-serif !important;
margin-bottom: 1em;
cursor: pointer;
width: -webkit-fill-available;;
padding: 0.3em 1em;
.logout-container {
display: flex;
align-items: center;
.logo {
padding-right: 4%;
color: #5F6368;
}
.text {
.sub-title {
color: #3E4245;
font-size: 0.85rem;
font-family: 'Google Sans', sans-serif !important;
}
}
}
}
}
//------------css of card end-------------------//
.image {
width: 35px;
height: 35px;
}
.fixed-top {
z-index: 1000;
position: relative;
}
.top-nav {
height: 60px;
background-color: transparent;
// box-shadow: (0px 4px 16px rgba(195, 209, 226, 0.25));
.parent-container {
// width: 1600px;
display: flex;
margin: auto;
justify-content: space-between;
/* border: 1px solid black; */
height: 100%;
align-items: center;
padding: 0% 1.5%;
.left-heading {
display: flex;
align-items: center;
cursor: pointer;
}
.logo-icon {
height: 4.2em;
width: 4em;
cursor: pointer;
background-image: url('../../../assets/images/new-pdc-logo.svg');
background-size: 443px;
background-repeat: round;
border-radius: 28px;
margin-right:1em;
}
.user-icon {
height: 50px;
width: 50px;
cursor: pointer;
border-radius: 28px;
}
.right-menu-items {
display: flex;
align-items: center;
.profile-container {
margin-left: 1%;
font-family: 'Google Sans', sans-serif !important;
.circle {
display: flex;
justify-content: center;
width: 35px;
height: 35px;
border-radius: 60%;
overflow: hidden;
}
.circle img {
width: 100%;
height: 100%;
object-fit: cover;
}
}
}
}
}
.icon-color {
color: #5F6368;
font-size: 24px;
}
::ng-deep .quick-link.mat-mdc-menu-panel.mat-mdc-menu-panel {
width: 420px !important;
display: flex;
flex-direction: column;
background: #F0F4F9;
.mat-mdc-menu-content {
padding: 0.5rem;
}
.ql-container {
width: 100%;
display: flex;
flex-direction: column;
.ql-child-container{
display: flex;
flex-direction: row;
text-wrap: nowrap ;
justify-content: space-between;
align-items: flex-end;
}
a, a:active{
text-decoration: none;
}
.align-link-content{
display: flex;
flex-direction: row;
align-items: baseline;
gap: 10px;
width: 100% !important;
box-shadow: 0px 4px 4px -6px #b9b8b8 !important;
}
.no-box-shadow{
box-shadow: none !important;
}
.icon-container {
display: flex;
alignment-baseline: after-edge;
mat-icon {
color: #161A1D !important;
}
}
}
.ql-header{
margin: 1rem;
margin-left: 1.7rem;
}
.avatar {
width: 40px;
height: 40px; //<--use the size you choose
border-radius: 100%;
text-align: center;
}
div.link-label {
color: #161A1D;
font-size: 16px;
font-style: normal;
font-weight: 400;
line-height: 24px;
letter-spacing: 0.4px;
height: 50px;
margin: 0px 5px;
width: 57%;
}
.avatar span {
line-height: 40px;
font-size: 16px;
}
.link-button {
margin: 1rem;
height: 41px;
width: 100% !important;
}
}
.notification-banner-container {
z-index: 999;
position: relative;
width: inherit;
display: flex;
flex-direction: row;
height: 8%;
background: #F6F9FE;
align-content: center;
flex-wrap: wrap;
padding-left: 1%;
gap: 1%;
margin-bottom: 0.2%;
.amber {
color: #fbc645;
}
.red {
color: red;
}
.blue {
color: #4285F4;
}
}
.red-border {
border-left: 5px solid red;
}
.amber-border {
border-left: 5px solid #fbc645;
}
.blue-border {
border-left: 5px solid #4285F4;
}
.notification-text {
display: flex;
flex-direction: row;
align-items: center;
gap: 15px;
width: 70%;
}
.notification-buttons-container {
width: 26%;
justify-content: flex-end;
display: flex;
flex-direction: row;
gap: 15px;
button {
font-family: 'Google Sans', sans-serif !important;
font-style: normal;
font-weight: 500;
line-height: 23px;
font-size: 14px;
}
.dismiss-button {
letter-spacing: 0.4px;
color: #4285F4;
}
.learn-more-button {
color: #fff !important;
background: #4285F4 !important;
border-radius: 30px;
letter-spacing: unset;
box-shadow: none;
}
}
::ng-deep .profile-menu.mat-mdc-menu-panel.mat-mdc-menu-panel {
max-width: 320px !important;
width: 300px !important;
}
::ng-deep .profile-menu .mat-mdc-menu-content {
padding: 0% !important;
}
.action-item-container {
width: -webkit-fill-available;
padding-inline: 5%;
padding-top: 2%;
padding-bottom: 2%;
}
.logout-button {
background: white !important;
border: 1px solid #4285F4 !important;
}
.logout-button-text {
color: #4285F4 !important;
}
.app-version-chip {
font-size: 9px;
border-radius: -2px;
padding-inline: 8px;
line-height: 16px;
border-radius: 11px;
margin-left: 8px;
color: #474c55;
background: linear-gradient(92.1deg, #bdd1f3 19.16%, rgba(199, 183, 245, 0.9) 79%, rgba(235, 165, 158, 0.9) 135.24%), linear-gradient(272.04deg, rgba(158, 188, 237, 0.7) 14.93%, rgba(158, 188, 237, 0) 104.51%) !important;
}
.bypassLoginLogo {
background: aliceblue;
color: cornflowerblue;
align-items: center;
cursor: pointer;
}
.bypassMenuContainer {
display: flex;
justify-content: center;
background: aliceblue;
width: 42% !important;
height: 40% !important;
border-radius: 65px !important;
}
.bypassMenuLogo {
font-size: 6rem;
height: 6rem;
width: 6rem;
}
.chat-footer {
min-height: 60px;
display: flex;
border: 1px solid #DDE6F6;
box-shadow: 0px 4px 18px rgba(195, 209, 226, 0.25);
border-radius: 30px;
width: 66%;
margin-top: 25px;
// box-shadow: 5px 4px 24px rgba(66, 133, 244, 0.15);
.chat-textarea {
flex-grow: 1;
display: flex;
align-items: center;
padding-left: 2%;
input {
width: 100%;
height: 45px;
border: 0px;
border-radius: 4px;
resize: none;
outline: none;
padding: 14px;
font-family: 'Google Sans', sans-serif !important;
box-sizing: border-box;
-ms-overflow-style: none;
scrollbar-width: none;
font-size: 0.95rem;
line-height: 1.1rem;
color: #534646;
}
input::-webkit-scrollbar {
display: none;
}
}
.micIcon {
display: flex;
align-items: center;
color: #1F1F1F;
margin-top: 2px;
padding-right: 1%;
img {
max-width: 40%;
cursor: pointer;
}
}
}
.blue-dot {
/* Ellipse 1226 */
content: '';
width: 8px;
height: 8px;
left: 1503px;
top: 445px;
display: flow-root;
border-radius: 15px;
/* Blue */
background: #4285F4;
animation: blue-transform 2s infinite;
}
.green-dot {
content: '';
width:8px;
height: 8px;
left: 1522px;
top: 445px;
display: flow-root;
border-radius: 15px;
/* Green */
background: #0F9D58;
animation: green-transform 2s infinite;
}
.yellow-dot {
content: '';
width: 8px;
height: 8px;
left: 1541px;
top: 445px;
display: flow-root;
border-radius: 15px;
/* Yellow */
background: #F4B400;
animation: yellow-transform 2s infinite;
}
.recording-container {
display: flex;
flex-direction: row;
gap: 10px;
cursor: pointer;
align-items: baseline;
}
@keyframes blue-transform {
0% {
transform: translateY(-14px)
}
33% {
transform: translateY(0)
}
66% {
transform: translateY(0)
}
100% {
transform: translateY(-14px)
}
}
@keyframes green-transform {
0% {
transform: translateY(0)
}
33% {
transform: translateY(-14px)
}
66% {
transform: translateY(0)
}
100% {
transform: translateY(0)
}
}
@keyframes yellow-transform {
0% {
transform: translateY(0)
}
33% {
transform: translateY(0)
}
66% {
transform: translateY(-14px)
}
100% {
transform: translateY(0)
}
}
@@ -0,0 +1,38 @@
/**
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {ComponentFixture, TestBed} from '@angular/core/testing';
import {HeaderComponent} from './header.component';
describe('HeaderComponent', () => {
let component: HeaderComponent;
let fixture: ComponentFixture<HeaderComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [HeaderComponent],
}).compileComponents();
fixture = TestBed.createComponent(HeaderComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,76 @@
/**
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {Component, EventEmitter, Output} from '@angular/core';
import {DomSanitizer} from '@angular/platform-browser';
import {MatIconRegistry} from '@angular/material/icon';
import {UserService} from 'src/app/services/user/user.service';
import {AuthService} from '../../services/login/auth.service';
import {environment} from 'src/environments/environment';
import {Router} from '@angular/router';
const GOOGLE_CLOUD_ICON = `<svg width="694px" height="558px" viewBox="0 0 694 558" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="google-cloud-1" fill-rule="nonzero">
<path d="M440.545766,152.920305 L461.717489,152.920305 L522.033632,92.9270295 L525,67.4718307 C412.772422,-31.0513593 241.450145,-20.4353843 142.396729,91.1914478 C114.856041,122.200508 94.8766816,159.08162 84,199 C90.7179504,196.251996 98.1629517,195.8181 105.171723,197.72724 L225.774927,177.941608 C225.774927,177.941608 231.911237,167.846308 235.081179,168.482688 C288.737536,109.877878 379.037259,103.051256 440.981997,152.920305 L440.545766,152.920305 Z" id="Path" fill="#EA4335"></path>
<path d="M607.866504,200.682375 C594.001378,149.778725 565.57351,104.008948 526.012848,69 L441.426861,153.404341 C477.150633,182.525289 497.497778,226.409746 496.625757,272.440567 L496.625757,287.436115 C538.221135,287.436115 571.910193,321.081832 571.910193,362.558879 C571.910193,404.064932 538.192067,437.681644 496.625757,437.681644 L346.02782,437.681644 L331,452.880226 L331,542.998538 L346.02782,557.994086 L496.625757,557.994086 C582.955785,558.6612 659.548251,502.826713 685.185654,420.568736 C710.764921,338.281754 679.372184,248.946575 607.866504,200.682375 L607.866504,200.682375 Z" id="Path" fill="#4285F4"></path>
<path d="M194.75446,555.998385 L346,555.998385 L346,436.702654 L194.75446,436.702654 C183.982485,436.702654 173.327279,434.43008 163.518651,430 L142.266623,436.47252 L81.3130068,496.134769 L76,517.076966 C110.184236,542.506777 151.900097,556.170985 194.75446,555.998385 L194.75446,555.998385 Z" id="Path" fill="#34A853"></path>
<path d="M195.36137,165 C111.412398,165.496534 37.0600945,219.208571 10.2827643,298.683735 C-16.4945658,378.158899 10.1952567,465.881761 76.7302131,517 L164.383674,429.422857 C126.347031,412.257155 109.458061,367.550553 126.638723,329.547028 C143.819384,291.543502 188.564945,274.669238 226.601588,291.834941 C243.344712,299.412331 256.762546,312.818482 264.346539,329.547028 L352,241.969885 C314.692587,193.270582 256.733377,164.797082 195.36137,165 L195.36137,165 Z" id="Path" fill="#FBBC05"></path>
</g>
</g>
</svg>`;
@Component({
selector: 'app-header',
templateUrl: './header.component.html',
styleUrls: ['./header.component.scss'],
})
export class HeaderComponent {
headerTitle: string = environment.chatbotName;
requiredLogin: string = environment.requiredLogin;
showLoading = false;
@Output() emitSearch: EventEmitter<string> = new EventEmitter();
constructor(
iconRegistry: MatIconRegistry,
sanitizer: DomSanitizer,
public _UserService: UserService,
public authService: AuthService,
private router: Router
) {
iconRegistry.addSvgIconLiteral(
'google-cloud-icon',
sanitizer.bypassSecurityTrustHtml(GOOGLE_CLOUD_ICON)
);
}
isSearchRoute(): boolean {
return this.router.url.startsWith('/search');
}
searchTerm(term: string) {
this.emitSearch.emit(term);
}
goToManageConfig() {
this.router.navigateByUrl('/manage-config');
}
logout() {
this.authService.logout();
}
}
@@ -0,0 +1,48 @@
<!--
Copyright 2025 Google LLC
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<div class="login-container">
<div class="left-container">
<div style="display: flex ;flex-direction:column;justify-content: center;align-items: center;">
<div class="parent-container"
style="margin: 0rem;width:90%;text-align: center;font-weight: 500; font-size: 1.5rem;">
<p class="login-app-text">
Quickly setup a <span class="linear-text">Vertex AI</span> powered Chat application
</p>
</div>
</div>
</div>
<div class="right-container">
<div class="login-card">
<p class="heading">{{chatbotName}}</p>
<div class="login-buttons-container">
<div>
<div class="register-text">
Single Sign on with Google Account.
</div>
<button (click)="loginWithGoogle()" class="login-button">
<span class="mat-button-wrapper"> Login in with Google </span>
</button>
</div>
</div>
<div class="spinner">
<mat-spinner [diameter]="50" *ngIf="this.loader"></mat-spinner>
</div>
</div>
</div>
</div>
@@ -0,0 +1,545 @@
/**
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
.login-container {
display: flex;
min-height: 100vh;
max-height: 100%;
.spinner{
display: flex;
justify-content: center;
margin: 1em;
}
.linear-text {
background: linear-gradient(89.9deg, rgba(66, 133, 244, 0.95) 21.17%, #A488F5 44.34%, rgba(234, 67, 53, 0.88) 81.26%);
-webkit-background-clip: text;
-moz-background-clip: text;
background-clip: text;
color: transparent;
}
.heading {
font-family: 'Google Sans', sans-serif !important;
font-size: 32px;
font-style: normal;
font-weight: 500;
line-height: 150.7%;
color: rgba(45, 44, 44, 0.7490196078);
}
.left-container {
flex: 0 1 55%;
min-height: 100%;
max-width: 60%;
align-items: center;
background: #fafcfe;
padding: .5em;
overflow: auto;
display: flex;
justify-content: center;
.login-app-text-sub {
color: #161A1D;
font-size: 1rem;
font-style: normal;
font-weight: 400;
line-height: 150.7%;
}
.login-app-text {
// margin-bottom: 2rem;
line-height: 1.5em;
}
.expand-button {
--mdc-fab-container-color: #4285F41A;
--mdc-fab-icon-color: #000000;
border: 1px solid #4285F4;
--mat-mdc-fab-color: #4285F4;
height: 2.2rem;
font-size: 0.8rem;
font-weight: 400;
box-shadow: none;
}
.expand-less-button {
--mdc-fab-container-color: rgba(92, 95, 97, 0.10);
--mdc-fab-icon-color: #000000;
border: 1px solid rgba(92, 95, 97, 0.50);
--mat-mdc-fab-color: rgba(92, 95, 97, 0.50);
margin-bottom: 1em;
height: 2.2rem;
font-size: 0.8rem;
font-weight: 400;
box-shadow: none
}
}
.right-container {
flex: 0 1 45%;
min-height: 100%;
max-width: 80%;
align-items: flex-start;
display: flex;
justify-content: center;
overflow-y: auto;
scrollbar-width: thin;
.login-card {
flex-direction: column;
display: flex;
max-width: 100%;
padding: 3%;
margin: auto;
.logo-icon {
height: 145px;
width: 100%;
// margin-bottom: 2rem;
}
.login-button {
background: #4285F4;
box-sizing: border-box;
border: 1px solid #e0e0e0;
color: #fff;
position: relative;
user-select: none;
cursor: pointer;
outline: none;
-webkit-tap-highlight-color: transparent;
display: inline-block;
font-family: 'Google Sans', sans-serif !important;
font-size: 14px;
font-weight: 500;
white-space: nowrap;
text-decoration: none;
vertical-align: baseline;
text-align: center;
margin: 0;
min-width: 100px;
line-height: 36px;
padding: 0 16px;
border-radius: 4px;
overflow: visible;
transform: translate3d(0, 0, 0);
transition: background 400ms cubic-bezier(0.25, 0.8, 0.25, 1), box-shadow 280ms cubic-bezier(0.4, 0, 0.2, 1);
}
.register-text {
display: flex;
flex-direction: column;
align-items: flex-start;
margin: 1em 0em;
font-weight: 400;
color: #161A1D
}
.register-button {
background: white;
box-sizing: border-box;
border: 1px solid #4285F4;
color: #4285F4;
position: relative;
user-select: none;
cursor: pointer;
outline: none;
-webkit-tap-highlight-color: transparent;
display: inline-block;
font-family: 'Google Sans', sans-serif !important;
font-size: 14px;
font-weight: 500;
white-space: nowrap;
text-decoration: none;
vertical-align: baseline;
text-align: center;
margin: 0;
min-width: 64px;
line-height: 36px;
padding: 0 16px;
border-radius: 4px;
overflow: visible;
transform: translate3d(0, 0, 0);
transition: background 400ms cubic-bezier(0.25, 0.8, 0.25, 1), box-shadow 280ms cubic-bezier(0.4, 0, 0.2, 1);
}
}
}
.slide {
width: 100%;
min-height: 15rem;
border-radius: 10px;
background-size: cover;
background-position: center;
font-size: 1rem;
font-style: normal;
font-weight: 400;
line-height: 200.7%;
padding-left: 0px;
.feature-heading {
font-weight: 700;
}
}
.slider {
position: relative;
height: 100%;
}
.dotsContainer {
display: flex;
justify-content: center;
align-items: center;
margin-top: 2em;
}
.dot {
margin: 0 3px;
cursor: pointer;
font-size: 18px;
color: #D0D3D7;
}
.active {
color: #1A73E8;
background-color: #D0D3D7;
padding: 2px;
border-radius: 50%;
font-size: 12px;
}
.material-symbols-outlined {
font-variation-settings:
'FILL' 1,
'wght' 400,
'GRAD' 0,
'opsz' 18
}
h6 {
position: relative;
z-index: 1;
overflow: hidden;
text-align: center;
font-size: 1.2em;
font-style: normal;
font-weight: 500;
line-height: 200.7%;
font-family: 'Google Sans', sans-serif !important;
}
h6:before,
h6:after {
position: absolute;
top: 51%;
overflow: hidden;
width: 50%;
height: 1px;
content: '\a0';
background: linear-gradient(86.95deg, #34A853 -65.34%, #FBBC05 -12.75%, #EA4335 25.15%, #1A73E8 103.88%);
}
h6:before {
margin-left: -50%;
text-align: right;
}
}
.login-buttons-container {
padding-inline: 4%;
gap: 12px;
display: flex;
flex-direction: column;
background: #F7F9FF;
padding-top: 4%;
padding-bottom: 4%;
border-radius: 5px;
}
.back-button {
color: #5C5F61 !important;
font-weight: 400 !important;
}
.help-container {
padding-top: 2%;
text-align: center;
}
.blue {
color: #4285F4;
cursor: pointer;
}
mat-form-field {
width: 100%;
}
.user-name-container{
display: flex;
flex-direction: row;
gap: 3%;
}
.error-message {
font-weight: 500;
font-size: 0.8rem;
letter-spacing: -0.005rem;
color: #ed0c0c;
.mat-mdc-form-field-subscript-wrapper {
margin-top: -10px;
}
}
.forgot-password-container {
display: flex;
flex-direction: column;
text-align: center;
padding: 4%;
}
.forgot-password-icon-container {
width: 100%;
height: 100%;
mat-icon{
font-size: 105px;
height: 100%;
width: 100%;
padding: 1%;
padding-top: 7%;
color: #3467bb;
}
}
.forgot-password-heading-container {
font-size: 29px;
font-weight: 600;
font-family: 'Google Sans', sans-serif !important;
line-height: 2.5em;
}
.forgot-password-subtext-container{
display: flex;
flex-direction: column;
text-align: center;
font-size: 19px;
gap: 10px;
line-height: 1.5em;
mat-icon{
color: #4385f4;
}
}
.logo-icon {
height: 170px;
width: 100%;
}
.login-button {
width: 100%;
background: #4285F4;
box-sizing: border-box;
border: 1px solid #e0e0e0;
color: #fff;
position: relative;
user-select: none;
cursor: pointer;
outline: none;
-webkit-tap-highlight-color: transparent;
display: inline-block;
font-family: 'Google Sans', sans-serif !important;
font-size: 14px;
font-weight: 500;
white-space: nowrap;
text-decoration: none;
vertical-align: baseline;
text-align: center;
margin: 0;
min-width: 64px;
line-height: 44px;
padding: 0 16px;
border-radius: 4px;
overflow: visible;
transform: translate3d(0, 0, 0);
transition: background 400ms cubic-bezier(0.25, 0.8, 0.25, 1), box-shadow 280ms cubic-bezier(0.4, 0, 0.2, 1);
}
.register-text {
display: flex;
flex-direction: column;
align-items: flex-start;
margin-bottom: 0.3em;
font-weight: 400;
color: #5C5F61
}
.register-button {
width: 100%;
background: white;
box-sizing: border-box;
border: 1px solid #4285F4;
color: #4285F4;
position: relative;
user-select: none;
cursor: pointer;
outline: none;
-webkit-tap-highlight-color: transparent;
display: inline-block;
font-family: 'Google Sans', sans-serif !important;
font-size: 14px;
font-weight: 500;
white-space: nowrap;
text-decoration: none;
vertical-align: baseline;
text-align: center;
margin: 0;
min-width: 64px;
line-height: 44px;
padding: 0 16px;
border-radius: 4px;
overflow: visible;
transform: translate3d(0, 0, 0);
transition: background 400ms cubic-bezier(0.25, 0.8, 0.25, 1), box-shadow 280ms cubic-bezier(0.4, 0, 0.2, 1);
}
.send-email-button {
background: #40bd40 !important;
font-weight: 600 !important;
font-size: 16px !important;
}
.close-button {
float: right !important;
}
.forgot-password-link-container {
width: 100%;
text-align: end;
display: flex;
flex-direction: column;
padding-bottom: 4%;
z-index: 100;
position: relative;
}
.button-css {
display: flex;
justify-content: center;
.ok-button{
justify-content: center;
display: flex;
width: 25%;
background: #4285F4;
box-sizing: border-box;
border: 1px solid #e0e0e0;
color: #fff;
position: relative;
user-select: none;
cursor: pointer;
outline: none;
-webkit-tap-highlight-color: transparent;
font-family: 'Google Sans', sans-serif !important;
font-size: 14px;
font-weight: 500;
white-space: nowrap;
text-decoration: none;
vertical-align: baseline;
text-align: center;
margin: 0;
min-width: 64px;
line-height: 44px;
padding: 0 16px;
border-radius: 4px;
overflow: visible;
transform: translate3d(0, 0, 0);
transition: background 400ms cubic-bezier(0.25, 0.8, 0.25, 1), box-shadow 280ms cubic-bezier(0.4, 0, 0.2, 1);
}
}
.icon-css {
font-size: 40px;
color: #f3f30aed
}
.text-css {
font-size: 15px;
line-height: 1.45em;
font-family: "Google Sans", sans-serif !important;
color: #645a5a;
}
.logo-container {
display: flex;
flex-direction: row;
justify-content: center;
text-align: center;
mat-icon{
height: 20% !important;
width: 100% !important;
font-size: 70px !important;
color: #4285F4 !important;
}
}
.heading-span {
display: flex;
flex-direction: row;
gap: 8px;
align-items: center;
padding: 2%;
width: 100%;
justify-content: center;
font-size: 18px;
font-weight: 700;
padding-left: 15%;
color: #4285F4 !important;
}
.heading-container {
display: flex;
flex-direction: row;
justify-content: center;
background: aliceblue;
}
.sub-parent-container {
display: flex;
flex-direction: column;
text-align: center;
gap: 15px;
font-style: normal;
line-height: normal;
font-family: 'Google Sans', sans-serif !important;
}
.info {
padding-bottom: 10px !important;
small {
color: #a8a8b5;
}
}
@@ -0,0 +1,38 @@
/**
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {ComponentFixture, TestBed} from '@angular/core/testing';
import {LoginComponent} from './login.component';
describe('LoginComponent', () => {
let component: LoginComponent;
let fixture: ComponentFixture<LoginComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [LoginComponent],
}).compileComponents();
fixture = TestBed.createComponent(LoginComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,92 @@
/**
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {Component, NgZone, inject} from '@angular/core';
import {Auth, signInWithPopup, GoogleAuthProvider} from '@angular/fire/auth';
import {Router} from '@angular/router';
import {AuthService} from 'src/app/services/login/auth.service';
import {MatSnackBar} from '@angular/material/snack-bar';
import {environment} from 'src/environments/environment';
import {ToastMessageComponent} from '../toast-message/toast-message.component';
const HOME_ROUTE = '/';
interface LooseObject {
[key: string]: any;
}
@Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.scss'],
})
export class LoginComponent {
private readonly auth: Auth = inject(Auth);
private readonly provider: GoogleAuthProvider = new GoogleAuthProvider();
loader = false;
chatbotName: string = environment.chatbotName;
constructor(
private authService: AuthService,
private router: Router,
public ngZone: NgZone,
private _snackBar: MatSnackBar
) {
this.provider.setCustomParameters({
prompt: 'select_account',
});
}
loginWithGoogle() {
this.loader = true;
signInWithPopup(this.auth, this.provider)
.then((result: any) => {
const user = result.user.toJSON();
this.ngZone.run(() => {
this.authService.saveUserSession(user.stsTokenManager.accessToken);
this.redirect(user);
});
})
.catch(error => {
this.loader = false;
if (error.message !== 'Firebase: Error (auth/popup-closed-by-user).') {
this._snackBar.openFromComponent(ToastMessageComponent, {
panelClass: ['red-toast'],
verticalPosition: 'top',
horizontalPosition: 'right',
duration: 5000,
data: {
text: 'Error with SignIn. Please try again later !!!',
icon: 'cross-in-circle-white',
},
});
}
console.error(`Error: ${error}`);
});
}
redirect(user: any) {
const userDetails: LooseObject = {};
userDetails['name'] = user.displayName;
userDetails['email'] = user.email;
userDetails['photoURL'] = user.photoURL;
userDetails['domain'] = user.domain;
(userDetails['uid'] = user.uid),
localStorage.setItem('USER_DETAILS', JSON.stringify(userDetails));
this.loader = false;
this.router.navigate([HOME_ROUTE]);
}
}
@@ -0,0 +1,43 @@
<!--
Copyright 2025 Google LLC
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<div class="chat-footer">
<div class="chat-textarea">
<mat-chip-grid class="intent-chip-textarea intent-chip"
style="width: 100%;" #chipGrid aria-label="Enter intent">
<input [matChipInputFor]="chipGrid"
placeholder="Ask your question here"
[(ngModel)]="term" (keydown.enter)="searchTerm()" />
</mat-chip-grid>
</div>
<div class="micIcon">
<button mat-icon-button (click)="startRecording()" matTooltip="Record"
*ngIf="!isRecording"><mat-icon>mic_none</mat-icon>
</button>
<span class="recording-container" *ngIf="isRecording">
<span class="blue-dot">&nbsp;</span>
<span class="green-dot">&nbsp;</span>
<span class="yellow-dot">&nbsp;</span>
<button (click)="stopRecording()" style="color: red;" class="reset-button initial" mat-icon-button
matTooltip="Stop Recording" aria-label="record button">
<mat-icon class="material-icons-outlined">stop_circle</mat-icon>
</button>
</span>
<button mat-icon-button (click)="searchTerm()">
<mat-icon>send</mat-icon>
</button>
</div>
</div>
@@ -0,0 +1,120 @@
/**
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
.chat-footer {
margin-top: 1rem;
min-height: 60px;
display: flex;
border: 1px solid #dde6f6;
box-shadow: 0px 4px 18px rgba(195, 209, 226, 0.25);
border-radius: 30px;
width: 100%;
min-width: 720px;
// box-shadow: 5px 4px 24px rgba(66, 133, 244, 0.15);
.chat-textarea {
flex-grow: 1;
display: flex;
align-items: center;
padding-left: 2%;
input {
width: 100%;
height: 45px;
border: 0px;
border-radius: 4px;
resize: none;
outline: none;
padding: 14px;
font-family: "Google Sans", sans-serif !important;
box-sizing: border-box;
-ms-overflow-style: none;
scrollbar-width: none;
font-size: 0.95rem;
line-height: 1.1rem;
color: #534646;
}
input::-webkit-scrollbar {
display: none;
}
}
.micIcon {
display: flex;
align-items: center;
color: #1f1f1f;
margin-top: 2px;
padding-right: 1%;
img {
max-width: 40%;
cursor: pointer;
}
}
}
.recording-container {
display: flex;
flex-direction: row;
gap: 10px;
cursor: pointer;
align-items: baseline;
}
.blue-dot {
/* Ellipse 1226 */
content: '';
width: 8px;
height: 8px;
left: 1503px;
top: 445px;
display: flow-root;
border-radius: 15px;
/* Blue */
background: #4285F4;
animation: blue-transform 2s infinite;
}
.green-dot {
content: '';
width:8px;
height: 8px;
left: 1522px;
top: 445px;
display: flow-root;
border-radius: 15px;
/* Green */
background: #0F9D58;
animation: green-transform 2s infinite;
}
.yellow-dot {
content: '';
width: 8px;
height: 8px;
left: 1541px;
top: 445px;
display: flow-root;
border-radius: 15px;
/* Yellow */
background: #F4B400;
animation: yellow-transform 2s infinite;
}
@@ -0,0 +1,38 @@
/**
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {ComponentFixture, TestBed} from '@angular/core/testing';
import {ChatInputComponent} from './chat-input.component';
describe('ChatInputComponent', () => {
let component: ChatInputComponent;
let fixture: ComponentFixture<ChatInputComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ChatInputComponent],
}).compileComponents();
fixture = TestBed.createComponent(ChatInputComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,86 @@
/**
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {Component, EventEmitter, Output} from '@angular/core';
import {ActivatedRoute} from '@angular/router';
import {SpeechToTextService} from 'src/app/services/speech-to-text';
@Component({
selector: 'app-chat-input',
templateUrl: './chat-input.component.html',
styleUrls: ['./chat-input.component.scss'],
})
export class ChatInputComponent {
isRecording = false;
transcribedText = '';
mediaRecorder: MediaRecorder | undefined;
audioChunks: Blob[] = [];
term = '';
@Output() emitSearch: EventEmitter<string> = new EventEmitter();
constructor(
private speechToTextService: SpeechToTextService,
private route: ActivatedRoute
) {
const query = this.route.snapshot.queryParamMap.get('q');
if (query) {
this.term = query;
}
}
ngOnInit() {
navigator.mediaDevices
.getUserMedia({audio: true})
.then(stream => this.setupMediaRecorder(stream))
.catch(err => console.error(err));
}
searchTerm() {
this.emitSearch.emit(this.term);
}
setupMediaRecorder(stream: MediaStream) {
this.mediaRecorder = new MediaRecorder(stream);
this.mediaRecorder.ondataavailable = event =>
this.audioChunks.push(event.data);
this.mediaRecorder.onstop = () => this.sendAudioToGCP();
}
startRecording() {
this.isRecording = true;
this.audioChunks = [];
if (this.mediaRecorder) this.mediaRecorder.start();
}
stopRecording() {
this.isRecording = false;
if (this.mediaRecorder) this.mediaRecorder.stop();
}
async sendAudioToGCP() {
const audioBlob = new Blob(this.audioChunks);
// console.log(audioBlob);
(await this.speechToTextService.transcribeAudio(audioBlob)).subscribe(
(response: any) => {
// console.log(response)
this.term = response[0];
this.searchTerm();
},
(error: any) => console.error(error)
);
}
}
@@ -0,0 +1,32 @@
<!--
Copyright 2025 Google LLC
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<app-header></app-header>
<div class="parent-container">
<div class="wrapping-container">
<div class="main-div">
<div class="sub-heading">
<div class="name-container">
Hey, {{savedUser.name ? savedUser.name!.split(" ")[0]+'!' : ''}}
</div>
<div class="helper-container">How can I help you today?</div>
</div>
</div>
</div>
<div class="chat-footer-outer">
<app-chat-input (emitSearch)="this.goToResults($event)"></app-chat-input>
</div>
@@ -0,0 +1,995 @@
/**
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@import url("https://fonts.googleapis.com/icon?family=Material+Icons|Material+Icons+Outlined");
:host ::ng-deep mat-form-feild .mat-form-field-appearance-outline .mat-form-field-outline-start {
color: red;
border-radius: 50px !important;
font-size: 10px !important;
}
:host ::ng-deep .mdc-text-field--outlined .mdc-notched-outline .mdc-notched-outline__leading {
border-radius: 28px 0 0 28px !important;
min-width: 28px !important;
border-color: #babfc4 !important;
}
:host ::ng-deep .mdc-text-field--outlined .mdc-notched-outline .mdc-notched-outline__trailing {
border-radius: 0 28px 28px 0 !important;
border-color: #babfc4 !important;
}
:host ::ng-deep .mat-form-field-outline-end {
border-radius: 0 28px 28px 0 !important;
}
:host ::ng-deep .mat-mdc-card {
box-shadow: 0px 2px 1px -1px rgb(217 220 224), 0px 1px 1px 0px rgba(0, 0, 0, 0.14), 0px 1px 3px 0px rgb(229 232 234) !important;
}
:host ::ng-deep .mat-mdc-standard-chip.mat-primary.mat-mdc-chip-selected,
.mat-mdc-standard-chip.mat-primary.mat-mdc-chip-highlighted {
--mdc-chip-elevated-container-color: #EDF3FE;
--mdc-chip-elevated-disabled-container-color: #e0e0e0;
--mdc-chip-label-text-color: #4285F4;
;
--mdc-chip-disabled-label-text-color: #4285F4;
;
--mdc-chip-with-icon-icon-color: #4285F4;
;
--mdc-chip-with-icon-disabled-icon-color: #4285F4;
;
--mdc-chip-with-trailing-icon-disabled-trailing-icon-color: #4285F4;
;
--mdc-chip-with-trailing-icon-trailing-icon-color: #4285F4;
;
--mdc-chip-with-icon-selected-icon-color: #4285F4;
;
}
.parent-container {
// background-position: bottom;
// background-image: url("../../../assets/images/footer.png");
// background-repeat: no-repeat;
// background-size: 30%;
// height: calc(100% - 60px);
// overflow: auto;
display: flex;
max-height: calc(100vh - 70px);
height: 100%;
font-family: 'Google Sans', sans-serif !important;
overflow-y: auto;
flex-direction: column;
.img-globe {
// position:absolute;
width: 66%;
}
}
.wrapping-container {
margin-top: 6%;
width: 100%;
padding-bottom: 1.5%;
}
.main-div {
display: flex;
justify-content: center;
align-items: center;
gap: 15px;
font-family: 'Google Sans', sans-serif !important;
}
.text-control {
width: 32%;
}
h1 {
line-height: 50px;
width: 34%;
}
h2 {
font-family: 'Google Sans', sans-serif !important;
}
.header {
-webkit-background-clip: text;
-webkit-text-fill-color: #48b975;
background-clip: text;
font-size: 3rem;
text-align: center;
font-weight: bold;
font-weight: 700;
-webkit-font-smoothing: antialiased;
height: 100%;
font-family: 'Google Sans', sans-serif;
}
.send-button {
color: #babfc4;
}
.logo-icon {
height: 120px;
width: 400px;
margin-bottom: 90px;
margin-left: -20px;
}
.user-info {
display: flex;
justify-content: center;
}
.user-info-column {
display: column;
}
.card-outer-div {
.card-inside-left {
.circle {
width: 100px;
height: 100px;
border: 5px solid rgb(255, 255, 255);
border-radius: 50%;
overflow: hidden;
box-shadow: 0px 2px 1px -1px rgba(0, 0, 0, 0.2), 0px 1px 1px 0px rgba(0, 0, 0, 0.14), 0px 1px 3px 0px rgba(0, 0, 0, 0.12);
}
.circle img {
width: 100%;
height: 100%;
object-fit: cover;
}
}
.card-inside-right {
width: 80%;
display: flex;
flex-direction: column;
align-items: center;
color: #747474;
font-size: 0.8rem;
font-weight: 500;
.name {
.title {
font: 1rem;
}
.sub-title {
font-size: 0.9rem;
margin-left: 2%;
}
}
.company {
display: flex;
align-items: center;
.logo {
padding-right: 1%;
}
.text {
width: 100%;
.sub-title {
margin-left: 2%;
}
}
}
}
}
.mat-mdc-standard-chip {
--mdc-chip-label-text-color: #4285F4;
--mdc-chip-elevated-container-color: #fff;
border: 1px solid #4285F4 !important;
font-family: "Google Sans", sans-serif !important;
font-size: 15px !important;
font-weight: 500 !important;
padding-left: 12px !important;
padding-right: 12px !important;
}
.sub-heading {
font-family: 'Google Sans', sans-serif;
width: 66%;
color: #161A1D;
text-align: center;
font-size: 30px;
line-height: 40px;
margin-top: -20px;
text-align: left;
z-index: 999;
}
.genAI {
//background-image: linear-gradient(to left, #4285F4, #DB4437, #F4B400, #0F9D58);
// background-image: linear-gradient(to left, #34A853, #FBBC05, #EA4335, #1A73E8);
background: linear-gradient(86.95deg, #34A853 -65.34%, #FBBC05 -12.75%, #EA4335 25.15%, #1A73E8 103.88%);
-webkit-background-clip: text;
-moz-background-clip: text;
background-clip: text;
color: transparent;
}
.margin-div {
margin-top: -20px;
}
.chat-footer-outer {
display: flex;
align-items: center;
width: 100%;
justify-content: center;
padding-bottom: 4.7%;
padding-top: 1.7%;
.chat-footer {
min-height: 60px;
display: flex;
border: 1px solid #DDE6F6;
box-shadow: 0px 4px 18px rgba(195, 209, 226, 0.25);
border-radius: 30px;
width: 66%;
// box-shadow: 5px 4px 24px rgba(66, 133, 244, 0.15);
.chat-textarea {
flex-grow: 1;
display: flex;
align-items: center;
padding-left: 2%;
input {
width: 100%;
height: 45px;
border: 0px;
border-radius: 4px;
resize: none;
outline: none;
padding: 14px;
font-family: 'Google Sans', sans-serif !important;
box-sizing: border-box;
-ms-overflow-style: none;
scrollbar-width: none;
font-size: 0.95rem;
line-height: 1.1rem;
color: #534646;
}
input::-webkit-scrollbar {
display: none;
}
}
.micIcon {
display: flex;
align-items: center;
color: #1F1F1F;
margin-top: 2px;
padding-right: 1%;
img {
max-width: 40%;
cursor: pointer;
}
}
}
}
.text-gnosis {
color: var(--White, #FFF);
font-size: 16px;
font-style: normal;
font-weight: 400;
line-height: 24px;
/* 150% */
letter-spacing: 0.4px;
}
.chip-cancel-button {
color: #002dff;
}
.categories-div {
.mat-mdc-standard-chip {
--mdc-chip-label-text-color: #0F9D58;
--mdc-chip-elevated-container-color: #fff;
border: 1px solid #0F9D58 !important;
font-family: 'Google Sans', sans-serif !important;
// font-weight: 600;
}
}
.intent-chip {
.mat-mdc-standard-chip {
--mdc-chip-label-text-color: #4285F4;
--mdc-chip-elevated-container-color: #fff;
border: 1px solid #4285F4 !important;
font-family: 'Google Sans', sans-serif !important;
// font-weight: 600;
}
.chip-cancel-button {
color: #002dff;
}
}
.category-chip {
.mat-mdc-standard-chip {
--mdc-chip-label-text-color: #0F9D58;
--mdc-chip-elevated-container-color: #fff;
border: 1px solid #0F9D58 !important;
font-family: 'Google Sans', sans-serif !important;
// font-weight: 600;
}
.chip-cancel-button {
color: #007d06;
}
}
.questions-div {
flex-direction: column;
margin-top: 2rem;
margin-bottom: 5rem;
.intent-card {
padding: 0px;
}
.intent-heading {
color: #787978;
font-size: 16px;
font-style: normal;
font-weight: 400;
line-height: normal;
font-family: 'Google Sans', sans-serif !important;
width: 66%;
}
.intent-container {
border-radius: 8px;
background-color: rgba(246, 249, 254, 1);
width: 66%;
box-shadow: none !important;
flex-shrink: 0;
.selected-question {
// margin: 0.5rem 0rem;
&:hover {
background-color: rgba(66, 133, 244, 0.10);
}
}
.question-item {
display: flex;
justify-content: space-between;
font-family: 'Google Sans', sans-serif !important;
color: #787978;
font-size: 1em;
line-height: 24px;
}
.question-link {
overflow: hidden;
text-overflow: ellipsis;
}
}
}
.intent-chip-textarea ::ng-deep .mdc-evolution-chip-set__chips {
align-items: center !important;
}
.float-button {
position: fixed;
right: 3em;
bottom: 6em;
--mdc-fab-container-color: #4285F4;
}
.name-container {
font-size: 3.5rem;
font-weight: 400;
line-height: 4rem;
font-family: Google Sans, Helvetica Neue, sans-serif;
letter-spacing: -.03em;
background: linear-gradient(89.9deg, rgba(66, 133, 244, 0.95) 0.17%, #A488F5 7.34%, rgba(234, 67, 53, 0.88) 24.26%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
text-fill-color: transparent;
animation: effect 2s linear;
display: flex;
flex-direction: row;
gap: 1%;
align-items: center;
justify-content: center;
}
@keyframes effect {
0% {
background: linear-gradient(89.9deg, rgba(240, 216, 214, 0.88) 0.17%, #ebcaec 7.34%, rgba(194, 214, 247, 0.95) 24.26%);
-webkit-background-clip: text;
}
10% {
background: linear-gradient(89.9deg, rgba(227, 237, 253, 0.95) 0.17%, #e5ddfd 7.34%, rgba(186, 210, 248, 0.95) 24.26%);
-webkit-background-clip: text;
}
20% {
background: linear-gradient(89.9deg, rgba(218, 231, 253, 0.95) 0.17%, #dcd1fc 7.34%, rgba(172, 202, 252, 0.95) 24.26%);
-webkit-background-clip: text;
}
30% {
background: linear-gradient(89.9deg, rgba(199, 219, 252, 0.95) 0.17%, #d0c2fa 7.34%, rgba(161, 196, 252, 0.95) 24.26%);
-webkit-background-clip: text;
}
40% {
background: linear-gradient(89.9deg, rgba(169, 201, 253, 0.95) 0.17%, #cdbcff 7.34%, rgba(149, 188, 250, 0.95) 24.26%);
-webkit-background-clip: text;
}
50% {
background: linear-gradient(89.9deg, rgba(200, 220, 253, 0.95) 0.17%, #bba5fc 7.34%, rgba(203, 221, 250, 0.95) 24.26%);
-webkit-background-clip: text;
}
60% {
background: linear-gradient(89.9deg, rgba(227, 237, 253, 0.95) 0.17%, #e5ddfd 7.34%, rgba(186, 210, 248, 0.95) 24.26%);
-webkit-background-clip: text;
}
70% {
background: linear-gradient(89.9deg, rgba(183, 210, 253, 0.95) 0.17%, #e5ddfd 7.34%, rgba(248, 217, 215, 0.88) 24.26%);
-webkit-background-clip: text;
}
80% {
background: linear-gradient(89.9deg, rgba(122, 172, 252, 0.95) 0.17%, #e5ddfd 7.34%, rgba(247, 182, 176, 0.88) 24.26%);
-webkit-background-clip: text;
}
90% {
background: linear-gradient(89.9deg, rgba(94, 148, 236, 0.95) 0.17%, #b49ff1 7.34%, rgba(240, 121, 111, 0.88) 24.26%);
-webkit-background-clip: text;
}
100% {
background: linear-gradient(89.9deg, rgba(66, 133, 244, 0.95) 0.17%, #A488F5 7.34%, rgba(234, 67, 53, 0.88) 24.26%);
-webkit-background-clip: text;
}
}
.helper-container {
white-space: nowrap;
overflow: hidden;
font-size: 3.5rem;
font-weight: 400;
line-height: 4rem;
font-family: Google Sans, Helvetica Neue, sans-serif;
letter-spacing: -.03em;
color: #8f9391;
padding-bottom: 1%;
opacity: 0.5;
animation: typing 2s steps(22), blink .5s step-end infinite alternate;
justify-content: center;
display: flex;
}
@keyframes typing {
from {
width: 0
}
}
@keyframes blink {
50% {
border-color: transparent
}
}
.quick-bot-introduction-container {
width: 100%;
display: flex;
flex-direction: row;
justify-content: center;
.introduction-sub-container {
width: 66%;
justify-content: center;
text-align: center;
display: flex;
flex-direction: column;
padding-top: 2%;
padding-bottom: 2%;
background: radial-gradient(white, #FAFCFF) padding-box, linear-gradient(89.86deg, #34A853 -40.93%, #FBBC05 4.92%, #EA4335 37.97%, #1A73E8 106.62%) border-box;
border-radius: 24px;
border: 1px solid transparent;
}
.logo-image-container {
img {
height: 110px;
width: 110px;
}
}
.logo-text-heading {
font-style: normal;
font-weight: 500;
font-size: 28px;
line-height: 202%;
/* or 65px */
text-align: center;
background: linear-gradient(89.86deg, #34A853 -40.93%, #FBBC05 4.92%, #EA4335 37.97%, #1A73E8 106.62%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
text-fill-color: transparent;
}
.logo-text-subheading {
font-style: normal;
font-weight: 500;
font-size: 21px;
line-height: 202%;
/* or 48px */
text-align: center;
}
.blue {
color: #4285F4;
}
}
.helpButton {
position: fixed !important;
bottom: 45px !important;
right: 20px !important;
background-color: #4285f4 !important;
color: white !important;
text-decoration: none !important;
border-radius: 60px !important;
height: 41px !important;
display: inline-flex !important;
align-items: center !important;
overflow: hidden !important;
width: auto !important;
max-width: 41px !important;
-webkit-transition: max-width 0.5s !important;
transition: max-width 0.5s !important;
z-index: 1000 !important;
&:hover {
max-width: 300px !important;
}
.text {
white-space: nowrap !important;
padding-right: 15px !important;
font-size: 15px !important;
font-weight: 600 !important;
}
}
::placeholder {
color: #646566;
}
.intent-container-box {
box-sizing: border-box;
width: 239px;
height: 239px;
border-radius: 14px;
transition-delay: 2s;
transition: width 8s;
padding: 1%;
cursor: pointer;
}
.selected-intent-box {
box-sizing: border-box;
height: 239px;
border-radius: 14px;
transition-delay: 2s;
transition: width 8s;
cursor: pointer;
width: max-content !important;
max-width: 100%;
display: flex;
flex-direction: column;
gap: 1%;
padding: 1%;
background: linear-gradient(92.1deg, #bdd1f3 19.16%, rgb(199 183 245 / 90%) 79%, rgb(235 165 158 / 90%) 135.24%), linear-gradient(272.04deg, rgba(158, 188, 237, 0.7) 14.93%, rgba(158, 188, 237, 0) 104.51%) !important;
}
.cards-outer-container {
width: 65%;
overflow: auto;
scrollbar-width: none;
}
.cards-outer-container:after {
z-index: -1;
content: '';
width: 10%;
opacity: 0.5 !important;
}
.cards-container-all-categories {
width: max-content;
display: flex;
overflow-x: auto;
gap: 1%;
max-width: 160%;
min-width: 100%;
scrollbar-width: none;
}
.cards-container-few-categories {
width: max-content;
display: flex;
overflow-x: auto;
gap: 1%;
max-width: 160%;
min-width: 100%;
scrollbar-width: none;
justify-content: center;
padding-inline: 5%;
}
.intent-container-box:hover {
background: #DDE3EA !important;
}
.card-detail-container {
width: 100%;
min-width: 250px;
display: flex;
flex-direction: row;
}
.selected-intent-suggested-question {
gap: 4%;
display: flex !important;
flex-direction: column;
}
.suggested-question-chip-row {
margin-top: 0.5rem;
.suggested-question-chip {
display: flex;
flex-direction: row;
margin: 0.5rem;
padding: 0.5rem;
width: 100%;
.suggested-question-icon {
padding-right: 0.25rem
}
.suggested-question-text {
margin-left: 0.25rem;
width: 100%;
}
}
}
.close-button-container {
display: none !important;
}
.expand-close-button-container {
display: block !important;
.close-button {
margin-top: -10px !important;
}
}
.card-heading {
padding-top: 2.5%;
font-size: 17px;
color: #1F1F1F;
width: 90%;
}
.card-content {
font-size: 13px;
color: #646566;
padding-top: 4%;
white-space: break-spaces;
text-wrap: balance;
padding-right: 3%;
text-align: left;
width: 85%;
}
::ng-deep .mat-mdc-snack-bar-container {
&.green-snackbar {
--mdc-snackbar-container-color: #0F9D58;
--mat-mdc-snack-bar-button-color: #fff;
--mdc-snackbar-supporting-text-color: #fff;
}
&.red-snackbar {
--mdc-snackbar-container-color: #e9103f;
--mat-mdc-snack-bar-button-color: #fff;
--mdc-snackbar-supporting-text-color: #fff;
}
}
::ng-deep .mat-mdc-dialog-surface {
justify-content: center !important;
display: flex !important;
flex-direction: column !important;
text-align: center !important;
background: #FFFFFF;
border-radius: 20px !important;
box-sizing: border-box;
box-shadow: none !important;
.badge-prompt-parent-container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
text-align: center;
.badge-greetings-container {
width: 100%;
display: flex;
flex-direction: row;
justify-content: center;
img {
width: 45%;
}
}
.badge-text-container {
font-style: normal;
font-weight: 400;
font-size: 17px;
line-height: 45px;
display: flex;
align-items: center;
color: rgba(92, 95, 97, 0.7);
}
}
}
.blue-dot {
/* Ellipse 1226 */
content: '';
width: 8px;
height: 8px;
left: 1503px;
top: 445px;
display: flow-root;
border-radius: 15px;
/* Blue */
background: #4285F4;
animation: blue-transform 2s infinite;
}
.green-dot {
content: '';
width:8px;
height: 8px;
left: 1522px;
top: 445px;
display: flow-root;
border-radius: 15px;
/* Green */
background: #0F9D58;
animation: green-transform 2s infinite;
}
.yellow-dot {
content: '';
width: 8px;
height: 8px;
left: 1541px;
top: 445px;
display: flow-root;
border-radius: 15px;
/* Yellow */
background: #F4B400;
animation: yellow-transform 2s infinite;
}
.recording-container {
display: flex;
flex-direction: row;
gap: 10px;
cursor: pointer;
align-items: baseline;
}
@keyframes blue-transform {
0% {
transform: translateY(-14px)
}
33% {
transform: translateY(0)
}
66% {
transform: translateY(0)
}
100% {
transform: translateY(-14px)
}
}
@keyframes green-transform {
0% {
transform: translateY(0)
}
33% {
transform: translateY(-14px)
}
66% {
transform: translateY(0)
}
100% {
transform: translateY(0)
}
}
@keyframes yellow-transform {
0% {
transform: translateY(0)
}
33% {
transform: translateY(0)
}
66% {
transform: translateY(-14px)
}
100% {
transform: translateY(0)
}
}
.tooltip-container {
display: flex;
width: 100%;
justify-content: center;
align-items: baseline;
padding-top: 2%;
padding-bottom: 2%;
.tooltip-sub-container {
width: 66%;
display: flex;
align-items: baseline;
justify-content: center;
}
}
.tooltip-icon-container {
display: flex;
flex-direction: row;
// align-self: flex-end;
gap: 10px;
flex-wrap: nowrap;
text-wrap: nowrap;
align-self: center;
color: #5F6368;
.tooltip-label {
font-family: 'Roboto';
font-style: normal;
font-weight: 500;
font-size: 16px;
line-height: 20px;
text-align: center;
}
mat-icon {
font-size: 22px !important;
}
}
.tooltip-text-container {
flex-wrap: wrap;
word-wrap: break-word;
justify-content: flex-start;
display: flex;
color: #5F6368;
.tooltip-text {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
font-size: 16px;
line-height: 20px;
}
}
.dismiss-container {
font-family: 'Roboto';
font-style: normal;
font-weight: 500;
font-size: 14px;
line-height: 20px;
/* identical to box height, or 143% */
text-align: center;
/* Blue */
color: #4285F4;
padding-left: 1%;
cursor: pointer;
}
.dismiss-container:hover {
text-decoration: underline;
}
.journey-chips {
max-height: 300px;
overflow-y: auto;
}
::ng-deep .mat-mdc-standard-chip:hover {
background: linear-gradient(92.1deg, #9EBCED 19.16%, rgba(164, 136, 245, 0.9) 79%) !important;
--mdc-chip-label-text-color: #fff;
}
.suggested-question-container {
width: 60%;
}
@@ -0,0 +1,38 @@
/**
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {ComponentFixture, TestBed} from '@angular/core/testing';
import {MainComponent} from './main.component';
describe('MainComponent', () => {
let component: MainComponent;
let fixture: ComponentFixture<MainComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [MainComponent],
}).compileComponents();
fixture = TestBed.createComponent(MainComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,76 @@
/**
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {Component, OnDestroy} from '@angular/core';
import {UserService} from 'src/app/services/user/user.service';
import {Router} from '@angular/router';
import {MatDialog} from '@angular/material/dialog';
import {ReplaySubject} from 'rxjs';
import {takeUntil} from 'rxjs/operators';
import {SearchResponse} from 'src/app/models/search.model';
import {SearchApplicationFormComponent} from '../search-application/search-application-form/search-application-form.component';
import {SearchApplicationService} from 'src/app/services/search_application.service';
@Component({
selector: 'app-main',
templateUrl: './main.component.html',
styleUrls: ['./main.component.scss'],
})
export class MainComponent implements OnDestroy {
private readonly destroyed = new ReplaySubject<void>(1);
term = '';
showResults = false;
searchResults: SearchResponse | undefined;
savedUser;
constructor(
public userService: UserService,
private router: Router,
public dialog: MatDialog,
private readonly searchApplicationService: SearchApplicationService
) {
this.savedUser = userService.getUserDetails();
this.checkConfiguration();
}
checkConfiguration() {
this.searchApplicationService
.get()
.pipe(takeUntil(this.destroyed))
.subscribe(searchApplication => {
if (!searchApplication) {
this.openAddAgentBuilderConfigForm();
}
});
}
goToResults(term: string) {
this.router.navigate(['/search'], {queryParams: {q: term}});
}
openAddAgentBuilderConfigForm() {
this.dialog.open(SearchApplicationFormComponent, {
disableClose: true,
height: '600px',
width: '1120px',
});
}
ngOnDestroy() {
this.destroyed.next();
this.destroyed.complete();
}
}
@@ -0,0 +1,73 @@
<!--
Copyright 2025 Google LLC
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<app-header (emitSearch)="this.searchTerm($event)"></app-header>
<div class="overview-container" *ngIf="summary">
<div class="overview-sub-container">
<div class="overview-text"><mat-icon>auto_awesome</mat-icon> AI Overview</div>
<div class="overview-content"> {{summary}}</div>
<div class="overview-button">
<button mat-button>Learn more</button>
</div>
</div>
</div>
<div class="overview-container padding-top">
<div class="search-result-parent-container" *ngIf="documents.length>0">
<div class="documents-container">
<div class="header-container">
Documents
</div>
<div class="navigation">
<button class="nav-button" (click)="prevPage()" [disabled]="currentPage === 0">
<span class="material-symbols-outlined">chevron_left</span>
</button>
<button class="nav-button" (click)="nextPage()" [disabled]="currentPage >= totalPages -1">
<span class="material-symbols-outlined">chevron_right</span>
</button>
</div>
</div>
<div class="card-list">
<div class="card" *ngFor="let result of pagedDocuments" (click)="openNewWindow(result.link)">
<div class="card-content">
<div class="card-header">
<img src="{{result.link.includes(pdf) ? imageName.get('pdf'): imageName.get('doc')}}" height="30" width="30"/>
<span class="card-title">{{ result.title | truncate }}</span>
</div>
<div class="card-description"><p>
{{ result.content | truncate }}
</p></div>
<div class="overview-button">
<button mat-button (click)="previewDocument($event, result)">Preview</button>
</div>
</div>
</div>
</div>
</div>
<div *ngIf="this.selectedDocument !== undefined" class="document-viewer">
<div class="document-viewer-header">
<div class="card-header">
<img src="{{this.selectedDocument.link.endsWith(pdf) ? imageName.get('pdf'): imageName.get('doc')}}" height="30" width="30"/>
<span class="card-title">{{ this.selectedDocument.title| truncate }}</span>
</div>
<div class="document-viewer-header-close">
<button (click)="closePreview()" mat-icon-button value="">
<mat-icon aria-hidden="true">close</mat-icon>
</button>
</div>
</div>
<iframe class="document-viewer-content" [src]="this.safeUrl" name="preview"></iframe>
</div>
@@ -0,0 +1,296 @@
/**
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
.overview-container {
display: flex;
justify-content: center;
padding-top: 2%;
padding-right: 1.5%;
.overview-sub-container {
width: 66%;
background: linear-gradient(92.1deg, #9ebcedb2 19.16%, rgba(163, 136, 245, 0.651) 79%, rgba(234, 68, 53, 0.493) 135.24%);
border-radius: 12px;
.overview-text {
font-family: 'Roboto';
font-style: normal;
font-weight: 500;
font-size: 20px;
line-height: 24px;
color: #5C5F61;
padding: 2%;
}
.overview-content {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
font-size: 19px;
line-height: 30px;
color: #161A1D;
padding-left: 2%;
padding-right: 2%;
padding-bottom: 2%;
}
.overview-button {
button {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
font-size: 20px;
line-height: 24px;
color: #4285F4;
padding: 40px;
}
}
}
}
.header-container {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
font-size: 16px;
line-height: 19px;
color: #5F6368;
display: flex;
justify-content: flex-start;
width: 66%;
}
.search-result:hover {
border: 1px solid #DFE0E2;
border-radius: 10px;
}
.search-result-parent-container{
display: flex;
flex-direction: column;
width: 66%;
gap: 15px;
padding-bottom: 5%;
}
.search-result-row-container {
display: flex;
flex-direction: row;
gap: 15px;
}
.search-result {
padding: 1%;
padding-top: 2%;
cursor: pointer;
border: 1px solid transparent;
width: 25%;
.title-container {
display: flex;
flex-direction: row;
font-family: 'Roboto';
font-style: normal;
font-weight: 500;
font-size: 16px;
line-height: 31px;
color: #161A1D;
display: flex;
gap: 15px;
.link-container {
font-size: 14px !important;
font-weight: 400 !important;
color: #676c6f !important;
}
}
.description-container {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
font-size: 16px;
line-height: 24px;
color: #161A1D;
opacity: 0.8;
padding: 1%;
padding-top: 4%;
}
.overview-button {
padding-top: 2%;
button {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
font-size: 16px;
line-height: 24px;
color: #4285F4;
}
}
}
.padding-top {
padding-top: 0;
}
.close-container {
display: flex;
justify-content: end;
}
.pdf-display{
display: flex;
justify-content: center;
}
.overview {
z-index: 1000;
}
/* card-list.component.scss */
.card-list {
display: flex;
gap: 20px; /* Adjust spacing between cards */
}
.card {
border: 1px solid #eee;
border-radius: 8px;
flex: 0 0 auto; /* Prevent cards from stretching */
box-shadow: 2px 2px 5px rgba(0,0,0,0.1);
padding: 16px;
transition: transform 0.2s;
&:hover {
transform: scale(1.02);
box-shadow: 3px 3px 7px rgba(0,0,0,0.15);
cursor: pointer;
}
.card-content {
display: flex;
flex-direction: column;
height: 100%;
}
.card-header {
display: flex;
align-items: center;
margin-bottom: 10px;
.material-symbols-outlined {
font-size: 1.2rem;
margin-right: 5px;
&.bookmark {
margin-left: auto; // Push bookmark to right
cursor: pointer;
}
}
.card-title {
font-weight: 500;
width: 250px;
}
}
.card-description {
flex-grow: 1; // Allow description to take up available space
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 10; /* Number of lines to show */
-webkit-box-orient: vertical;
width: 250px;
}
.card-footer {
display: flex;
align-items: center;
margin-top: 10px;
.material-symbols-outlined {
font-size: 1.2rem;
margin-right: 5px;
}
}
}
.documents-container {
margin-top: 2rem;
display: flex;
justify-content: space-between;
align-items: center;
max-width: 1000px;
}
.navigation {
display: flex;
justify-content: center;
margin-top: 20px;
.nav-button {
border: none;
background: none;
cursor: pointer;
transition: background-color 0.2s;
&:hover {
background-color: #f0f0f0;
}
&:disabled {
opacity: 0.5;
cursor: default;
}
.material-symbols-outlined {
font-size: 1.5rem;
}
}
}
.document-viewer {
background: rgb(255, 255, 255);
border-left: 1px solid var(--md-sys-color-outline);
border-top: 1px solid var(--md-sys-color-outline);
display: block;
height: 75%;
position: fixed;
right: 0px;
bottom: 0px;
width: 35%;
z-index: 9999;
.document-viewer-header {
-webkit-box-pack: justify;
-webkit-box-align: center;
-webkit-box-flex: 1;
align-items: center;
border-bottom: 1px solid var(--md-sys-color-outline);
display: flex;
flex: 1 1 0%;
justify-content: space-between;
padding: 8px;
}
.document-viewer-content {
width: 100%;
height: 100%;
}
}
@@ -0,0 +1,38 @@
/**
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {ComponentFixture, TestBed} from '@angular/core/testing';
import {SearchResultsComponent} from './search-results.component';
describe('SearchResultsComponent', () => {
let component: SearchResultsComponent;
let fixture: ComponentFixture<SearchResultsComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [SearchResultsComponent],
}).compileComponents();
fixture = TestBed.createComponent(SearchResultsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,139 @@
/**
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {Component, OnDestroy, ViewChild, TemplateRef} from '@angular/core';
import {SearchService} from 'src/app/services/search.service';
import {ReplaySubject} from 'rxjs';
import {UserService} from 'src/app/services/user/user.service';
import {ActivatedRoute, Router} from '@angular/router';
import {search_image_type, PDF, image_name} from 'src/environments/constant';
import {
DomSanitizer,
SafeResourceUrl,
SafeUrl,
} from '@angular/platform-browser';
import {MatDialog} from '@angular/material/dialog';
@Component({
selector: 'app-search-results',
templateUrl: './search-results.component.html',
styleUrls: ['./search-results.component.scss'],
})
export class SearchResultsComponent implements OnDestroy {
@ViewChild('preview', {static: true})
previewRef!: TemplateRef<{}>;
summary = '';
private readonly destroyed = new ReplaySubject<void>(1);
serachResult: any = [];
documents: any = [];
images: any = [];
pdf = PDF;
imageName = image_name;
documentURL: SafeResourceUrl | undefined;
openPreviewDocument: any;
currentPage = 0;
pageSize = 3;
selectedDocument: any;
safeUrl: SafeUrl | undefined;
constructor(
private router: Router,
private route: ActivatedRoute,
private service: SearchService,
private userService: UserService,
private dialog: MatDialog,
private sanitizer: DomSanitizer
) {
const query = this.route.snapshot.queryParamMap.get('q');
this.service.search(query!).subscribe({
next: (searchRespone: any) => {
this.summary = searchRespone.summary;
this.serachResult = searchRespone.results;
this.serachResult.forEach((element: any) => {
this.documents.push(element);
if (search_image_type.includes(element.link.split('.')[1])) {
this.images.push(element);
}
});
console.log(this.documents, this.images);
this.userService.hideLoading();
},
error: () => {
this.userService.hideLoading();
},
});
}
searchTerm(term: string) {
this.router.navigate(['/search'], {queryParams: {q: term}});
this.service.search(term).subscribe({
next: (searchRespone: any) => {
this.serachResult = searchRespone.results;
this.summary = searchRespone.summary;
this.userService.hideLoading();
},
error: () => {
this.userService.hideLoading();
},
});
}
openNewWindow(link: string) {
window.open(link, '_blank');
}
previewDocument(event: any, document: any) {
event.stopPropagation();
if (document.link.endsWith('.pdf') || document.link.endsWith('.docx')) {
this.selectedDocument = document;
this.safeUrl = this.sanitizer.bypassSecurityTrustResourceUrl(
this.selectedDocument.link
);
}
}
closePreview() {
this.selectedDocument = undefined;
}
ngOnDestroy() {
this.destroyed.next();
this.destroyed.complete();
}
get pagedDocuments() {
const startIndex = this.currentPage * this.pageSize;
return this.documents.slice(startIndex, startIndex + this.pageSize);
}
get totalPages() {
return Math.ceil(this.documents.length / this.pageSize);
}
nextPage() {
if (this.currentPage < this.totalPages - 1) {
this.currentPage++;
}
}
prevPage() {
if (this.currentPage > 0) {
this.currentPage--;
}
}
}
@@ -0,0 +1,74 @@
<!--
Copyright 2025 Google LLC
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<div class="buttons-container">
<button mat-raised-button class="inactive-button" (click)="navigateToMain()">
<mat-icon>keyboard_backspace</mat-icon>
Back
</button>
</div>
<div class="root-container">
<div class="max-width-container">
<div class="buttons-container">
<div class="heading-container">
<span>Manage Agent Builder Config</span>
</div>
</div>
<div class="table-container">
<form>
<mat-form-field
class="form-field"
appearance="outline"
[floatLabel]="'always'"
>
<mat-label>Choose an Engine</mat-label>
<mat-select [disabled]="!this.editMode" [(ngModel)]="selectedEngine" name="engine">
<mat-option *ngFor="let engine of this.engines" [value]="engine">{{
engine.name
}}</mat-option>
</mat-select>
</mat-form-field>
<div class="intent-buttons-container">
<button
mat-raised-button
class="reset-button"
*ngIf="!this.editMode"
(click)="enableForm()"
>
<mat-icon>edit</mat-icon>
Edit
</button>
<button
mat-raised-button
class="reset-button"
*ngIf="this.editMode"
(click)="disableForm()"
>
<span> Discard Changes </span>
</button>
<button
mat-raised-button
class="reset-button"
*ngIf="this.editMode"
(click)="saveForm()"
>
<span>Save</span>
</button>
</div>
</form>
</div>
</div>
</div>
@@ -0,0 +1,571 @@
/**
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
.root-container {
display: flex;
width: 100%;
justify-content: center;
.max-width-container {
width: 100%;
max-width: 1280px;
}
}
.action-button-container {
padding: 0;
}
.table-container {
justify-content: center;
display: block;
flex-direction: row;
padding: 5.5% 3%;
padding-bottom: 5%;
.row {
display: flex;
flex-direction: row;
.mat-mdc-form-field {
width: 100% !important;
}
}
}
.amber {
color: #fbc645;
}
.red {
color: red;
}
.blue {
color: #4285F4;
}
::ng-deep .mat-checkbox-checked .mat-checkbox-background,
.mat-checkbox-indeterminate .mat-checkbox-background {
background-color: #4285F4 !important;
}
// overwrite the ripple overlay on hover and click
::ng-deep .mat-checkbox:not(.mat-checkbox-disabled) .mat-checkbox-ripple .mat-ripple-element {
background-color: #4285F4 !important;
}
::ng-deep .mdc-checkbox__ripple {
background-color: #4285F4 !important;
}
::ng-deep .mat-mdc-checkbox .mdc-checkbox .mdc-checkbox__native-control:enabled:checked~.mdc-checkbox__background {
background-color: #4285F4 !important;
border-color: #4285F4 !important;
}
::ng-deep .mat-mdc-checkbox .mdc-checkbox .mdc-checkbox__native-control:enabled:indeterminate~.mdc-checkbox__background {
background-color: #4285F4 !important;
border-color: #4285F4 !important;
}
::ng-deep .mat-mdc-slide-toggle.mat-mdc-slide-toggle-checked:not(.mat-disabled) .mdc-switch__shadow {
background-color: #4285F4;
/*replace with your color*/
}
::ng-deep .mat-mdc-slide-toggle.mat-mdc-slide-toggle-checked:not(.mat-disabled) .mdc-switch__track::after {
background-color: #4285F4 !important;
/*replace with your color*/
}
.buttons-container {
display: flex;
flex-direction: row;
justify-content: flex-start;
width: 100%;
padding-top: 1%;
padding-bottom: 1%;
padding-left: 3%;
}
.saved-intents-container {
padding-top: 2%;
padding-bottom: 2%;
}
.heading-container {
width: 73%;
align-content: center;
flex-wrap: wrap;
display: flex;
flex-direction: row;
span {
font-family: 'Google Sans', sans-serif !important;
font-style: normal;
font-weight: 400;
font-size: 20px;
line-height: 14px;
color: #161A1D;
}
}
.button-child-container {
display: flex;
flex-direction: row;
gap: 20px;
width: 24%;
justify-content: center;
button {
border-radius: 30px;
font-family: 'Google Sans', sans-serif !important;
font-style: normal;
font-weight: 500;
font-size: 14px;
line-height: 23px;
letter-spacing: unset;
box-shadow: none;
}
.bulk-delete-button {
color: #f70808;
border: 1px solid #f70808;
}
.create-notification-button {
color: #fff;
background: #4285F4;
}
}
.form-field {
width: 100%;
}
.dialog-toggle-button-container {
font-family: 'Google Sans', sans-serif !important;
font-style: normal;
width: 100%;
display: flex;
flex-direction: row;
gap: 10px;
padding-bottom: 4%;
button {
height: 45px;
border: none;
}
.error {
background: #e9867e33;
width: 28%;
}
.warning {
background: rgb(241 226 183 / 44%);
width: 28%;
}
.info {
background: #F6F9FE;
width: 35%;
}
.errorOutline {
border: 1px solid red;
}
.warningOutline {
border: 1px solid #fbc645;
}
.infoOutline {
border: 1px solid #4285F4;
}
}
.delete-confirmation-text {
font-family: 'Google Sans', sans-serif !important;
font-style: normal;
width: 100%;
display: flex;
justify-content: center;
font-size: 20px;
color: #161A1D;
}
.dialog-delete-button {
border-radius: 30px;
font-family: 'Google Sans', sans-serif !important;
font-style: normal;
font-weight: 500;
font-size: 14px;
line-height: 23px;
letter-spacing: unset;
box-shadow: none;
color: #f70808;
border: 1px solid #f70808;
}
.disableDiv {
pointer-events: none;
}
.spinner-container {
width: 100%;
justify-content: center;
display: flex;
flex-direction: row;
text-align: center;
position: absolute;
top: 50%;
}
.spinner-dialog-container {
width: 100%;
justify-content: flex-end;
display: flex;
flex-direction: row;
padding-right: 5%;
padding-bottom: 4%;
}
.text-column {
width: 30% !important;
word-break: break-word;
}
.comment-detail-container {
display: flex;
flex-direction: column;
gap: 20px;
}
.details-container {
font-weight: 600;
border-radius: 5px;
border: 1px solid #9e9e9e;
text-align: start;
padding: 3%;
gap: 14px;
display: flex;
flex-direction: column;
.question-details-container {
background: white;
font-weight: 400;
border-radius: 12px;
}
}
.chip-color {
background: aliceblue !important;
border: 1px solid #4285f4;
}
.action-column {
width: 8%;
}
::-webkit-scrollbar {
width: 9px;
}
::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
background-color: rgba(155, 155, 155, 0.5);
border-radius: 20px;
border: transparent;
}
.search-question-field {
width: 100%;
mat-icon {
color: #363e45;
cursor: pointer;
}
}
::ng-deep .search-question-field .mat-mdc-text-field-wrapper.mdc-text-field--outlined .mat-mdc-form-field-infix {
padding-top: 10px !important;
padding-bottom: 10px !important;
}
::ng-deep .search-question-field .mdc-text-field__input {
height: 34px !important;
}
.input-field-container {
display: flex;
flex-direction: row;
gap: 2%;
align-items: center;
}
.submit-button {
// width: 8% !important;
background: #4285F4 !important;
border: 1px solid #4285F4;
box-sizing: border-box;
// border: 1px solid #e0e0e0 !important;
color: #fff !important;
position: relative;
user-select: none;
cursor: pointer;
outline: none;
-webkit-tap-highlight-color: transparent;
font-family: 'Google Sans', sans-serif !important;
font-size: 14px;
font-weight: 500;
white-space: nowrap;
text-decoration: none;
vertical-align: baseline;
text-align: center;
margin: 0;
min-width: 64px;
line-height: 36px;
padding: 20px 46px;
border-radius: 48px !important;
overflow: visible;
transform: translate3d(0, 0, 0);
transition: background 400ms cubic-bezier(0.25, 0.8, 0.25, 1), box-shadow 280ms cubic-bezier(0.4, 0, 0.2, 1);
}
.reset-button {
background: white !important;
box-sizing: border-box !important;
border: 1px solid #4285F4 !important;
color: #4285F4 !important;
cursor: pointer;
outline: none;
font-family: 'Google Sans', sans-serif !important;
font-size: 14px;
font-weight: 500;
margin: 0;
min-width: 130px;
line-height: 36px;
padding: 0px 25px;
height: 100%;
border-radius: 48px !important;
display: flex;
justify-content: center;
align-content: center;
align-items: center;
flex-direction: row;
}
.inactive-button {
background: #ffffff !important;
box-sizing: border-box !important;
border: 1px solid rgba(43, 41, 40, 0.502) !important;
color: rgba(43, 41, 40, 0.93) !important;
cursor: pointer;
outline: none;
font-family: "Google Sans", sans-serif !important;
font-size: 14px;
font-weight: 500;
margin: 0;
min-width: 64px;
line-height: 36px;
padding: 0px 45px;
height: 60%;
margin-left: 2rem;
border-radius: 48px !important;
display: flex;
justify-content: center;
flex-direction: row;
flex-wrap: wrap;
align-content: center;
align-items: center;
}
.active-button {
background: rgb(226 249 225) !important;
box-sizing: border-box !important;
border: 1px solid rgb(59 153 29 / 84%) !important;
color: rgb(17 193 56 / 93%) !important;
cursor: pointer;
outline: none;
font-family: "Google Sans", sans-serif !important;
font-size: 14px;
font-weight: 500;
margin: 0;
min-width: 64px;
line-height: 36px;
padding: 0px 45px;
height: 60%;
margin-left: 2rem;
border-radius: 48px !important;
display: flex;
justify-content: center;
flex-direction: column;
flex-wrap: wrap;
align-content: center;
align-items: center;
}
.intent-field {
width: 50%;
.my-icon {
margin-right: 0.5rem;
color: #005cbb;
border-radius: 2rem;
padding: 1rem;
}
}
.intent-heading-container {
font-style: normal;
font-weight: 400;
font-size: 16px;
line-height: 23px;
color: #363e45;
padding-top: 2%;
padding-bottom: 1%;
}
.internal-spinner-container {
height: 31px
}
pre {
width: 100%;
color: white;
padding: 2%;
font-family: monospace;
text-wrap: pretty;
display: flex;
flex-direction: row;
}
::-webkit-scrollbar-track {
-webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
border-radius: 10px;
background-color: #F5F5F5;
}
::-webkit-scrollbar {
width: 12px;
background-color: #F5F5F5;
}
::-webkit-scrollbar-thumb {
border-radius: 10px;
-webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, .3);
background-color: #D62929;
}
.intent-chunk {
display: flex;
flex-direction: row;
width: 90%;
}
.copy-button-container {
width: 10%;
display: flex;
justify-content: flex-end;
}
.configure-title {
display: flex;
align-content: center;
flex-wrap: wrap;
width: 50%;
}
.intent-buttons-container {
display: flex;
gap: 2%;
justify-content: flex-end;
padding-top: 1%;
padding-bottom: 1%;
}
.image-container {
justify-content: center;
display: flex;
width: 90%;
img {
height: 47%;
width: 47%;
}
}
.delete-confirmation-text {
font-family: 'Google Sans', sans-serif !important;
font-style: normal;
width: 100%;
display: flex;
justify-content: center;
font-size: 20px;
color: #161A1D;
}
.dialog-actions-container {
padding-bottom: 2%;
justify-content: center;
display: flex;
flex-direction: row;
text-align: end;
width: 100%;
padding-right: 2%;
.discard-button {
color: rgba(92, 95, 97, 0.5);
border: 1px solid rgb(92 95 97 / 28%);
border-radius: 40px;
box-shadow: none;
letter-spacing: inherit;
}
.save-button {
background: #4285F4;
color: #fff;
border-radius: 40px;
box-shadow: none;
letter-spacing: inherit;
}
.dialog-delete-button {
border-radius: 30px;
font-family: 'Google Sans', sans-serif !important;
font-style: normal;
font-weight: 500;
font-size: 14px;
line-height: 23px;
letter-spacing: unset;
box-shadow: none;
color: #f70808;
border: 1px solid #f70808;
}
}
@@ -0,0 +1,38 @@
/**
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {ComponentFixture, TestBed} from '@angular/core/testing';
import {ManageSearchApplicationComponent} from './manage-search-application.component';
describe('ManageAgentBuilderComponent', () => {
let component: ManageSearchApplicationComponent;
let fixture: ComponentFixture<ManageSearchApplicationComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ManageSearchApplicationComponent],
}).compileComponents();
fixture = TestBed.createComponent(ManageSearchApplicationComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,107 @@
/**
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {Component, OnDestroy} from '@angular/core';
import {SearchApplicationService} from 'src/app/services/search_application.service';
import {ReplaySubject} from 'rxjs';
import {takeUntil} from 'rxjs/operators';
import {UserService} from 'src/app/services/user/user.service';
import {Router} from '@angular/router';
import {Engine} from 'src/app/models/engine.model';
import {EnginesService} from 'src/app/services/engines.service';
@Component({
selector: 'app-manage-search-application',
templateUrl: './manage-search-application.component.html',
styleUrls: ['./manage-search-application.component.scss'],
})
export class ManageSearchApplicationComponent implements OnDestroy {
selectedEngine: Engine | undefined;
engines: Engine[] = [];
editMode = false;
savedEngineID = '';
private readonly destroyed = new ReplaySubject<void>(1);
constructor(
private readonly searchApplicationService: SearchApplicationService,
private readonly userService: UserService,
private readonly router: Router,
private enginesService: EnginesService
) {
this.enginesService
.getAll()
.subscribe(response => (this.engines = response));
this.disableForm();
this.getConfigData();
}
disableForm() {
this.editMode = false;
}
enableForm() {
this.editMode = true;
}
getConfigData() {
this.userService.showLoading();
this.searchApplicationService
.get()
.pipe(takeUntil(this.destroyed))
.subscribe({
next: response => {
this.savedEngineID = response.engine_id;
this.selectedEngine = this.engines.filter(
e => e.engine_id === response.engine_id
)[0];
this.userService.hideLoading();
},
error: () => {
this.userService.hideLoading();
},
});
}
saveForm() {
this.userService.showLoading();
const searchApplication = {
engine_id: this.selectedEngine?.engine_id || "",
region: this.selectedEngine?.region || "",
};
this.searchApplicationService
.update(this.savedEngineID, searchApplication)
.subscribe({
next: () => {
this.savedEngineID = this.selectedEngine?.engine_id || "";
this.userService.hideLoading();
this.disableForm();
},
error: () => {
this.userService.hideLoading();
},
});
}
navigateToMain() {
this.router.navigateByUrl('/');
}
ngOnDestroy(): void {
this.destroyed.next();
this.destroyed.complete();
}
}
@@ -0,0 +1,52 @@
<!--
Copyright 2025 Google LLC
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<div>
<div class="configure-title">
<span>Configure Search Application</span>
</div>
<form>
<mat-form-field
class="form-field"
appearance="outline"
[floatLabel]="'always'"
>
<mat-label>Choose an Engine</mat-label>
<mat-select
[(ngModel)]="selectedEngine"
name="engine"
>
<mat-option *ngFor="let engine of this.engines" [value]="engine">{{engine.name}}</mat-option>
</mat-select>
</mat-form-field>
</form>
<div class="button-container">
<button
mat-button
*ngIf="!this.showSpinner"
class="submit-button"
[disabled]="!this.selectedEngine"
(click)="saveForm()"
>
Save
</button>
<div class="spinner-container" *ngIf="this.showSpinner">
<mat-spinner style="width: 60px; height: 60px"></mat-spinner>
</div>
</div>
</div>
@@ -0,0 +1,106 @@
/**
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
.configure-title {
display: flex;
align-content: center;
flex-wrap: wrap;
width: 100%;
justify-content: center;
margin-top: -26px;
padding-bottom: 5%;
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
font-size: 24px;
line-height: 24px;
color: #161A1D;
}
.form-field {
width: 70%;
.my-icon {
margin-right: 0.5rem;
color: #005cbb;
border-radius: 2rem;
padding: 1rem;
}
}
.button-container {
justify-content: center;
display: flex;
gap: 10px;
margin-bottom: -30px;
padding-top: 3%;
.submit-button {
width: 7%;
background: #4285F4;
box-sizing: border-box;
border: 1px solid #e0e0e0;
color: #fff;
position: relative;
user-select: none;
cursor: pointer;
outline: none;
-webkit-tap-highlight-color: transparent;
display: inline-block;
font-family: 'Google Sans', sans-serif !important;
font-size: 14px;
font-weight: 500;
white-space: nowrap;
text-decoration: none;
vertical-align: baseline;
text-align: center;
margin: 0;
min-width: 64px;
padding: 0 16px;
border-radius: 35px;
overflow: visible;
transform: translate3d(0, 0, 0);
transition: background 400ms cubic-bezier(0.25, 0.8, 0.25, 1), box-shadow 280ms cubic-bezier(0.4, 0, 0.2, 1);
}
.cancel-button {
width: 7%;
background: white;
box-sizing: border-box;
border: 1px solid #4285F4;
color: #4285F4;
position: relative;
user-select: none;
cursor: pointer;
outline: none;
-webkit-tap-highlight-color: transparent;
display: inline-block;
font-family: 'Google Sans', sans-serif !important;
font-size: 14px;
font-weight: 500;
white-space: nowrap;
text-decoration: none;
vertical-align: baseline;
text-align: center;
margin: 0;
min-width: 64px;
padding: 0 16px;
border-radius: 35px;
overflow: visible;
transform: translate3d(0, 0, 0);
transition: background 400ms cubic-bezier(0.25, 0.8, 0.25, 1), box-shadow 280ms cubic-bezier(0.4, 0, 0.2, 1);
}
}
@@ -0,0 +1,38 @@
/**
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {ComponentFixture, TestBed} from '@angular/core/testing';
import {SearchApplicationFormComponent} from './search-application-form.component';
describe('AddAgentBuilderComponent', () => {
let component: SearchApplicationFormComponent;
let fixture: ComponentFixture<SearchApplicationFormComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [SearchApplicationFormComponent],
}).compileComponents();
fixture = TestBed.createComponent(SearchApplicationFormComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,66 @@
/**
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {Component} from '@angular/core';
import {MatDialogRef} from '@angular/material/dialog';
import {Router} from '@angular/router';
import {Engine} from 'src/app/models/engine.model';
import {EnginesService} from 'src/app/services/engines.service';
import {SearchApplicationService} from 'src/app/services/search_application.service';
import {UserService} from 'src/app/services/user/user.service';
@Component({
selector: 'app-search-application-form',
templateUrl: './search-application-form.component.html',
styleUrls: ['./search-application-form.component.scss'],
})
export class SearchApplicationFormComponent {
showSpinner = false;
selectedEngine: Engine | undefined;
engines: Engine[] = [];
constructor(
private dialogRef: MatDialogRef<SearchApplicationFormComponent>,
private readonly router: Router,
private readonly searchApplicationService: SearchApplicationService,
private userService: UserService,
private enginesService: EnginesService
) {
this.enginesService
.getAll()
.subscribe(response => (this.engines = response));
}
saveForm() {
if (this.selectedEngine) {
const searchApplication = {
engine_id: this.selectedEngine.engine_id,
region: this.selectedEngine.region,
};
this.userService.showLoading();
this.searchApplicationService.create(searchApplication).subscribe({
next: () => {
this.userService.hideLoading();
this.dialogRef.close()!;
this.router.navigateByUrl('/');
},
error: () => {
this.userService.hideLoading();
},
});
}
}
}
@@ -0,0 +1,26 @@
<!--
Copyright 2025 Google LLC
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<div fxLayout="row" fxLayoutGap="2%">
<img src="assets/images/{{ icon }}.svg" alt="icon inside toast message" />
<span class="toast-content">{{ text }}</span>
<img
class="close-toast"
src="assets/images/cancel-toast.svg"
alt="cross icon inside toast message"
(click)="closeToast()"
/>
</div>
@@ -0,0 +1,37 @@
/**
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
.toast-content {
color: #ffffff;
font-family: "Google Sans", sans-serif !important;
}
.close-toast {
margin-left: auto;
align-self: baseline;
cursor: pointer;
}
.red-toast {
background-color: #ed0c0c !important;
}
.green-toast {
background-color: #06865e !important;
}
.mat-mdc-snack-bar-container .mdc-snackbar__surface {
background-color: transparent !important;
}
@@ -0,0 +1,38 @@
/**
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {ComponentFixture, TestBed} from '@angular/core/testing';
import {ToastMessageComponent} from './toast-message.component';
describe('ToastMessageComponent', () => {
let component: ToastMessageComponent;
let fixture: ComponentFixture<ToastMessageComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ToastMessageComponent],
}).compileComponents();
fixture = TestBed.createComponent(ToastMessageComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,40 @@
/**
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {Component, Inject, ViewEncapsulation} from '@angular/core';
import {MatSnackBar, MAT_SNACK_BAR_DATA} from '@angular/material/snack-bar';
@Component({
selector: 'app-toast-message',
templateUrl: './toast-message.component.html',
styleUrls: ['./toast-message.component.scss'],
encapsulation: ViewEncapsulation.None,
})
export class ToastMessageComponent {
text: string;
icon: string;
constructor(
private _snackBar: MatSnackBar,
@Inject(MAT_SNACK_BAR_DATA) public snackBarData: any
) {
this.text = snackBarData.text;
this.icon = snackBarData.icon;
}
closeToast() {
this._snackBar.dismiss();
}
}
@@ -0,0 +1,20 @@
/**
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export type config = {
name: string;
url: string;
};
@@ -0,0 +1,21 @@
/**
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export type Engine = {
name: string;
engine_id: string;
region: string;
};
@@ -0,0 +1,52 @@
/**
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export type SearchRequest = {
term: string;
};
export type SearchResponse = {
summary: any;
results: SearchResult[];
totalSize: number;
};
export type SearchResult = {
document: Document;
};
export type Document = {
derivedStructData: DocumentData;
};
export type DocumentData = {
title: string;
link: string;
snippets: Snippet[];
pagemap: PageMap;
};
export type Snippet = {
snippet: string;
};
export type PageMap = {
cse_image: ImagesData[];
};
export type ImagesData = {
src: string;
};
@@ -0,0 +1,20 @@
/**
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export type SearchApplication = {
engine_id: string;
region: string;
};
@@ -0,0 +1,24 @@
/**
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {TruncatePipe} from './truncate.pipe';
describe('TruncatePipe', () => {
it('create an instance', () => {
const pipe = new TruncatePipe();
expect(pipe).toBeTruthy();
});
});
@@ -0,0 +1,26 @@
/**
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {Pipe, PipeTransform} from '@angular/core';
@Pipe({
name: 'truncate',
})
export class TruncatePipe implements PipeTransform {
transform(value: string, limit = 300, trail = '...'): string {
return value.length > limit ? value.substring(0, limit) + trail : value;
}
}
@@ -0,0 +1,32 @@
/**
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {TestBed} from '@angular/core/testing';
import {EnginesService} from './engines.service';
describe('EnginesService', () => {
let service: EnginesService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(EnginesService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});
@@ -0,0 +1,36 @@
/**
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {map, Observable} from 'rxjs';
import {HttpClient} from '@angular/common/http';
import {Injectable} from '@angular/core';
import {Engine} from '../models/engine.model';
import {environment} from 'src/environments/environment';
const ENGINES_URL = `${environment.backendURL}/search/engines`;
@Injectable({
providedIn: 'root',
})
export class EnginesService {
constructor(private http: HttpClient) {}
getAll(): Observable<Engine[]> {
return this.http
.get(ENGINES_URL)
.pipe(map(response => response as Engine[]));
}
}
@@ -0,0 +1,32 @@
/**
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {TestBed} from '@angular/core/testing';
import {AuthGuard} from './auth.guard';
describe('AuthGuard', () => {
let guard: AuthGuard;
beforeEach(() => {
TestBed.configureTestingModule({});
guard = TestBed.inject(AuthGuard);
});
it('should be created', () => {
expect(guard).toBeTruthy();
});
});
@@ -0,0 +1,54 @@
/**
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {Injectable} from '@angular/core';
import {
ActivatedRouteSnapshot,
CanActivate,
Router,
RouterStateSnapshot,
UrlTree,
} from '@angular/router';
import {Observable} from 'rxjs';
import {AuthService} from './auth.service';
import {environment} from 'src/environments/environment';
const LOGIN_ROUTE = '/login';
@Injectable({
providedIn: 'root',
})
export class AuthGuard implements CanActivate {
constructor(
private auth: AuthService,
private router: Router
) {}
canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
):
| Observable<boolean | UrlTree>
| Promise<boolean | UrlTree>
| boolean
| UrlTree {
if (environment.requiredLogin === 'True' && !this.auth.isLoggedIn()) {
this.router.navigate([LOGIN_ROUTE]);
return false;
}
return true;
}
}
@@ -0,0 +1,32 @@
/**
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {TestBed} from '@angular/core/testing';
import {AuthService} from './auth.service';
describe('AuthService', () => {
let service: AuthService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(AuthService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});
@@ -0,0 +1,57 @@
/**
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {Injectable} from '@angular/core';
import {Router} from '@angular/router';
import {environment} from 'src/environments/environment';
const USER_TOKEN_KEY = 'gpau_id';
const USER_DETAILS = 'USER_DETAILS';
const LOGIN_ROUTE = '/login';
@Injectable({
providedIn: 'root',
})
export class AuthService {
private requiredLogin: boolean = environment.requiredLogin === 'True';
constructor(private router: Router) {}
saveUserSession(token: string) {
localStorage.setItem(USER_TOKEN_KEY, token);
}
logout(route: string = LOGIN_ROUTE) {
localStorage.removeItem(USER_TOKEN_KEY);
localStorage.removeItem(USER_DETAILS);
localStorage.removeItem('showTooltip');
this.router.navigateByUrl(route);
}
isLoggedIn() {
const isLoggedIn = localStorage.getItem(USER_TOKEN_KEY) !== null;
if (!isLoggedIn && this.router.url !== LOGIN_ROUTE) {
this.router.navigate([LOGIN_ROUTE]);
}
return isLoggedIn;
}
isUserLoggedIn() {
if (!this.requiredLogin) return true;
const isLoggedIn = localStorage.getItem(USER_TOKEN_KEY) !== null;
return isLoggedIn;
}
}
@@ -0,0 +1,32 @@
/**
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {TestBed} from '@angular/core/testing';
import {SearchService} from './search.service';
describe('SearchService', () => {
let service: SearchService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(SearchService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});
@@ -0,0 +1,37 @@
/**
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {HttpClient} from '@angular/common/http';
import {Injectable} from '@angular/core';
import {environment} from 'src/environments/environment';
import {map} from 'rxjs/operators';
import {SearchRequest, SearchResponse} from '../models/search.model';
const searchURL = `${environment.backendURL}/search`;
@Injectable({
providedIn: 'root',
})
export class SearchService {
constructor(private http: HttpClient) {}
search(term: string) {
const request: SearchRequest = {term: term};
return this.http
.post(searchURL, request)
.pipe(map(response => response as SearchResponse));
}
}
@@ -0,0 +1,32 @@
/**
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {TestBed} from '@angular/core/testing';
import {SearchApplicationService} from './search_application.service';
describe('SearchApplicationService', () => {
let service: SearchApplicationService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(SearchApplicationService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});
@@ -0,0 +1,45 @@
/**
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {Injectable} from '@angular/core';
import {HttpClient} from '@angular/common/http';
import {environment} from 'src/environments/environment';
import {map} from 'rxjs/operators';
import {Observable} from 'rxjs';
import {SearchApplication} from '../models/search_application.model';
const configURL = `${environment.backendURL}/search/application`;
@Injectable({
providedIn: 'root',
})
export class SearchApplicationService {
constructor(private readonly http: HttpClient) {}
get(): Observable<SearchApplication> {
return this.http
.get(configURL)
.pipe(map(response => response as SearchApplication));
}
create(searchApplication: SearchApplication) {
return this.http.post(configURL, searchApplication);
}
update(engine_id: string, searchApplication: SearchApplication) {
return this.http.put(`${configURL}/${engine_id}`, searchApplication);
}
}
@@ -0,0 +1,34 @@
/**
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {Injectable} from '@angular/core';
import {HttpClient} from '@angular/common/http';
import {environment} from 'src/environments/environment';
const audioChatUrl = `${environment.backendURL}/audio_chat`;
@Injectable({
providedIn: 'root',
})
export class SpeechToTextService {
constructor(private http: HttpClient) {}
async transcribeAudio(audioBlob: Blob) {
const formData = new FormData();
formData.append('audio_file', audioBlob, 'audio.wav');
return this.http.post(audioChatUrl, formData);
}
}
@@ -0,0 +1,32 @@
/**
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {TestBed} from '@angular/core/testing';
import {SessionService} from './session.service';
describe('SessionService', () => {
let service: SessionService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(SessionService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});
@@ -0,0 +1,35 @@
/**
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {Injectable} from '@angular/core';
import {v4 as uuid} from 'uuid';
const SESSION_KEY = 'pasid';
@Injectable({
providedIn: 'root',
})
export class SessionService {
constructor() {}
getSession(): string | null {
return sessionStorage.getItem(SESSION_KEY);
}
createSession() {
sessionStorage.setItem(SESSION_KEY, uuid());
}
}
@@ -0,0 +1,54 @@
/**
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {Injectable} from '@angular/core';
import {BehaviorSubject} from 'rxjs';
type UserStored = {
uid?: string;
name?: string;
email?: string;
photoURL?: string;
displayName?: string;
domain?: string;
};
@Injectable({
providedIn: 'root',
})
export class UserService {
readonly loadingSubject = new BehaviorSubject<boolean>(false);
constructor() {}
showLoading() {
this.loadingSubject.next(true);
}
hideLoading() {
this.loadingSubject.next(false);
}
getUserDetails(): UserStored {
if (localStorage.getItem('USER_DETAILS') !== null) {
const userObj = localStorage.getItem('USER_DETAILS');
return JSON.parse(userObj || '{}');
} else {
const userDetails: UserStored = {};
return userDetails;
}
}
}
@@ -0,0 +1,11 @@
<svg width="694px" height="558px" viewBox="0 0 694 558" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="google-cloud-1" fill-rule="nonzero">
<path d="M440.545766,152.920305 L461.717489,152.920305 L522.033632,92.9270295 L525,67.4718307 C412.772422,-31.0513593 241.450145,-20.4353843 142.396729,91.1914478 C114.856041,122.200508 94.8766816,159.08162 84,199 C90.7179504,196.251996 98.1629517,195.8181 105.171723,197.72724 L225.774927,177.941608 C225.774927,177.941608 231.911237,167.846308 235.081179,168.482688 C288.737536,109.877878 379.037259,103.051256 440.981997,152.920305 L440.545766,152.920305 Z" id="Path" fill="#EA4335"></path>
<path d="M607.866504,200.682375 C594.001378,149.778725 565.57351,104.008948 526.012848,69 L441.426861,153.404341 C477.150633,182.525289 497.497778,226.409746 496.625757,272.440567 L496.625757,287.436115 C538.221135,287.436115 571.910193,321.081832 571.910193,362.558879 C571.910193,404.064932 538.192067,437.681644 496.625757,437.681644 L346.02782,437.681644 L331,452.880226 L331,542.998538 L346.02782,557.994086 L496.625757,557.994086 C582.955785,558.6612 659.548251,502.826713 685.185654,420.568736 C710.764921,338.281754 679.372184,248.946575 607.866504,200.682375 L607.866504,200.682375 Z" id="Path" fill="#4285F4"></path>
<path d="M194.75446,555.998385 L346,555.998385 L346,436.702654 L194.75446,436.702654 C183.982485,436.702654 173.327279,434.43008 163.518651,430 L142.266623,436.47252 L81.3130068,496.134769 L76,517.076966 C110.184236,542.506777 151.900097,556.170985 194.75446,555.998385 L194.75446,555.998385 Z" id="Path" fill="#34A853"></path>
<path d="M195.36137,165 C111.412398,165.496534 37.0600945,219.208571 10.2827643,298.683735 C-16.4945658,378.158899 10.1952567,465.881761 76.7302131,517 L164.383674,429.422857 C126.347031,412.257155 109.458061,367.550553 126.638723,329.547028 C143.819384,291.543502 188.564945,274.669238 226.601588,291.834941 C243.344712,299.412331 256.762546,312.818482 264.346539,329.547028 L352,241.969885 C314.692587,193.270582 256.733377,164.797082 195.36137,165 L195.36137,165 Z" id="Path" fill="#FBBC05"></path>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
<g>
<g>
<path fill="#919191" d="M346.991,149.886l-21.508-21.509l-176.36,176.355c-21.903,21.903-21.903,57.543,0,79.447
c21.903,21.904,57.543,21.904,79.447,0L465.422,147.32c16.3-16.3,25.277-37.971,25.277-61.022s-8.977-44.722-25.277-61.022
C449.123,8.977,427.451,0,404.4,0c-23.052,0-44.723,8.977-61.022,25.277L55.401,313.254c-21.99,21.99-34.1,51.226-34.1,82.323
c0,31.098,12.11,60.333,34.1,82.323c21.99,21.99,51.226,34.1,82.323,34.1s60.333-12.11,82.323-34.1l227.479-227.48l-21.508-21.508
l-227.479,227.48c-16.245,16.245-37.842,25.19-60.815,25.19c-22.973,0-44.57-8.946-60.815-25.19
c-16.245-16.245-25.19-37.842-25.19-60.815c0-22.973,8.947-44.571,25.19-60.815L364.887,46.785
c21.788-21.787,57.24-21.787,79.028,0c21.788,21.788,21.788,57.24,0,79.028L207.063,362.67c-10.044,10.044-26.387,10.044-36.431,0
s-10.044-26.387,0-36.431L346.991,149.886z"/>
</g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

@@ -0,0 +1,4 @@
<svg width="38" height="39" viewBox="0 0 38 39" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M19 0.394531C8.5234 0.394531 0 9.0223 0 19.6272C0 30.2321 8.5234 38.8598 19 38.8598C29.4766 38.8598 38 30.2321 38 19.6272C38 9.0223 29.4766 0.394531 19 0.394531ZM19 35.0133C10.6191 35.0133 3.8 28.1107 3.8 19.6272C3.8 11.1437 10.6191 4.24106 19 4.24106C27.3809 4.24106 34.2 11.1437 34.2 19.6272C34.2 28.1107 27.3809 35.0133 19 35.0133Z" fill="#0F9D58"/>
<path d="M16.8434 21.341C16.4503 21.7495 15.7965 21.7498 15.403 21.3416L12.432 18.2599C12.0381 17.8513 11.3835 17.8521 10.9906 18.2615L9.66615 19.6416C9.29415 20.0292 9.29492 20.6415 9.66788 21.0282L15.4068 26.9785C15.8003 27.3866 16.454 27.3862 16.8471 26.9778L28.3326 15.0435C28.7053 14.6563 28.7053 14.0438 28.3326 13.6566L27.0063 12.2784C26.6129 11.8696 25.9586 11.8696 25.5652 12.2784L16.8434 21.341Z" fill="#0F9D58"/>
</svg>

After

Width:  |  Height:  |  Size: 889 B

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 52 KiB

@@ -0,0 +1,8 @@
<svg width="33" height="43" viewBox="0 0 33 43" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="16.9761" cy="14.4448" r="14.4448" fill="#ED5A4D"/>
<circle cx="16.9763" cy="14.4451" r="7.22241" fill="white"/>
<path d="M10.2422 20.4648L19.8031 25.5596L10.5096 43L7.12316 37.8365L0.948678 37.9052L10.2422 20.4648Z" fill="#ED5A4D"/>
<path d="M22.9277 20.4648L13.3668 25.5596L22.6603 43L26.0468 37.8365L32.2212 37.9052L22.9277 20.4648Z" fill="#ED5A4D"/>
<path d="M12.8828 27.0831L10.5957 23.2312L6.26222 22.2682L6.68353 17.8144L3.73438 14.4439L6.68353 11.0735L6.26222 6.61963L10.5957 5.65664L12.8828 1.80469L16.9755 3.5501L21.0682 1.80469L23.3553 5.65664L27.6887 6.61963L27.2674 11.0735L30.2166 14.4439L27.2674 17.8144L27.6887 22.2682L23.3553 23.2312L21.0682 27.0831L16.9755 25.3377L12.8828 27.0831ZM13.9059 24.0136L16.9755 22.6895L20.1052 24.0136L21.7904 21.1246L25.1007 20.3422L24.7997 16.9718L27.0267 14.4439L24.7997 11.8559L25.1007 8.48542L21.7904 7.76318L20.045 4.87421L16.9755 6.19832L13.8458 4.87421L12.1605 7.76318L8.85025 8.48542L9.15119 11.8559L6.92427 14.4439L9.15119 16.9718L8.85025 20.4024L12.1605 21.1246L13.9059 24.0136Z" fill="white"/>
<path d="M22.5138 11.9173L15.7127 18.7184L11.4395 14.4451L13.1247 12.7599L15.7127 15.2878L20.8286 10.1719L22.5138 11.9173Z" fill="#ED5A4D"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

@@ -0,0 +1,24 @@
<svg width="65" height="65" viewBox="0 0 65 65" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Group 11224">
<circle id="Ellipse 411" cx="16" cy="16" r="16" transform="matrix(-1 0 0 1 48.791 12.8398)" fill="#D9D9D9"/>
<g id="Rectangle 10140" filter="url(#filter0_d_494_382)">
<rect x="16.791" y="12.6562" width="32" height="32" rx="16" fill="#4285F4"/>
</g>
<path id="Vector" d="M34.1666 34.3388H41.878C41.9322 34.0692 42.0056 33.8062 42.0056 33.525V27.6464C42.0056 25.4558 41.1008 23.4008 39.563 21.872L38.6533 22.7246C38.4116 22.9506 38.0977 23.0686 37.7796 23.0686C37.6084 23.0686 37.4346 23.0337 37.272 22.9641C36.8042 22.7611 36.5025 22.301 36.5025 21.7925V19.8863H36.0674H36.0664L36.0625 19.8856C35.8285 19.885 34.8842 19.6143 34.6998 19.5082C34.6864 19.5005 31.7903 19.4492 31.7903 19.4492C27.2715 19.4492 23.5957 23.126 23.5957 27.6458V33.5244C23.5957 33.9342 23.6691 34.3372 23.7814 34.7108C24.3018 36.5234 25.9543 37.8611 27.9311 37.8611H37.6702C39.2555 37.8611 40.6298 36.9966 41.3858 35.723H34.1666C33.7838 35.723 33.4737 35.4136 33.4737 35.0311C33.4734 34.6483 33.7838 34.3388 34.1666 34.3388ZM28.4704 32.6144C26.8358 32.6144 25.5067 31.2863 25.5067 29.6527V27.2632C25.5067 25.6296 26.8358 24.3015 28.4704 24.3015H37.1261C38.7607 24.3015 40.0898 25.6296 40.0898 27.2632V29.6527C40.0898 31.2863 38.7607 32.6144 37.1261 32.6144H28.4704Z" fill="white"/>
<path id="Vector 516" d="M40.1076 22.4459C38.2117 19.3575 34.6039 19.509 34.6039 19.509C34.5267 20.5977 34.4554 22.8258 34.6407 23.1409C34.826 23.4559 36.6483 23.4111 37.5363 23.3494C38.7486 23.6968 41.2565 24.3176 40.1076 22.4459Z" fill="white"/>
<path id="Vector_2" d="M38.7053 29.6545V27.2651C38.7053 26.3952 37.9977 25.6875 37.1278 25.6875H28.4721C27.6022 25.6875 26.8945 26.3952 26.8945 27.2651V29.6545C26.8945 30.5244 27.6022 31.232 28.4721 31.232H37.1278C37.9977 31.2317 38.7053 30.5244 38.7053 29.6545ZM30.8442 29.1517H29.8678C29.485 29.1517 29.174 28.8423 29.174 28.4598C29.174 28.0772 29.4853 27.7678 29.8678 27.7678H30.8442C31.227 27.7678 31.5381 28.0772 31.5381 28.4598C31.5381 28.8423 31.227 29.1517 30.8442 29.1517ZM35.733 29.1517H34.7547C34.3719 29.1517 34.0618 28.8423 34.0618 28.4598C34.0618 28.0772 34.3722 27.7678 34.7547 27.7678H35.733C36.1158 27.7678 36.4249 28.0772 36.4249 28.4598C36.4249 28.8423 36.1158 29.1517 35.733 29.1517Z" fill="white"/>
<path id="Vector_3" d="M45.4704 29.2305C45.4704 28.1827 44.6178 27.3301 43.57 27.3301H43.3862C43.3901 27.4365 43.392 33.5258 43.392 33.5258C43.392 33.8022 43.3718 34.0728 43.334 34.3396H43.57C44.0708 34.3396 44.554 34.1405 44.9317 33.7791C45.2719 33.4235 45.4701 32.94 45.4701 32.4376L45.4704 29.2305Z" fill="white"/>
<path id="Vector_4" d="M20.1311 29.2305C20.1311 28.1827 20.9837 27.3301 22.0316 27.3301H22.2153C22.2115 27.4365 22.2095 33.5258 22.2095 33.5258C22.2095 33.8022 22.2297 34.0728 22.2676 34.3396H22.0316C21.5307 34.3396 21.0475 34.1405 20.6698 33.7791C20.3296 33.4235 20.1315 32.94 20.1315 32.4376L20.1311 29.2305Z" fill="white"/>
</g>
<defs>
<filter id="filter0_d_494_382" x="0.791016" y="0.65625" width="64" height="64" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="4"/>
<feGaussianBlur stdDeviation="8"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.549974 0 0 0 0 0.548698 0 0 0 0 0.6125 0 0 0 0.25 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_494_382"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_494_382" result="shape"/>
</filter>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 3.6 KiB

@@ -0,0 +1,4 @@
<svg width="12" height="13" viewBox="0 0 12 13" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1 1L11 11.9" stroke="white" stroke-linecap="round"/>
<path d="M11 1L1 11.9" stroke="white" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 229 B

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="#919191" d="M19.005 3.175H4.674C3.642 3.175 3 3.789 3 4.821V21.02l3.544-3.514h12.461c1.033 0 2.064-1.06 2.064-2.093V4.821c-.001-1.032-1.032-1.646-2.064-1.646zm-4.989 9.869H7.041V11.1h6.975v1.944zm3-4H7.041V7.1h9.975v1.944z"></path></svg>

After

Width:  |  Height:  |  Size: 332 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 2C17.514 2 22 6.486 22 12C22 17.514 17.514 22 12 22C6.486 22 2 17.514 2 12C2 6.486 6.486 2 12 2ZM12 0C5.373 0 0 5.373 0 12C0 18.627 5.373 24 12 24C18.627 24 24 18.627 24 12C24 5.373 18.627 0 12 0ZM18 16.538L13.408 11.99L17.954 7.403L16.538 6L11.993 10.589L7.405 6.046L6 7.451L10.593 12.003L6.046 16.595L7.451 18L12.006 13.404L16.597 17.954L18 16.538Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 483 B

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 2C17.514 2 22 6.486 22 12C22 17.514 17.514 22 12 22C6.486 22 2 17.514 2 12C2 6.486 6.486 2 12 2ZM12 0C5.373 0 0 5.373 0 12C0 18.627 5.373 24 12 24C18.627 24 24 18.627 24 12C24 5.373 18.627 0 12 0ZM18 16.538L13.408 11.99L17.954 7.403L16.538 6L11.993 10.589L7.405 6.046L6 7.451L10.593 12.003L6.046 16.595L7.451 18L12.006 13.404L16.597 17.954L18 16.538Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 483 B

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M12 2c5.514 0 10 4.486 10 10s-4.486 10-10 10-10-4.486-10-10 4.486-10 10-10zm0-2c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12zm6 16.094l-4.157-4.104 4.1-4.141-1.849-1.849-4.105 4.159-4.156-4.102-1.833 1.834 4.161 4.12-4.104 4.157 1.834 1.832 4.118-4.159 4.143 4.102 1.848-1.849z"/></svg>

After

Width:  |  Height:  |  Size: 402 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

@@ -0,0 +1,26 @@
<svg width="540" height="280" viewBox="0 0 540 280" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M113.346 24.5469H430.528V212.601H113.346V175.42V24.5469Z" fill="white" stroke="#3C4043" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M113.346 50.1484H336.637" stroke="#3C4043" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M113.346 174.82H430.528" stroke="#3C4043" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M131.491 40.6183C133.027 40.6183 134.273 39.3729 134.273 37.8365C134.273 36.3001 133.027 35.0547 131.491 35.0547C129.954 35.0547 128.709 36.3001 128.709 37.8365C128.709 39.3729 129.954 40.6183 131.491 40.6183Z" stroke="#3C4043" stroke-miterlimit="10"/>
<path d="M145.618 40.6183C147.154 40.6183 148.4 39.3729 148.4 37.8365C148.4 36.3001 147.154 35.0547 145.618 35.0547C144.081 35.0547 142.836 36.3001 142.836 37.8365C142.836 39.3729 144.081 40.6183 145.618 40.6183Z" stroke="#3C4043" stroke-miterlimit="10"/>
<path d="M302.018 67.0547H150.945V124.146H302.018V67.0547Z" fill="#D2E3FC" stroke="#D2E3FC" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M150.945 140.73H191.273" stroke="#3C4043" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M150.945 157.238H175.654" stroke="#3C4043" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M217.236 140.73H257.564" stroke="#3C4043" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M217.236 157.238H241.945" stroke="#3C4043" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M291.418 160.966C296.72 160.966 301.018 156.668 301.018 151.366C301.018 146.064 296.72 141.766 291.418 141.766C286.116 141.766 281.818 146.064 281.818 151.366C281.818 156.668 286.116 160.966 291.418 160.966Z" fill="#EA4335"/>
<path d="M230.164 256.347C230.164 256.347 249.455 242.202 250.091 212.602H295.164C295.782 242.183 315.091 256.347 315.091 256.347H230.164Z" fill="#E8EAED" stroke="#3C4043" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M296.181 222.983C295.6 219.765 295.218 216.311 295.145 212.602H250.072L296.181 222.983Z" fill="#3C4043" stroke="#3C4043" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M403.181 165.909C437.152 165.909 464.69 138.37 464.69 104.4C464.69 70.4292 437.152 42.8906 403.181 42.8906C369.21 42.8906 341.672 70.4292 341.672 104.4C341.672 138.37 369.21 165.909 403.181 165.909Z" fill="#EA4335" stroke="#EA4335" stroke-miterlimit="10"/>
<path d="M403.181 165.909C437.152 165.909 464.69 138.37 464.69 104.4C464.69 70.4292 437.152 42.8906 403.181 42.8906C369.21 42.8906 341.672 70.4292 341.672 104.4C341.672 138.37 369.21 165.909 403.181 165.909Z" stroke="#3C4043" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M442.891 151.363L492.727 212.6" stroke="#3C4043" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M385.854 124.144L420.781 88.8711" stroke="white" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M420.945 123.964L385.709 89.0547" stroke="white" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M84.8367 87.0209C84.0731 69.5664 94.9458 54.5664 109.182 54.5664C123.419 54.5664 134.309 69.5664 133.528 87.0391L128.509 199.948H89.8549L84.8367 87.0209Z" fill="#34A853" stroke="#34A853" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M44.5817 162.02C38.6908 150.366 35.8181 132.511 46.0181 128.729C59.6181 123.693 72.709 136.329 78.2908 151.82C83.4908 166.275 94.4908 199.947 94.4908 199.947H64.9454L44.5817 162.02Z" fill="#34A853" stroke="#34A853" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M114.4 199.292L122.964 79.9105C122.964 63.7832 115.073 54.5469 110.019 54.5469C105.491 54.5469 95.9277 65.456 95.9277 79.9105L98.855 199.292" stroke="#3C4043" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M87.8178 199.291C87.8178 199.291 72.6905 160.218 66.8542 148.472C59.5451 133.709 49.9996 126.509 46.0178 128.727C41.2178 131.4 44.6723 142.818 50.8542 155.291C56.7087 167.091 77.036 199.291 77.036 199.291" stroke="#3C4043" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M98.4182 255.6C129.164 255.6 154.073 230.673 154.073 199.945H42.7637C42.7637 230.673 67.6728 255.6 98.4182 255.6Z" fill="#E8EAED" stroke="#E8EAED" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M516.181 256.344H32.7266" stroke="#3C4043" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 4.7 KiB

@@ -0,0 +1,32 @@
<svg width="285" height="207" viewBox="0 0 285 207" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M156.945 159.559L65.5498 185.653H248.34L156.945 159.559Z" fill="white" stroke="#3C4043" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M156.945 128.364L65.5498 74.8359V185.654L156.945 159.56V128.364Z" fill="white" stroke="#3C4043" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M156.944 159.56L248.339 185.654V74.8359L156.944 128.364V159.56Z" fill="white" stroke="#3C4043" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M248.34 74.8364L156.945 21.3086L65.5498 74.8364H248.34Z" fill="white" stroke="#3C4043" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M248.34 74.8359H65.5498L156.945 128.364L248.34 74.8359Z" fill="#3C4043" stroke="#3C4043" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M207.052 148.263C216.96 148.263 224.992 140.232 224.992 130.327C224.992 120.421 216.96 112.391 207.052 112.391C197.144 112.391 189.111 120.421 189.111 130.327C189.111 140.232 197.144 148.263 207.052 148.263Z" fill="#D2E3FC" stroke="#D2E3FC" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M200.121 122.619L205.931 120.634C210.839 118.954 216.259 119.697 220.534 122.619C224.821 125.541 230.229 126.284 235.137 124.604L240.947 122.619" stroke="#3C4043" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M200.121 128.502L205.931 126.517C210.839 124.837 216.259 125.58 220.534 128.502C224.821 131.424 230.229 132.167 235.137 130.487L240.947 128.502" stroke="#3C4043" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M200.121 134.381L205.931 132.396C210.839 130.716 216.259 131.459 220.534 134.381C224.821 137.303 230.229 138.046 235.137 136.366L240.947 134.381" stroke="#3C4043" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M200.121 140.264L205.931 138.279C210.839 136.599 216.259 137.341 220.534 140.264C224.821 143.186 230.229 143.929 235.137 142.248L240.947 140.264" stroke="#3C4043" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M49.084 83.8711L84.4045 85.8802L156.946 128.364L202.728 101.576L194.166 83.8711H49.084Z" fill="white" stroke="#3C4043" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M173.704 146.069H28.6221L49.0836 83.8711H194.166L173.704 146.069Z" fill="white" stroke="#3C4043" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M173.704 53.8555H28.6221V146.056H173.704V53.8555Z" fill="white" stroke="#3C4043" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M123.098 82.9453H78.96" stroke="#3C4043" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M135.314 91.9453H66.7314" stroke="#3C4043" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M101.028 107.066V118.926" stroke="#FBBC04" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M106.168 110.023L95.8887 115.953" stroke="#FBBC04" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M95.8887 110.023L106.168 115.953" stroke="#FBBC04" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M82.7598 107.066V118.926" stroke="#FBBC04" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M87.8996 110.023L77.6201 115.953" stroke="#FBBC04" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M77.6201 110.023L87.8996 115.953" stroke="#FBBC04" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M64.4902 107.066V118.926" stroke="#FBBC04" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M69.6301 110.023L59.3506 115.953" stroke="#FBBC04" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M59.3506 110.023L69.6301 115.953" stroke="#FBBC04" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M119.298 107.066V118.926" stroke="#FBBC04" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M124.438 110.023L114.158 115.953" stroke="#FBBC04" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M114.158 110.023L124.438 115.953" stroke="#FBBC04" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M137.567 107.066V118.926" stroke="#FBBC04" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M142.707 110.023L132.428 115.953" stroke="#FBBC04" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M132.428 110.023L142.707 115.953" stroke="#FBBC04" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 191 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 45 KiB

@@ -0,0 +1,18 @@
<svg width="206" height="206" viewBox="0 0 206 206" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_3556_21484)">
<path d="M183.232 206H22.7684C10.1916 206 0 195.808 0 183.232V22.7684C0 10.1916 10.1916 0 22.7684 0H183.232C195.808 0 206 10.1916 206 22.7684V183.232C206 195.808 195.808 206 183.232 206Z" fill="white"/>
<path d="M24.4336 58.6704V95.5743C24.4336 95.9673 24.6233 96.3468 24.935 96.5772L27.117 98.2577C27.7404 98.7456 28.662 98.5423 29.0279 97.8375L44.9116 67.195C45.2504 66.5445 44.9658 65.7449 44.2882 65.4468L26.2225 57.5049C25.3823 57.139 24.4336 57.7624 24.4336 58.6704Z" fill="#EA4335"/>
<path d="M181.566 58.8046V95.7084C181.566 96.1014 181.377 96.4809 181.065 96.7113L178.883 98.3918C178.26 98.8797 177.338 98.6764 176.972 97.9717L161.088 67.3292C160.75 66.6786 161.034 65.879 161.712 65.5809L179.777 57.639C180.618 57.2731 181.566 57.883 181.566 58.8046Z" fill="#34A853"/>
<path d="M46.6066 126.867C48.4362 128.697 51.4042 128.697 53.2203 126.867L65.5938 114.494C67.4234 112.664 67.4234 109.696 65.5938 107.88C63.7642 106.05 60.7962 106.05 58.9801 107.88L46.6066 120.254C44.777 122.083 44.777 125.038 46.6066 126.867Z" fill="#4285F4"/>
<path d="M58.4269 135.785C60.2565 137.614 63.2245 137.614 65.0406 135.785L77.4141 123.412C79.2437 121.582 79.2437 118.614 77.4141 116.798C75.5845 114.968 72.6165 114.968 70.8004 116.798L58.4269 129.172C56.5973 130.988 56.5973 133.956 58.4269 135.785Z" fill="#4285F4"/>
<path d="M70.7449 142.926C72.5745 144.755 75.5425 144.755 77.3585 142.926L89.7321 130.552C91.5617 128.723 91.5617 125.755 89.7321 123.939C87.9025 122.109 84.9345 122.109 83.1184 123.939L70.7449 136.312C68.9288 138.142 68.9288 141.11 70.7449 142.926Z" fill="#4285F4"/>
<path d="M82.7667 150.191C84.5963 152.021 87.5644 152.021 89.3804 150.191L101.754 137.817C103.584 135.988 103.584 133.02 101.754 131.204C99.9244 129.375 96.9563 129.375 95.1403 131.204L82.7667 143.577C80.9371 145.393 80.9371 148.361 82.7667 150.191Z" fill="#4285F4"/>
<path d="M153.523 116.514L170.518 103.923C172.416 102.514 173.039 99.9524 171.982 97.8247L158.673 70.9498C157.535 68.6594 154.824 67.6294 152.452 68.5916L145.581 71.3701C144.172 71.8986 142.315 72.4272 140.079 72.6169C136.081 72.9557 132.855 72.0748 130.957 71.3701C128.775 70.4485 126.133 69.5405 123.083 68.8899C118.57 67.9141 114.532 67.7785 111.32 67.9411C110.276 67.9953 109.273 68.402 108.474 69.0797C100.057 76.1269 91.6548 83.188 83.2386 90.2353C82.3171 91.0078 81.6667 92.0919 81.5311 93.2981C81.504 93.4878 81.5039 93.6911 81.5039 93.8944C81.5039 93.908 81.5039 93.9215 81.5039 93.9351C81.5175 95.5207 82.4119 96.9844 83.7672 97.8247C84.7836 98.4481 86.1526 99.0851 87.8602 99.4239C91.3297 100.102 94.135 99.0988 95.3547 98.5702C95.5173 98.5024 95.68 98.4076 95.829 98.3263C100.762 95.3989 105.695 92.4714 110.629 89.544C111.455 89.0561 112.418 88.8122 113.38 88.8664L115.426 88.9749C116.537 89.0291 117.608 89.4762 118.435 90.2216L147.343 116.229C149.064 117.774 151.653 117.896 153.523 116.514Z" fill="#4285F4"/>
<path d="M49.9804 67.9783L60.8497 72.7488C61.0529 72.8437 61.2697 72.8708 61.5 72.8437C64.3597 72.5184 68.5204 71.9085 73.4807 70.6617C77.7633 69.5775 78.6306 69.0218 81.9374 68.5746C85.2036 68.1273 90.2047 67.6123 95.1243 70.2009C96.2085 70.7701 97.1164 71.4071 97.8482 72.0034C98.4581 72.5048 98.4581 73.44 97.8618 73.955L78.2783 90.7467C78.2106 90.8009 78.1562 90.8551 78.102 90.9229C77.5193 91.6818 76.1099 93.7147 76.3403 96.3575C76.6656 100.288 80.284 102.565 81.5037 103.093C82.981 103.744 85.3391 104.57 88.3614 104.76C91.37 104.95 93.8366 104.421 95.3952 103.947C95.4901 103.92 95.5714 103.879 95.6662 103.825L113.014 93.8502C113.474 93.5792 114.071 93.6334 114.477 93.9858L145.066 120.278C145.229 120.413 145.351 120.59 145.432 120.793C146.896 124.574 145.703 128.884 142.545 131.296C140.187 133.099 137.07 133.559 134.278 132.638C133.424 132.353 132.556 133.085 132.665 133.98C133.004 136.758 132.055 139.482 130.09 141.189C127.637 143.317 123.842 143.629 120.725 141.989C119.898 141.555 118.896 142.165 118.896 143.1C118.896 144.822 118.218 146.475 116.985 147.613C115.128 149.307 112.255 149.592 109.896 148.386C109.097 147.979 108.175 148.481 108.04 149.362C107.742 151.354 106.563 153.075 104.882 153.915C102.307 155.203 98.8511 154.281 96.9944 151.625C96.5878 151.042 96.7911 150.256 97.3739 149.863C106.63 143.697 109.368 134.549 106.481 129.995C104.367 126.648 99.4068 126.065 97.1841 125.97C96.6962 125.943 96.2762 125.658 96.0865 125.225C95.436 123.747 93.7283 120.671 90.0827 119.112C87.8736 118.177 85.7999 118.164 84.4447 118.313C83.8348 118.381 83.252 117.987 83.0894 117.391C82.6964 115.968 81.707 113.542 79.1727 111.862C76.6248 110.168 73.9956 110.195 72.5454 110.398C71.9491 110.479 71.3935 110.127 71.1902 109.558C70.7429 108.257 69.7672 106.251 67.7071 104.665C64.4545 102.158 60.7683 102.47 59.4673 102.673C59.2098 102.714 58.9794 102.822 58.7897 103.012L48.1778 113.095C47.7306 113.529 47.026 113.556 46.5245 113.176L32.2264 102.199C31.7385 101.819 31.5895 101.155 31.8741 100.613L48.3135 68.5881C48.6387 67.9647 49.357 67.7072 49.9804 67.9783Z" fill="#FBBC04"/>
</g>
<defs>
<clipPath id="clip0_3556_21484">
<rect width="206" height="206" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 5.1 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 212 212" width="212" height="212"><path fill="#DFE5E7" class="background" d="M106.251.5C164.653.5 212 47.846 212 106.25S164.653 212 106.25 212C47.846 212 .5 164.654.5 106.25S47.846.5 106.251.5z"></path><path fill="#FFF" class="primary" d="M173.561 171.615a62.767 62.767 0 0 0-2.065-2.955 67.7 67.7 0 0 0-2.608-3.299 70.112 70.112 0 0 0-3.184-3.527 71.097 71.097 0 0 0-5.924-5.47 72.458 72.458 0 0 0-10.204-7.026 75.2 75.2 0 0 0-5.98-3.055c-.062-.028-.118-.059-.18-.087-9.792-4.44-22.106-7.529-37.416-7.529s-27.624 3.089-37.416 7.529c-.338.153-.653.318-.985.474a75.37 75.37 0 0 0-6.229 3.298 72.589 72.589 0 0 0-9.15 6.395 71.243 71.243 0 0 0-5.924 5.47 70.064 70.064 0 0 0-3.184 3.527 67.142 67.142 0 0 0-2.609 3.299 63.292 63.292 0 0 0-2.065 2.955 56.33 56.33 0 0 0-1.447 2.324c-.033.056-.073.119-.104.174a47.92 47.92 0 0 0-1.07 1.926c-.559 1.068-.818 1.678-.818 1.678v.398c18.285 17.927 43.322 28.985 70.945 28.985 27.678 0 52.761-11.103 71.055-29.095v-.289s-.619-1.45-1.992-3.778a58.346 58.346 0 0 0-1.446-2.322zM106.002 125.5c2.645 0 5.212-.253 7.68-.737a38.272 38.272 0 0 0 3.624-.896 37.124 37.124 0 0 0 5.12-1.958 36.307 36.307 0 0 0 6.15-3.67 35.923 35.923 0 0 0 9.489-10.48 36.558 36.558 0 0 0 2.422-4.84 37.051 37.051 0 0 0 1.716-5.25c.299-1.208.542-2.443.725-3.701.275-1.887.417-3.827.417-5.811s-.142-3.925-.417-5.811a38.734 38.734 0 0 0-1.215-5.494 36.68 36.68 0 0 0-3.648-8.298 35.923 35.923 0 0 0-9.489-10.48 36.347 36.347 0 0 0-6.15-3.67 37.124 37.124 0 0 0-5.12-1.958 37.67 37.67 0 0 0-3.624-.896 39.875 39.875 0 0 0-7.68-.737c-21.162 0-37.345 16.183-37.345 37.345 0 21.159 16.183 37.342 37.345 37.342z"></path></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 282 KiB

Some files were not shown because too many files have changed in this diff Show More