Skip to content

Commit 49e953b

Browse files
authored
Add branding customization support for logo and title (#1339)
1 parent ffa9ce9 commit 49e953b

File tree

13 files changed

+171
-4
lines changed

13 files changed

+171
-4
lines changed

docs/en/DEPLOY_OPTION.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1505,6 +1505,38 @@ const envs: Record<string, Partial<StackInput>> = {
15051505
}
15061506
```
15071507

1508+
## Branding Customization
1509+
1510+
You can customize the logo and title displayed on the landing page by creating a branding configuration file.
1511+
1512+
### Configuration
1513+
1514+
1. Create `packages/cdk/branding.json` with your custom settings:
1515+
1516+
```json
1517+
{
1518+
"logoPath": "your-logo.svg",
1519+
"title": "Your Custom Title"
1520+
}
1521+
```
1522+
1523+
2. Place your custom SVG logo file in `packages/web/src/assets/`:
1524+
1525+
```
1526+
packages/web/src/assets/your-logo.svg
1527+
```
1528+
1529+
### Parameters
1530+
1531+
- `logoPath` (optional): Filename of the SVG logo in `packages/web/src/assets/`
1532+
- `title` (optional): Custom title text to display
1533+
1534+
### Notes
1535+
1536+
- If `branding.json` doesn't exist, default AWS logo and title are used
1537+
- Only SVG format is supported for custom logos
1538+
- The logo will be displayed at 80x80 pixels (size-20 class)
1539+
15081540
## Security-Related Settings
15091541

15101542
### Disable Self-Signup

docs/ja/DEPLOY_OPTION.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1512,6 +1512,38 @@ const envs: Record<string, Partial<StackInput>> = {
15121512
}
15131513
```
15141514

1515+
## ブランディングカスタマイズ
1516+
1517+
ランディングページに表示されるロゴとタイトルをカスタマイズできます。
1518+
1519+
### 設定方法
1520+
1521+
1. `packages/cdk/branding.json` にカスタム設定を作成:
1522+
1523+
```json
1524+
{
1525+
"logoPath": "your-logo.svg",
1526+
"title": "カスタムタイトル"
1527+
}
1528+
```
1529+
1530+
2. カスタムSVGロゴファイルを `packages/web/src/assets/` に配置:
1531+
1532+
```
1533+
packages/web/src/assets/your-logo.svg
1534+
```
1535+
1536+
### パラメータ
1537+
1538+
- `logoPath` (オプション): `packages/web/src/assets/` 内のSVGロゴファイル名
1539+
- `title` (オプション): 表示するカスタムタイトルテキスト
1540+
1541+
### 注意事項
1542+
1543+
- `branding.json` が存在しない場合、デフォルトのAWSロゴとタイトルが使用されます
1544+
- カスタムロゴはSVG形式のみサポートされています
1545+
- ロゴは80x80ピクセル(size-20クラス)で表示されます
1546+
15151547
## セキュリティ関連設定
15161548

15171549
### セルフサインアップを無効化する

packages/cdk/branding.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import * as fs from 'fs';
2+
import * as path from 'path';
3+
4+
// Branding configuration interface
5+
interface BrandingConfig {
6+
logoPath?: string;
7+
title?: string;
8+
}
9+
10+
// Load branding configuration from JSON file
11+
export const loadBrandingConfig = (): BrandingConfig => {
12+
const brandingPath = path.join(__dirname, 'branding.json');
13+
try {
14+
if (fs.existsSync(brandingPath)) {
15+
const brandingData = fs.readFileSync(brandingPath, 'utf8');
16+
return JSON.parse(brandingData);
17+
}
18+
} catch (error) {
19+
console.warn('Failed to load branding.json, using defaults:', error);
20+
}
21+
return {};
22+
};

packages/cdk/lib/construct/web.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,10 @@ export interface WebProps {
7070
readonly agentCoreAgentBuilderRuntime?: AgentCoreConfiguration;
7171
readonly agentCoreExternalRuntimes: AgentCoreConfiguration[];
7272
readonly agentCoreRegion?: string;
73+
readonly brandingConfig?: {
74+
logoPath?: string;
75+
title?: string;
76+
};
7377
}
7478

7579
export class Web extends Construct {
@@ -307,6 +311,8 @@ export class Web extends Construct {
307311
VITE_APP_AGENT_CORE_EXTERNAL_RUNTIMES: JSON.stringify(
308312
props.agentCoreExternalRuntimes
309313
),
314+
VITE_APP_BRANDING_LOGO_PATH: props.brandingConfig?.logoPath ?? '',
315+
VITE_APP_BRANDING_TITLE: props.brandingConfig?.title ?? '',
310316
},
311317
});
312318
// Enhance computing resources

packages/cdk/lib/generative-ai-use-cases-stack.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,8 @@ export class GenerativeAiUseCasesStack extends Stack {
293293
webBucket: props.webBucket,
294294
cognitoUserPoolProxyEndpoint: props.cognitoUserPoolProxyEndpoint,
295295
cognitoIdentityPoolProxyEndpoint: props.cognitoIdentityPoolProxyEndpoint,
296+
// Branding
297+
brandingConfig: params.brandingConfig,
296298
});
297299

298300
// RAG

packages/cdk/lib/stack-input.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,13 @@ export const processedStackInputSchema = baseStackInputSchema.extend({
254254
),
255255
// Processed agentCoreRegion (null -> modelRegion)
256256
agentCoreRegion: z.string(),
257+
// Branding configuration
258+
brandingConfig: z
259+
.object({
260+
logoPath: z.string().optional(),
261+
title: z.string().optional(),
262+
})
263+
.optional(),
257264
});
258265

