Skip to content

Commit 31e7052

Browse files
committed
Add labels and instructions on top
1 parent b7f92d7 commit 31e7052

File tree

3 files changed

+106
-66
lines changed

3 files changed

+106
-66
lines changed

src/app/app.component.css

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
.prompt-controls input {
3131
font-size: 1em;
3232
padding: 8px;
33+
width: calc(100% - 80px);
3334
}
3435

3536
.prompt-controls mat-icon {
@@ -44,12 +45,12 @@
4445

4546
.prompt-config {
4647
padding-top: 10px;
47-
display: flex;
48-
justify-content: space-around;
4948
}
5049

5150
.prompt-config select {
52-
width: calc(100% - 60px);
51+
width: calc(100% - 135px);
52+
height: 36px;
53+
font-size: 1em;
5354
margin-right: 20px;
5455
}
5556

@@ -99,4 +100,21 @@ app-canvas {
99100

100101
.api-key-input {
101102
margin-bottom: 10px;
103+
}
104+
105+
.prompt-input-wrapper {
106+
display: flex;
107+
justify-content: flex-start;
108+
}
109+
110+
.prompt-input-wrapper label input {
111+
width: 230px;
112+
}
113+
114+
.prompt-input-wrapper mat-icon {
115+
margin-left: 12px;
116+
}
117+
118+
.error {
119+
color: red;
102120
}

src/app/app.component.html

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
<div class="instructions">
2+
To use the app make sure you generate a Gemini API key from
3+
<a href="https://aistudio.google.com/app/apikey">here</a>. <br /><br />
4+
This app sends the image from the canvas below and the prompt to Gemini Pro
5+
Vision. You can enter the prompt with your voice using the Web Speech API. The
6+
app will read Gemini's response using the Web Speech Synthesis API. You can
7+
select a language for the Web Speech API from the dropdown below.
8+
<br /><br />
9+
You can clear the canvas by clicking on the trash bin which appears when you
10+
move your cursor to the top left of the canvas.
11+
<br /><br />
12+
Try drawing something and asking Gemini what's on the canvas!
13+
</div>
14+
15+
<app-canvas [width]="400" [height]="400" />
16+
<section class="prompt-controls">
17+
<label>
18+
API key:
19+
<input
20+
[(ngModel)]="apiKey"
21+
placeholder="Enter your API key"
22+
class="api-key-input"
23+
type="text"
24+
/>
25+
</label>
26+
<div class="prompt-input-wrapper">
27+
<label>
28+
Prompt:
29+
<input
30+
[disabled]="speechActive"
31+
[(ngModel)]="prompt"
32+
placeholder="Type or ask a question..."
33+
type="text"
34+
/>
35+
</label>
36+
<mat-icon
37+
[hidden]="true"
38+
(click)="speechInput()"
39+
aria-hidden="false"
40+
aria-label="Microphone"
41+
fontIcon="microphone"
42+
/>
43+
<button
44+
mat-button
45+
color="primary"
46+
[class.spinner]="disabled"
47+
[disabled]="disabled || prompt.length < 5 || speechActive"
48+
(click)="recognize()"
49+
>
50+
Ask
51+
</button>
52+
</div>
53+
<div class="prompt-config">
54+
<label>
55+
Output language:
56+
<select [disabled]="speechActive" [(ngModel)]="currentLanguage">
57+
@for (lang of languages; track lang.lang) {
58+
<option [value]="lang.lang">{{ lang.label }}</option>
59+
}
60+
</select>
61+
</label>
62+
</div>
63+
</section>
64+
<div class="answer">
65+
@if (error !== '') {
66+
<span
67+
class="error"
68+
>{{error}}</span>
69+
}
70+
@if (output !== '') {
71+
<span
72+
><span class="gemini-name">🤖 says:</span> <span [innerHTML]="output"></span
73+
></span>
74+
}
75+
</div>

src/app/app.component.ts

Lines changed: 10 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -40,66 +40,7 @@ import { GenerativeService } from './generative.service';
4040
FormsModule,
4141
MatIconModule,
4242
],
43-
template: `
44-
<app-canvas [width]="400" [height]="400" />
45-
<section class="prompt-controls">
46-
<input
47-
[(ngModel)]="apiKey"
48-
placeholder="API key"
49-
class="api-key-input"
50-
type="text"
51-
/>
52-
<input
53-
[disabled]="speechActive"
54-
[(ngModel)]="prompt"
55-
placeholder="Type or ask a question..."
56-
type="text"
57-
/>
58-
<div class="prompt-config">
59-
<select [disabled]="speechActive" [(ngModel)]="currentLanguage">
60-
@for (lang of languages; track lang.lang) {
61-
<option [value]="lang.lang">{{ lang.label }}</option>
62-
}
63-
</select>
64-
<mat-icon
65-
[hidden]="true"
66-
(click)="speechInput()"
67-
aria-hidden="false"
68-
aria-label="Microphone"
69-
fontIcon="microphone"
70-
/>
71-
<button
72-
mat-button
73-
color="primary"
74-
[class.spinner]="disabled"
75-
[disabled]="disabled || prompt.length < 5 || speechActive"
76-
(click)="recognize()"
77-
>
78-
Ask
79-
</button>
80-
</div>
81-
</section>
82-
<div class="answer">
83-
@if (output !== '') {
84-
<span><span class="gemini-name">🤖 says:</span> <span [innerHTML]="output"></span></span>
85-
}
86-
</div>
87-
<div class="instructions">
88-
<details>
89-
<summary>Instructions</summary>
90-
This app sends the image from the canvas below and the prompt to Gemini
91-
Pro Vision. You can enter the prompt with your voice using the Web
92-
Speech API. The app will read Gemini's response using the Web Speech
93-
Synthesis API. You can select a language for the Web Speech API from the
94-
dropdown below.
95-
<br /><br />
96-
You can clear the canvas by clicking on the trash bin which appears when
97-
you move your cursor to the top left of the canvas.
98-
<br /><br />
99-
Try drawing something and asking Gemini what's on the canvas!
100-
</details>
101-
</div>
102-
`,
43+
templateUrl: 'app.component.html',
10344
styleUrl: 'app.component.css',
10445
})
10546
export class AppComponent {
@@ -109,14 +50,14 @@ export class AppComponent {
10950
private model: GenerativeModel | null = null;
11051
private generativeService: GenerativeService = inject(GenerativeService);
11152

112-
11353
protected disabled = false;
11454
protected speechActive = false;
11555
protected output: SafeHtml = '';
11656
protected prompt = '';
11757
protected languages = this.speech.languages;
11858
protected currentLanguage = 'en-US';
11959
protected apiKey = '';
60+
protected error: any = null;
12061

12162
async speechInput() {
12263
this.prompt = '';
@@ -134,6 +75,8 @@ export class AppComponent {
13475
if (!this.canvas()) return;
13576
if (!this.prompt) return;
13677

78+
this.error = null;
79+
13780
console.info('Querying the model with prompt', this.prompt);
13881
const data = this.canvas()!.getBase64Drawing();
13982
if (!data) return;
@@ -155,11 +98,15 @@ export class AppComponent {
15598

15699
const response = await result.response;
157100
const text = response.text();
158-
159-
this.output = this.sanitizer.bypassSecurityTrustHtml(await marked.parse(text));
101+
102+
this.output = this.sanitizer.bypassSecurityTrustHtml(
103+
await marked.parse(text)
104+
);
160105

161106
console.info('Received output from the model', text);
162107
await this.say(text);
108+
} catch (e) {
109+
this.error = e;
163110
} finally {
164111
this.disabled = false;
165112
}

0 commit comments

Comments
 (0)