Compare commits
10 Commits
b1ef5803dc
...
5947edec45
| Author | SHA1 | Date | |
|---|---|---|---|
| 5947edec45 | |||
|
|
cd536ff8f2 | ||
|
|
d3e04dbd46 | ||
|
|
1d1dd43c96 | ||
|
|
601defa3ee | ||
|
|
dc2495dca0 | ||
|
|
c75898546a | ||
|
|
d8c2bdf404 | ||
|
|
252d550c07 | ||
|
|
4b8e0752e5 |
12
.metadata
@@ -4,7 +4,7 @@
|
||||
# This file should be version controlled and should not be manually edited.
|
||||
|
||||
version:
|
||||
revision: "20f82749394e68bcfbbeee96bad384abaae09c13"
|
||||
revision: "582a0e7c5581dc0ca5f7bfd8662bb8db6f59d536"
|
||||
channel: "stable"
|
||||
|
||||
project_type: app
|
||||
@@ -13,11 +13,11 @@ project_type: app
|
||||
migration:
|
||||
platforms:
|
||||
- platform: root
|
||||
create_revision: 20f82749394e68bcfbbeee96bad384abaae09c13
|
||||
base_revision: 20f82749394e68bcfbbeee96bad384abaae09c13
|
||||
- platform: macos
|
||||
create_revision: 20f82749394e68bcfbbeee96bad384abaae09c13
|
||||
base_revision: 20f82749394e68bcfbbeee96bad384abaae09c13
|
||||
create_revision: 582a0e7c5581dc0ca5f7bfd8662bb8db6f59d536
|
||||
base_revision: 582a0e7c5581dc0ca5f7bfd8662bb8db6f59d536
|
||||
- platform: linux
|
||||
create_revision: 582a0e7c5581dc0ca5f7bfd8662bb8db6f59d536
|
||||
base_revision: 582a0e7c5581dc0ca5f7bfd8662bb8db6f59d536
|
||||
|
||||
# User provided section
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# Conduit Privacy Policy
|
||||
# iiEasy Privacy Policy
|
||||
|
||||
Effective date: 2025-08-09
|
||||
|
||||
Conduit is an open‑source mobile client for Open‑WebUI. This app acts as a client to a server you choose and configure. This policy describes how the app itself handles data on your device. Your configured server may collect, process, and store data under its own policies; please review your server's privacy terms separately.
|
||||
iiEasy is a mobile client for Open‑WebUI (based on the open-source Conduit project). This app acts as a client to a server you choose and configure. This policy describes how the app itself handles data on your device. Your configured server may collect, process, and store data under its own policies; please review your server's privacy terms separately.
|
||||
|
||||
## Information We Collect
|
||||
- Device-stored data: minimal settings and preferences (e.g., theme, UI options) saved locally on your device.
|
||||
@@ -20,7 +20,7 @@ Conduit is an open‑source mobile client for Open‑WebUI. This app acts as a c
|
||||
- Network transfer: when you interact with the app, your data is sent to the server you configured. The app does not send your data to any developer‑controlled servers.
|
||||
|
||||
## Permissions
|
||||
Depending on how you use Conduit, the app may request:
|
||||
Depending on how you use iiEasy, the app may request:
|
||||
- Microphone: to capture voice input when you opt in.
|
||||
- Photos/Files: to let you pick and upload attachments.
|
||||
- Network access: to connect to your configured server.
|
||||
@@ -40,12 +40,12 @@ We use platform‑provided secure storage for sensitive credentials where suppor
|
||||
- You can choose not to grant optional permissions; some features may not work without them.
|
||||
|
||||
## Children’s Privacy
|
||||
Conduit is not directed to children under 13 (or the minimum age required in your jurisdiction). Do not use the app if you do not meet the applicable age requirements.
|
||||
iiEasy is not directed to children under 13 (or the minimum age required in your jurisdiction). Do not use the app if you do not meet the applicable age requirements.
|
||||
|
||||
## Changes to This Policy
|
||||
We may update this policy to reflect improvements or legal requirements. Material changes will be reflected in the app bundle and version notes.
|
||||
|
||||
## Contact
|
||||
For questions or requests about this policy, please contact the app maintainer(s) through the project repository.
|
||||
For questions or requests about this policy, please contact us at [iiEasy.ru](https://iiEasy.ru).
|
||||
|
||||
|
||||
|
||||
74
README.md
@@ -1,16 +1,11 @@
|
||||
# Conduit
|
||||
# iiEasy
|
||||
|
||||
**iiEasy** — мобильный клиент для Open-WebUI на базе [Conduit](https://github.com/cogwheel0/conduit). Нативная работа с вашей self-hosted AI инфраструктурой.
|
||||
|
||||
<div align="center">
|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
<a href="https://play.google.com/store/apps/details?id=app.cogwheel.conduit">
|
||||
<img src="docs/store-badges/google.webp" alt="Get it on Google Play" style="height:56px;"/>
|
||||
</a>
|
||||
<a href="https://apps.apple.com/us/app/conduit-open-webui-client/id6749840287">
|
||||
<img src="docs/store-badges/apple.webp" alt="Download on the App Store" style="height:56px;"/>
|
||||
<a href="https://iiEasy.ru">
|
||||
<img src="docs/store-badges/google.webp" alt="iiEasy" style="height:56px;"/>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
@@ -18,12 +13,12 @@
|
||||
<br>
|
||||
|
||||
<div align="center">
|
||||
<img src="docs/screenshots/conduit-demo.gif" alt="Conduit Demo" style="height:600px;"/>
|
||||
<img src="docs/screenshots/conduit-demo.gif" alt="iiEasy Demo" style="height:600px;"/>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
|
||||
Conduit is an open-source, cross-platform mobile application for Open-WebUI, providing a native mobile experience for interacting with your self-hosted AI infrastructure.
|
||||
iiEasy is a cross-platform mobile application for Open-WebUI (based on the open-source Conduit project), providing a native mobile experience for interacting with your self-hosted AI infrastructure.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
@@ -71,13 +66,13 @@ flutter run -d ios # or: -d android
|
||||
- **Tools (Function Calling)**: Invoke server-side tools exposed by Open‑WebUI, with result rendering
|
||||
|
||||
### Authentication
|
||||
Conduit supports multiple authentication flows when connecting to your Open‑WebUI:
|
||||
iiEasy supports multiple authentication flows when connecting to your Open‑WebUI (same as Conduit):
|
||||
- **Username + Password**: Sign in directly against servers that expose a login endpoint. Credentials are stored securely using platform keychains.
|
||||
- **SSO / OAuth** (iOS & Android): Authenticate via your server's configured OAuth providers (Google, Microsoft, GitHub, OIDC, etc.) using an in-app WebView. The token is automatically captured after the OAuth flow completes.
|
||||
- **Reverse Proxy Support** (iOS & Android): Seamlessly connect to Open WebUI instances behind authentication proxies like oauth2-proxy, Authelia, Authentik, Pangolin, Cloudflare Tunnel, etc. Conduit automatically detects when proxy authentication is required and guides you through the login flow—no endpoint allowlisting or server-side configuration needed. Proxy session cookies are captured from the native cookie store and included in all subsequent API requests.
|
||||
- **LDAP**: Sign in using LDAP credentials if enabled on your server.
|
||||
- **JWT Token**: Paste a server‑issued JWT token for manual token-based auth.
|
||||
- **Custom Headers**: Add headers during login (e.g., `X-API-Key`, `Authorization`, `X-Org`) that Conduit will include on all HTTP/WebSocket requests.
|
||||
- **Custom Headers**: Add headers during login (e.g., `X-API-Key`, `Authorization`, `X-Org`) that iiEasy will include on all HTTP/WebSocket requests.
|
||||
|
||||
The authentication page dynamically displays available options based on your server's configuration.
|
||||
|
||||
@@ -124,17 +119,49 @@ flutter run -d android
|
||||
## Building for Release
|
||||
|
||||
### Android
|
||||
|
||||
**Если Flutter пишет «No Android SDK found» или «Android SDK not found at this location»:** переменная `ANDROID_HOME` должна указывать на каталог, где **реально установлен** SDK. Пустая или несуществующая папка не подойдёт.
|
||||
|
||||
1. **Вариант А — Android Studio**
|
||||
Установите с [developer.android.com/studio](https://developer.android.com/studio). При первом запуске выберите установку SDK — он появится в `~/Android/Sdk`.
|
||||
|
||||
2. **Вариант Б — только command-line tools (без Android Studio)**
|
||||
```bash
|
||||
mkdir -p ~/Android/Sdk/cmdline-tools
|
||||
cd ~/Android/Sdk
|
||||
# Скачать с https://developer.android.com/studio#command-tools (Linux)
|
||||
# Распаковать так, чтобы внутри было cmdline-tools/latest/bin/sdkmanager
|
||||
unzip -q commandlinetools-linux-*.zip && mv cmdline-tools latest && mv latest cmdline-tools/
|
||||
export ANDROID_HOME="$HOME/Android/Sdk"
|
||||
export PATH="$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/platform-tools:$PATH"
|
||||
yes | sdkmanager --licenses
|
||||
sdkmanager "platform-tools" "platforms;android-34" "build-tools;34.0.0"
|
||||
```
|
||||
3. **Настройте окружение** (добавьте в `~/.bashrc` и выполните `source ~/.bashrc` или откройте новый терминал):
|
||||
```bash
|
||||
export ANDROID_HOME="$HOME/Android/Sdk"
|
||||
export PATH="$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/platform-tools:$PATH"
|
||||
```
|
||||
4. Примите лицензии: `flutter doctor --android-licenses` (все запросы — `y`).
|
||||
5. Проверка: `flutter doctor -v` — в блоке Android toolchain не должно быть ошибок.
|
||||
|
||||
После этого:
|
||||
```bash
|
||||
flutter build apk --release
|
||||
# or for App Bundle
|
||||
# или для выгрузки в Google Play
|
||||
flutter build appbundle --release
|
||||
```
|
||||
APK: `build/app/outputs/flutter-apk/app-release.apk`
|
||||
AAB: `build/app/outputs/bundle/release/app-release.aab`
|
||||
|
||||
### iOS
|
||||
```bash
|
||||
flutter build ios --release
|
||||
```
|
||||
|
||||
### Linux (desktop)
|
||||
Если сборка под Linux падает с ошибкой линковки (`libsecret`, `undefined reference to g_task_set_static_name` и т.п.), это типично для **Flutter из snap**: в snap старая glibc, а системные библиотеки (libsecret, glib) требуют новую. Решение: ставить Flutter **не из snap** — [официальная установка](https://docs.flutter.dev/get-started/install/linux) (manual install). После установки Flutter вручную `flutter run -d linux` и `flutter build linux` должны собираться. Для проверки приложения без Linux-сборки можно использовать эмулятор Android или устройство.
|
||||
|
||||
## Configuration
|
||||
|
||||
### Android
|
||||
@@ -180,17 +207,7 @@ lib/
|
||||
|
||||
## Contributing
|
||||
|
||||
Conduit is currently in active development. We welcome your feedback and contributions!
|
||||
|
||||
**How to Contribute:**
|
||||
|
||||
- **Bug Reports**: Found a bug? Please [create an issue](https://github.com/cogwheel0/conduit/issues) with details about the problem, steps to reproduce, and your device/platform information.
|
||||
|
||||
- **Feature Requests**: Have an idea for a new feature? Start a [discussion](https://github.com/cogwheel0/conduit/discussions) to share your ideas and gather feedback from the community.
|
||||
|
||||
- **Questions & Feedback**: Use [GitHub Discussions](https://github.com/cogwheel0/conduit/discussions) to ask questions, share your experience, or discuss the project.
|
||||
|
||||
**Note:** As the project is actively evolving, we're not accepting pull requests at this time. Instead, please use issues and discussions to share your ideas, report bugs, and contribute to the project's development.
|
||||
iiEasy is based on Conduit. For upstream contributions and discussions, see the [Conduit repository](https://github.com/cogwheel0/conduit). For iiEasy-specific feedback and support, visit [iiEasy.ru](https://iiEasy.ru).
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
@@ -217,8 +234,9 @@ This project is licensed under the GPL3 License - see the LICENSE file for detai
|
||||
- <a href="https://vercel.com/oss"><img alt="Vercel OSS Program" src="https://vercel.com/oss/program-badge.svg" /></a>
|
||||
- Open-WebUI team for creating an amazing self-hosted AI interface
|
||||
- Flutter team for the excellent mobile framework
|
||||
- All contributors and users of Conduit
|
||||
- Conduit project and all its contributors
|
||||
- All users of iiEasy
|
||||
|
||||
## Support
|
||||
|
||||
For issues and feature requests, please use the [GitHub Issues](https://github.com/cogwheel0/conduit/issues) page.
|
||||
For support and information, please visit [iiEasy.ru](https://iiEasy.ru).
|
||||
|
||||
@@ -17,7 +17,7 @@ if (keystorePropertiesFile.exists()) {
|
||||
android {
|
||||
namespace = "app.cogwheel.conduit"
|
||||
compileSdk = 36
|
||||
ndkVersion = "29.0.14206865"
|
||||
ndkVersion = "28.0.13004108"
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "app.cogwheel.conduit"
|
||||
@@ -56,8 +56,8 @@ android {
|
||||
if (keystorePropertiesFile.exists()) {
|
||||
signingConfig = signingConfigs.getByName("release")
|
||||
}
|
||||
isMinifyEnabled = true
|
||||
isShrinkResources = true
|
||||
isMinifyEnabled = false
|
||||
isShrinkResources = false
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
"proguard-rules.pro"
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
ForegroundServiceDidNotStartInTimeException. This ensures channels exist before
|
||||
any foreground service attempts to use them. -->
|
||||
<application
|
||||
android:label="Conduit"
|
||||
android:label="iiEasy"
|
||||
android:name=".ConduitApplication"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:allowBackup="false"
|
||||
@@ -103,7 +103,7 @@
|
||||
<activity
|
||||
android:name=".ProcessTextActivity"
|
||||
android:exported="true"
|
||||
android:label="Ask Conduit"
|
||||
android:label="Ask iiEasy"
|
||||
android:noHistory="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.PROCESS_TEXT" />
|
||||
|
||||
@@ -94,7 +94,7 @@ class BackgroundStreamingService : Service() {
|
||||
// Otherwise startForeground throws "Bad notification" error
|
||||
ensureNotificationChannel()
|
||||
val fallbackNotification = NotificationCompat.Builder(this, CHANNEL_ID)
|
||||
.setContentTitle("Conduit")
|
||||
.setContentTitle("iiEasy")
|
||||
.setSmallIcon(R.mipmap.ic_launcher)
|
||||
.setSilent(true)
|
||||
.setOngoing(true) // Prevent user from dismissing foreground service notification
|
||||
@@ -264,7 +264,7 @@ class BackgroundStreamingService : Service() {
|
||||
|
||||
// Create a minimal, silent notification (required for foreground service)
|
||||
return NotificationCompat.Builder(this, CHANNEL_ID)
|
||||
.setContentTitle("Conduit")
|
||||
.setContentTitle("iiEasy")
|
||||
.setContentText("Background service active")
|
||||
.setSmallIcon(R.mipmap.ic_launcher)
|
||||
.setContentIntent(pendingIntent)
|
||||
@@ -289,7 +289,7 @@ class BackgroundStreamingService : Service() {
|
||||
"Background Service",
|
||||
NotificationManager.IMPORTANCE_MIN,
|
||||
).apply {
|
||||
description = "Background service for Conduit"
|
||||
description = "Background service for iiEasy"
|
||||
setShowBadge(false)
|
||||
enableLights(false)
|
||||
enableVibration(false)
|
||||
@@ -324,7 +324,7 @@ class BackgroundStreamingService : Service() {
|
||||
val powerManager = getSystemService(Context.POWER_SERVICE) as PowerManager
|
||||
wakeLock = powerManager.newWakeLock(
|
||||
PowerManager.PARTIAL_WAKE_LOCK,
|
||||
"Conduit::StreamingWakeLock"
|
||||
"iiEasy::StreamingWakeLock"
|
||||
).apply {
|
||||
// Disable reference counting for deterministic single-holder behavior
|
||||
// This prevents accumulation if acquireWakeLock is called multiple times
|
||||
@@ -728,7 +728,7 @@ class BackgroundStreamingHandler(private val activity: MainActivity) : MethodCal
|
||||
private fun createNotificationChannel() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
val name = "Background Service"
|
||||
val descriptionText = "Background service for Conduit"
|
||||
val descriptionText = "Background service for iiEasy"
|
||||
val importance = NotificationManager.IMPORTANCE_MIN
|
||||
val channel = NotificationChannel(BackgroundStreamingService.CHANNEL_ID, name, importance).apply {
|
||||
description = descriptionText
|
||||
|
||||
@@ -35,7 +35,7 @@ class ConduitApplication : Application() {
|
||||
notificationManager,
|
||||
channelId = BackgroundStreamingService.CHANNEL_ID,
|
||||
channelName = "Background Service",
|
||||
description = "Background service for Conduit",
|
||||
description = "Background service for iiEasy",
|
||||
importance = NotificationManager.IMPORTANCE_MIN,
|
||||
)
|
||||
|
||||
|
||||
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 5.3 KiB |
|
After Width: | Height: | Size: 9.0 KiB |
|
After Width: | Height: | Size: 13 KiB |
24
android/app/src/main/res/drawable/ic_launcher_foreground.xml
Normal file
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- iiEasy logo: 3 concentric dashed circles (from assets/icons/logo.svg) -->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="200"
|
||||
android:viewportHeight="200">
|
||||
<!-- Three concentric circles (solid stroke; dash not supported by AAPT in drawable) -->
|
||||
<path
|
||||
android:pathData="M 170,100 A 70,70 0 1,1 169.99,100"
|
||||
android:strokeColor="#1F2937"
|
||||
android:strokeWidth="6"
|
||||
android:fillColor="#00000000"/>
|
||||
<path
|
||||
android:pathData="M 150,100 A 50,50 0 1,1 149.99,100"
|
||||
android:strokeColor="#1F2937"
|
||||
android:strokeWidth="6"
|
||||
android:fillColor="#00000000"/>
|
||||
<path
|
||||
android:pathData="M 130,100 A 30,30 0 1,1 129.99,100"
|
||||
android:strokeColor="#1F2937"
|
||||
android:strokeWidth="6"
|
||||
android:fillColor="#00000000"/>
|
||||
</vector>
|
||||
@@ -89,7 +89,7 @@
|
||||
android:paddingStart="20dp"
|
||||
android:paddingEnd="8dp">
|
||||
|
||||
<!-- Ask Conduit text -->
|
||||
<!-- Ask iiEasy text -->
|
||||
<TextView
|
||||
android:id="@+id/input_area"
|
||||
android:layout_width="wrap_content"
|
||||
@@ -97,7 +97,7 @@
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_toStartOf="@id/btn_voice"
|
||||
android:layout_centerVertical="true"
|
||||
android:text="Ask Conduit"
|
||||
android:text="Ask iiEasy"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="16sp"
|
||||
android:alpha="0.6"
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@mipmap/ic_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
<monochrome android:drawable="@mipmap/ic_launcher_monochrome"/>
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground>
|
||||
<inset
|
||||
android:drawable="@drawable/ic_launcher_foreground"
|
||||
android:inset="16%" />
|
||||
</foreground>
|
||||
</adaptive-icon>
|
||||
|
Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 893 B |
|
Before Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 9.0 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 5.0 KiB |
|
Before Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 9.4 KiB |
|
Before Width: | Height: | Size: 9.0 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 12 KiB |
@@ -27,8 +27,5 @@
|
||||
<color name="widget_secondary_container_fallback">#E8DEF8</color>
|
||||
<color name="widget_on_secondary_container_fallback">#1D192B</color>
|
||||
<color name="widget_surface_fallback">#FFFBFE</color>
|
||||
<color name="ic_launcher_background">#FFFFFF</color>
|
||||
</resources>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">Conduit</string>
|
||||
<string name="app_name">iiEasy</string>
|
||||
|
||||
<!-- Widget strings -->
|
||||
<string name="widget_name">Conduit</string>
|
||||
<string name="widget_description">Quick access to Conduit chat with camera, photos, and clipboard shortcuts</string>
|
||||
<string name="widget_ask_conduit">Ask Conduit</string>
|
||||
<string name="widget_name">iiEasy</string>
|
||||
<string name="widget_description">Quick access to iiEasy chat with camera, photos, and clipboard shortcuts</string>
|
||||
<string name="widget_ask_conduit">Ask iiEasy</string>
|
||||
<string name="widget_camera">Camera</string>
|
||||
<string name="widget_photos">Photos</string>
|
||||
<string name="widget_clipboard">Clipboard</string>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Conduit is an open-source, native mobile client for Open‑WebUI. Connect to your own server to chat with AI models, manage conversations, and take your self‑hosted AI with you—securely and on the go.
|
||||
iiEasy is a native mobile client for Open‑WebUI, based on the open-source Conduit project. Connect to your own server to chat with AI models, manage conversations, and take your self‑hosted AI with you—securely and on the go.
|
||||
|
||||
Features
|
||||
- Real-time streaming chat
|
||||
@@ -13,7 +13,7 @@ Features
|
||||
- Offline-aware experience
|
||||
|
||||
Requirements
|
||||
- Requires an existing Open‑WebUI server. Conduit does not host or provide AI models.
|
||||
- Requires an existing Open‑WebUI server. iiEasy does not host or provide AI models.
|
||||
- No data is sent to third-party services by default; everything stays with your configured server.
|
||||
|
||||
Permissions
|
||||
@@ -21,11 +21,10 @@ Permissions
|
||||
- Camera and Photos/Storage: Image/file attachments
|
||||
- Network: Connect to your Open‑WebUI server
|
||||
|
||||
Open Source:
|
||||
Conduit is an open-source project. For support, to report issues, or to view the source code, please visit our GitHub repository:
|
||||
For support, issues, and more information, please visit:
|
||||
|
||||
https://github.com/cogwheel0/conduit
|
||||
https://iiEasy.ru
|
||||
|
||||
-----
|
||||
|
||||
Disclaimer: This is an independent, third-party application licensed under the GNU General Public License v3.0 (GPLv3) and is not officially affiliated with the OpenWebUI project.
|
||||
Disclaimer: iiEasyWeb is an independent, third-party application licensed under the GNU General Public License v3.0 (GPLv3) and is not officially affiliated with the OpenWebUI project.
|
||||
|
||||
@@ -1 +1 @@
|
||||
Conduit: OpenWebUI Client
|
||||
iiEasyWeb
|
||||
|
||||
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 76 KiB |
BIN
assets/icons/icon_dark.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
5
assets/icons/logo.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg width="32" height="32" viewBox="0 0 200 200" fill="none" xmlns="http://www.w3.org/2000/svg" aria-label="iiEasy Logo">
|
||||
<circle cx="100" cy="100" r="70" stroke="#1F2937" stroke-width="6" fill="none" stroke-dasharray="15 85" transform="rotate(-90 100 100)"/>
|
||||
<circle cx="100" cy="100" r="50" stroke="#1F2937" stroke-width="6" fill="none" stroke-dasharray="12 58" transform="rotate(-60 100 100)"/>
|
||||
<circle cx="100" cy="100" r="30" stroke="#1F2937" stroke-width="6" fill="none" stroke-dasharray="8 32" transform="rotate(-30 100 100)"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 549 B |
5
assets/icons/logo_dark.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg width="32" height="32" viewBox="0 0 200 200" fill="none" xmlns="http://www.w3.org/2000/svg" aria-label="iiEasy Logo (dark)">
|
||||
<circle cx="100" cy="100" r="70" stroke="#FFFFFF" stroke-width="6" fill="none" stroke-dasharray="15 85" transform="rotate(-90 100 100)"/>
|
||||
<circle cx="100" cy="100" r="50" stroke="#FFFFFF" stroke-width="6" fill="none" stroke-dasharray="12 58" transform="rotate(30 100 100)"/>
|
||||
<circle cx="100" cy="100" r="30" stroke="#FFFFFF" stroke-width="6" fill="none" stroke-dasharray="8 32" transform="rotate(60 100 100)"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 554 B |
@@ -4,29 +4,29 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Conduit — Your AI, Everywhere</title>
|
||||
<title>iiEasy — Your AI, Everywhere</title>
|
||||
<meta name="description"
|
||||
content="Conduit is an open-source, cross-platform mobile application for Open-WebUI. A native mobile experience for your self-hosted AI infrastructure.">
|
||||
content="iiEasy is a mobile client for Open-WebUI (based on Conduit). A native mobile experience for your self-hosted AI infrastructure.">
|
||||
|
||||
<!-- Open Graph / Facebook -->
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:url" content="https://conduit.cogwheel.app/">
|
||||
<meta property="og:title" content="Conduit — Your AI, Everywhere">
|
||||
<meta property="og:url" content="https://iiEasy.ru/">
|
||||
<meta property="og:title" content="iiEasy — Your AI, Everywhere">
|
||||
<meta property="og:description" content="The native mobile client for Open-WebUI. Beautiful, fast, and secure.">
|
||||
<meta property="og:image" content="https://conduit.cogwheel.app/og-image.png">
|
||||
<meta property="og:image" content="https://iiEasy.ru/og-image.png">
|
||||
|
||||
<!-- Twitter -->
|
||||
<meta property="twitter:card" content="summary_large_image">
|
||||
<meta property="twitter:url" content="https://conduit.cogwheel.app/">
|
||||
<meta property="twitter:title" content="Conduit — Your AI, Everywhere">
|
||||
<meta property="twitter:url" content="https://iiEasy.ru/">
|
||||
<meta property="twitter:title" content="iiEasy — Your AI, Everywhere">
|
||||
<meta property="twitter:description" content="The native mobile client for Open-WebUI. Beautiful, fast, and secure.">
|
||||
<meta property="twitter:image" content="https://conduit.cogwheel.app/og-image.png">
|
||||
<meta property="twitter:image" content="https://iiEasy.ru/og-image.png">
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:opsz,wght@12..96,300;12..96,400;12..96,500;12..96,600;12..96,700;12..96,800&family=Crimson+Pro:ital,wght@0,400;0,500;0,600;1,400;1,500&family=Inter:wght@400;500;600&display=swap" rel="stylesheet">
|
||||
<link rel="icon" type="image/png"
|
||||
href="https://raw.githubusercontent.com/cogwheel0/conduit/main/assets/icons/icon.png">
|
||||
href="assets/icons/icon.png">
|
||||
<script src="https://app.rybbit.io/api/script.js" data-site-id="1c79ee193aa9" defer></script>
|
||||
</head>
|
||||
|
||||
@@ -41,15 +41,15 @@
|
||||
|
||||
<nav class="navbar">
|
||||
<div class="container">
|
||||
<a href="#" class="logo">
|
||||
<img src="https://raw.githubusercontent.com/cogwheel0/conduit/main/assets/icons/icon.png" alt="Conduit" class="logo-icon">
|
||||
<span class="logo-text">Conduit</span>
|
||||
<a href="https://iiEasy.ru" class="logo">
|
||||
<img src="assets/icons/icon.png" alt="iiEasy" class="logo-icon">
|
||||
<span class="logo-text">iiEasy</span>
|
||||
</a>
|
||||
<div class="nav-links">
|
||||
<a href="#features">Features</a>
|
||||
<a href="#gallery">Gallery</a>
|
||||
<a href="https://github.com/cogwheel0/conduit" target="_blank" class="nav-cta">
|
||||
<span>View on GitHub</span>
|
||||
<a href="https://iiEasy.ru" target="_blank" class="nav-cta">
|
||||
<span>iiEasy.ru</span>
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M7 17L17 7M17 7H7M17 7V17"/>
|
||||
</svg>
|
||||
@@ -66,7 +66,7 @@
|
||||
<div class="mobile-menu">
|
||||
<a href="#features">Features</a>
|
||||
<a href="#gallery">Gallery</a>
|
||||
<a href="https://github.com/cogwheel0/conduit" target="_blank">GitHub</a>
|
||||
<a href="https://iiEasy.ru" target="_blank">iiEasy.ru</a>
|
||||
</div>
|
||||
|
||||
<header class="hero">
|
||||
@@ -76,14 +76,14 @@
|
||||
<span class="badge-dot"></span>
|
||||
Open Source & Privacy-First
|
||||
</div>
|
||||
<a href="https://github.com/cogwheel0/conduit" target="_blank" class="social-badge">
|
||||
<a href="https://iiEasy.ru" target="_blank" class="social-badge">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
|
||||
</svg>
|
||||
<span class="badge-value" id="github-stars">—</span>
|
||||
<span class="badge-label">Stars</span>
|
||||
</a>
|
||||
<a href="https://github.com/cogwheel0/conduit/releases" target="_blank" class="social-badge">
|
||||
<a href="https://iiEasy.ru/releases" target="_blank" class="social-badge">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
|
||||
<polyline points="7 10 12 15 17 10"/>
|
||||
@@ -122,7 +122,7 @@
|
||||
<div class="hero-device">
|
||||
<div class="device-glow"></div>
|
||||
<div class="device-frame">
|
||||
<img src="screenshots/conduit-demo.gif" alt="Conduit App Demo" loading="eager">
|
||||
<img src="screenshots/conduit-demo.gif" alt="iiEasy App Demo" loading="eager">
|
||||
</div>
|
||||
<div class="device-reflection"></div>
|
||||
</div>
|
||||
@@ -403,7 +403,7 @@
|
||||
</div>
|
||||
<div class="mini-feature">
|
||||
<span class="mini-feature-name">Share Extension</span>
|
||||
<span class="mini-feature-desc">Send from any app to Conduit</span>
|
||||
<span class="mini-feature-desc">Send from any app to iiEasy</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -423,7 +423,7 @@
|
||||
<div class="category-features">
|
||||
<div class="mini-feature">
|
||||
<span class="mini-feature-name">Siri Shortcuts</span>
|
||||
<span class="mini-feature-desc">"Hey Siri, ask Conduit..."</span>
|
||||
<span class="mini-feature-desc">"Hey Siri, ask iiEasy..."</span>
|
||||
</div>
|
||||
<div class="mini-feature">
|
||||
<span class="mini-feature-name">Android Assistant</span>
|
||||
@@ -526,7 +526,7 @@
|
||||
<div class="cta-card">
|
||||
<div class="cta-content">
|
||||
<h2>Ready to get started?</h2>
|
||||
<p>Join thousands of users who've made Conduit their gateway to self-hosted AI. Open source and available now.</p>
|
||||
<p>Join thousands of users who've made iiEasy their gateway to self-hosted AI. Based on Conduit. Open source and available now.</p>
|
||||
<div class="cta-buttons">
|
||||
<a href="https://apps.apple.com/us/app/conduit-open-webui-client/id6749840287" class="btn btn-white">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
|
||||
@@ -556,8 +556,8 @@
|
||||
<div class="footer-main">
|
||||
<div class="footer-brand">
|
||||
<a href="#" class="logo">
|
||||
<img src="https://raw.githubusercontent.com/cogwheel0/conduit/main/assets/icons/icon.png" alt="Conduit" class="logo-icon">
|
||||
<span class="logo-text">Conduit</span>
|
||||
<img src="assets/icons/icon.png" alt="iiEasy" class="logo-icon">
|
||||
<span class="logo-text">iiEasy</span>
|
||||
</a>
|
||||
<p>The native mobile client for Open-WebUI.<br>Open source. Privacy-first. Beautiful.</p>
|
||||
</div>
|
||||
@@ -566,25 +566,25 @@
|
||||
<h4>Product</h4>
|
||||
<a href="#features">Features</a>
|
||||
<a href="#gallery">Gallery</a>
|
||||
<a href="https://github.com/cogwheel0/conduit/releases">Releases</a>
|
||||
<a href="https://iiEasy.ru/releases">Releases</a>
|
||||
</div>
|
||||
<div class="footer-col">
|
||||
<h4>Community</h4>
|
||||
<a href="https://github.com/cogwheel0/conduit">GitHub</a>
|
||||
<a href="https://github.com/cogwheel0/conduit/issues">Issues</a>
|
||||
<a href="https://github.com/cogwheel0/conduit/discussions">Discussions</a>
|
||||
<a href="https://iiEasy.ru">GitHub</a>
|
||||
<a href="https://iiEasy.ru/issues">Issues</a>
|
||||
<a href="https://iiEasy.ru/discussions">Discussions</a>
|
||||
</div>
|
||||
<div class="footer-col">
|
||||
<h4>Legal</h4>
|
||||
<a href="https://github.com/cogwheel0/conduit/blob/main/LICENSE">License (GPLv3)</a>
|
||||
<a href="https://github.com/cogwheel0/conduit/blob/main/PRIVACY_POLICY.md">Privacy Policy</a>
|
||||
<a href="https://iiEasy.ru/blob/main/LICENSE">License (GPLv3)</a>
|
||||
<a href="https://iiEasy.ru/blob/main/PRIVACY_POLICY.md">Privacy Policy</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer-bottom">
|
||||
<p>© 2025 Conduit. Not affiliated with Open-WebUI.</p>
|
||||
<p>© 2025 iiEasy. Based on Conduit. Not affiliated with Open-WebUI.</p>
|
||||
<div class="footer-social">
|
||||
<a href="https://github.com/cogwheel0/conduit" aria-label="GitHub">
|
||||
<a href="https://iiEasy.ru" aria-label="GitHub">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
|
||||
</svg>
|
||||
|
||||
@@ -739,7 +739,7 @@
|
||||
DEVELOPMENT_TEAM = X2662V5DT2;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = Conduit;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = iiEasy;
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
@@ -813,7 +813,7 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
@@ -870,7 +870,7 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
@@ -925,7 +925,7 @@
|
||||
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO;
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-Debug";
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
@@ -934,7 +934,7 @@
|
||||
DEVELOPMENT_TEAM = X2662V5DT2;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "Conduit Debug";
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "iiEasy Debug";
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
@@ -969,7 +969,7 @@
|
||||
DEVELOPMENT_TEAM = X2662V5DT2;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = Conduit;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = iiEasy;
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
@@ -992,8 +992,8 @@
|
||||
F15AFED22EE5499E00A1FABB /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = AppIcon;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
@@ -1009,7 +1009,7 @@
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = ConduitWidget/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = ConduitWidget;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = iiEasy;
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 26.1;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@@ -1038,8 +1038,8 @@
|
||||
F15AFED32EE5499E00A1FABB /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = AppIcon;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
@@ -1055,7 +1055,7 @@
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = ConduitWidget/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = ConduitWidget;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = iiEasy;
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 26.1;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@@ -1081,8 +1081,8 @@
|
||||
F15AFED42EE5499E00A1FABB /* Profile */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = AppIcon;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
@@ -1098,7 +1098,7 @@
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = ConduitWidget/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = ConduitWidget;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = iiEasy;
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 26.1;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@@ -1141,7 +1141,7 @@
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = ShareExtension/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = ShareExtension;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "Ask iiEasy";
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@@ -1189,7 +1189,7 @@
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = ShareExtension/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = ShareExtension;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "Ask iiEasy";
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@@ -1234,7 +1234,7 @@
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = ShareExtension/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = ShareExtension;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "Ask iiEasy";
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
|
||||
@@ -1,134 +1 @@
|
||||
{
|
||||
"images": [
|
||||
{
|
||||
"filename": "AppIcon@2x.png",
|
||||
"idiom": "iphone",
|
||||
"scale": "2x",
|
||||
"size": "60x60"
|
||||
},
|
||||
{
|
||||
"filename": "AppIcon@3x.png",
|
||||
"idiom": "iphone",
|
||||
"scale": "3x",
|
||||
"size": "60x60"
|
||||
},
|
||||
{
|
||||
"filename": "AppIcon~ipad.png",
|
||||
"idiom": "ipad",
|
||||
"scale": "1x",
|
||||
"size": "76x76"
|
||||
},
|
||||
{
|
||||
"filename": "AppIcon@2x~ipad.png",
|
||||
"idiom": "ipad",
|
||||
"scale": "2x",
|
||||
"size": "76x76"
|
||||
},
|
||||
{
|
||||
"filename": "AppIcon-83.5@2x~ipad.png",
|
||||
"idiom": "ipad",
|
||||
"scale": "2x",
|
||||
"size": "83.5x83.5"
|
||||
},
|
||||
{
|
||||
"filename": "AppIcon-40@2x.png",
|
||||
"idiom": "iphone",
|
||||
"scale": "2x",
|
||||
"size": "40x40"
|
||||
},
|
||||
{
|
||||
"filename": "AppIcon-40@3x.png",
|
||||
"idiom": "iphone",
|
||||
"scale": "3x",
|
||||
"size": "40x40"
|
||||
},
|
||||
{
|
||||
"filename": "AppIcon-40~ipad.png",
|
||||
"idiom": "ipad",
|
||||
"scale": "1x",
|
||||
"size": "40x40"
|
||||
},
|
||||
{
|
||||
"filename": "AppIcon-40@2x~ipad.png",
|
||||
"idiom": "ipad",
|
||||
"scale": "2x",
|
||||
"size": "40x40"
|
||||
},
|
||||
{
|
||||
"filename": "AppIcon-20@2x.png",
|
||||
"idiom": "iphone",
|
||||
"scale": "2x",
|
||||
"size": "20x20"
|
||||
},
|
||||
{
|
||||
"filename": "AppIcon-20@3x.png",
|
||||
"idiom": "iphone",
|
||||
"scale": "3x",
|
||||
"size": "20x20"
|
||||
},
|
||||
{
|
||||
"filename": "AppIcon-20~ipad.png",
|
||||
"idiom": "ipad",
|
||||
"scale": "1x",
|
||||
"size": "20x20"
|
||||
},
|
||||
{
|
||||
"filename": "AppIcon-20@2x~ipad.png",
|
||||
"idiom": "ipad",
|
||||
"scale": "2x",
|
||||
"size": "20x20"
|
||||
},
|
||||
{
|
||||
"filename": "AppIcon-29.png",
|
||||
"idiom": "iphone",
|
||||
"scale": "1x",
|
||||
"size": "29x29"
|
||||
},
|
||||
{
|
||||
"filename": "AppIcon-29@2x.png",
|
||||
"idiom": "iphone",
|
||||
"scale": "2x",
|
||||
"size": "29x29"
|
||||
},
|
||||
{
|
||||
"filename": "AppIcon-29@3x.png",
|
||||
"idiom": "iphone",
|
||||
"scale": "3x",
|
||||
"size": "29x29"
|
||||
},
|
||||
{
|
||||
"filename": "AppIcon-29~ipad.png",
|
||||
"idiom": "ipad",
|
||||
"scale": "1x",
|
||||
"size": "29x29"
|
||||
},
|
||||
{
|
||||
"filename": "AppIcon-29@2x~ipad.png",
|
||||
"idiom": "ipad",
|
||||
"scale": "2x",
|
||||
"size": "29x29"
|
||||
},
|
||||
{
|
||||
"filename": "AppIcon-60@2x~car.png",
|
||||
"idiom": "car",
|
||||
"scale": "2x",
|
||||
"size": "60x60"
|
||||
},
|
||||
{
|
||||
"filename": "AppIcon-60@3x~car.png",
|
||||
"idiom": "car",
|
||||
"scale": "3x",
|
||||
"size": "60x60"
|
||||
},
|
||||
{
|
||||
"filename": "AppIcon~ios-marketing.png",
|
||||
"idiom": "ios-marketing",
|
||||
"scale": "1x",
|
||||
"size": "1024x1024"
|
||||
}
|
||||
],
|
||||
"info": {
|
||||
"author": "iconkitchen",
|
||||
"version": 1
|
||||
}
|
||||
}
|
||||
{"images":[{"size":"20x20","idiom":"iphone","filename":"Icon-App-20x20@2x.png","scale":"2x"},{"size":"20x20","idiom":"iphone","filename":"Icon-App-20x20@3x.png","scale":"3x"},{"size":"29x29","idiom":"iphone","filename":"Icon-App-29x29@1x.png","scale":"1x"},{"size":"29x29","idiom":"iphone","filename":"Icon-App-29x29@2x.png","scale":"2x"},{"size":"29x29","idiom":"iphone","filename":"Icon-App-29x29@3x.png","scale":"3x"},{"size":"40x40","idiom":"iphone","filename":"Icon-App-40x40@2x.png","scale":"2x"},{"size":"40x40","idiom":"iphone","filename":"Icon-App-40x40@3x.png","scale":"3x"},{"size":"57x57","idiom":"iphone","filename":"Icon-App-57x57@1x.png","scale":"1x"},{"size":"57x57","idiom":"iphone","filename":"Icon-App-57x57@2x.png","scale":"2x"},{"size":"60x60","idiom":"iphone","filename":"Icon-App-60x60@2x.png","scale":"2x"},{"size":"60x60","idiom":"iphone","filename":"Icon-App-60x60@3x.png","scale":"3x"},{"size":"20x20","idiom":"ipad","filename":"Icon-App-20x20@1x.png","scale":"1x"},{"size":"20x20","idiom":"ipad","filename":"Icon-App-20x20@2x.png","scale":"2x"},{"size":"29x29","idiom":"ipad","filename":"Icon-App-29x29@1x.png","scale":"1x"},{"size":"29x29","idiom":"ipad","filename":"Icon-App-29x29@2x.png","scale":"2x"},{"size":"40x40","idiom":"ipad","filename":"Icon-App-40x40@1x.png","scale":"1x"},{"size":"40x40","idiom":"ipad","filename":"Icon-App-40x40@2x.png","scale":"2x"},{"size":"50x50","idiom":"ipad","filename":"Icon-App-50x50@1x.png","scale":"1x"},{"size":"50x50","idiom":"ipad","filename":"Icon-App-50x50@2x.png","scale":"2x"},{"size":"72x72","idiom":"ipad","filename":"Icon-App-72x72@1x.png","scale":"1x"},{"size":"72x72","idiom":"ipad","filename":"Icon-App-72x72@2x.png","scale":"2x"},{"size":"76x76","idiom":"ipad","filename":"Icon-App-76x76@1x.png","scale":"1x"},{"size":"76x76","idiom":"ipad","filename":"Icon-App-76x76@2x.png","scale":"2x"},{"size":"83.5x83.5","idiom":"ipad","filename":"Icon-App-83.5x83.5@2x.png","scale":"2x"},{"size":"1024x1024","idiom":"ios-marketing","filename":"Icon-App-1024x1024@1x.png","scale":"1x"}],"info":{"version":1,"author":"xcode"}}
|
||||
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 349 B |
|
After Width: | Height: | Size: 701 B |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 489 B |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 701 B |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 942 B |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
@@ -11,7 +11,7 @@
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>Conduit</string>
|
||||
<string>iiEasy</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
@@ -32,7 +32,7 @@
|
||||
<string>zh-Hant</string>
|
||||
</array>
|
||||
<key>CFBundleName</key>
|
||||
<string>conduit</string>
|
||||
<string>iiEasy</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
@@ -64,6 +64,8 @@
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(FLUTTER_BUILD_NUMBER)</string>
|
||||
<key>AppGroupId</key>
|
||||
<string>group.app.cogwheel.conduit</string>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<false/>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
@@ -76,13 +78,13 @@
|
||||
<true/>
|
||||
</dict>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>Conduit uses the camera to take photos or videos you choose to share in chats. For example, you can snap a photo of a document and attach it to a message.</string>
|
||||
<string>iiEasy uses the camera to take photos or videos you choose to share in chats. For example, you can snap a photo of a document and attach it to a message.</string>
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string>Conduit uses the microphone to record voice messages and enable voice-to-text in chats. For example, when you hold the mic button in a conversation, we capture your speech to send as an audio message or transcript.</string>
|
||||
<string>iiEasy uses the microphone to record voice messages and enable voice-to-text in chats. For example, when you hold the mic button in a conversation, we capture your speech to send as an audio message or transcript.</string>
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
<string>Conduit needs access to your photo library so you can select existing images or videos to share in chats. For example, you can pick a screenshot to include in a conversation.</string>
|
||||
<string>iiEasy needs access to your photo library so you can select existing images or videos to share in chats. For example, you can pick a screenshot to include in a conversation.</string>
|
||||
<key>NSSpeechRecognitionUsageDescription</key>
|
||||
<string>Conduit uses on-device speech recognition so you can dictate messages hands‑free. Your speech is converted to text on your device when available.</string>
|
||||
<string>iiEasy uses on-device speech recognition so you can dictate messages hands‑free. Your speech is converted to text on your device when available.</string>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true/>
|
||||
<key>UIBackgroundModes</key>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* Localized versions of Info.plist keys */
|
||||
CFBundleDisplayName = "Conduit";
|
||||
CFBundleDisplayName = "iiEasy";
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* Localized versions of Info.plist keys */
|
||||
CFBundleDisplayName = "Conduit";
|
||||
CFBundleDisplayName = "iiEasy";
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* Localized versions of Info.plist keys */
|
||||
CFBundleDisplayName = "Conduit";
|
||||
CFBundleDisplayName = "iiEasy";
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* Localized versions of Info.plist keys */
|
||||
CFBundleDisplayName = "Conduit";
|
||||
CFBundleDisplayName = "iiEasy";
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* Localized versions of Info.plist keys */
|
||||
CFBundleDisplayName = "Conduit";
|
||||
CFBundleDisplayName = "iiEasy";
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* Localized versions of Info.plist keys */
|
||||
CFBundleDisplayName = "Conduit";
|
||||
CFBundleDisplayName = "iiEasy";
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* Localized versions of Info.plist keys */
|
||||
CFBundleDisplayName = "Conduit";
|
||||
CFBundleDisplayName = "iiEasy";
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* Localized versions of Info.plist keys */
|
||||
CFBundleDisplayName = "Conduit";
|
||||
CFBundleDisplayName = "iiEasy";
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* Localized versions of Info.plist keys */
|
||||
CFBundleDisplayName = "Conduit";
|
||||
CFBundleDisplayName = "iiEasy";
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* Localized versions of Info.plist keys */
|
||||
CFBundleDisplayName = "Conduit";
|
||||
CFBundleDisplayName = "iiEasy";
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -3,10 +3,9 @@
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>Ask Conduit</string>
|
||||
<!-- Uncomment below lines if you want to use a custom group id rather than the default. Set it in Build Settings -> User-Defined -->
|
||||
<!-- <key>AppGroupId</key>
|
||||
<string>$(CUSTOM_GROUP_ID)</string> -->
|
||||
<string>Ask iiEasy</string>
|
||||
<key>AppGroupId</key>
|
||||
<string>group.app.cogwheel.conduit</string>
|
||||
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(FLUTTER_BUILD_NUMBER)</string>
|
||||
|
||||
@@ -1 +1 @@
|
||||
Conduit: OpenWebUI Client
|
||||
iiEasyWeb
|
||||
|
||||
@@ -1 +1 @@
|
||||
Conduit: OpenWebUI Client
|
||||
iiEasyWeb
|
||||
|
||||
@@ -1 +1 @@
|
||||
Conduit: OpenWebUI Client
|
||||
iiEasyWeb
|
||||
|
||||
@@ -1 +1 @@
|
||||
Conduit: Cliente OpenWebUI
|
||||
iiEasyWeb
|
||||
|
||||
@@ -1 +1 @@
|
||||
Conduit : Client OpenWebUI
|
||||
iiEasyWeb
|
||||
|
||||
@@ -1 +1 @@
|
||||
Conduit: Client OpenWebUI
|
||||
iiEasyWeb
|
||||
|
||||
@@ -1 +1 @@
|
||||
Conduit: OpenWebUI 클라이언트
|
||||
iiEasyWeb
|
||||
|
||||
@@ -1 +1 @@
|
||||
Conduit: OpenWebUI Client
|
||||
iiEasyWeb
|
||||
|
||||
@@ -1 +1 @@
|
||||
Conduit: Клиент OpenWebUI
|
||||
iiEasyWeb
|
||||
|
||||
@@ -1 +1 @@
|
||||
Conduit: OpenWebUI 客户端
|
||||
iiEasyWeb
|
||||
|
||||
@@ -1 +1 @@
|
||||
Conduit
|
||||
iiEasyWeb
|
||||
|
||||
@@ -226,6 +226,73 @@ class AuthStateManager extends _$AuthStateManager {
|
||||
// Fast path: trust token format to avoid blocking startup on network
|
||||
final formatOk = _isValidTokenFormat(token);
|
||||
if (formatOk) {
|
||||
// Network readiness gate: Wait for API to be reachable before
|
||||
// transitioning to authenticated state. This prevents race conditions
|
||||
// on cold starts with Cloudflare tunnels where the tunnel connection
|
||||
// may not be established yet.
|
||||
final apiReady = await _waitForApiReadiness();
|
||||
if (!apiReady) {
|
||||
DebugLogger.auth(
|
||||
'API not reachable on cold start - keeping loading state',
|
||||
);
|
||||
// Keep loading state and retry via silent login if we have creds
|
||||
final hasCreds = await storage.hasCredentials();
|
||||
if (hasCreds) {
|
||||
DebugLogger.auth(
|
||||
'Has credentials - attempting silent login after API ready',
|
||||
);
|
||||
// Schedule a delayed retry that will wait for network.
|
||||
// Allow time for network stack to stabilize after initial failure.
|
||||
unawaited(
|
||||
Future.delayed(const Duration(milliseconds: 500), () async {
|
||||
if (!ref.mounted) return;
|
||||
try {
|
||||
final retryReady = await _waitForApiReadiness(
|
||||
timeout: const Duration(seconds: 10),
|
||||
);
|
||||
if (!ref.mounted) return;
|
||||
if (retryReady) {
|
||||
await _performSilentLogin();
|
||||
} else {
|
||||
_update(
|
||||
(current) => current.copyWith(
|
||||
status: AuthStatus.error,
|
||||
error: 'Unable to connect to server',
|
||||
isLoading: false,
|
||||
),
|
||||
);
|
||||
}
|
||||
} catch (e, stack) {
|
||||
if (!ref.mounted) return;
|
||||
DebugLogger.error(
|
||||
'delayed-retry-failed',
|
||||
scope: 'auth/state',
|
||||
error: e,
|
||||
stackTrace: stack,
|
||||
);
|
||||
_update(
|
||||
(current) => current.copyWith(
|
||||
status: AuthStatus.error,
|
||||
error: 'Connection retry failed',
|
||||
isLoading: false,
|
||||
),
|
||||
);
|
||||
}
|
||||
}),
|
||||
);
|
||||
return;
|
||||
}
|
||||
// No credentials - show error
|
||||
_update(
|
||||
(current) => current.copyWith(
|
||||
status: AuthStatus.error,
|
||||
error: 'Unable to connect to server',
|
||||
isLoading: false,
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
_update(
|
||||
(current) => current.copyWith(
|
||||
status: AuthStatus.authenticated,
|
||||
@@ -652,6 +719,61 @@ class AuthStateManager extends _$AuthStateManager {
|
||||
}
|
||||
}
|
||||
|
||||
/// Wait for the API to be reachable (network readiness gate).
|
||||
///
|
||||
/// On cold starts with Cloudflare tunnels or other proxy setups, the network
|
||||
/// connection may not be established immediately. This method performs a
|
||||
/// health check with retries to ensure we don't show the wrong screen due to
|
||||
/// a race condition between auth state initialization and network readiness.
|
||||
///
|
||||
/// Returns true if the API is reachable within the timeout, false otherwise.
|
||||
Future<bool> _waitForApiReadiness({
|
||||
Duration timeout = const Duration(seconds: 3),
|
||||
Duration retryDelay = const Duration(milliseconds: 300),
|
||||
}) async {
|
||||
final stopwatch = Stopwatch()..start();
|
||||
|
||||
// First ensure the API service provider is available
|
||||
await _ensureApiServiceAvailable(timeout: const Duration(seconds: 1));
|
||||
|
||||
while (stopwatch.elapsed < timeout) {
|
||||
if (!ref.mounted) return false;
|
||||
|
||||
final api = ref.read(apiServiceProvider);
|
||||
if (api == null) {
|
||||
await Future.delayed(retryDelay);
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
// Use checkHealth which hits the /health endpoint
|
||||
final healthy = await api.checkHealth();
|
||||
if (healthy) {
|
||||
DebugLogger.auth(
|
||||
'API readiness confirmed in ${stopwatch.elapsedMilliseconds}ms',
|
||||
);
|
||||
return true;
|
||||
}
|
||||
} catch (e) {
|
||||
DebugLogger.auth(
|
||||
'API readiness check failed (${stopwatch.elapsedMilliseconds}ms): $e',
|
||||
);
|
||||
}
|
||||
|
||||
// Wait before retrying
|
||||
if (stopwatch.elapsed + retryDelay < timeout) {
|
||||
await Future.delayed(retryDelay);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
DebugLogger.auth(
|
||||
'API readiness timed out after ${stopwatch.elapsedMilliseconds}ms',
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Perform silent auto-login with saved credentials
|
||||
Future<bool> silentLogin() async {
|
||||
// Coalesce concurrent calls (e.g., UI + interceptor retry)
|
||||
|
||||
@@ -794,15 +794,7 @@ class ApiService {
|
||||
DebugLogger.log('settings-ok', scope: 'api/user-settings');
|
||||
|
||||
final data = response.data;
|
||||
if (data is! Map<String, dynamic>) {
|
||||
DebugLogger.warning(
|
||||
'settings-format',
|
||||
scope: 'api/user-settings',
|
||||
data: {'type': data.runtimeType},
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (data is Map<String, dynamic>) {
|
||||
// Extract default model from ui.models array
|
||||
final ui = data['ui'];
|
||||
if (ui is Map<String, dynamic>) {
|
||||
@@ -818,21 +810,51 @@ class ApiService {
|
||||
return defaultModel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DebugLogger.warning('default-model-missing', scope: 'api/user-settings');
|
||||
return null;
|
||||
// Fallback: user has no default model configured, pick first available
|
||||
// This fixes issue #353 where secondary accounts couldn't send messages
|
||||
DebugLogger.log(
|
||||
'default-model-fallback',
|
||||
scope: 'api/user-settings',
|
||||
);
|
||||
return _getFirstAvailableModelId();
|
||||
} catch (e) {
|
||||
DebugLogger.error(
|
||||
'default-model-error',
|
||||
scope: 'api/user-settings',
|
||||
error: e,
|
||||
);
|
||||
// Do not call admin-only configs endpoint here; let the caller
|
||||
// handle fallback (e.g., first available model from /api/models).
|
||||
return null;
|
||||
// Attempt fallback even on error
|
||||
return _getFirstAvailableModelId();
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the ID of the first available model, or null if none available.
|
||||
///
|
||||
/// Used as a fallback when user has no default model configured.
|
||||
Future<String?> _getFirstAvailableModelId() async {
|
||||
try {
|
||||
final models = await getModels();
|
||||
if (models.isNotEmpty) {
|
||||
final fallbackId = models.first.id;
|
||||
DebugLogger.log(
|
||||
'default-model-fallback-selected',
|
||||
scope: 'api/user-settings',
|
||||
data: {'id': fallbackId},
|
||||
);
|
||||
return fallbackId;
|
||||
}
|
||||
} catch (e) {
|
||||
DebugLogger.error(
|
||||
'default-model-fallback-failed',
|
||||
scope: 'api/user-settings',
|
||||
error: e,
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Conversations - Updated to use correct OpenWebUI API
|
||||
Future<List<Conversation>> getConversations({int? limit, int? skip}) async {
|
||||
final pinnedFuture = _fetchChatCollection(
|
||||
@@ -1498,7 +1520,12 @@ class ApiService {
|
||||
Future<Map<String, dynamic>> getUserSettings() async {
|
||||
_traceApi('Fetching user settings');
|
||||
final response = await _dio.get('/api/v1/users/user/settings');
|
||||
return response.data as Map<String, dynamic>;
|
||||
final data = response.data;
|
||||
// Handle null response from server (happens for new users with no settings)
|
||||
if (data is Map<String, dynamic>) {
|
||||
return data;
|
||||
}
|
||||
return <String, dynamic>{};
|
||||
}
|
||||
|
||||
Future<void> updateUserSettings(Map<String, dynamic> settings) async {
|
||||
|
||||
@@ -405,12 +405,7 @@ class _AuthenticationPageState extends ConsumerState<AuthenticationPage> {
|
||||
Widget _buildWelcomeSection() {
|
||||
return Column(
|
||||
children: [
|
||||
BrandService.createBrandIcon(
|
||||
size: 48,
|
||||
useGradient: false,
|
||||
addShadow: false,
|
||||
context: context,
|
||||
),
|
||||
BrandService.createLogoImage(size: 48, context: context),
|
||||
const SizedBox(height: Spacing.lg),
|
||||
Text(
|
||||
AppLocalizations.of(context)!.signIn,
|
||||
|
||||
@@ -604,12 +604,7 @@ class _ServerConnectionPageState extends ConsumerState<ServerConnectionPage> {
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
// Brand logo
|
||||
BrandService.createBrandIcon(
|
||||
size: 56,
|
||||
useGradient: false,
|
||||
addShadow: false,
|
||||
context: context,
|
||||
),
|
||||
BrandService.createLogoImage(size: 56, context: context),
|
||||
// Reviewer mode badge
|
||||
if (reviewerMode)
|
||||
Positioned(
|
||||
|
||||
@@ -149,6 +149,9 @@ class TextToSpeechService {
|
||||
Future<void> dispose() async {
|
||||
await _eventSubscription?.cancel();
|
||||
_eventSubscription = null;
|
||||
|
||||
// Reset the singleton state for next session
|
||||
await TtsManager.instance.reset();
|
||||
}
|
||||
|
||||
/// Updates TTS settings.
|
||||
|
||||
@@ -376,6 +376,28 @@ class TtsManager {
|
||||
}
|
||||
}
|
||||
|
||||
/// Resets the manager state for a new session.
|
||||
///
|
||||
/// Call this between voice calls to ensure clean state. This clears
|
||||
/// playback buffers and resets session tracking without destroying
|
||||
/// the singleton instance.
|
||||
Future<void> reset() async {
|
||||
await stop();
|
||||
|
||||
// Reset playback state
|
||||
_resetPlaybackState();
|
||||
_activeSession = null;
|
||||
_sessionCounter = 0;
|
||||
|
||||
// Reset server audio buffer
|
||||
_serverAudioBuffer.clear();
|
||||
_serverWaitingForNext = false;
|
||||
|
||||
// Reset cached voice defaults so they're refetched if needed
|
||||
_serverDefaultVoice = null;
|
||||
_serverDefaultVoiceFuture = null;
|
||||
}
|
||||
|
||||
/// Disposes the manager and releases resources.
|
||||
Future<void> dispose() async {
|
||||
await stop();
|
||||
|
||||
@@ -395,7 +395,7 @@ class VoiceCallService {
|
||||
_activeAssistantMessageId = null;
|
||||
_responseCompleted = false;
|
||||
_listeningSuspendedForSpeech = false;
|
||||
_resetServerAudio(stopPlayback: true);
|
||||
await _resetServerAudio(stopPlayback: true);
|
||||
|
||||
if (_pauseReasons.isNotEmpty) {
|
||||
_listeningPaused = true;
|
||||
@@ -584,7 +584,11 @@ class VoiceCallService {
|
||||
_speechQueue.clear();
|
||||
_enqueuedSentenceCount = 0;
|
||||
_responseCompleted = false;
|
||||
_resetServerAudio(stopPlayback: true);
|
||||
// Fire-and-forget: This is a synchronous event handler where blocking
|
||||
// would delay socket event processing. The audio player stop is fast
|
||||
// and race conditions are acceptable here since new playback will
|
||||
// wait for this session to complete anyway.
|
||||
unawaited(_resetServerAudio(stopPlayback: true));
|
||||
if (_isSpeaking) {
|
||||
_isSpeaking = false;
|
||||
unawaited(_tts.stop());
|
||||
@@ -749,14 +753,14 @@ class VoiceCallService {
|
||||
_maybeResumeListeningAfterSpeech();
|
||||
}
|
||||
|
||||
void _resetServerAudio({bool stopPlayback = false}) {
|
||||
Future<void> _resetServerAudio({bool stopPlayback = false}) async {
|
||||
_serverAudioBuffer.clear();
|
||||
_pendingServerAudioFetches = 0;
|
||||
_serverAudioSession++;
|
||||
_nextServerChunkId = 0;
|
||||
_nextServerPlaybackId = 0;
|
||||
if (stopPlayback) {
|
||||
unawaited(_serverAudioPlayer.stop());
|
||||
await _serverAudioPlayer.stop();
|
||||
_isSpeaking = false;
|
||||
}
|
||||
_serverPipelineActive = false;
|
||||
@@ -819,7 +823,7 @@ class VoiceCallService {
|
||||
if (_isDisposed) return;
|
||||
_isSpeaking = false;
|
||||
_speechQueue.clear();
|
||||
_resetServerAudio(stopPlayback: true);
|
||||
unawaited(_resetServerAudio(stopPlayback: true));
|
||||
_listeningSuspendedForSpeech = false;
|
||||
_updateState(VoiceCallState.error);
|
||||
// Try to recover by restarting listening
|
||||
@@ -865,7 +869,7 @@ class VoiceCallService {
|
||||
_listeningSuspendedForSpeech = false;
|
||||
_activeAssistantMessageId = null;
|
||||
_isSpeaking = false;
|
||||
_resetServerAudio(stopPlayback: true);
|
||||
await _resetServerAudio(stopPlayback: true);
|
||||
_updateState(VoiceCallState.disconnected);
|
||||
}
|
||||
|
||||
@@ -911,7 +915,7 @@ class VoiceCallService {
|
||||
_enqueuedSentenceCount = 0;
|
||||
_responseCompleted = false;
|
||||
_listeningSuspendedForSpeech = false;
|
||||
_resetServerAudio(stopPlayback: true);
|
||||
await _resetServerAudio(stopPlayback: true);
|
||||
await _tts.stop();
|
||||
_isSpeaking = false;
|
||||
_accumulatedResponse = '';
|
||||
@@ -985,7 +989,7 @@ class VoiceCallService {
|
||||
_enqueuedSentenceCount = 0;
|
||||
_responseCompleted = false;
|
||||
_listeningSuspendedForSpeech = false;
|
||||
_resetServerAudio(stopPlayback: true);
|
||||
unawaited(_resetServerAudio(stopPlayback: true));
|
||||
pauseListening(reason: VoiceCallPauseReason.mute);
|
||||
} else {
|
||||
resumeListening(reason: VoiceCallPauseReason.mute);
|
||||
@@ -1006,7 +1010,7 @@ class VoiceCallService {
|
||||
_callKitEventSubscription = null;
|
||||
_socketSubscription?.dispose();
|
||||
|
||||
_voiceInput.dispose();
|
||||
await _voiceInput.dispose();
|
||||
await _tts.dispose();
|
||||
await _serverAudioStateSub?.cancel();
|
||||
await _serverAudioPlayer.dispose();
|
||||
@@ -1029,7 +1033,7 @@ class VoiceCallService {
|
||||
}
|
||||
}
|
||||
|
||||
@Riverpod(keepAlive: true)
|
||||
@riverpod
|
||||
VoiceCallService voiceCallService(Ref ref) {
|
||||
final voiceInput = ref.watch(voiceInputServiceProvider);
|
||||
final api = ref.watch(apiServiceProvider);
|
||||
|
||||
@@ -1026,12 +1026,12 @@ class VoiceInputService {
|
||||
});
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
stopListening();
|
||||
unawaited(_disposeVadHandler());
|
||||
unawaited(_microphonePermissionProbe.dispose());
|
||||
Future<void> dispose() async {
|
||||
await stopListening();
|
||||
await _disposeVadHandler();
|
||||
await _microphonePermissionProbe.dispose();
|
||||
try {
|
||||
_speech.stop();
|
||||
await _speech.stop();
|
||||
} catch (_) {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +1,18 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import '../../../shared/theme/theme_extensions.dart';
|
||||
import '../../../shared/widgets/ii_easy_loading_logo.dart';
|
||||
|
||||
class SplashLauncherPage extends StatelessWidget {
|
||||
const SplashLauncherPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final isDark = context.conduitTheme.isDark;
|
||||
return Scaffold(
|
||||
backgroundColor: context.conduitTheme.surfaceBackground,
|
||||
body: Center(
|
||||
child: SizedBox(
|
||||
width: 28,
|
||||
height: 28,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2.5,
|
||||
valueColor: AlwaysStoppedAnimation<Color>(
|
||||
context.conduitTheme.loadingIndicator,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
backgroundColor: isDark
|
||||
? const Color(0xFF0A0A0A)
|
||||
: Colors.white,
|
||||
body: IiEasyLoadingLogo(isDark: isDark),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,9 +35,6 @@ import '../../../shared/widgets/model_avatar.dart';
|
||||
/// Profile page (You tab) showing user info and main actions
|
||||
/// Enhanced with production-grade design tokens for better cohesion
|
||||
class ProfilePage extends ConsumerWidget {
|
||||
static const _githubSponsorsUrl = 'https://github.com/sponsors/cogwheel0';
|
||||
static const _buyMeACoffeeUrl = 'https://www.buymeacoffee.com/cogwheel0';
|
||||
|
||||
const ProfilePage({super.key});
|
||||
|
||||
@override
|
||||
@@ -117,113 +114,10 @@ class ProfilePage extends ConsumerWidget {
|
||||
_buildProfileHeader(context, userData, api),
|
||||
const SizedBox(height: Spacing.xl),
|
||||
_buildAccountSection(context, ref),
|
||||
const SizedBox(height: Spacing.xl),
|
||||
_buildSupportSection(context),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSupportSection(BuildContext context) {
|
||||
final theme = context.conduitTheme;
|
||||
final textTheme =
|
||||
theme.bodySmall?.copyWith(
|
||||
color: theme.sidebarForeground.withValues(alpha: 0.75),
|
||||
) ??
|
||||
TextStyle(color: theme.sidebarForeground.withValues(alpha: 0.75));
|
||||
|
||||
final supportTiles = [
|
||||
_buildSupportOption(
|
||||
context,
|
||||
icon: UiUtils.platformIcon(
|
||||
ios: CupertinoIcons.gift,
|
||||
android: Icons.coffee,
|
||||
),
|
||||
title: AppLocalizations.of(context)!.buyMeACoffeeTitle,
|
||||
subtitle: AppLocalizations.of(context)!.buyMeACoffeeSubtitle,
|
||||
url: _buyMeACoffeeUrl,
|
||||
color: theme.warning,
|
||||
),
|
||||
_buildSupportOption(
|
||||
context,
|
||||
icon: UiUtils.platformIcon(
|
||||
ios: CupertinoIcons.heart,
|
||||
android: Icons.favorite_border,
|
||||
),
|
||||
title: AppLocalizations.of(context)!.githubSponsorsTitle,
|
||||
subtitle: AppLocalizations.of(context)!.githubSponsorsSubtitle,
|
||||
url: _githubSponsorsUrl,
|
||||
color: theme.success,
|
||||
),
|
||||
];
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
AppLocalizations.of(context)!.supportConduit,
|
||||
style: theme.headingSmall?.copyWith(color: theme.sidebarForeground),
|
||||
),
|
||||
const SizedBox(height: Spacing.xs),
|
||||
Text(
|
||||
AppLocalizations.of(context)!.supportConduitSubtitle,
|
||||
style: textTheme,
|
||||
),
|
||||
const SizedBox(height: Spacing.sm),
|
||||
for (var i = 0; i < supportTiles.length; i++) ...[
|
||||
supportTiles[i],
|
||||
if (i != supportTiles.length - 1) const SizedBox(height: Spacing.md),
|
||||
],
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSupportOption(
|
||||
BuildContext context, {
|
||||
required IconData icon,
|
||||
required String title,
|
||||
required String subtitle,
|
||||
required String url,
|
||||
required Color color,
|
||||
}) {
|
||||
final theme = context.conduitTheme;
|
||||
return _ProfileSettingTile(
|
||||
onTap: () => _openExternalLink(context, url),
|
||||
leading: _buildIconBadge(context, icon, color: color),
|
||||
title: title,
|
||||
subtitle: subtitle,
|
||||
trailing: Icon(
|
||||
UiUtils.platformIcon(
|
||||
ios: CupertinoIcons.arrow_up_right,
|
||||
android: Icons.open_in_new,
|
||||
),
|
||||
color: theme.iconSecondary,
|
||||
size: IconSize.small,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _openExternalLink(BuildContext context, String url) async {
|
||||
try {
|
||||
final launched = await launchUrlString(
|
||||
url,
|
||||
mode: LaunchMode.externalApplication,
|
||||
);
|
||||
|
||||
if (!launched && context.mounted) {
|
||||
UiUtils.showMessage(
|
||||
context,
|
||||
AppLocalizations.of(context)!.errorMessage,
|
||||
);
|
||||
}
|
||||
} on PlatformException catch (_) {
|
||||
if (!context.mounted) return;
|
||||
UiUtils.showMessage(context, AppLocalizations.of(context)!.errorMessage);
|
||||
} catch (_) {
|
||||
if (!context.mounted) return;
|
||||
UiUtils.showMessage(context, AppLocalizations.of(context)!.errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildProfileHeader(
|
||||
BuildContext context,
|
||||
dynamic user,
|
||||
@@ -551,8 +445,8 @@ class ProfilePage extends ConsumerWidget {
|
||||
try {
|
||||
final info = await PackageInfo.fromPlatform();
|
||||
// Update dialog with dynamic version each time
|
||||
// GitHub repo URL source of truth
|
||||
const githubUrl = 'https://github.com/cogwheel0/conduit';
|
||||
// Developer / app info URL
|
||||
const githubUrl = 'https://iiEasy.ru';
|
||||
|
||||
if (!context.mounted) return;
|
||||
await showDialog<void>(
|
||||
@@ -606,6 +500,13 @@ class ProfilePage extends ConsumerWidget {
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: Spacing.sm),
|
||||
Text(
|
||||
'Based on Conduit',
|
||||
style: ctx.conduitTheme.bodySmall?.copyWith(
|
||||
color: ctx.sidebarTheme.foreground.withValues(alpha: 0.6),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"@@locale": "de",
|
||||
"appTitle": "Conduit",
|
||||
"appTitle": "iiEasy",
|
||||
"appSlogan": "Zukunft. Einfach.",
|
||||
"retry": "Erneut versuchen",
|
||||
"back": "Zurück",
|
||||
"you": "Du",
|
||||
@@ -15,8 +16,8 @@
|
||||
"description": "Untertitel mit den verfügbaren Aktionen, wenn der Server nicht erreichbar ist"
|
||||
},
|
||||
"account": "Konto",
|
||||
"supportConduit": "Conduit unterstützen",
|
||||
"supportConduitSubtitle": "Hilf, die Weiterentwicklung und neue Funktionen zu finanzieren.",
|
||||
"supportConduit": "iiEasy unterstützen",
|
||||
"supportConduitSubtitle": "Unterstütze iiEasy durch Finanzierung der Weiterentwicklung.",
|
||||
"githubSponsorsTitle": "GitHub Sponsors",
|
||||
"githubSponsorsSubtitle": "Werde monatliche*r Sponsor*in und unterstütze die Roadmap.",
|
||||
"buyMeACoffeeTitle": "Buy Me a Coffee",
|
||||
@@ -50,7 +51,7 @@
|
||||
"password": "Passwort",
|
||||
"signInWithToken": "Mit Token anmelden",
|
||||
"connectToServer": "Mit Server verbinden",
|
||||
"enterServerAddress": "Gib die Adresse deines Open-WebUI-Servers ein, um zu beginnen",
|
||||
"enterServerAddress": "Gib die Adresse deines iiEasyWeb-Servers ein, um zu beginnen",
|
||||
"serverUrl": "Server-URL",
|
||||
"serverUrlHint": "https://dein-server.com",
|
||||
"enterServerUrlSemantic": "Gib deine Server-URL oder IP-Adresse ein",
|
||||
@@ -116,7 +117,7 @@
|
||||
"voicePromptTapStart": "Tippe auf \"Starten\", um zu beginnen",
|
||||
"voiceActionStop": "Stopp",
|
||||
"voiceActionStart": "Starten",
|
||||
"messageHintText": "Frag Conduit",
|
||||
"messageHintText": "Frag iiEasy",
|
||||
"stopGenerating": "Generierung stoppen",
|
||||
"codeCopiedToClipboard": "Code in die Zwischenablage kopiert.",
|
||||
"send": "Senden",
|
||||
@@ -204,7 +205,7 @@
|
||||
"deleteFolderMessage": "Dieser Ordner und seine Zuordnungen werden entfernt.",
|
||||
"failedToDeleteFolder": "Ordner konnte nicht gelöscht werden",
|
||||
"aboutApp": "Über",
|
||||
"aboutAppSubtitle": "Conduit Informationen und Links",
|
||||
"aboutAppSubtitle": "iiEasy Informationen und Links",
|
||||
"web": "Web",
|
||||
"imageGen": "Bild-Gen",
|
||||
"pinned": "Angeheftet",
|
||||
@@ -280,7 +281,7 @@
|
||||
"themeLight": "Hell",
|
||||
"currentlyUsingDarkTheme": "Aktuell dunkles Thema",
|
||||
"currentlyUsingLightTheme": "Aktuell helles Thema",
|
||||
"aboutConduit": "Über Conduit",
|
||||
"aboutConduit": "Über iiEasy",
|
||||
"versionLabel": "Version: {version} ({build})",
|
||||
"@versionLabel": {
|
||||
"placeholders": {
|
||||
@@ -317,7 +318,7 @@
|
||||
"androidAssistantTitle": "Android digital assistant",
|
||||
"androidAssistantDescription": "Choose what happens when you trigger the Android digital assistant.",
|
||||
"androidAssistantOverlayOption": "Show quick overlay (default)",
|
||||
"androidAssistantNewChatOption": "Open Conduit with a new chat",
|
||||
"androidAssistantNewChatOption": "Open iiEasy with a new chat",
|
||||
"androidAssistantVoiceCallOption": "Start a voice call",
|
||||
"sttSettings": "Sprache zu Text",
|
||||
"sttEngineLabel": "Erkennungs-Engine",
|
||||
@@ -650,11 +651,11 @@
|
||||
"@nextLabel": {
|
||||
"description": "Label for navigating to the next item."
|
||||
},
|
||||
"themePaletteConduitLabel": "Conduit",
|
||||
"themePaletteConduitLabel": "iiEasy",
|
||||
"@themePaletteConduitLabel": {
|
||||
"description": "Palette name for the default Conduit theme."
|
||||
},
|
||||
"themePaletteConduitDescription": "Schlichtes neutrales Design für Conduit.",
|
||||
"themePaletteConduitDescription": "Schlichtes neutrales Design für iiEasy.",
|
||||
"@themePaletteConduitDescription": {
|
||||
"description": "Description of the Conduit palette."
|
||||
},
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
{
|
||||
"@@locale": "en",
|
||||
"appTitle": "Conduit",
|
||||
"appTitle": "iiEasy",
|
||||
"@appTitle": {
|
||||
"description": "Application name displayed in the app and OS UI."
|
||||
},
|
||||
"appSlogan": "Future. Simple.",
|
||||
"@appSlogan": {
|
||||
"description": "Brand slogan shown on loading/splash."
|
||||
},
|
||||
"retry": "Retry",
|
||||
"@retry": {
|
||||
"description": "Button label to try an action again."
|
||||
@@ -36,11 +40,11 @@
|
||||
"@account": {
|
||||
"description": "Section header for account-related options."
|
||||
},
|
||||
"supportConduit": "Support Conduit",
|
||||
"supportConduit": "Support iiEasy",
|
||||
"@supportConduit": {
|
||||
"description": "Section header inviting the user to financially support the project."
|
||||
},
|
||||
"supportConduitSubtitle": "Keep Conduit independent by funding ongoing development.",
|
||||
"supportConduitSubtitle": "Keep iiEasy independent by funding ongoing development.",
|
||||
"@supportConduitSubtitle": {
|
||||
"description": "Subtitle explaining why donations are helpful."
|
||||
},
|
||||
@@ -232,7 +236,7 @@
|
||||
"@connectToServer": {
|
||||
"description": "Call-to-action button for server connection."
|
||||
},
|
||||
"enterServerAddress": "Enter your Open-WebUI server address to get started",
|
||||
"enterServerAddress": "Enter your iiEasyWeb server address to get started",
|
||||
"@enterServerAddress": {
|
||||
"description": "Instruction telling user to provide server URL to begin."
|
||||
},
|
||||
@@ -554,7 +558,7 @@
|
||||
"@voiceCallErrorHelp": {
|
||||
"description": "Guidance shown when the voice call encounters an error."
|
||||
},
|
||||
"messageHintText": "Ask Conduit",
|
||||
"messageHintText": "Ask iiEasy",
|
||||
"@messageHintText": {
|
||||
"description": "Short placeholder text in the message input."
|
||||
},
|
||||
@@ -920,7 +924,7 @@
|
||||
"@aboutApp": {
|
||||
"description": "Settings tile title to view app information."
|
||||
},
|
||||
"aboutAppSubtitle": "Conduit information and links",
|
||||
"aboutAppSubtitle": "iiEasy information and links",
|
||||
"@aboutAppSubtitle": {
|
||||
"description": "Subtitle/description for the About section."
|
||||
},
|
||||
@@ -1173,11 +1177,11 @@
|
||||
"@themePalette": {
|
||||
"description": "Title for selecting the app color palette."
|
||||
},
|
||||
"themePaletteConduitLabel": "Conduit",
|
||||
"themePaletteConduitLabel": "iiEasy",
|
||||
"@themePaletteConduitLabel": {
|
||||
"description": "Palette name for the default Conduit theme."
|
||||
},
|
||||
"themePaletteConduitDescription": "Clean neutral theme designed for Conduit.",
|
||||
"themePaletteConduitDescription": "Clean neutral theme designed for iiEasy.",
|
||||
"@themePaletteConduitDescription": {
|
||||
"description": "Description of the Conduit palette."
|
||||
},
|
||||
@@ -1225,7 +1229,7 @@
|
||||
"@currentlyUsingLightTheme": {
|
||||
"description": "Status text indicating light theme is active."
|
||||
},
|
||||
"aboutConduit": "About Conduit",
|
||||
"aboutConduit": "About iiEasy",
|
||||
"@aboutConduit": {
|
||||
"description": "Dialog title for app information."
|
||||
},
|
||||
@@ -1241,7 +1245,7 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"githubRepository": "GitHub Repository",
|
||||
"githubRepository": "iiEasy.ru",
|
||||
"@githubRepository": {
|
||||
"description": "Link label pointing to the app repository."
|
||||
},
|
||||
@@ -1325,7 +1329,7 @@
|
||||
"@androidAssistantOverlayOption": {
|
||||
"description": "Option label for keeping the current assistant overlay."
|
||||
},
|
||||
"androidAssistantNewChatOption": "Open Conduit with a new chat",
|
||||
"androidAssistantNewChatOption": "Open iiEasy with a new chat",
|
||||
"@androidAssistantNewChatOption": {
|
||||
"description": "Option label for opening the app to a fresh chat from the assistant trigger."
|
||||
},
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"@@locale": "es",
|
||||
"appTitle": "Conduit",
|
||||
"appTitle": "iiEasy",
|
||||
"appSlogan": "Futuro. Simple.",
|
||||
"retry": "Reintentar",
|
||||
"back": "Atrás",
|
||||
"you": "Tú",
|
||||
@@ -15,8 +16,8 @@
|
||||
"description": "Subtítulo que explica las acciones disponibles cuando no se puede acceder al servidor"
|
||||
},
|
||||
"account": "Cuenta",
|
||||
"supportConduit": "Apoyar Conduit",
|
||||
"supportConduitSubtitle": "Mantén Conduit independiente financiando el desarrollo continuo.",
|
||||
"supportConduit": "Apoyar iiEasy",
|
||||
"supportConduitSubtitle": "Mantén iiEasy independiente financiando el desarrollo continuo.",
|
||||
"githubSponsorsTitle": "GitHub Sponsors",
|
||||
"githubSponsorsSubtitle": "Conviértete en un patrocinador recurrente para financiar elementos del roadmap.",
|
||||
"buyMeACoffeeTitle": "Buy Me a Coffee",
|
||||
@@ -50,7 +51,7 @@
|
||||
"password": "Contraseña",
|
||||
"signInWithToken": "Iniciar sesión con token",
|
||||
"connectToServer": "Conectar al servidor",
|
||||
"enterServerAddress": "Ingresa la dirección de tu servidor Open-WebUI para comenzar",
|
||||
"enterServerAddress": "Ingresa la dirección de tu servidor iiEasyWeb para comenzar",
|
||||
"serverUrl": "URL del servidor",
|
||||
"serverUrlHint": "https://tu-servidor.com",
|
||||
"enterServerUrlSemantic": "Ingresa la URL o dirección IP de tu servidor",
|
||||
@@ -116,7 +117,7 @@
|
||||
"voicePromptTapStart": "Toca Iniciar para comenzar",
|
||||
"voiceActionStop": "Detener",
|
||||
"voiceActionStart": "Iniciar",
|
||||
"messageHintText": "Pregunta a Conduit",
|
||||
"messageHintText": "Pregunta a iiEasy",
|
||||
"stopGenerating": "Detener generación",
|
||||
"codeCopiedToClipboard": "Código copiado al portapapeles.",
|
||||
"send": "Enviar",
|
||||
@@ -204,7 +205,7 @@
|
||||
"deleteFolderMessage": "Esta carpeta y sus referencias de asignación se eliminarán.",
|
||||
"failedToDeleteFolder": "No se pudo eliminar la carpeta",
|
||||
"aboutApp": "Acerca de",
|
||||
"aboutAppSubtitle": "Información y enlaces de Conduit",
|
||||
"aboutAppSubtitle": "Información y enlaces de iiEasy",
|
||||
"web": "Web",
|
||||
"imageGen": "Generación de imágenes",
|
||||
"pinned": "Anclado",
|
||||
@@ -280,7 +281,7 @@
|
||||
"themeLight": "Claro",
|
||||
"currentlyUsingDarkTheme": "Usando actualmente el tema oscuro",
|
||||
"currentlyUsingLightTheme": "Usando actualmente el tema claro",
|
||||
"aboutConduit": "Acerca de Conduit",
|
||||
"aboutConduit": "Acerca de iiEasy",
|
||||
"versionLabel": "Versión: {version} ({build})",
|
||||
"@versionLabel": {
|
||||
"placeholders": {
|
||||
@@ -317,7 +318,7 @@
|
||||
"androidAssistantTitle": "Android digital assistant",
|
||||
"androidAssistantDescription": "Choose what happens when you trigger the Android digital assistant.",
|
||||
"androidAssistantOverlayOption": "Show quick overlay (default)",
|
||||
"androidAssistantNewChatOption": "Open Conduit with a new chat",
|
||||
"androidAssistantNewChatOption": "Open iiEasy with a new chat",
|
||||
"androidAssistantVoiceCallOption": "Start a voice call",
|
||||
"sttSettings": "Voz a texto",
|
||||
"sttEngineLabel": "Motor de reconocimiento",
|
||||
@@ -650,11 +651,11 @@
|
||||
"@nextLabel": {
|
||||
"description": "Label for navigating to the next item."
|
||||
},
|
||||
"themePaletteConduitLabel": "Conduit",
|
||||
"themePaletteConduitLabel": "iiEasy",
|
||||
"@themePaletteConduitLabel": {
|
||||
"description": "Palette name for the default Conduit theme."
|
||||
},
|
||||
"themePaletteConduitDescription": "Tema neutro y limpio diseñado para Conduit.",
|
||||
"themePaletteConduitDescription": "Tema neutro y limpio diseñado para iiEasy.",
|
||||
"@themePaletteConduitDescription": {
|
||||
"description": "Description of the Conduit palette."
|
||||
},
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"@@locale": "fr",
|
||||
"appTitle": "Conduit",
|
||||
"appTitle": "iiEasy",
|
||||
"appSlogan": "Avenir. Simple.",
|
||||
"retry": "Réessayer",
|
||||
"back": "Retour",
|
||||
"you": "Vous",
|
||||
@@ -15,8 +16,8 @@
|
||||
"description": "Sous-titre expliquant les actions possibles quand le serveur est injoignable"
|
||||
},
|
||||
"account": "Compte",
|
||||
"supportConduit": "Soutenir Conduit",
|
||||
"supportConduitSubtitle": "Financez le développement continu et les nouvelles fonctionnalités.",
|
||||
"supportConduit": "Soutenir iiEasy",
|
||||
"supportConduitSubtitle": "Soutenez iiEasy en finançant le développement continu.",
|
||||
"githubSponsorsTitle": "GitHub Sponsors",
|
||||
"githubSponsorsSubtitle": "Devenez sponsor récurrent pour soutenir la feuille de route.",
|
||||
"buyMeACoffeeTitle": "Buy Me a Coffee",
|
||||
@@ -50,7 +51,7 @@
|
||||
"password": "Mot de passe",
|
||||
"signInWithToken": "Se connecter avec un jeton",
|
||||
"connectToServer": "Se connecter au serveur",
|
||||
"enterServerAddress": "Saisissez l'adresse de votre serveur Open-WebUI pour commencer",
|
||||
"enterServerAddress": "Saisissez l'adresse de votre serveur iiEasyWeb pour commencer",
|
||||
"serverUrl": "URL du serveur",
|
||||
"serverUrlHint": "https://votre-serveur.com",
|
||||
"enterServerUrlSemantic": "Saisissez l'URL ou l'adresse IP de votre serveur",
|
||||
@@ -116,7 +117,7 @@
|
||||
"voicePromptTapStart": "Appuyez sur \"Démarrer\" pour commencer",
|
||||
"voiceActionStop": "Arrêter",
|
||||
"voiceActionStart": "Démarrer",
|
||||
"messageHintText": "Demander à Conduit",
|
||||
"messageHintText": "Demander à iiEasy",
|
||||
"stopGenerating": "Arrêter la génération",
|
||||
"codeCopiedToClipboard": "Code copié dans le presse-papiers.",
|
||||
"send": "Envoyer",
|
||||
@@ -204,7 +205,7 @@
|
||||
"deleteFolderMessage": "Ce dossier et ses associations seront supprimés.",
|
||||
"failedToDeleteFolder": "Échec de la suppression du dossier",
|
||||
"aboutApp": "À propos",
|
||||
"aboutAppSubtitle": "Informations et liens Conduit",
|
||||
"aboutAppSubtitle": "Informations et liens iiEasy",
|
||||
"web": "Web",
|
||||
"imageGen": "Gén. image",
|
||||
"pinned": "Épinglé",
|
||||
@@ -280,7 +281,7 @@
|
||||
"themeLight": "Clair",
|
||||
"currentlyUsingDarkTheme": "Thème sombre actuellement utilisé",
|
||||
"currentlyUsingLightTheme": "Thème clair actuellement utilisé",
|
||||
"aboutConduit": "À propos de Conduit",
|
||||
"aboutConduit": "À propos de iiEasy",
|
||||
"versionLabel": "Version : {version} ({build})",
|
||||
"@versionLabel": {
|
||||
"placeholders": {
|
||||
@@ -317,7 +318,7 @@
|
||||
"androidAssistantTitle": "Android digital assistant",
|
||||
"androidAssistantDescription": "Choose what happens when you trigger the Android digital assistant.",
|
||||
"androidAssistantOverlayOption": "Show quick overlay (default)",
|
||||
"androidAssistantNewChatOption": "Open Conduit with a new chat",
|
||||
"androidAssistantNewChatOption": "Open iiEasy with a new chat",
|
||||
"androidAssistantVoiceCallOption": "Start a voice call",
|
||||
"sttSettings": "Voix vers texte",
|
||||
"sttEngineLabel": "Moteur de reconnaissance",
|
||||
@@ -650,11 +651,11 @@
|
||||
"@nextLabel": {
|
||||
"description": "Label for navigating to the next item."
|
||||
},
|
||||
"themePaletteConduitLabel": "Conduit",
|
||||
"themePaletteConduitLabel": "iiEasy",
|
||||
"@themePaletteConduitLabel": {
|
||||
"description": "Palette name for the default Conduit theme."
|
||||
},
|
||||
"themePaletteConduitDescription": "Thème neutre et épuré conçu pour Conduit.",
|
||||
"themePaletteConduitDescription": "Thème neutre et épuré conçu pour iiEasy.",
|
||||
"@themePaletteConduitDescription": {
|
||||
"description": "Description of the Conduit palette."
|
||||
},
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"@@locale": "it",
|
||||
"appTitle": "Conduit",
|
||||
"appTitle": "iiEasy",
|
||||
"appSlogan": "Futuro. Semplice.",
|
||||
"retry": "Riprova",
|
||||
"back": "Indietro",
|
||||
"you": "Tu",
|
||||
@@ -15,8 +16,8 @@
|
||||
"description": "Sottotitolo che spiega le azioni disponibili quando il server non è raggiungibile"
|
||||
},
|
||||
"account": "Account",
|
||||
"supportConduit": "Sostieni Conduit",
|
||||
"supportConduitSubtitle": "Mantieni Conduit indipendente finanziando lo sviluppo continuo.",
|
||||
"supportConduit": "Sostieni iiEasy",
|
||||
"supportConduitSubtitle": "Mantieni iiEasy indipendente finanziando lo sviluppo continuo.",
|
||||
"githubSponsorsTitle": "GitHub Sponsors",
|
||||
"githubSponsorsSubtitle": "Diventa sponsor ricorrente per supportare la roadmap.",
|
||||
"buyMeACoffeeTitle": "Buy Me a Coffee",
|
||||
@@ -50,7 +51,7 @@
|
||||
"password": "Password",
|
||||
"signInWithToken": "Accedi con token",
|
||||
"connectToServer": "Connetti al server",
|
||||
"enterServerAddress": "Inserisci l'indirizzo del server Open-WebUI per iniziare",
|
||||
"enterServerAddress": "Inserisci l'indirizzo del server iiEasyWeb per iniziare",
|
||||
"serverUrl": "URL del server",
|
||||
"serverUrlHint": "https://tuo-server.com",
|
||||
"enterServerUrlSemantic": "Inserisci l'URL o l'indirizzo IP del server",
|
||||
@@ -116,7 +117,7 @@
|
||||
"voicePromptTapStart": "Tocca \"Avvia\" per iniziare",
|
||||
"voiceActionStop": "Stop",
|
||||
"voiceActionStart": "Avvia",
|
||||
"messageHintText": "Chiedi a Conduit",
|
||||
"messageHintText": "Chiedi a iiEasy",
|
||||
"stopGenerating": "Interrompi generazione",
|
||||
"codeCopiedToClipboard": "Codice copiato negli appunti.",
|
||||
"send": "Invia",
|
||||
@@ -204,7 +205,7 @@
|
||||
"deleteFolderMessage": "Questa cartella e le sue associazioni verranno rimosse.",
|
||||
"failedToDeleteFolder": "Impossibile eliminare la cartella",
|
||||
"aboutApp": "Informazioni",
|
||||
"aboutAppSubtitle": "Informazioni e link di Conduit",
|
||||
"aboutAppSubtitle": "Informazioni e link di iiEasy",
|
||||
"web": "Web",
|
||||
"imageGen": "Gen. immagini",
|
||||
"pinned": "Fissati",
|
||||
@@ -280,7 +281,7 @@
|
||||
"themeLight": "Chiaro",
|
||||
"currentlyUsingDarkTheme": "Attualmente tema scuro",
|
||||
"currentlyUsingLightTheme": "Attualmente tema chiaro",
|
||||
"aboutConduit": "Informazioni su Conduit",
|
||||
"aboutConduit": "Informazioni su iiEasy",
|
||||
"versionLabel": "Versione: {version} ({build})",
|
||||
"@versionLabel": {
|
||||
"placeholders": {
|
||||
@@ -317,7 +318,7 @@
|
||||
"androidAssistantTitle": "Android digital assistant",
|
||||
"androidAssistantDescription": "Choose what happens when you trigger the Android digital assistant.",
|
||||
"androidAssistantOverlayOption": "Show quick overlay (default)",
|
||||
"androidAssistantNewChatOption": "Open Conduit with a new chat",
|
||||
"androidAssistantNewChatOption": "Open iiEasy with a new chat",
|
||||
"androidAssistantVoiceCallOption": "Start a voice call",
|
||||
"sttSettings": "Voce in testo",
|
||||
"sttEngineLabel": "Motore di riconoscimento",
|
||||
@@ -650,11 +651,11 @@
|
||||
"@nextLabel": {
|
||||
"description": "Label for navigating to the next item."
|
||||
},
|
||||
"themePaletteConduitLabel": "Conduit",
|
||||
"themePaletteConduitLabel": "iiEasy",
|
||||
"@themePaletteConduitLabel": {
|
||||
"description": "Palette name for the default Conduit theme."
|
||||
},
|
||||
"themePaletteConduitDescription": "Tema neutro e pulito progettato per Conduit.",
|
||||
"themePaletteConduitDescription": "Tema neutro e pulito progettato per iiEasy.",
|
||||
"@themePaletteConduitDescription": {
|
||||
"description": "Description of the Conduit palette."
|
||||
},
|
||||
|
||||