259266
export type StackInput = z.infer<typeof stackInputSchema>;

packages/cdk/parameter.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
ProcessedStackInput,
66
} from './lib/stack-input';
77
import { ModelConfiguration } from 'generative-ai-use-cases';
8+
import { loadBrandingConfig } from './branding';
89

910
// Get parameters from CDK Context
1011
const getContext = (app: cdk.App): StackInput => {
@@ -77,5 +78,7 @@ export const getParams = (app: cdk.App): ProcessedStackInput => {
7778
),
7879
// Process agentCoreRegion: null -> modelRegion
7980
agentCoreRegion: params.agentCoreRegion || params.modelRegion,
81+
// Load branding configuration
82+
brandingConfig: loadBrandingConfig(),
8083
};
8184
};

packages/cdk/test/__snapshots__/generative-ai-use-cases.test.ts.snap

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19017,6 +19017,8 @@ exports[`GenerativeAiUseCases matches the snapshot (closed network mode) 6`] = `
1901719017
],
1901819018
],
1901919019
},
19020+
"VITE_APP_BRANDING_LOGO_PATH": "",
19021+
"VITE_APP_BRANDING_TITLE": "",
1902019022
"VITE_APP_COGNITO_IDENTITY_POOL_PROXY_ENDPOINT": {
1902119023
"Fn::Join": [
1902219024
"",
@@ -39957,6 +39959,8 @@ exports[`GenerativeAiUseCases matches the snapshot 6`] = `
3995739959
],
3995839960
],
3995939961
},
39962+
"VITE_APP_BRANDING_LOGO_PATH": "",
39963+
"VITE_APP_BRANDING_TITLE": "",
3996039964
"VITE_APP_COGNITO_IDENTITY_POOL_PROXY_ENDPOINT": "",
3996139965
"VITE_APP_COGNITO_USER_POOL_PROXY_ENDPOINT": "",
3996239966
"VITE_APP_ENDPOINT_NAMES": "[]",
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { useMemo } from 'react';
2+
3+
interface BrandingConfig {
4+
logoPath: string;
5+
title: string;
6+
}
7+
8+
const useBranding = (): BrandingConfig => {
9+
const brandingConfig = useMemo(() => {
10+
const logoPath = import.meta.env.VITE_APP_BRANDING_LOGO_PATH;
11+
const title = import.meta.env.VITE_APP_BRANDING_TITLE;
12+
13+
return {
14+
logoPath: logoPath || '',
15+
title: title || '',
16+
};
17+
}, []);
18+
19+
return brandingConfig;
20+
};
21+
22+
export default useBranding;

packages/web/src/pages/LandingPage.tsx

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React from 'react';
1+
import React, { useMemo } from 'react';
22
import { useNavigate } from 'react-router-dom';
33
import CardDemo from '../components/CardDemo';
44
import Button from '../components/Button';
@@ -23,6 +23,7 @@ import {
2323
} from 'react-icons/pi';
2424
import AwsIcon from '../assets/aws.svg?react';
2525
import useInterUseCases from '../hooks/useInterUseCases';
26+
2627
import {
2728
AgentPageQueryParams,
2829
ChatPageQueryParams,
@@ -51,6 +52,8 @@ const agentCoreEnabled: boolean =
5152
import.meta.env.VITE_APP_AGENT_CORE_ENABLED === 'true';
5253
const inlineAgents: boolean = import.meta.env.VITE_APP_INLINE_AGENTS === 'true';
5354
const mcpEnabled: boolean = import.meta.env.VITE_APP_MCP_ENABLED === 'true';
55+
const logoPath: string = import.meta.env.VITE_APP_BRANDING_LOGO_PATH || '';
56+
const brandingTitle: string = import.meta.env.VITE_APP_BRANDING_TITLE || '';
5457
const {
5558
imageGenModelIds,
5659
videoGenModelIds,
@@ -66,6 +69,22 @@ const LandingPage: React.FC = () => {
6669
const { setIsShow, init } = useInterUseCases();
6770
const { t } = useTranslation();
6871

72+
const displayLogo = useMemo(() => {
73+
if (logoPath) {
74+
const logoUrl = new URL(`../assets/${logoPath}`, import.meta.url).href;
75+
return (
76+
<img
77+
src={logoUrl}
78+
alt={brandingTitle || 'Logo'}
79+
className="mr-5 size-20"
80+
/>
81+
);
82+
}
83+
return <AwsIcon className="mr-5 size-20" />;
84+
}, []);
85+
86+
const displayTitle = brandingTitle || t('landing.title');
87+
6988
const demoChat = () => {
7089
const params: ChatPageQueryParams = {
7190
content: t('landing.demo.chat.content'),
@@ -282,8 +301,8 @@ const LandingPage: React.FC = () => {
282301
return (
283302
<div className="pb-24">
284303
<div className="bg-aws-squid-ink flex flex-col items-center justify-center px-3 py-5 text-xl font-semibold text-white lg:flex-row">
285-
<AwsIcon className="mr-5 size-20" />
286-
{t('landing.title')}
304+
{displayLogo}
305+
{displayTitle}
287306
</div>
288307

289308
<div className="mx-3 mb-6 mt-5 flex flex-col items-center justify-center text-xs lg:flex-row">

0 commit comments

Comments
 (0)