Skip to content

Commit fc02928

Browse files
hoonjicopybara-github
authored andcommitted
ADK changes
PiperOrigin-RevId: 811560119
1 parent d2454fc commit fc02928

File tree

12 files changed

+282
-6
lines changed

12 files changed

+282
-6
lines changed
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<!--
2+
Copyright 2025 Google LLC
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
-->
16+
17+
@let imgUrl = logoConfig?.imageUrl;
18+
@let text = logoConfig?.text;
19+
20+
@if (imgUrl && text) {
21+
<a href="/">
22+
<img class="orcas-logo" src="{{imgUrl}}" width="32px" height="32px" />
23+
{{text}}
24+
</a>
25+
} @else {
26+
<div>
27+
Invalid custom logo config. Make sure that your runtime config specifies
28+
both imgUrl and text in the logo field.
29+
</div>
30+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/**
2+
* @license
3+
* Copyright 2025 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
a {
19+
color: inherit;
20+
text-decoration: none;
21+
display: flex;
22+
align-items: center;
23+
gap: 8px;
24+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/**
2+
* @license
3+
* Copyright 2025 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
import { ComponentFixture, TestBed } from '@angular/core/testing';
19+
import { By } from '@angular/platform-browser';
20+
import { CustomLogoComponent } from './custom-logo.component';
21+
import {RuntimeConfigUtil} from '../../../utils/runtime-config-util';
22+
23+
describe('CustomLogoComponent', () => {
24+
let component: CustomLogoComponent;
25+
let fixture: ComponentFixture<CustomLogoComponent>;
26+
27+
const setupTest = (logoConfig: any) => {
28+
spyOn(RuntimeConfigUtil, 'getRuntimeConfig').and.returnValue({
29+
logo: logoConfig,
30+
} as any);
31+
32+
TestBed.configureTestingModule({
33+
imports: [CustomLogoComponent],
34+
}).compileComponents();
35+
36+
fixture = TestBed.createComponent(CustomLogoComponent);
37+
component = fixture.componentInstance;
38+
fixture.detectChanges();
39+
};
40+
41+
describe('with valid logo config', () => {
42+
beforeEach(() => {
43+
setupTest({ imageUrl: 'test.png', text: 'Test Logo' });
44+
});
45+
46+
it('should render the logo and text', () => {
47+
const linkElement = fixture.debugElement.query(By.css('a'));
48+
const imgElement = fixture.debugElement.query(By.css('img'));
49+
50+
expect(imgElement.nativeElement.src).toContain('test.png');
51+
expect(linkElement.nativeElement.textContent).toContain('Test Logo');
52+
});
53+
});
54+
55+
describe('with invalid logo config', () => {
56+
it('should show an error if imageUrl is missing', () => {
57+
setupTest({ text: 'Test Logo' });
58+
const errorElement = fixture.debugElement.query(By.css('div'));
59+
60+
expect(errorElement.nativeElement.textContent).toContain(
61+
'Invalid custom logo config'
62+
);
63+
});
64+
65+
it('should show an error if text is missing', () => {
66+
setupTest({ imageUrl: 'test.png' });
67+
const errorElement = fixture.debugElement.query(By.css('div'));
68+
69+
expect(errorElement.nativeElement.textContent).toContain(
70+
'Invalid custom logo config'
71+
);
72+
});
73+
74+
it('should show an error if logoConfig is missing', () => {
75+
setupTest(undefined);
76+
const errorElement = fixture.debugElement.query(By.css('div'));
77+
78+
expect(errorElement.nativeElement.textContent).toContain(
79+
'Invalid custom logo config'
80+
);
81+
});
82+
});
83+
});
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/**
2+
* @license
3+
* Copyright 2025 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
import {Component} from '@angular/core';
19+
20+
import {RuntimeConfigUtil} from '../../../utils/runtime-config-util';
21+
22+
/** Logo component to override the default logo. */
23+
@Component({
24+
selector: 'app-custom-logo',
25+
standalone: true,
26+
templateUrl: './custom-logo.component.html',
27+
styleUrls: ['./custom-logo.component.scss'],
28+
})
29+
export class CustomLogoComponent {
30+
readonly logoConfig = RuntimeConfigUtil.getRuntimeConfig().logo;
31+
}

src/app/components/side-panel/side-panel.component.html

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,15 @@
33
<div style="width: 100%">
44
<div class="drawer-header">
55
<div class="drawer-logo">
6-
<img src="assets/ADK-512-color.svg" width="32px" height="32px" />
7-
{{ i18n.agentDevelopmentKitLabel }}
6+
@if (logoComponent) {
7+
<div>
8+
<ng-container *ngComponentOutlet="logoComponent"></ng-container>
9+
<div class="powered-by-adk">Powered by Agent Development Kit</div>
10+
</div>
11+
} @else {
12+
<img src="assets/ADK-512-color.svg" width="32px" height="32px" />
13+
{{ i18n.agentDevelopmentKitLabel }}
14+
}
815
</div>
916
<span
1017
class="material-symbols-outlined"

src/app/components/side-panel/side-panel.component.scss

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,3 +127,10 @@
127127
line-height: 24px;
128128
letter-spacing: 0.1px;
129129
}
130+
131+
.powered-by-adk {
132+
font-size: 10px;
133+
color: grey;
134+
text-align: right;
135+
margin-top: -5px;
136+
}

