Skip to content

Commit d300187

Browse files
authored
feat: supports dark mode (#42)
1 parent eb8c288 commit d300187

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+2176
-1500
lines changed

.github/workflows/release-template.yml

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
name: Release Template
22

33
on:
4-
workflow_dispatch:
5-
inputs:
6-
reason:
7-
description: 'the reason for triggering this workflow'
8-
required: false
9-
default: 'manually publish the ecr images and templates'
4+
push:
5+
branches: [ "main" ]
6+
pull_request:
7+
branches: [ "main" ]
108
jobs:
119
release_template:
1210
runs-on: ubuntu-latest

README.md

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,24 @@ across Android, iOS, and macOS platforms.
2323

2424
### What's New 🔥
2525

26-
- 🚀 Support Speech to Speech By Amazon Nova Sonic on Apple Platform. Check [How to Use](#amazon-nova-sonic) for
26+
- Supports dark mode on Android, iOS, and Mac (Following system settings, From v2.4.0).
27+
- 🚀 Support Speech to Speech By Amazon Nova Sonic on Apple Platform.
28+
Check [How to Use](#amazon-nova-sonic-speech-to-speech-model) for
2729
more details. (From v2.3.0).
28-
- Support Request Latency and token response speed display (From v2.3.0).
29-
- Change to new bubble format UI for user question (From v2.3.0).
3030
- Support for OpenAI Compatible models. You can now
31-
use [easy-model-deployer](https://github.com/aws-samples/easy-model-deployer),
31+
use [Easy Model Deployer](https://github.com/aws-samples/easy-model-deployer),
3232
OpenRouter, or any OpenAI-compatible model provider via SwiftChat. Please
3333
check [Configure OpenAI Compatible](#openai-compatible) section for more details(From v2.2.0).
3434

35+
#### Dark Mode
36+
37+
<div style="display: flex; flex-direction: 'row'; background-color: #888888;">
38+
<img src="assets/animations/dark_markdown.avif" width=24%>
39+
<img src="assets/animations/dark_voice.avif" width=24%>
40+
<img src="assets/animations/dark_gen_image.avif" width=24%>
41+
<img src="assets/animations/dark_settings.avif" width=24%>
42+
</div>
43+
3544
### Key Features
3645

3746
- Real-time streaming chat with AI
@@ -42,7 +51,7 @@ across Android, iOS, and macOS platforms.
4251
- Cross-platform support (Android, iOS, macOS)
4352
- Tablet-optimized for iPad and Android tablets
4453
- Fast launch and responsive performance
45-
- Multiple AI model
54+
- Multiple AI models
4655
supported ([Amazon Bedrock](https://aws.amazon.com/bedrock/), [Ollama](https://github.com/ollama/ollama), [DeepSeek](https://www.deepseek.com/), [OpenAI](https://openai.com/)
4756
and [OpenAI Compatible](#openai-compatible) Models)
4857
- Fully Customizable System Prompt Assistant
@@ -53,12 +62,12 @@ across Android, iOS, and macOS platforms.
5362

5463
**Usage Guide**
5564

56-
1. Amazon Nova Sonic model is supported starting from v2.3.0. If you have deployed it before, You Need to:
65+
1. Amazon Nova Sonic model is supported starting from v2.3.0. If you have deployed it before, you need to:
5766
* [Update CloudFormation](#upgrade-cloudformation) Stack
5867
* [Update API](#upgrade-api)
5968
* [Upgrade your App](#-quick-download) to v2.3.0 or later
6069

61-
If you have not Deployed your CloudFormation Stack please
70+
If you have not deployed your CloudFormation Stack please
6271
finish [Getting Started with Amazon Bedrock](#getting-started-with-amazon-bedrock) section.
6372
2. Switch the **Region** to `us-east-1` in the settings page and select the `Nova Sonic` under **Chat Model**.
6473
3. Return to Chat page, select a system prompt or directly click the microphone icon to start your conversation.
@@ -68,14 +77,11 @@ across Android, iOS, and macOS platforms.
6877
1. Built-in spoken language practice for words and sentences, as well as storytelling scenarios. You can also add
6978
**Custom System Prompts** for voice chatting in different scenarios.
7079
2. Support **Barge In** by default, Also you can disable in system prompt.
71-
3. Support selecting voices in the settings page, including American/British English, and options for male and female voices.
80+
3. Support selecting voices in the settings page, including American/British English, Spanish and options for male and
81+
female voices.
7282
4. Support **Echo Cancellation**, You can talk directly to the device without wearing headphones.
7383
5. Support **Voice Waveform** to display volume level.
7484

75-
**General Talk**
76-
77-
https://github.com/user-attachments/assets/d3028312-c420-476c-88c2-ba870015f3c4
78-
7985
**Learn Sentences**
8086

8187
https://github.com/user-attachments/assets/ebf21b12-9c93-4d2e-a109-1d6484019838
@@ -385,7 +391,7 @@ npm run android
385391

386392
### Build for iOS
387393

388-
also open a new terminal, for the first time you need to install the native dependencies
394+
also open a new terminal. For the first time you need to install the native dependencies
389395
by execute `cd ios && pod install && cd ..`, then execute the follow command:
390396

391397
```bash

README_CN.md

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,29 @@
1414
[English](/README.md)
1515

1616
SwiftChat 是一款快速响应的 AI 聊天应用,采用 [React Native](https://reactnative.dev/)
17-
开发,并依托 [Amazon Bedrock](https://aws.amazon.com/bedrock/) 提供强大支持,同时兼容 Ollama、DeepSeek、OpenAI 和 OpenAI API 兼容的其他模型供应商。
18-
凭借其极简设计理念与坚实的隐私保护措施,该应用在 Android、iOS 和 macOS 平台上实现了实时流式对话、AI 图像生成和语音对话助手功能。
17+
开发,并依托 [Amazon Bedrock](https://aws.amazon.com/bedrock/) 提供强大支持,同时兼容 Ollama、DeepSeek、OpenAI 和 OpenAI API
18+
兼容的其他模型供应商。 凭借其极简设计理念与坚实的隐私保护措施,该应用在 Android、iOS 和 macOS 平台上实现了实时流式对话、AI
19+
图像生成和语音对话助手功能。
1920

2021
![](assets/promo.avif)
2122

2223
### 新功能 🔥
2324

24-
- 🚀 在 Apple 平台上支持 Amazon Nova Sonic 语音对话功能。查看 [使用方法](#amazon-nova-系列功能) 了解更多详情。(自 v2.3.0 起)。
25-
- 支持请求延迟和 token 响应速度显示(自 v2.3.0 起)。
26-
- 用户问题展示为新的气泡 UI 格式(自 v2.3.0 起)。
27-
- 支持 OpenAI Compatible 模型。您现在可以通过 SwiftChat 使用 [easy-model-deployer](https://github.com/aws-samples/easy-model-deployer)
28-
OpenRouter 或任何 OpenAI API 兼容的模型。更多详情请查看 [配置 OpenAI Compatible](#openai-compatible) 部分(自 v2.2.0 起)。
25+
- 支持 Android、iOS 和 Mac 上的暗黑模式(跟随系统设置,自 v2.4.0 起)。
26+
- 🚀 在 Apple 平台上支持 Amazon Nova Sonic 语音对话功能。查看 [使用方法](#amazon-nova-sonic-语音对话模型) 了解更多详情。(自
27+
v2.3.0 起)。
28+
- 支持 OpenAI Compatible 模型。您现在可以通过 SwiftChat
29+
使用 [easy-model-deployer](https://github.com/aws-samples/easy-model-deployer)
30+
OpenRouter 或任何 OpenAI API 兼容的模型。更多详情请查看 [配置 OpenAI Compatible](#openai-api-兼容) 部分(自 v2.2.0 起)。
31+
32+
#### 暗黑模式
33+
34+
<div style="display: flex; flex-direction: 'row'; background-color: #888888;">
35+
<img src="assets/animations/dark_markdown.avif" width=24%>
36+
<img src="assets/animations/dark_voice.avif" width=24%>
37+
<img src="assets/animations/dark_gen_image.avif" width=24%>
38+
<img src="assets/animations/dark_settings.avif" width=24%>
39+
</div>
2940

3041
### 主要特点
3142

@@ -40,7 +51,7 @@ SwiftChat 是一款快速响应的 AI 聊天应用,采用 [React Native](https
4051
- 快速启动和响应性能
4152
- 支持多种 AI 模型及切换 (
4253
包括 [Amazon Bedrock](https://aws.amazon.com/bedrock/)[Ollama](https://github.com/ollama/ollama)[DeepSeek](https://www.deepseek.com/)[OpenAI](https://openai.com/)
43-
[OpenAI Compatible](#openai-compatible) 模型)
54+
[OpenAI Compatible](#openai-api-兼容) 模型)
4455
- 支持完全自定义的系统提示词助手
4556

4657
### Amazon Nova 系列功能
@@ -61,15 +72,11 @@ SwiftChat 是一款快速响应的 AI 聊天应用,采用 [React Native](https
6172
**语音对话功能**
6273

6374
1. 内置单词和句子的口语练习,以及讲故事场景。您还可以添加 **自定义系统提示词** 用于不同场景的语音聊天。
64-
2. 支持在设置页面中选择声音类型,支持美式/英式英语,以及男声和女声的选择
65-
3. 默认支持 **插话功能**,您也可以在系统提示词中禁用
75+
2. 默认支持 **插话功能**,您也可以在系统提示词中禁用
76+
3. 支持在设置页面中选择声音类型,支持美式/英式英语,西班牙语,以及男声和女声的选择
6677
4. 支持 **回声消除**,您可以直接对着设备说话而无需佩戴耳机。
6778
5. 支持 **语音波形** 显示音量级别。
6879

69-
**日常对话**
70-
71-
https://github.com/user-attachments/assets/d3028312-c420-476c-88c2-ba870015f3c4
72-
7380
**学习句子**
7481

7582
https://github.com/user-attachments/assets/ebf21b12-9c93-4d2e-a109-1d6484019838
@@ -142,7 +149,9 @@ https://github.com/user-attachments/assets/c70fc2b4-8960-4a5e-b4f8-420fcd5eafd4
142149
- 文本模型: `Amazon Nova Pro`
143150
- 图像模型: `Stable Diffusion 3.5 Large`
144151

145-
如果您要使用图片生成功能,请确保已开启 `Amazon Nova Lite` 模型的访问权限。您可以参考 [Amazon Bedrock 用户指南](https://docs.aws.amazon.com/bedrock/latest/userguide/model-access-modify.html) 来启用您的模型。
152+
如果您要使用图片生成功能,请确保已开启 `Amazon Nova Lite`
153+
模型的访问权限。您可以参考 [Amazon Bedrock 用户指南](https://docs.aws.amazon.com/bedrock/latest/userguide/model-access-modify.html)
154+
来启用您的模型。
146155

147156
<details>
148157
<summary><b>🔧 配置步骤(点击展开)</b></summary>
@@ -172,10 +181,12 @@ https://github.com/user-attachments/assets/c70fc2b4-8960-4a5e-b4f8-420fcd5eafd4
172181
2. 点击 **下一步**,在"指定堆栈详细信息"页面中提供以下信息:
173182
- 使用存储 API Key 的参数名填写`ApiKeyParam`(例如"SwiftChatAPIKey")。
174183
- 对于 App Runner,根据您的需求选择`InstanceTypeParam`
175-
3. 点击 **下一步**,保持 "配置堆栈选项" 页面默认设置,阅读功能并勾选底部的 "我确认,AWS CloudFormation 可能会创建 IAM 资源" 复选框。
184+
3. 点击 **下一步**,保持 "配置堆栈选项" 页面默认设置,阅读功能并勾选底部的 "我确认,AWS CloudFormation 可能会创建 IAM 资源"
185+
复选框。
176186
4. 点击 **下一步**,在 "审核并创建" 中检查配置并点击 **提交**
177187

178-
等待约 3-5 分钟部署完成,然后点击 CloudFormation 堆栈并转到 **输出** 选项卡,您可以找到 **API URL** 类似`https://xxx.xxx.awsapprunner.com``https://xxx.lambda-url.xxx.on.aws`
188+
等待约 3-5 分钟部署完成,然后点击 CloudFormation 堆栈并转到 **输出** 选项卡,您可以找到 **API URL**
189+
类似`https://xxx.xxx.awsapprunner.com``https://xxx.lambda-url.xxx.on.aws`
179190

180191
### 第 3 步: 启动应用并设置 API URL 和 API Key
181192

@@ -361,7 +372,7 @@ cd react-native && npm i && npm start
361372
npm run android
362373
```
363374

364-
## 构建 iOS
375+
### 构建 iOS
365376

366377
先打开一个新的终端,如果是第一次运行,请先执行 `cd ios && pod install && cd ..` 来安装原生依赖,然后运行:
367378

@@ -411,7 +422,8 @@ npm run ios
411422
https://aws-gcr-solutions.s3.amazonaws.com/swift-chat/latest/SwiftChatLambda.template
412423
```
413424
4. 点击 **下一步** 按钮并继续点击 **下一步** 按钮。在 **配置堆栈选项** 页面上,
414-
勾选 `我确认,AWS CloudFormation 可能会创建 IAM 资源。` 然后点击 **下一步****提交** 按钮来更新您的 CloudFormation 模板。
425+
勾选 `我确认,AWS CloudFormation 可能会创建 IAM 资源。` 然后点击 **下一步****提交** 按钮来更新您的 CloudFormation
426+
模板。
415427

416428
## 安全
417429

assets/animations/dark_gen_image.avif

99.2 KB
Binary file not shown.

assets/animations/dark_markdown.avif

73.4 KB
Binary file not shown.

assets/animations/dark_settings.avif

78.3 KB
Binary file not shown.

assets/animations/dark_voice.avif

71.9 KB
Binary file not shown.

react-native/android/app/src/main/java/com/aws/swiftchat/MainActivity.kt

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.aws.swiftchat
22

3+
import android.content.res.Configuration
34
import android.graphics.Color
45
import android.os.Bundle
56
import com.facebook.react.ReactActivity
@@ -17,7 +18,29 @@ class MainActivity : ReactActivity() {
1718

1819
override fun onCreate(savedInstanceState: Bundle?) {
1920
super.onCreate(null)
20-
window.navigationBarColor = Color.WHITE
21+
updateNavigationBarColor()
22+
}
23+
24+
25+
override fun onConfigurationChanged(newConfig: Configuration) {
26+
super.onConfigurationChanged(newConfig)
27+
updateNavigationBarColor()
28+
}
29+
30+
override fun onResume() {
31+
super.onResume()
32+
updateNavigationBarColor()
33+
}
34+
35+
private fun updateNavigationBarColor() {
36+
val isDarkMode =
37+
resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES
38+
39+
runOnUiThread {
40+
window.navigationBarColor = if (isDarkMode) Color.BLACK else Color.WHITE
41+
// Force update for all child windows (including modals)
42+
window.decorView.requestLayout()
43+
}
2144
}
2245

2346
/**

react-native/android/app/src/main/java/com/aws/swiftchat/MainApplication.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ class MainApplication : Application(), ReactApplication {
3636
override fun onCreate() {
3737
super.onCreate()
3838
SoLoader.init(this, false)
39-
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
39+
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);
4040
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
4141
// If you opted-in for the New Architecture, we load the native entry point for this app.
4242
load()

react-native/android/app/src/main/res/values/styles.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,12 @@
77
<item name="android:navigationBarColor">@android:color/white</item>
88
</style>
99

10+
<style name="Theme.FullScreenDialog">
11+
<item name="android:windowNoTitle">true</item>
12+
<item name="android:windowIsFloating">false</item>
13+
<item name="android:windowBackground">@android:color/transparent</item>
14+
<item name="android:statusBarColor">@android:color/transparent</item>
15+
<item name="android:navigationBarColor">@android:color/transparent</item>
16+
</style>
17+
1018
</resources>

react-native/ios/SwiftChat/Info.plist

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@
5555
<string>UIInterfaceOrientationPortraitUpsideDown</string>
5656
</array>
5757
<key>UIUserInterfaceStyle</key>
58-
<string>Light</string>
58+
<string>Automatic</string>
5959
<key>UIViewControllerBasedStatusBarAppearance</key>
6060
<false/>
6161
</dict>

react-native/ios/SwiftChat/LaunchScreen.storyboard

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="32700.99.1234" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
3-
<device id="retina5_9" orientation="portrait" appearance="light"/>
3+
<device id="retina5_9" orientation="portrait" appearance="automatic"/>
44
<accessibilityOverrides/>
55
<dependencies>
66
<deployment identifier="iOS"/>

react-native/src/App.tsx

Lines changed: 50 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ import Toast from 'react-native-toast-message';
1515
import TokenUsageScreen from './settings/TokenUsageScreen.tsx';
1616
import { createNativeStackNavigator } from '@react-navigation/native-stack';
1717
import PromptScreen from './prompt/PromptScreen.tsx';
18-
import { isMacCatalyst } from './utils/PlatformUtils';
18+
import { isAndroid, isMacCatalyst } from './utils/PlatformUtils';
19+
import { ThemeProvider, useTheme } from './theme';
1920

2021
export const isMac = isMacCatalyst;
2122
const { width: screenWidth, height: screenHeight } = Dimensions.get('window');
@@ -31,14 +32,26 @@ const renderCustomDrawerContent = (
3132

3233
const DrawerNavigator = () => {
3334
const { drawerType } = useAppContext();
35+
const { colors, isDark } = useTheme();
3436
return (
3537
<Drawer.Navigator
3638
initialRouteName="Bedrock"
3739
screenOptions={{
38-
headerTintColor: 'black',
40+
overlayColor: isDark ? 'rgba(255, 255, 255, 0.1)' : undefined,
41+
headerTintColor: colors.text,
3942
headerTitleAlign: 'center',
40-
drawerStyle: { width: width },
41-
headerStyle: { height: isMac ? 66 : undefined },
43+
drawerStyle: {
44+
width: width,
45+
backgroundColor: colors.background,
46+
borderRightWidth: isMac ? 1 : isAndroid ? 0.3 : 0,
47+
borderRightColor: colors.border,
48+
},
49+
headerStyle: {
50+
height: isMac ? 66 : undefined,
51+
backgroundColor: colors.background,
52+
borderBottomWidth: isDark ? 0.3 : undefined,
53+
borderBottomColor: isDark ? colors.chatScreenSplit : undefined,
54+
},
4255
drawerType: isMac ? drawerType : 'slide',
4356
}}
4457
drawerContent={renderCustomDrawerContent}>
@@ -48,6 +61,7 @@ const DrawerNavigator = () => {
4861
);
4962
};
5063
const AppNavigator = () => {
64+
const { colors } = useTheme();
5165
return (
5266
<Stack.Navigator initialRouteName="Drawer" screenOptions={{}}>
5367
<Stack.Screen
@@ -62,35 +76,57 @@ const AppNavigator = () => {
6276
title: 'Usage',
6377
contentStyle: {
6478
height: isMac ? 66 : undefined,
79+
backgroundColor: colors.background,
6580
},
6681
headerTitleAlign: 'center',
82+
headerStyle: { backgroundColor: colors.background },
83+
headerTintColor: colors.text,
6784
}}
6885
/>
6986
<Stack.Screen
7087
name="Prompt"
7188
component={PromptScreen}
7289
options={{
7390
title: 'System Prompt',
74-
contentStyle: { height: isMac ? 66 : undefined },
91+
contentStyle: {
92+
height: isMac ? 66 : undefined,
93+
backgroundColor: colors.background,
94+
},
7595
headerTitleAlign: 'center',
96+
headerStyle: { backgroundColor: colors.background },
97+
headerTintColor: colors.text,
7698
}}
7799
/>
78100
</Stack.Navigator>
79101
);
80102
};
81103

104+
const AppWithTheme = () => {
105+
const { colors, isDark } = useTheme();
106+
return (
107+
<>
108+
<StatusBar
109+
barStyle={isDark ? 'light-content' : 'dark-content'}
110+
backgroundColor={colors.background}
111+
/>
112+
<NavigationContainer
113+
onStateChange={_ => {
114+
Keyboard.dismiss();
115+
}}>
116+
<AppNavigator />
117+
</NavigationContainer>
118+
</>
119+
);
120+
};
121+
82122
const App = () => {
83123
return (
84124
<>
85-
<AppProvider>
86-
<StatusBar barStyle="dark-content" backgroundColor="#ffffff" />
87-
<NavigationContainer
88-
onStateChange={_ => {
89-
Keyboard.dismiss();
90-
}}>
91-
<AppNavigator />
92-
</NavigationContainer>
93-
</AppProvider>
125+
<ThemeProvider>
126+
<AppProvider>
127+
<AppWithTheme />
128+
</AppProvider>
129+
</ThemeProvider>
94130
<Toast />
95131
</>
96132
);

0 commit comments

Comments
 (0)