diff --git a/Angular/.prettier.json b/Angular/.prettier.json new file mode 100644 index 0000000..ba96a0f --- /dev/null +++ b/Angular/.prettier.json @@ -0,0 +1,10 @@ +{ + "tabWidth": 2, + "useTabs": false, + "singleQuote": true, + "semi": true, + "bracketSpacing": true, + "trailingComma": "es5", + "printWidth": 80, + "endOfLine": "If" +} diff --git a/Angular/package-lock.json b/Angular/package-lock.json index 6d7b8a6..00bf879 100644 --- a/Angular/package-lock.json +++ b/Angular/package-lock.json @@ -32,6 +32,7 @@ "karma-coverage": "~2.2.0", "karma-jasmine": "~5.1.0", "karma-jasmine-html-reporter": "~2.1.0", + "prettier": "^3.3.3", "typescript": "~5.5.2" } }, @@ -10106,6 +10107,21 @@ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", "dev": true }, + "node_modules/prettier": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", + "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/proc-log": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-4.2.0.tgz", diff --git a/Angular/package.json b/Angular/package.json index 7ecaf4d..1144e49 100644 --- a/Angular/package.json +++ b/Angular/package.json @@ -34,6 +34,7 @@ "karma-coverage": "~2.2.0", "karma-jasmine": "~5.1.0", "karma-jasmine-html-reporter": "~2.1.0", + "prettier": "^3.3.3", "typescript": "~5.5.2" } } diff --git a/Angular/src/app/app.routes.ts b/Angular/src/app/app.routes.ts index 4d90637..8783b14 100644 --- a/Angular/src/app/app.routes.ts +++ b/Angular/src/app/app.routes.ts @@ -1,8 +1,14 @@ import { Routes } from '@angular/router'; export const routes: Routes = [ - { - path: '', - loadComponent: () => import('../components/text-based-gemini/text-based-gemini.component').then(c => c.TextBasedGeminiComponent) - } + { + path: '', + redirectTo: 'chat', + pathMatch: 'full', + }, + { + path: 'chat', + loadComponent: () => + import('../pages/chat/chat.component').then((c) => c.ChatComponent), + }, ]; diff --git a/Angular/src/components/text-based-gemini/text-based-gemini.component.spec.ts b/Angular/src/components/text-based-gemini/text-based-gemini.component.spec.ts deleted file mode 100644 index ff5cc4f..0000000 --- a/Angular/src/components/text-based-gemini/text-based-gemini.component.spec.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { TextBasedGeminiComponent } from './text-based-gemini.component'; - -describe('TextBasedGeminiComponent', () => { - let component: TextBasedGeminiComponent; - let fixture: ComponentFixture; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [TextBasedGeminiComponent] - }) - .compileComponents(); - - fixture = TestBed.createComponent(TextBasedGeminiComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/Angular/src/components/text-based-gemini/text-based-gemini.component.ts b/Angular/src/components/text-based-gemini/text-based-gemini.component.ts index ec78e3a..863765c 100644 --- a/Angular/src/components/text-based-gemini/text-based-gemini.component.ts +++ b/Angular/src/components/text-based-gemini/text-based-gemini.component.ts @@ -7,14 +7,12 @@ import { Subscription } from 'rxjs'; standalone: true, imports: [], templateUrl: './text-based-gemini.component.html', - styleUrl: './text-based-gemini.component.scss' + styleUrl: './text-based-gemini.component.scss', }) export class TextBasedGeminiComponent implements OnInit, OnDestroy { private subscription: Subscription | null = null; - constructor( - private genAiService: GeminiGoogleAiService - ) { } + constructor(private genAiService: GeminiGoogleAiService) {} ngOnInit(): void { this.askGemini('Hey, How are you doing today?'); @@ -22,22 +20,22 @@ export class TextBasedGeminiComponent implements OnInit, OnDestroy { /** * Communication with Gemini using Frontend Compatible Service - * @param text + * @param text */ askGemini(text: string) { - this.subscription = this.genAiService.askGemini(text).subscribe({ - next: (response: string) => { - console.log(response); - alert(`Response from Gemini: ${response}`); - }, - error: (error: any) => { - console.error('Error:', error); - alert('An error occurred while fetching the response from Gemini.'); - }, - complete: () => { - console.log('Gemini response completed successfully.'); - } - }); + // this.subscription = this.genAiService.askGemini(text).subscribe({ + // next: (response: string) => { + // console.log(response); + // alert(`Response from Gemini: ${response}`); + // }, + // error: (error: any) => { + // console.error('Error:', error); + // alert('An error occurred while fetching the response from Gemini.'); + // }, + // complete: () => { + // console.log('Gemini response completed successfully.'); + // } + // }); } ngOnDestroy(): void { diff --git a/Angular/src/environments/environment.ts b/Angular/src/environments/environment.ts index 9ad6e74..f8fd994 100644 --- a/Angular/src/environments/environment.ts +++ b/Angular/src/environments/environment.ts @@ -1,4 +1,4 @@ export const environment = { - production: false, - googleAiKey: "" -} \ No newline at end of file + production: false, + googleAiKey: '', +}; diff --git a/Angular/src/pages/chat/chat.component.html b/Angular/src/pages/chat/chat.component.html new file mode 100644 index 0000000..27edb1d --- /dev/null +++ b/Angular/src/pages/chat/chat.component.html @@ -0,0 +1,21 @@ +
+
+
+ {{ msg.text }} +
+
+
+
+ +
+
diff --git a/Angular/src/pages/chat/chat.component.scss b/Angular/src/pages/chat/chat.component.scss new file mode 100644 index 0000000..f35aaa3 --- /dev/null +++ b/Angular/src/pages/chat/chat.component.scss @@ -0,0 +1,75 @@ +.chat-container { + display: flex; + flex-direction: column; + height: 100%; + max-width: 600px; + margin: 0 auto; + border: 1px solid #ddd; + border-radius: 8px; +} + +.messages { + flex: 1; + overflow-y: auto; + padding: 10px; +} + +.user-message, +.bot-message { + padding: 8px 12px; + border-radius: 8px; + margin-bottom: 10px; + word-wrap: break-word; +} + +.user-message { + margin-left: auto; + align-self: flex-end; + width: fit-content; + background-color: #cce7ff; + text-align: right; +} + +.bot-message { + width: 80%; + align-self: flex-start; + background-color: #f0f0f0; + text-align: left; +} + +.input-form { + display: flex; + border-top: 1px solid #ddd; + padding: 10px; +} + +input { + flex: 1; + padding: 12px; + font-size: 1rem; + border: none; + outline: none; + color: #ffffff; + background-color: #333333; + border-radius: 20px; +} + +/* Loader styles */ +.loader { + border: 4px solid #f3f3f3; + border-top: 4px solid #3498db; + border-radius: 50%; + width: 24px; + height: 24px; + animation: spin 1s linear infinite; + margin: 10px auto; +} + +@keyframes spin { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} diff --git a/Angular/src/pages/chat/chat.component.ts b/Angular/src/pages/chat/chat.component.ts new file mode 100644 index 0000000..719096f --- /dev/null +++ b/Angular/src/pages/chat/chat.component.ts @@ -0,0 +1,37 @@ +import { Component } from '@angular/core'; +import { FormsModule } from '@angular/forms'; + +import { GeminiGoogleAiService } from '../../services/gemini-google-ai/gemini-google-ai.service'; +import { CommonModule } from '@angular/common'; + +@Component({ + selector: 'app-chat', + standalone: true, + imports: [FormsModule, CommonModule], + templateUrl: './chat.component.html', + styleUrls: ['./chat.component.scss'], +}) +export class ChatComponent { + messages: { text: string; isUser: boolean }[] = []; + userInput: string = ''; + isLoading: boolean = false; + + constructor(private geminiService: GeminiGoogleAiService) {} + + async onSubmit() { + if (!this.userInput.trim()) return; + + this.isLoading = true; + + this.messages.push({ text: this.userInput, isUser: true }); + + const userMessage = this.userInput; + this.userInput = ''; + + // TODO: Handle errors + const response = await this.geminiService.askGemini(userMessage); + this.messages.push({ text: response, isUser: false }); + + this.isLoading = false; + } +} diff --git a/Angular/src/services/gemini-google-ai/gemini-google-ai.service.ts b/Angular/src/services/gemini-google-ai/gemini-google-ai.service.ts index 0cf9738..485e37a 100644 --- a/Angular/src/services/gemini-google-ai/gemini-google-ai.service.ts +++ b/Angular/src/services/gemini-google-ai/gemini-google-ai.service.ts @@ -1,13 +1,11 @@ import { Injectable } from '@angular/core'; -import { GoogleGenerativeAI } from '@google/generative-ai'; +import { GenerativeModel, GoogleGenerativeAI } from '@google/generative-ai'; import { environment } from '../../environments/environment'; -import { asyncScheduler, from, map, Observable, of, scheduled } from 'rxjs'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class GeminiGoogleAiService { - // instance to initiate Gemini - Google Ai with API_KEY private genAI: GoogleGenerativeAI; @@ -18,12 +16,12 @@ export class GeminiGoogleAiService { /** * Communicate with Gemini - Google Ai using text prompt */ - askGemini(prompt: string): Observable { - const model: any = this.genAI.getGenerativeModel({ model: "gemini-1.5-flash" }); + async askGemini(prompt: string): Promise { + const model: GenerativeModel = this.genAI.getGenerativeModel({ + model: 'gemini-1.5-flash', + }); - // converting Promise to Observable - latest way "After Deprecation" - return scheduled(from(model.generateContent(prompt)), asyncScheduler).pipe( - map((result: any) => result.response.text()) - ); + const content = await model.generateContent(prompt); + return content.response.text(); } } diff --git a/Angular/src/styles.scss b/Angular/src/styles.scss index 90d4ee0..094b7a5 100644 --- a/Angular/src/styles.scss +++ b/Angular/src/styles.scss @@ -1 +1,4 @@ -/* You can add global styles to this file, and also import other style files */ +body { + font-size: 14px; + font-family: Verdana, Geneva, Tahoma, sans-serif; +}