src/app/components/side-panel/side-panel.component.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,19 @@
1515
* limitations under the License.
1616
*/
1717

18-
import {CommonModule} from '@angular/common';
19-
import {Component, EventEmitter, inject, Input, Output, viewChild} from '@angular/core';
18+
import {Component, EventEmitter, inject, Input, Output, viewChild, Type} from '@angular/core';
2019
import {MatMiniFabButton} from '@angular/material/button';
2120
import {MatIcon} from '@angular/material/icon';
2221
import {MatPaginator, PageEvent} from '@angular/material/paginator';
2322
import {MatTab, MatTabGroup, MatTabLabel} from '@angular/material/tabs';
2423
import {MatTooltip} from '@angular/material/tooltip';
2524
import {type SafeHtml} from '@angular/platform-browser';
2625
import {NgxJsonViewerModule} from 'ngx-json-viewer';
26+
import {AsyncPipe, NgComponentOutlet} from '@angular/common';
2727

2828
import {EvalCase} from '../../core/models/Eval';
2929
import {Session} from '../../core/models/Session';
30+
import {LOGO_COMPONENT} from '../../injection_tokens';
3031
import {FEATURE_FLAG_SERVICE} from '../../core/services/feature-flag.service';
3132
import {ArtifactTabComponent} from '../artifact-tab/artifact-tab.component';
3233
import {EvalTabComponent} from '../eval-tab/eval-tab.component';
@@ -46,7 +47,8 @@ import {SidePanelMessagesInjectionToken} from './side-panel.component.i18n';
4647
styleUrls: ['./side-panel.component.scss'],
4748
standalone: true,
4849
imports: [
49-
CommonModule,
50+
AsyncPipe,
51+
NgComponentOutlet,
5052
MatTooltip,
5153
MatTabGroup,
5254
MatTab,
@@ -96,6 +98,9 @@ export class SidePanelComponent {
9698
readonly sessionTabComponent = viewChild(SessionTabComponent);
9799
readonly evalTabComponent = viewChild(EvalTabComponent);
98100

101+
readonly logoComponent: Type<Component> | null = inject(LOGO_COMPONENT, {
102+
optional: true,
103+
});
99104
readonly i18n = inject(SidePanelMessagesInjectionToken);
100105
readonly featureFlagService = inject(FEATURE_FLAG_SERVICE);
101106

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/**
2+
* @license
3+
* Copyright 2025 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
/**
19+
* Runtime configuration for the ADK web app. Corresponds to the JSON file
20+
* read by the web app at runtime (runtime-config.json).
21+
*/
22+
export interface RuntimeConfig {
23+
backendUrl: string;
24+
logo?: LogoConfig;
25+
}
26+
27+
/**
28+
* Logo configuration for the ADK web app.
29+
*/
30+
export interface LogoConfig {
31+
text: string;
32+
imageUrl: string;
33+
}

src/app/injection_tokens.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/**
2+
* @license
3+
* Copyright 2025 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
/** @fileoverview App-level injection tokens. */
19+
20+
import {Component, InjectionToken, Type} from '@angular/core';
21+
22+
/**
23+
* Represents a component to be used as the logo across the app.
24+
*/
25+
export const LOGO_COMPONENT = new InjectionToken<Type<Component>>(
26+
'LOGO_COMPONENT',
27+
);
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
22
"backendUrl": "http://localhost:8000"
3-
}
3+
}

0 commit comments

Comments
 (0)