refactor(android): remove superseded app surfaces (#101680)

This commit is contained in:
Vincent Koc 2026-07-07 06:07:36 -07:00 committed by GitHub
parent 4e29b2f5ab
commit cd86107a10
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
28 changed files with 5 additions and 2592 deletions

View file

@ -1009,134 +1009,6 @@
"surface": "android",
"id": "native.android.73e6ab1a168fd381"
},
{
"kind": "conditional-branch",
"line": 392,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/ConnectTabScreen.kt",
"source": "Last gateway error",
"surface": "android",
"id": "native.android.f1c67abfbe3ec6e0"
},
{
"kind": "conditional-branch",
"line": 392,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/ConnectTabScreen.kt",
"source": "Pairing required",
"surface": "android",
"id": "native.android.6c4cd299250df476"
},
{
"kind": "ui-call",
"line": 403,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/ConnectTabScreen.kt",
"source": "OpenClaw Android ${openClawAndroidVersionLabel()}",
"surface": "android",
"id": "native.android.d4cf912850bd01fa"
},
{
"kind": "ui-call",
"line": 444,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/ConnectTabScreen.kt",
"source": "Setup code, endpoint, TLS, token, password, onboarding.",
"surface": "android",
"id": "native.android.cbff50e93346f3c5"
},
{
"kind": "conditional-branch",
"line": 448,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/ConnectTabScreen.kt",
"source": "Collapse advanced controls",
"surface": "android",
"id": "native.android.207b536e12b5360e"
},
{
"kind": "conditional-branch",
"line": 448,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/ConnectTabScreen.kt",
"source": "Expand advanced controls",
"surface": "android",
"id": "native.android.03c5bbc5a9f0b75e"
},
{
"kind": "ui-call",
"line": 479,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/ConnectTabScreen.kt",
"source": "Run these on the gateway host:",
"surface": "android",
"id": "native.android.bcc45347b4e15232"
},
{
"kind": "ui-call",
"line": 482,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/ConnectTabScreen.kt",
"source": "For Tailscale or public hosts, use wss:// or Tailscale Serve. Private LAN ws:// remains supported.",
"surface": "android",
"id": "native.android.8b607ce7ba3ac4d3"
},
{
"kind": "ui-named-argument",
"line": 511,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/ConnectTabScreen.kt",
"source": "Android Emulator",
"surface": "android",
"id": "native.android.e23c1c9a2c8391f9"
},
{
"kind": "ui-named-argument",
"line": 520,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/ConnectTabScreen.kt",
"source": "Localhost",
"surface": "android",
"id": "native.android.b28ff3f4d4bd3830"
},
{
"kind": "conditional-branch",
"line": 547,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/ConnectTabScreen.kt",
"source": "Port",
"surface": "android",
"id": "native.android.8eb9efc461827277"
},
{
"kind": "conditional-branch",
"line": 547,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/ConnectTabScreen.kt",
"source": "Port (optional, defaults to 443)",
"surface": "android",
"id": "native.android.e1ad7cf446f7d370"
},
{
"kind": "ui-call",
"line": 573,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/ConnectTabScreen.kt",
"source": "Turn this on for Tailscale or public hosts. Private LAN ws:// remains supported.",
"surface": "android",
"id": "native.android.bdb650918c75aa9b"
},
{
"kind": "ui-call",
"line": 599,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/ConnectTabScreen.kt",
"source": "Leave blank to keep saved token",
"surface": "android",
"id": "native.android.d56dcd9606f37263"
},
{
"kind": "ui-call",
"line": 608,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/ConnectTabScreen.kt",
"source": "Password (optional)",
"surface": "android",
"id": "native.android.8123f2cfbe415437"
},
{
"kind": "ui-call",
"line": 710,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/ConnectTabScreen.kt",
"source": "CF-Access-Client-Id",
"surface": "android",
"id": "native.android.4ddaa88cd4939df2"
},
{
"kind": "ui-named-argument",
"line": 52,
@ -5569,142 +5441,6 @@
"surface": "android",
"id": "native.android.b10a51fe9d4b8d2d"
},
{
"kind": "ui-call",
"line": 178,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceTabScreen.kt",
"source": "Tap mic or Talk",
"surface": "android",
"id": "native.android.aadc50cd77652b2d"
},
{
"kind": "ui-call",
"line": 183,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceTabScreen.kt",
"source": "Mic sends turns; Talk keeps the conversation open.",
"surface": "android",
"id": "native.android.63dcad83fd6c28dd"
},
{
"kind": "conditional-branch",
"line": 243,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceTabScreen.kt",
"source": "Mute speaker",
"surface": "android",
"id": "native.android.b6bbe61ec94523ae"
},
{
"kind": "conditional-branch",
"line": 243,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceTabScreen.kt",
"source": "Unmute speaker",
"surface": "android",
"id": "native.android.e62290e2a1e6f464"
},
{
"kind": "conditional-branch",
"line": 249,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceTabScreen.kt",
"source": "Muted",
"surface": "android",
"id": "native.android.428e2bd904f67872"
},
{
"kind": "conditional-branch",
"line": 249,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceTabScreen.kt",
"source": "Speaker",
"surface": "android",
"id": "native.android.723510d6f6b85cea"
},
{
"kind": "conditional-branch",
"line": 306,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceTabScreen.kt",
"source": "Turn microphone off",
"surface": "android",
"id": "native.android.a018a5ecea3cda3a"
},
{
"kind": "conditional-branch",
"line": 306,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceTabScreen.kt",
"source": "Turn microphone on",
"surface": "android",
"id": "native.android.5512f9eb8f18274f"
},
{
"kind": "conditional-branch",
"line": 334,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceTabScreen.kt",
"source": "Turn Talk Mode off",
"surface": "android",
"id": "native.android.4e7af9bb2202c677"
},
{
"kind": "conditional-branch",
"line": 334,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceTabScreen.kt",
"source": "Turn Talk Mode on",
"surface": "android",
"id": "native.android.195abf15cebfbf9a"
},
{
"kind": "conditional-branch",
"line": 341,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceTabScreen.kt",
"source": "Talk",
"surface": "android",
"id": "native.android.774e74150a1d9bae"
},
{
"kind": "conditional-branch",
"line": 341,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceTabScreen.kt",
"source": "Talk on",
"surface": "android",
"id": "native.android.d6cd8b5669156542"
},
{
"kind": "ui-call",
"line": 371,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceTabScreen.kt",
"source": "$gatewayStatus · $stateText",
"surface": "android",
"id": "native.android.959507fd4a6ba3a4"
},
{
"kind": "ui-call",
"line": 401,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceTabScreen.kt",
"source": "Open settings",
"surface": "android",
"id": "native.android.b0cd6db035f9049f"
},
{
"kind": "conditional-branch",
"line": 437,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceTabScreen.kt",
"source": "OpenClaw",
"surface": "android",
"id": "native.android.dd372d2915032feb"
},
{
"kind": "conditional-branch",
"line": 437,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceTabScreen.kt",
"source": "You",
"surface": "android",
"id": "native.android.5ec494944bda138e"
},
{
"kind": "ui-call",
"line": 469,
"path": "apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceTabScreen.kt",
"source": "OpenClaw is thinking…",
"surface": "android",
"id": "native.android.09137c880dd8890e"
},
{
"kind": "ui-named-argument",
"line": 176,
@ -6749,37 +6485,13 @@
"kind": "resource-string",
"line": 3,
"path": "apps/android/app/src/main/res/values/strings.xml",
"source": "Gateway Connection",
"surface": "android",
"id": "native.android.55da01a287502809"
},
{
"kind": "resource-string",
"line": 4,
"path": "apps/android/app/src/main/res/values/strings.xml",
"source": "Connect Gateway",
"surface": "android",
"id": "native.android.edb177f35510f636"
},
{
"kind": "resource-string",
"line": 5,
"path": "apps/android/app/src/main/res/values/strings.xml",
"source": "Disconnect",
"surface": "android",
"id": "native.android.ed2b7c53127062b1"
},
{
"kind": "resource-string",
"line": 6,
"path": "apps/android/app/src/main/res/values/strings.xml",
"source": "Trust this gateway?",
"surface": "android",
"id": "native.android.cd32b06480d9bb69"
},
{
"kind": "resource-string",
"line": 7,
"line": 4,
"path": "apps/android/app/src/main/res/values/strings.xml",
"source": "Trust and continue",
"surface": "android",
@ -6787,7 +6499,7 @@
},
{
"kind": "resource-string",
"line": 8,
"line": 5,
"path": "apps/android/app/src/main/res/values/strings.xml",
"source": "Cancel",
"surface": "android",
@ -6795,7 +6507,7 @@
},
{
"kind": "resource-string",
"line": 9,
"line": 6,
"path": "apps/android/app/src/main/res/values/strings.xml",
"source": "New chat in worktree",
"surface": "android",
@ -6803,183 +6515,7 @@
},
{
"kind": "resource-string",
"line": 10,
"path": "apps/android/app/src/main/res/values/strings.xml",
"source": "Endpoint",
"surface": "android",
"id": "native.android.c12edfa8fda16801"
},
{
"kind": "resource-string",
"line": 11,
"path": "apps/android/app/src/main/res/values/strings.xml",
"source": "Status",
"surface": "android",
"id": "native.android.75efde06ba05f289"
},
{
"kind": "resource-string",
"line": 12,
"path": "apps/android/app/src/main/res/values/strings.xml",
"source": "Your gateway is active and ready.",
"surface": "android",
"id": "native.android.3fa18ce9f5965142"
},
{
"kind": "resource-string",
"line": 13,
"path": "apps/android/app/src/main/res/values/strings.xml",
"source": "Connect to your gateway to get started.",
"surface": "android",
"id": "native.android.df305a8c797e5600"
},
{
"kind": "resource-string",
"line": 14,
"path": "apps/android/app/src/main/res/values/strings.xml",
"source": "Copy Report for Claw",
"surface": "android",
"id": "native.android.6b6a08e8d4eff2d2"
},
{
"kind": "resource-string",
"line": 15,
"path": "apps/android/app/src/main/res/values/strings.xml",
"source": "Advanced controls",
"surface": "android",
"id": "native.android.8f1072da28d67483"
},
{
"kind": "resource-string",
"line": 16,
"path": "apps/android/app/src/main/res/values/strings.xml",
"source": "Connection method",
"surface": "android",
"id": "native.android.a6739759bcab830c"
},
{
"kind": "resource-string",
"line": 17,
"path": "apps/android/app/src/main/res/values/strings.xml",
"source": "Setup Code",
"surface": "android",
"id": "native.android.139b14a5e9980de6"
},
{
"kind": "resource-string",
"line": 18,
"path": "apps/android/app/src/main/res/values/strings.xml",
"source": "Manual",
"surface": "android",
"id": "native.android.be082a5c0a39d06b"
},
{
"kind": "resource-string",
"line": 19,
"path": "apps/android/app/src/main/res/values/strings.xml",
"source": "Paste setup code",
"surface": "android",
"id": "native.android.9bab58464c4fc9b0"
},
{
"kind": "resource-string",
"line": 20,
"path": "apps/android/app/src/main/res/values/strings.xml",
"source": "Host",
"surface": "android",
"id": "native.android.f23d9864f1b619f7"
},
{
"kind": "resource-string",
"line": 21,
"path": "apps/android/app/src/main/res/values/strings.xml",
"source": "Use TLS",
"surface": "android",
"id": "native.android.5677de0e2a1c0faf"
},
{
"kind": "resource-string",
"line": 22,
"path": "apps/android/app/src/main/res/values/strings.xml",
"source": "Token (optional)",
"surface": "android",
"id": "native.android.9f17b363626f2516"
},
{
"kind": "resource-string",
"line": 23,
"path": "apps/android/app/src/main/res/values/strings.xml",
"source": "Password",
"surface": "android",
"id": "native.android.005e23b33d333f6a"
},
{
"kind": "resource-string",
"line": 24,
"path": "apps/android/app/src/main/res/values/strings.xml",
"source": "Custom headers (optional)",
"surface": "android",
"id": "native.android.43900769f2cf0458"
},
{
"kind": "resource-string",
"line": 25,
"path": "apps/android/app/src/main/res/values/strings.xml",
"source": "Sent with every connection to this gateway, for example Cloudflare Access service token headers. Stored encrypted and applied on the next reconnect.",
"surface": "android",
"id": "native.android.6d42cb48a4a32331"
},
{
"kind": "resource-string",
"line": 26,
"path": "apps/android/app/src/main/res/values/strings.xml",
"source": "Header value",
"surface": "android",
"id": "native.android.c4d4f0299965fe55"
},
{
"kind": "resource-string",
"line": 27,
"path": "apps/android/app/src/main/res/values/strings.xml",
"source": "This header is managed by the connection and cannot be overridden.",
"surface": "android",
"id": "native.android.123e497273b8586e"
},
{
"kind": "resource-string",
"line": 28,
"path": "apps/android/app/src/main/res/values/strings.xml",
"source": "Add header",
"surface": "android",
"id": "native.android.94f0633a1fb099bc"
},
{
"kind": "resource-string",
"line": 29,
"path": "apps/android/app/src/main/res/values/strings.xml",
"source": "Remove %1$s",
"surface": "android",
"id": "native.android.ff4c8c3b56827d8e"
},
{
"kind": "resource-string",
"line": 30,
"path": "apps/android/app/src/main/res/values/strings.xml",
"source": "Run onboarding again",
"surface": "android",
"id": "native.android.a43f43c1338a6960"
},
{
"kind": "resource-string",
"line": 31,
"path": "apps/android/app/src/main/res/values/strings.xml",
"source": "Resolved endpoint",
"surface": "android",
"id": "native.android.8a14b91132d358be"
},
{
"kind": "resource-string",
"line": 32,
"line": 7,
"path": "apps/android/app/src/main/res/values/strings.xml",
"source": "Verify the certificate fingerprint before trusting this gateway.\n\n%1$s",
"surface": "android",
@ -6987,36 +6523,12 @@
},
{
"kind": "resource-string",
"line": 33,
"line": 8,
"path": "apps/android/app/src/main/res/values/strings.xml",
"source": "The gateway certificate changed. Continue only if you expected this.\n\nOld SHA-256:\n%1$s\n\nNew SHA-256:\n%2$s",
"surface": "android",
"id": "native.android.6ad2d3f084142bed"
},
{
"kind": "resource-string",
"line": 34,
"path": "apps/android/app/src/main/res/values/strings.xml",
"source": "Switch gateway",
"surface": "android",
"id": "native.android.7c49ec7fe94a9063"
},
{
"kind": "resource-string",
"line": 35,
"path": "apps/android/app/src/main/res/values/strings.xml",
"source": "Select gateway",
"surface": "android",
"id": "native.android.9e0a677fd629a15f"
},
{
"kind": "resource-string",
"line": 36,
"path": "apps/android/app/src/main/res/values/strings.xml",
"source": "Active gateway",
"surface": "android",
"id": "native.android.b0034ac5bbc6b25f"
},
{
"kind": "plist-string",
"line": 8,

View file

@ -1,78 +0,0 @@
package ai.openclaw.app.node
import ai.openclaw.app.SecurePrefs
import ai.openclaw.app.gateway.GatewaySession
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonArray
/**
* Handles gateway-originated events that need to update local Android preferences.
*/
class GatewayEventHandler(
private val scope: CoroutineScope,
private val prefs: SecurePrefs,
private val json: Json,
private val operatorSession: GatewaySession,
private val isConnected: () -> Boolean,
) {
private var suppressWakeWordsSync = false
private var wakeWordsSyncJob: Job? = null
/** Applies gateway wake words locally without echoing the same change back to the gateway. */
fun applyWakeWordsFromGateway(words: List<String>) {
suppressWakeWordsSync = true
prefs.setWakeWords(words)
suppressWakeWordsSync = false
}
/** Debounces local wake-word edits before sending voicewake.set to the operator session. */
fun scheduleWakeWordsSyncIfNeeded() {
if (suppressWakeWordsSync) return
if (!isConnected()) return
val snapshot = prefs.wakeWords.value
wakeWordsSyncJob?.cancel()
wakeWordsSyncJob =
scope.launch {
delay(650)
val jsonList = snapshot.joinToString(separator = ",") { it.toJsonString() }
val params = """{"triggers":[$jsonList]}"""
try {
operatorSession.request("voicewake.set", params)
} catch (_: Throwable) {
// ignore
}
}
}
/** Loads gateway wake words on connect so Android settings show server truth. */
suspend fun refreshWakeWordsFromGateway() {
if (!isConnected()) return
try {
val res = operatorSession.request("voicewake.get", "{}")
val payload = json.parseToJsonElement(res).asObjectOrNull() ?: return
val array = payload["triggers"] as? JsonArray ?: return
val triggers = array.mapNotNull { it.asStringOrNull() }
applyWakeWordsFromGateway(triggers)
} catch (_: Throwable) {
// ignore
}
}
/** Applies voicewake.changed event payloads emitted by the gateway. */
fun handleVoiceWakeChangedEvent(payloadJson: String?) {
if (payloadJson.isNullOrBlank()) return
try {
val payload = json.parseToJsonElement(payloadJson).asObjectOrNull() ?: return
val array = payload["triggers"] as? JsonArray ?: return
val triggers = array.mapNotNull { it.asStringOrNull() }
applyWakeWordsFromGateway(triggers)
} catch (_: Throwable) {
// ignore
}
}
}

View file

@ -1,11 +0,0 @@
package ai.openclaw.app.ui
import ai.openclaw.app.MainViewModel
import ai.openclaw.app.ui.chat.ChatSheetContent
import androidx.compose.runtime.Composable
/** Keeps the public shell entry point stable while chat internals live under ui.chat. */
@Composable
fun ChatSheet(viewModel: MainViewModel) {
ChatSheetContent(viewModel = viewModel)
}

View file

@ -1,848 +0,0 @@
package ai.openclaw.app.ui
import ai.openclaw.app.GatewayConnectionProblem
import ai.openclaw.app.MainViewModel
import ai.openclaw.app.R
import ai.openclaw.app.gateway.GatewayCustomHeaders
import ai.openclaw.app.gateway.GatewayEndpoint
import ai.openclaw.app.ui.mobileCardSurface
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Check
import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.Cloud
import androidx.compose.material.icons.filled.ContentCopy
import androidx.compose.material.icons.filled.ExpandLess
import androidx.compose.material.icons.filled.ExpandMore
import androidx.compose.material.icons.filled.Link
import androidx.compose.material.icons.filled.PowerSettingsNew
import androidx.compose.material.icons.filled.SwapHoriz
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.OutlinedTextFieldDefaults
import androidx.compose.material3.Surface
import androidx.compose.material3.Switch
import androidx.compose.material3.SwitchDefaults
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.unit.dp
private enum class ConnectInputMode {
SetupCode,
Manual,
}
/** Gateway connection screen for setup-code and manual endpoint pairing. */
@Composable
fun ConnectTabScreen(viewModel: MainViewModel) {
val context = LocalContext.current
val gatewayConnectionDisplay by viewModel.gatewayConnectionDisplay.collectAsState()
val statusText = gatewayConnectionDisplay.statusText
val gatewayConnectionProblem = gatewayConnectionDisplay.problem
val isConnected = gatewayConnectionDisplay.isConnected
val remoteAddress by viewModel.remoteAddress.collectAsState()
val manualHost by viewModel.manualHost.collectAsState()
val manualPort by viewModel.manualPort.collectAsState()
val manualTls by viewModel.manualTls.collectAsState()
val manualEnabled by viewModel.manualEnabled.collectAsState()
val pendingTrust by viewModel.pendingGatewayTrust.collectAsState()
val pairedGateways by viewModel.pairedGateways.collectAsState()
val activeGatewayStableId by viewModel.activeGatewayStableId.collectAsState()
var advancedOpen by rememberSaveable { mutableStateOf(false) }
var gatewaySwitcherOpen by remember { mutableStateOf(false) }
var inputMode by
remember(manualEnabled, manualHost) {
mutableStateOf(
if (manualEnabled || manualHost.isNotBlank()) {
ConnectInputMode.Manual
} else {
ConnectInputMode.SetupCode
},
)
}
var setupCode by rememberSaveable { mutableStateOf("") }
var manualHostInput by rememberSaveable { mutableStateOf(manualHost.ifBlank { "10.0.2.2" }) }
var manualPortInput by rememberSaveable { mutableStateOf(manualPort.toString()) }
var manualTlsInput by rememberSaveable { mutableStateOf(manualTls) }
var tokenInput by remember { mutableStateOf("") }
var passwordInput by rememberSaveable { mutableStateOf("") }
var validationText by rememberSaveable { mutableStateOf<String?>(null) }
if (pendingTrust != null) {
val prompt = pendingTrust!!
AlertDialog(
onDismissRequest = { viewModel.declineGatewayTrustPrompt() },
containerColor = mobileCardSurface,
title = { Text(stringResource(R.string.trust_this_gateway), style = mobileHeadline, color = mobileText) },
text = {
val message =
if (prompt.previousFingerprintSha256.isNullOrBlank()) {
stringResource(R.string.gateway_trust_first_seen, prompt.fingerprintSha256)
} else {
stringResource(
R.string.gateway_trust_changed,
prompt.previousFingerprintSha256,
prompt.fingerprintSha256,
)
}
Text(
message,
style = mobileCallout,
color = mobileText,
)
},
confirmButton = {
TextButton(
onClick = { viewModel.acceptGatewayTrustPrompt() },
colors = ButtonDefaults.textButtonColors(contentColor = mobileAccent),
) {
Text(stringResource(R.string.trust_and_continue))
}
},
dismissButton = {
TextButton(
onClick = { viewModel.declineGatewayTrustPrompt() },
colors = ButtonDefaults.textButtonColors(contentColor = mobileTextSecondary),
) {
Text(stringResource(R.string.cancel))
}
},
)
}
val setupResolvedEndpoint = remember(setupCode) { decodeGatewaySetupCode(setupCode)?.url?.let { parseGatewayEndpoint(it)?.displayUrl } }
val manualEndpointConfig =
remember(manualHostInput, manualPortInput, manualTlsInput) {
composeGatewayManualUrl(manualHostInput, manualPortInput, manualTlsInput)?.let(::parseGatewayEndpoint)
}
val manualResolvedEndpoint = manualEndpointConfig?.displayUrl
val activeEndpoint =
remember(isConnected, remoteAddress, setupResolvedEndpoint, manualResolvedEndpoint, inputMode) {
when {
isConnected && !remoteAddress.isNullOrBlank() -> remoteAddress!!
inputMode == ConnectInputMode.SetupCode -> setupResolvedEndpoint ?: "Not set"
else -> manualResolvedEndpoint ?: "Not set"
}
}
val showDiagnostics = !isConnected && (gatewayConnectionProblem != null || gatewayStatusHasDiagnostics(statusText))
val pairingRequired = !isConnected && (gatewayConnectionProblem?.isPairingRequired == true || gatewayStatusLooksLikePairing(statusText))
val pairingInstruction = gatewayPairingInstruction(gatewayConnectionProblem)
val statusLabel = gatewayStatusForDisplay(gatewayConnectionProblem?.message ?: statusText)
Column(
modifier = Modifier.verticalScroll(rememberScrollState()).padding(horizontal = 20.dp, vertical = 16.dp),
verticalArrangement = Arrangement.spacedBy(14.dp),
) {
Column(verticalArrangement = Arrangement.spacedBy(6.dp)) {
Text(stringResource(R.string.gateway_connection), style = mobileTitle1, color = mobileText)
Text(
if (isConnected) {
stringResource(R.string.connected_gateway_ready)
} else {
stringResource(R.string.connect_gateway_get_started)
},
style = mobileCallout,
color = mobileTextSecondary,
)
}
// Status cards in a unified card group
Surface(
modifier = Modifier.fillMaxWidth(),
shape = RoundedCornerShape(14.dp),
color = mobileCardSurface,
border = BorderStroke(1.dp, mobileBorder),
) {
Column {
Row(
modifier = Modifier.fillMaxWidth().padding(horizontal = 14.dp, vertical = 12.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(12.dp),
) {
Surface(
shape = RoundedCornerShape(10.dp),
color = mobileAccentSoft,
) {
Icon(
imageVector = Icons.Default.Link,
contentDescription = null,
modifier = Modifier.padding(8.dp).size(18.dp),
tint = mobileAccent,
)
}
Column(verticalArrangement = Arrangement.spacedBy(2.dp)) {
Text(stringResource(R.string.endpoint), style = mobileCaption1.copy(fontWeight = FontWeight.SemiBold), color = mobileTextSecondary)
Text(activeEndpoint, style = mobileBody.copy(fontFamily = FontFamily.Monospace), color = mobileText)
}
}
HorizontalDivider(color = mobileBorder)
Row(
modifier = Modifier.fillMaxWidth().padding(horizontal = 14.dp, vertical = 12.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(12.dp),
) {
Surface(
shape = RoundedCornerShape(10.dp),
color = if (isConnected) mobileSuccessSoft else mobileSurface,
) {
Icon(
imageVector = Icons.Default.Cloud,
contentDescription = null,
modifier = Modifier.padding(8.dp).size(18.dp),
tint = if (isConnected) mobileSuccess else mobileTextTertiary,
)
}
Column(verticalArrangement = Arrangement.spacedBy(2.dp)) {
Text(stringResource(R.string.status), style = mobileCaption1.copy(fontWeight = FontWeight.SemiBold), color = mobileTextSecondary)
Text(statusText, style = mobileBody, color = if (isConnected) mobileSuccess else mobileText)
}
}
}
}
if (pairedGateways.size > 1) {
Box(modifier = Modifier.fillMaxWidth()) {
Surface(
modifier = Modifier.fillMaxWidth(),
shape = RoundedCornerShape(14.dp),
color = mobileCardSurface,
border = BorderStroke(1.dp, mobileBorder),
onClick = { gatewaySwitcherOpen = true },
) {
Row(
modifier = Modifier.fillMaxWidth().padding(horizontal = 14.dp, vertical = 12.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(10.dp),
) {
Icon(Icons.Default.SwapHoriz, contentDescription = null, tint = mobileAccent)
Column(modifier = Modifier.weight(1f)) {
Text(stringResource(R.string.switch_gateway), style = mobileCaption1, color = mobileTextSecondary)
Text(
pairedGateways.firstOrNull { it.stableId == activeGatewayStableId }?.name
?: stringResource(R.string.select_gateway),
style = mobileHeadline,
color = mobileText,
)
}
Icon(Icons.Default.ExpandMore, contentDescription = null, tint = mobileTextSecondary)
}
}
DropdownMenu(
expanded = gatewaySwitcherOpen,
onDismissRequest = { gatewaySwitcherOpen = false },
) {
pairedGateways.forEach { entry ->
DropdownMenuItem(
text = { Text(entry.name) },
leadingIcon = {
if (entry.stableId == activeGatewayStableId) {
Icon(Icons.Default.Check, contentDescription = stringResource(R.string.active_gateway))
}
},
onClick = {
gatewaySwitcherOpen = false
if (entry.stableId != activeGatewayStableId) {
viewModel.switchToGateway(entry.stableId)
}
},
)
}
}
}
}
if (isConnected) {
// Outlined secondary button when connected — don't scream "danger"
Button(
onClick = {
viewModel.disconnect()
validationText = null
},
modifier = Modifier.fillMaxWidth().height(48.dp),
shape = RoundedCornerShape(14.dp),
colors =
ButtonDefaults.buttonColors(
containerColor = mobileCardSurface,
contentColor = mobileDanger,
),
border = BorderStroke(1.dp, mobileDanger.copy(alpha = 0.4f)),
) {
Icon(Icons.Default.PowerSettingsNew, contentDescription = null, modifier = Modifier.size(18.dp))
Spacer(modifier = Modifier.width(8.dp))
Text(stringResource(R.string.disconnect), style = mobileHeadline.copy(fontWeight = FontWeight.SemiBold))
}
} else {
Button(
onClick = {
if (statusText.contains("operator offline", ignoreCase = true)) {
validationText = null
viewModel.refreshGatewayConnection()
return@Button
}
val plan =
resolveGatewayConnectPlan(
useSetupCode = inputMode == ConnectInputMode.SetupCode,
setupCode = setupCode,
savedManualHost = manualHost,
savedManualPort = manualPort.toString(),
savedManualTls = manualTls,
manualHostInput = manualHostInput,
manualPortInput = manualPortInput,
manualTlsInput = manualTlsInput,
bootstrapTokenInput = "",
tokenInput = tokenInput,
passwordInput = passwordInput,
)
if (plan == null) {
validationText =
if (inputMode == ConnectInputMode.SetupCode) {
val parsedSetup = decodeGatewaySetupCode(setupCode)
if (parsedSetup == null) {
"Paste a valid setup code to connect."
} else {
val parsedGateway = parseGatewayEndpointResult(parsedSetup.url)
gatewayEndpointValidationMessage(
parsedGateway.error ?: GatewayEndpointValidationError.INVALID_URL,
GatewayEndpointInputSource.SETUP_CODE,
)
}
} else {
val manualUrl = composeGatewayManualUrl(manualHostInput, manualPortInput, manualTlsInput)
val parsedGateway = manualUrl?.let(::parseGatewayEndpointResult)
gatewayEndpointValidationMessage(
parsedGateway?.error ?: GatewayEndpointValidationError.INVALID_URL,
GatewayEndpointInputSource.MANUAL,
)
}
return@Button
}
validationText = null
viewModel.saveGatewayConfigAndConnect(plan)
tokenInput = ""
},
modifier = Modifier.fillMaxWidth().height(52.dp),
shape = RoundedCornerShape(14.dp),
colors =
ButtonDefaults.buttonColors(
containerColor = mobileAccent,
contentColor = Color.White,
),
) {
Text(stringResource(R.string.connect_gateway), style = mobileHeadline.copy(fontWeight = FontWeight.Bold))
}
}
if (showDiagnostics) {
Surface(
modifier = Modifier.fillMaxWidth(),
shape = RoundedCornerShape(14.dp),
color = mobileWarningSoft,
border = BorderStroke(1.dp, mobileWarning.copy(alpha = 0.25f)),
) {
Column(
modifier = Modifier.fillMaxWidth().padding(horizontal = 14.dp, vertical = 14.dp),
verticalArrangement = Arrangement.spacedBy(10.dp),
) {
Text(if (pairingRequired) "Pairing required" else "Last gateway error", style = mobileHeadline, color = mobileWarning)
Text(statusLabel, style = mobileBody.copy(fontFamily = FontFamily.Monospace), color = mobileText)
if (pairingRequired) {
Text(
pairingInstruction,
style = mobileCallout,
color = mobileTextSecondary,
)
CommandBlock("openclaw devices list")
CommandBlock("openclaw devices approve <requestId>")
}
Text("OpenClaw Android ${openClawAndroidVersionLabel()}", style = mobileCaption1, color = mobileTextSecondary)
Button(
onClick = {
copyGatewayDiagnosticsReport(
context = context,
screen = "connect tab",
gatewayAddress = activeEndpoint,
statusText = statusLabel,
)
},
modifier = Modifier.fillMaxWidth().height(46.dp),
shape = RoundedCornerShape(12.dp),
colors =
ButtonDefaults.buttonColors(
containerColor = mobileCardSurface,
contentColor = mobileWarning,
),
border = BorderStroke(1.dp, mobileWarning.copy(alpha = 0.3f)),
) {
Icon(Icons.Default.ContentCopy, contentDescription = null, modifier = Modifier.size(18.dp))
Spacer(modifier = Modifier.width(8.dp))
Text(stringResource(R.string.copy_report_for_claw), style = mobileCallout.copy(fontWeight = FontWeight.Bold))
}
}
}
}
Surface(
modifier = Modifier.fillMaxWidth(),
shape = RoundedCornerShape(14.dp),
color = mobileSurface,
border = BorderStroke(1.dp, mobileBorder),
onClick = { advancedOpen = !advancedOpen },
) {
Row(
modifier = Modifier.fillMaxWidth().padding(horizontal = 14.dp, vertical = 12.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween,
) {
Column(verticalArrangement = Arrangement.spacedBy(2.dp)) {
Text(stringResource(R.string.advanced_controls), style = mobileHeadline, color = mobileText)
Text("Setup code, endpoint, TLS, token, password, onboarding.", style = mobileCaption1, color = mobileTextSecondary)
}
Icon(
imageVector = if (advancedOpen) Icons.Default.ExpandLess else Icons.Default.ExpandMore,
contentDescription = if (advancedOpen) "Collapse advanced controls" else "Expand advanced controls",
tint = mobileTextSecondary,
)
}
}
AnimatedVisibility(visible = advancedOpen) {
Surface(
modifier = Modifier.fillMaxWidth(),
shape = RoundedCornerShape(14.dp),
color = mobileCardSurface,
border = BorderStroke(1.dp, mobileBorder),
) {
Column(
modifier = Modifier.fillMaxWidth().padding(horizontal = 14.dp, vertical = 14.dp),
verticalArrangement = Arrangement.spacedBy(12.dp),
) {
Text(stringResource(R.string.connection_method), style = mobileCaption1.copy(fontWeight = FontWeight.SemiBold), color = mobileTextSecondary)
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
MethodChip(
label = stringResource(R.string.setup_code),
active = inputMode == ConnectInputMode.SetupCode,
onClick = { inputMode = ConnectInputMode.SetupCode },
)
MethodChip(
label = stringResource(R.string.manual),
active = inputMode == ConnectInputMode.Manual,
onClick = { inputMode = ConnectInputMode.Manual },
)
}
Text("Run these on the gateway host:", style = mobileCallout, color = mobileTextSecondary)
CommandBlock("openclaw qr --setup-code-only")
CommandBlock("openclaw qr --json")
Text(
"For Tailscale or public hosts, use wss:// or Tailscale Serve. Private LAN ws:// remains supported.",
style = mobileCaption1,
color = mobileTextSecondary,
)
if (inputMode == ConnectInputMode.SetupCode) {
Text(stringResource(R.string.setup_code), style = mobileCaption1.copy(fontWeight = FontWeight.SemiBold), color = mobileTextSecondary)
OutlinedTextField(
value = setupCode,
onValueChange = {
setupCode = it
validationText = null
},
placeholder = { Text(stringResource(R.string.paste_setup_code), style = mobileBody, color = mobileTextTertiary) },
modifier = Modifier.fillMaxWidth(),
minLines = 3,
maxLines = 5,
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Ascii),
textStyle = mobileBody.copy(fontFamily = FontFamily.Monospace, color = mobileText),
shape = RoundedCornerShape(14.dp),
colors = outlinedColors(),
)
if (!setupResolvedEndpoint.isNullOrBlank()) {
EndpointPreview(endpoint = setupResolvedEndpoint)
}
} else {
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
QuickFillChip(
label = "Android Emulator",
onClick = {
manualHostInput = "10.0.2.2"
manualPortInput = "18789"
manualTlsInput = false
validationText = null
},
)
QuickFillChip(
label = "Localhost",
onClick = {
manualHostInput = "127.0.0.1"
manualPortInput = "18789"
manualTlsInput = false
validationText = null
},
)
}
Text(stringResource(R.string.host), style = mobileCaption1.copy(fontWeight = FontWeight.SemiBold), color = mobileTextSecondary)
OutlinedTextField(
value = manualHostInput,
onValueChange = {
manualHostInput = it
validationText = null
},
placeholder = { Text("10.0.2.2", style = mobileBody, color = mobileTextTertiary) },
modifier = Modifier.fillMaxWidth(),
singleLine = true,
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Uri),
textStyle = mobileBody.copy(color = mobileText),
shape = RoundedCornerShape(14.dp),
colors = outlinedColors(),
)
Text(
if (manualTlsInput) "Port (optional, defaults to 443)" else "Port",
style = mobileCaption1.copy(fontWeight = FontWeight.SemiBold),
color = mobileTextSecondary,
)
OutlinedTextField(
value = manualPortInput,
onValueChange = {
manualPortInput = it
validationText = null
},
placeholder = { Text(if (manualTlsInput) "443" else "18789", style = mobileBody, color = mobileTextTertiary) },
modifier = Modifier.fillMaxWidth(),
singleLine = true,
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
textStyle = mobileBody.copy(fontFamily = FontFamily.Monospace, color = mobileText),
shape = RoundedCornerShape(14.dp),
colors = outlinedColors(),
)
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween,
) {
Column(verticalArrangement = Arrangement.spacedBy(2.dp)) {
Text(stringResource(R.string.use_tls), style = mobileHeadline, color = mobileText)
Text(
"Turn this on for Tailscale or public hosts. Private LAN ws:// remains supported.",
style = mobileCallout,
color = mobileTextSecondary,
)
}
Switch(
checked = manualTlsInput,
onCheckedChange = {
manualTlsInput = it
validationText = null
},
colors =
SwitchDefaults.colors(
checkedTrackColor = mobileAccent,
uncheckedTrackColor = mobileBorderStrong,
checkedThumbColor = Color.White,
uncheckedThumbColor = Color.White,
),
)
}
Text(stringResource(R.string.token_optional), style = mobileCaption1.copy(fontWeight = FontWeight.SemiBold), color = mobileTextSecondary)
OutlinedTextField(
value = tokenInput,
onValueChange = { tokenInput = it },
placeholder = { Text("Leave blank to keep saved token", style = mobileBody, color = mobileTextTertiary) },
modifier = Modifier.fillMaxWidth(),
singleLine = true,
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Ascii),
textStyle = mobileBody.copy(color = mobileText),
shape = RoundedCornerShape(14.dp),
colors = outlinedColors(),
)
Text(
"Password (optional)",
style = mobileCaption1.copy(fontWeight = FontWeight.SemiBold),
color = mobileTextSecondary,
)
OutlinedTextField(
value = passwordInput,
onValueChange = { passwordInput = it },
placeholder = { Text(stringResource(R.string.password), style = mobileBody, color = mobileTextTertiary) },
modifier = Modifier.fillMaxWidth(),
singleLine = true,
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Ascii),
textStyle = mobileBody.copy(color = mobileText),
shape = RoundedCornerShape(14.dp),
colors = outlinedColors(),
)
if (!manualResolvedEndpoint.isNullOrBlank()) {
EndpointPreview(endpoint = manualResolvedEndpoint)
}
// Proxy credentials are unavailable on cleartext ws:// routes, including pasted URLs
// whose explicit scheme overrides the TLS toggle. The transport enforces this too.
manualEndpointConfig?.takeIf { it.tls }?.let { endpoint ->
ManualGatewayCustomHeadersEditor(
viewModel = viewModel,
stableId = GatewayEndpoint.manual(host = endpoint.host, port = endpoint.port).stableId,
)
}
}
HorizontalDivider(color = mobileBorder)
TextButton(onClick = { viewModel.setOnboardingCompleted(false) }) {
Text(stringResource(R.string.run_onboarding_again), style = mobileCallout.copy(fontWeight = FontWeight.SemiBold), color = mobileAccent)
}
}
}
}
if (!validationText.isNullOrBlank()) {
Text(validationText!!, style = mobileCaption1, color = mobileWarning)
}
}
}
/**
* Per-gateway proxy credential headers for the manual endpoint (Cloudflare Access-style
* service tokens). Values are secrets: masked while typing, hidden once saved, stored
* encrypted, and applied on the next reconnect.
*/
@Composable
private fun ManualGatewayCustomHeadersEditor(
viewModel: MainViewModel,
stableId: String,
) {
var headers by remember(stableId) { mutableStateOf(viewModel.gatewayCustomHeaders(stableId)) }
var nameInput by rememberSaveable(stableId) { mutableStateOf("") }
// Not rememberSaveable: header values are credentials and must not land in saved instance state.
var valueInput by remember(stableId) { mutableStateOf("") }
val trimmedName = nameInput.trim()
val nameIsReserved = GatewayCustomHeaders.isReservedName(trimmedName)
val nameIsDuplicate = headers.keys.any { it.equals(trimmedName, ignoreCase = true) }
fun persist(updated: Map<String, String>) {
viewModel.setGatewayCustomHeaders(stableId, updated)
headers = viewModel.gatewayCustomHeaders(stableId)
}
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
Text(
stringResource(R.string.custom_headers_optional),
style = mobileCaption1.copy(fontWeight = FontWeight.SemiBold),
color = mobileTextSecondary,
)
Text(
stringResource(R.string.custom_headers_explainer),
style = mobileCaption1,
color = mobileTextSecondary,
)
for (name in headers.keys.sorted()) {
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween,
) {
Text(name, style = mobileBody.copy(fontFamily = FontFamily.Monospace), color = mobileText)
IconButton(onClick = { persist(headers - name) }) {
Icon(
Icons.Default.Close,
contentDescription = stringResource(R.string.remove_custom_header, name),
tint = mobileTextSecondary,
)
}
}
}
OutlinedTextField(
value = nameInput,
onValueChange = { nameInput = it },
placeholder = { Text("CF-Access-Client-Id", style = mobileBody, color = mobileTextTertiary) },
modifier = Modifier.fillMaxWidth(),
singleLine = true,
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Ascii),
textStyle = mobileBody.copy(fontFamily = FontFamily.Monospace, color = mobileText),
shape = RoundedCornerShape(14.dp),
colors = outlinedColors(),
)
OutlinedTextField(
value = valueInput,
onValueChange = { valueInput = it },
placeholder = { Text(stringResource(R.string.custom_header_value), style = mobileBody, color = mobileTextTertiary) },
modifier = Modifier.fillMaxWidth(),
singleLine = true,
visualTransformation = PasswordVisualTransformation(),
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password),
textStyle = mobileBody.copy(color = mobileText),
shape = RoundedCornerShape(14.dp),
colors = outlinedColors(),
)
if (nameIsReserved) {
Text(stringResource(R.string.custom_header_reserved), style = mobileCaption1, color = mobileWarning)
}
Button(
onClick = {
persist(headers + (trimmedName to valueInput))
nameInput = ""
valueInput = ""
},
enabled = trimmedName.isNotEmpty() && !nameIsReserved && !nameIsDuplicate,
modifier = Modifier.height(40.dp),
shape = RoundedCornerShape(12.dp),
contentPadding = PaddingValues(horizontal = 12.dp, vertical = 8.dp),
colors =
ButtonDefaults.buttonColors(
containerColor = mobileSurface,
contentColor = mobileText,
),
border = BorderStroke(1.dp, mobileBorderStrong),
) {
Text(stringResource(R.string.add_custom_header), style = mobileCaption1.copy(fontWeight = FontWeight.Bold))
}
}
}
private fun gatewayPairingInstruction(problem: GatewayConnectionProblem?): String =
if (problem?.canAutoRetry == true) {
"Approve this phone on the gateway. OpenClaw will reconnect automatically."
} else {
"Approve this phone on the gateway, then retry the connection."
}
@Composable
private fun MethodChip(
label: String,
active: Boolean,
onClick: () -> Unit,
) {
Button(
onClick = onClick,
modifier = Modifier.height(40.dp),
shape = RoundedCornerShape(12.dp),
contentPadding = PaddingValues(horizontal = 12.dp, vertical = 8.dp),
colors =
ButtonDefaults.buttonColors(
containerColor = if (active) mobileAccent else mobileSurface,
contentColor = if (active) Color.White else mobileText,
),
border = BorderStroke(1.dp, if (active) mobileAccentBorderStrong else mobileBorderStrong),
) {
Text(label, style = mobileCaption1.copy(fontWeight = FontWeight.Bold))
}
}
@Composable
private fun QuickFillChip(
label: String,
onClick: () -> Unit,
) {
Button(
onClick = onClick,
shape = RoundedCornerShape(999.dp),
contentPadding = PaddingValues(horizontal = 12.dp, vertical = 6.dp),
colors =
ButtonDefaults.buttonColors(
containerColor = mobileAccentSoft,
contentColor = mobileAccent,
),
elevation = null,
) {
Text(label, style = mobileCaption1.copy(fontWeight = FontWeight.SemiBold))
}
}
@Composable
private fun CommandBlock(command: String) {
Surface(
modifier = Modifier.fillMaxWidth(),
shape = RoundedCornerShape(12.dp),
color = mobileCodeBg,
border = BorderStroke(1.dp, mobileCodeBorder),
) {
Row(modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
Box(modifier = Modifier.width(3.dp).height(42.dp).background(mobileCodeAccent))
Text(
text = command,
modifier = Modifier.padding(horizontal = 12.dp, vertical = 10.dp),
style = mobileCallout.copy(fontFamily = FontFamily.Monospace),
color = mobileCodeText,
)
}
}
}
@Composable
private fun EndpointPreview(endpoint: String) {
Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
HorizontalDivider(color = mobileBorder)
Text(
stringResource(R.string.resolved_endpoint),
style = mobileCaption1.copy(fontWeight = FontWeight.SemiBold),
color = mobileTextSecondary,
)
Text(endpoint, style = mobileCallout.copy(fontFamily = FontFamily.Monospace), color = mobileText)
HorizontalDivider(color = mobileBorder)
}
}
@Composable
private fun outlinedColors() =
OutlinedTextFieldDefaults.colors(
focusedContainerColor = mobileSurface,
unfocusedContainerColor = mobileSurface,
focusedBorderColor = mobileAccent,
unfocusedBorderColor = mobileBorder,
focusedTextColor = mobileText,
unfocusedTextColor = mobileText,
cursorColor = mobileAccent,
)

View file

@ -165,15 +165,6 @@ internal val mobileFontFamily =
Font(resId = R.font.manrope_700_bold, weight = FontWeight.Bold),
)
internal val mobileDisplay =
TextStyle(
fontFamily = mobileFontFamily,
fontWeight = FontWeight.Bold,
fontSize = 34.sp,
lineHeight = 40.sp,
letterSpacing = (-0.8).sp,
)
internal val mobileTitle1 =
TextStyle(
fontFamily = mobileFontFamily,
@ -183,15 +174,6 @@ internal val mobileTitle1 =
letterSpacing = (-0.5).sp,
)
internal val mobileTitle2 =
TextStyle(
fontFamily = mobileFontFamily,
fontWeight = FontWeight.SemiBold,
fontSize = 20.sp,
lineHeight = 26.sp,
letterSpacing = (-0.3).sp,
)
internal val mobileHeadline =
TextStyle(
fontFamily = mobileFontFamily,

View file

@ -1,528 +0,0 @@
package ai.openclaw.app.ui
import ai.openclaw.app.MainViewModel
import ai.openclaw.app.VoiceCaptureMode
import ai.openclaw.app.voice.VoiceConversationEntry
import ai.openclaw.app.voice.VoiceConversationRole
import android.Manifest
import android.app.Activity
import android.content.Context
import android.content.ContextWrapper
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.provider.Settings
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.WindowInsetsSides
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeDrawing
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.VolumeOff
import androidx.compose.material.icons.automirrored.filled.VolumeUp
import androidx.compose.material.icons.filled.Mic
import androidx.compose.material.icons.filled.MicOff
import androidx.compose.material.icons.filled.RecordVoiceOver
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.IconButtonDefaults
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.compose.LocalLifecycleOwner
import kotlin.math.max
/**
* Voice tab that switches between push-to-send mic capture and continuous Talk Mode.
*/
@Composable
fun VoiceTabScreen(viewModel: MainViewModel) {
val context = LocalContext.current
val lifecycleOwner = LocalLifecycleOwner.current
val activity = remember(context) { context.findActivity() }
val listState = rememberLazyListState()
val gatewayStatus by viewModel.statusText.collectAsState()
val voiceCaptureMode by viewModel.voiceCaptureMode.collectAsState()
val micEnabled by viewModel.micEnabled.collectAsState()
val micCooldown by viewModel.micCooldown.collectAsState()
val speakerEnabled by viewModel.speakerEnabled.collectAsState()
val micStatusText by viewModel.micStatusText.collectAsState()
val micLiveTranscript by viewModel.micLiveTranscript.collectAsState()
val micQueuedMessages by viewModel.micQueuedMessages.collectAsState()
val micConversation by viewModel.micConversation.collectAsState()
val micInputLevel by viewModel.micInputLevel.collectAsState()
val micIsSending by viewModel.micIsSending.collectAsState()
val talkModeEnabled by viewModel.talkModeEnabled.collectAsState()
val talkModeListening by viewModel.talkModeListening.collectAsState()
val talkModeSpeaking by viewModel.talkModeSpeaking.collectAsState()
val talkModeConversation by viewModel.talkModeConversation.collectAsState()
val activeConversation = if (voiceCaptureMode == VoiceCaptureMode.TalkMode) talkModeConversation else micConversation
val hasStreamingAssistant = activeConversation.any { it.role == VoiceConversationRole.Assistant && it.isStreaming }
val showThinkingBubble = micIsSending && !hasStreamingAssistant
var hasMicPermission by remember { mutableStateOf(context.hasRecordAudioPermission()) }
var pendingVoicePermissionAction by remember { mutableStateOf<PendingVoicePermissionAction?>(null) }
DisposableEffect(lifecycleOwner, context) {
val observer =
LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_RESUME) {
hasMicPermission = context.hasRecordAudioPermission()
}
}
lifecycleOwner.lifecycle.addObserver(observer)
onDispose {
lifecycleOwner.lifecycle.removeObserver(observer)
// Manual mic is tab-scoped; Talk Mode is user-enabled and can continue elsewhere.
viewModel.setVoiceScreenActive(false)
}
}
val requestMicPermission =
rememberLauncherForActivityResult(ActivityResultContracts.RequestPermission()) { granted ->
hasMicPermission = granted
if (granted) {
when (pendingVoicePermissionAction) {
PendingVoicePermissionAction.ManualMic -> viewModel.setMicEnabled(true)
PendingVoicePermissionAction.TalkMode -> viewModel.setTalkModeEnabled(true)
null -> Unit
}
}
pendingVoicePermissionAction = null
}
LaunchedEffect(voiceCaptureMode, activeConversation.size, showThinkingBubble) {
val total = activeConversation.size + if (showThinkingBubble) 1 else 0
if (total > 0) {
listState.animateScrollToItem(total - 1)
}
}
Column(
modifier =
Modifier
.fillMaxSize()
.background(mobileBackgroundGradient)
.imePadding()
.windowInsetsPadding(WindowInsets.safeDrawing.only(WindowInsetsSides.Bottom))
.padding(horizontal = 20.dp, vertical = 14.dp),
verticalArrangement = Arrangement.spacedBy(10.dp),
) {
LazyColumn(
state = listState,
modifier = Modifier.fillMaxWidth().weight(1f),
contentPadding = PaddingValues(vertical = 4.dp),
verticalArrangement = Arrangement.spacedBy(10.dp),
) {
if (activeConversation.isEmpty() && !showThinkingBubble) {
item {
Box(
modifier = Modifier.fillParentMaxHeight().fillMaxWidth(),
contentAlignment = Alignment.Center,
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(10.dp),
) {
Icon(
imageVector = Icons.Default.Mic,
contentDescription = null,
modifier = Modifier.size(48.dp),
tint = mobileTextTertiary,
)
Text(
"Tap mic or Talk",
style = mobileHeadline,
color = mobileTextSecondary,
)
Text(
"Mic sends turns; Talk keeps the conversation open.",
style = mobileCallout,
color = mobileTextTertiary,
)
}
}
}
}
items(items = activeConversation, key = { it.id }) { entry ->
VoiceTurnBubble(entry = entry)
}
if (showThinkingBubble) {
item {
VoiceThinkingBubble()
}
}
}
Column(
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(6.dp),
) {
if (!micLiveTranscript.isNullOrBlank()) {
Surface(
modifier = Modifier.fillMaxWidth(),
shape = RoundedCornerShape(14.dp),
color = mobileAccentSoft,
border = BorderStroke(1.dp, mobileAccent.copy(alpha = 0.2f)),
) {
Text(
micLiveTranscript!!.trim(),
modifier = Modifier.padding(horizontal = 12.dp, vertical = 10.dp),
style = mobileCallout,
color = mobileText,
)
}
}
// Mic button with input-reactive ring + speaker toggle
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically,
) {
// Speaker toggle
Column(horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy(4.dp)) {
IconButton(
onClick = { viewModel.setSpeakerEnabled(!speakerEnabled) },
modifier = Modifier.size(48.dp),
colors =
IconButtonDefaults.iconButtonColors(
containerColor = if (speakerEnabled) mobileSurface else mobileDangerSoft,
),
) {
Icon(
imageVector = if (speakerEnabled) Icons.AutoMirrored.Filled.VolumeUp else Icons.AutoMirrored.Filled.VolumeOff,
contentDescription = if (speakerEnabled) "Mute speaker" else "Unmute speaker",
modifier = Modifier.size(22.dp),
tint = if (speakerEnabled) mobileTextSecondary else mobileDanger,
)
}
Text(
if (speakerEnabled) "Speaker" else "Muted",
style = mobileCaption2,
color = if (speakerEnabled) mobileTextTertiary else mobileDanger,
)
}
// Ring size = 68dp base + up to 22dp driven by mic input level.
// The outer Box is fixed at 90dp (max ring size) so the ring never shifts the button.
Box(
modifier = Modifier.padding(horizontal = 16.dp).size(90.dp),
contentAlignment = Alignment.Center,
) {
if (micEnabled) {
val ringLevel = micInputLevel.coerceIn(0f, 1f)
val ringSize = 68.dp + (22.dp * max(ringLevel, 0.05f))
Box(
modifier =
Modifier
.size(ringSize)
.background(mobileAccent.copy(alpha = 0.12f + 0.14f * ringLevel), CircleShape),
)
}
Button(
onClick = {
if (micCooldown) return@Button
if (micEnabled) {
viewModel.setMicEnabled(false)
return@Button
}
if (hasMicPermission) {
viewModel.setMicEnabled(true)
} else {
pendingVoicePermissionAction = PendingVoicePermissionAction.ManualMic
requestMicPermission.launch(Manifest.permission.RECORD_AUDIO)
}
},
enabled = !micCooldown,
shape = CircleShape,
contentPadding = PaddingValues(0.dp),
modifier = Modifier.size(60.dp),
colors =
ButtonDefaults.buttonColors(
containerColor =
if (micCooldown) {
mobileTextSecondary
} else if (micEnabled) {
mobileDanger
} else {
mobileAccent
},
contentColor = Color.White,
disabledContainerColor = mobileTextSecondary,
disabledContentColor = Color.White.copy(alpha = 0.5f),
),
) {
Icon(
imageVector = if (micEnabled) Icons.Default.MicOff else Icons.Default.Mic,
contentDescription = if (micEnabled) "Turn microphone off" else "Turn microphone on",
modifier = Modifier.size(24.dp),
)
}
}
Column(horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy(4.dp)) {
IconButton(
onClick = {
if (talkModeEnabled) {
viewModel.setTalkModeEnabled(false)
return@IconButton
}
if (hasMicPermission) {
viewModel.setTalkModeEnabled(true)
} else {
pendingVoicePermissionAction = PendingVoicePermissionAction.TalkMode
requestMicPermission.launch(Manifest.permission.RECORD_AUDIO)
}
},
modifier = Modifier.size(48.dp),
colors =
IconButtonDefaults.iconButtonColors(
containerColor = if (talkModeEnabled) mobileSuccessSoft else mobileSurface,
),
) {
Icon(
imageVector = Icons.Default.RecordVoiceOver,
contentDescription = if (talkModeEnabled) "Turn Talk Mode off" else "Turn Talk Mode on",
modifier = Modifier.size(22.dp),
tint = if (talkModeEnabled) mobileSuccess else mobileTextSecondary,
)
}
Spacer(modifier = Modifier.height(4.dp))
Text(
if (talkModeEnabled) "Talk on" else "Talk",
style = mobileCaption2,
color = if (talkModeEnabled) mobileSuccess else mobileTextTertiary,
)
}
}
// Status + labels
val queueCount = micQueuedMessages.size
val stateText =
when {
voiceCaptureMode == VoiceCaptureMode.TalkMode && talkModeSpeaking -> "Talk speaking"
voiceCaptureMode == VoiceCaptureMode.TalkMode && talkModeListening -> "Talk listening"
voiceCaptureMode == VoiceCaptureMode.TalkMode -> "Talk on"
micEnabled || micIsSending || micCooldown -> micStatusText
queueCount > 0 -> "$queueCount queued"
else -> "Mic off"
}
val stateColor =
when {
voiceCaptureMode == VoiceCaptureMode.TalkMode -> mobileSuccess
micEnabled -> mobileSuccess
micIsSending -> mobileAccent
else -> mobileTextSecondary
}
Surface(
shape = RoundedCornerShape(999.dp),
color = if (micEnabled || talkModeEnabled) mobileSuccessSoft else mobileSurface,
border = BorderStroke(1.dp, if (micEnabled || talkModeEnabled) mobileSuccess.copy(alpha = 0.3f) else mobileBorder),
) {
Text(
"$gatewayStatus · $stateText",
style = mobileCallout.copy(fontWeight = FontWeight.SemiBold),
color = stateColor,
modifier = Modifier.padding(horizontal = 14.dp, vertical = 6.dp),
)
}
if (!hasMicPermission) {
val showRationale =
if (activity == null) {
false
} else {
ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.RECORD_AUDIO)
}
Text(
if (showRationale) {
"Microphone permission is required for voice mode."
} else {
"Microphone blocked. Open app settings to enable it."
},
style = mobileCaption1,
color = mobileWarning,
textAlign = TextAlign.Center,
)
Button(
onClick = { openAppSettings(context) },
shape = RoundedCornerShape(12.dp),
colors = ButtonDefaults.buttonColors(containerColor = mobileSurfaceStrong, contentColor = mobileText),
) {
Text("Open settings", style = mobileCallout.copy(fontWeight = FontWeight.SemiBold))
}
}
}
}
}
/**
* Permission continuation captured before Android's runtime permission dialog suspends the action.
*/
private enum class PendingVoicePermissionAction {
ManualMic,
TalkMode,
}
/**
* Renders one transcript turn, preserving side and color by speaker role.
*/
@Composable
private fun VoiceTurnBubble(entry: VoiceConversationEntry) {
val isUser = entry.role == VoiceConversationRole.User
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = if (isUser) Arrangement.End else Arrangement.Start,
) {
Surface(
modifier = Modifier.fillMaxWidth(0.90f),
shape = RoundedCornerShape(12.dp),
color = if (isUser) mobileAccentSoft else mobileCardSurface,
border = BorderStroke(1.dp, if (isUser) mobileAccent else mobileBorderStrong),
) {
Column(
modifier = Modifier.fillMaxWidth().padding(horizontal = 11.dp, vertical = 8.dp),
verticalArrangement = Arrangement.spacedBy(3.dp),
) {
Text(
if (isUser) "You" else "OpenClaw",
style = mobileCaption2.copy(fontWeight = FontWeight.SemiBold, letterSpacing = 0.6.sp),
color = if (isUser) mobileAccent else mobileTextSecondary,
)
Text(
if (entry.isStreaming && entry.text.isBlank()) "Listening response…" else entry.text,
style = mobileCallout,
color = mobileText,
)
}
}
}
}
/**
* Placeholder assistant turn shown while a manual mic request is queued but not streaming yet.
*/
@Composable
private fun VoiceThinkingBubble() {
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Start) {
Surface(
modifier = Modifier.fillMaxWidth(0.68f),
shape = RoundedCornerShape(12.dp),
color = mobileCardSurface,
border = BorderStroke(1.dp, mobileBorderStrong),
) {
Row(
modifier = Modifier.padding(horizontal = 11.dp, vertical = 8.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalAlignment = Alignment.CenterVertically,
) {
ThinkingDots(color = mobileTextSecondary)
Text("OpenClaw is thinking…", style = mobileCallout, color = mobileTextSecondary)
}
}
}
}
/**
* Static dot cluster used by VoiceThinkingBubble to avoid starting another animation loop.
*/
@Composable
private fun ThinkingDots(color: Color) {
Row(horizontalArrangement = Arrangement.spacedBy(5.dp), verticalAlignment = Alignment.CenterVertically) {
ThinkingDot(alpha = 0.38f, color = color)
ThinkingDot(alpha = 0.62f, color = color)
ThinkingDot(alpha = 0.90f, color = color)
}
}
@Composable
private fun ThinkingDot(
alpha: Float,
color: Color,
) {
Surface(
modifier = Modifier.size(6.dp).alpha(alpha),
shape = CircleShape,
color = color,
) {}
}
/**
* Checks RECORD_AUDIO using ContextCompat so wrapped activity contexts behave the same.
*/
private fun Context.hasRecordAudioPermission(): Boolean =
(
ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) ==
PackageManager.PERMISSION_GRANTED
)
/**
* Walks ContextWrappers until an Activity is found for permission rationale checks.
*/
private fun Context.findActivity(): Activity? =
when (this) {
is Activity -> this
is ContextWrapper -> baseContext.findActivity()
else -> null
}
/**
* Opens this app's settings page after Android reports the mic permission as blocked.
*/
private fun openAppSettings(context: Context) {
val intent =
Intent(
Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
Uri.fromParts("package", context.packageName, null),
)
context.startActivity(intent)
}

View file

@ -1,37 +1,9 @@
<resources>
<string name="app_name">OpenClaw Node</string>
<string name="gateway_connection">اتصال البوابة</string>
<string name="connect_gateway">توصيل البوابة</string>
<string name="disconnect">قطع الاتصال</string>
<string name="trust_this_gateway">هل تثق بهذه البوابة؟</string>
<string name="trust_and_continue">الثقة والمتابعة</string>
<string name="cancel">إلغاء</string>
<string name="new_chat_in_worktree">دردشة جديدة في worktree</string>
<string name="endpoint">نقطة النهاية</string>
<string name="status">الحالة</string>
<string name="connected_gateway_ready">بوابتك نشطة وجاهزة.</string>
<string name="connect_gateway_get_started">اتصل ببوابتك للبدء.</string>
<string name="copy_report_for_claw">نسخ التقرير لـ Claw</string>
<string name="advanced_controls">عناصر التحكم المتقدمة</string>
<string name="connection_method">طريقة الاتصال</string>
<string name="setup_code">رمز الإعداد</string>
<string name="manual">يدوي</string>
<string name="paste_setup_code">الصق رمز الإعداد</string>
<string name="host">المضيف</string>
<string name="use_tls">استخدام TLS</string>
<string name="token_optional">الرمز المميز (اختياري)</string>
<string name="password">كلمة المرور</string>
<string name="custom_headers_optional">رؤوس مخصصة (اختياري)</string>
<string name="custom_headers_explainer">تُرسل مع كل اتصال بهذا الـ gateway، على سبيل المثال رؤوس رمز خدمة Cloudflare Access. تُخزَّن مشفّرة وتُطبَّق عند إعادة الاتصال التالية.</string>
<string name="custom_header_value">قيمة الرأس</string>
<string name="custom_header_reserved">تتم إدارة هذا الرأس بواسطة الاتصال ولا يمكن تجاوزه.</string>
<string name="add_custom_header">إضافة رأس</string>
<string name="remove_custom_header">إزالة %1$s</string>
<string name="run_onboarding_again">تشغيل الإعداد الأولي مرة أخرى</string>
<string name="resolved_endpoint">نقطة النهاية التي تم حلها</string>
<string name="gateway_trust_first_seen">تحقق من بصمة الشهادة قبل الوثوق بهذه البوابة.\n\n%1$s</string>
<string name="gateway_trust_changed">تم تغيير شهادة البوابة. تابع فقط إذا كنت تتوقع هذا التغيير.\n\nSHA-256 القديم:\n%1$s\n\nSHA-256 الجديد:\n%2$s</string>
<string name="switch_gateway">تبديل Gateway</string>
<string name="select_gateway">تحديد Gateway</string>
<string name="active_gateway">Gateway النشط</string>
</resources>

View file

@ -1,37 +1,9 @@
<resources>
<string name="app_name">OpenClaw Node</string>
<string name="gateway_connection">Gateway-Verbindung</string>
<string name="connect_gateway">Gateway verbinden</string>
<string name="disconnect">Trennen</string>
<string name="trust_this_gateway">Diesem Gateway vertrauen?</string>
<string name="trust_and_continue">Vertrauen und fortfahren</string>
<string name="cancel">Abbrechen</string>
<string name="new_chat_in_worktree">Neuer Chat im Worktree</string>
<string name="endpoint">Endpunkt</string>
<string name="status">Status</string>
<string name="connected_gateway_ready">Ihr Gateway ist aktiv und bereit.</string>
<string name="connect_gateway_get_started">Verbinden Sie sich mit Ihrem Gateway, um loszulegen.</string>
<string name="copy_report_for_claw">Bericht für Claw kopieren</string>
<string name="advanced_controls">Erweiterte Steuerungen</string>
<string name="connection_method">Verbindungsmethode</string>
<string name="setup_code">Einrichtungscode</string>
<string name="manual">Manuell</string>
<string name="paste_setup_code">Einrichtungscode einfügen</string>
<string name="host">Host</string>
<string name="use_tls">TLS verwenden</string>
<string name="token_optional">Token (optional)</string>
<string name="password">Passwort</string>
<string name="custom_headers_optional">Benutzerdefinierte Header (optional)</string>
<string name="custom_headers_explainer">Wird bei jeder Verbindung zu diesem Gateway gesendet, zum Beispiel Cloudflare Access-Diensttoken-Header. Wird verschlüsselt gespeichert und beim nächsten erneuten Verbindungsaufbau angewendet.</string>
<string name="custom_header_value">Header-Wert</string>
<string name="custom_header_reserved">Dieser Header wird von der Verbindung verwaltet und kann nicht überschrieben werden.</string>
<string name="add_custom_header">Header hinzufügen</string>
<string name="remove_custom_header">%1$s entfernen</string>
<string name="run_onboarding_again">Onboarding erneut ausführen</string>
<string name="resolved_endpoint">Aufgelöster Endpunkt</string>
<string name="gateway_trust_first_seen">Überprüfen Sie den Zertifikatfingerabdruck, bevor Sie diesem Gateway vertrauen.\n\n%1$s</string>
<string name="gateway_trust_changed">Das Gateway-Zertifikat wurde geändert. Fahren Sie nur fort, wenn Sie diese Änderung erwartet haben.\n\nAlter SHA-256-Wert:\n%1$s\n\nNeuer SHA-256-Wert:\n%2$s</string>
<string name="switch_gateway">Gateway wechseln</string>
<string name="select_gateway">Gateway auswählen</string>
<string name="active_gateway">Aktives Gateway</string>
</resources>

View file

@ -1,37 +1,9 @@
<resources>
<string name="app_name">OpenClaw Node</string>
<string name="gateway_connection">Conexión de Gateway</string>
<string name="connect_gateway">Conectar Gateway</string>
<string name="disconnect">Desconectar</string>
<string name="trust_this_gateway">¿Confiar en este gateway?</string>
<string name="trust_and_continue">Confiar y continuar</string>
<string name="cancel">Cancelar</string>
<string name="new_chat_in_worktree">Nuevo chat en worktree</string>
<string name="endpoint">Endpoint</string>
<string name="status">Estado</string>
<string name="connected_gateway_ready">Tu gateway está activo y listo.</string>
<string name="connect_gateway_get_started">Conéctate a tu gateway para empezar.</string>
<string name="copy_report_for_claw">Copiar informe para Claw</string>
<string name="advanced_controls">Controles avanzados</string>
<string name="connection_method">Método de conexión</string>
<string name="setup_code">Código de configuración</string>
<string name="manual">Manual</string>
<string name="paste_setup_code">Pegar código de configuración</string>
<string name="host">Host</string>
<string name="use_tls">Usar TLS</string>
<string name="token_optional">Token (opcional)</string>
<string name="password">Contraseña</string>
<string name="custom_headers_optional">Encabezados personalizados (opcional)</string>
<string name="custom_headers_explainer">Se envía con cada conexión a este Gateway, por ejemplo, encabezados de token de servicio de Cloudflare Access. Se almacena cifrado y se aplica en la próxima reconexión.</string>
<string name="custom_header_value">Valor del encabezado</string>
<string name="custom_header_reserved">Este encabezado lo gestiona la conexión y no se puede sobrescribir.</string>
<string name="add_custom_header">Añadir encabezado</string>
<string name="remove_custom_header">Eliminar %1$s</string>
<string name="run_onboarding_again">Ejecutar la incorporación de nuevo</string>
<string name="resolved_endpoint">Endpoint resuelto</string>
<string name="gateway_trust_first_seen">Verifica la huella digital del certificado antes de confiar en este gateway.\n\n%1$s</string>
<string name="gateway_trust_changed">El certificado del gateway cambió. Continúa solo si esperabas este cambio.\n\nSHA-256 anterior:\n%1$s\n\nSHA-256 nuevo:\n%2$s</string>
<string name="switch_gateway">Cambiar de gateway</string>
<string name="select_gateway">Seleccionar gateway</string>
<string name="active_gateway">Gateway activo</string>
</resources>

View file

@ -1,37 +1,9 @@
<resources>
<string name="app_name">OpenClaw Node</string>
<string name="gateway_connection">اتصال دروازه</string>
<string name="connect_gateway">اتصال به دروازه</string>
<string name="disconnect">قطع اتصال</string>
<string name="trust_this_gateway">به این دروازه اعتماد دارید؟</string>
<string name="trust_and_continue">اعتماد و ادامه</string>
<string name="cancel">لغو</string>
<string name="new_chat_in_worktree">چت جدید در worktree</string>
<string name="endpoint">نقطه پایانی</string>
<string name="status">وضعیت</string>
<string name="connected_gateway_ready">دروازه شما فعال و آماده است.</string>
<string name="connect_gateway_get_started">برای شروع، به دروازه خود متصل شوید.</string>
<string name="copy_report_for_claw">کپی گزارش برای Claw</string>
<string name="advanced_controls">کنترل‌های پیشرفته</string>
<string name="connection_method">روش اتصال</string>
<string name="setup_code">کد راه‌اندازی</string>
<string name="manual">دستی</string>
<string name="paste_setup_code">کد راه‌اندازی را جای‌گذاری کنید</string>
<string name="host">میزبان</string>
<string name="use_tls">استفاده از TLS</string>
<string name="token_optional">توکن (اختیاری)</string>
<string name="password">رمز عبور</string>
<string name="custom_headers_optional">هدرهای سفارشی (اختیاری)</string>
<string name="custom_headers_explainer">با هر اتصال به این gateway ارسال می‌شود، برای مثال هدرهای توکن سرویس Cloudflare Access. به‌صورت رمزگذاری‌شده ذخیره می‌شود و در اتصال مجدد بعدی اعمال می‌شود.</string>
<string name="custom_header_value">مقدار هدر</string>
<string name="custom_header_reserved">این هدر توسط اتصال مدیریت می‌شود و قابل بازنویسی نیست.</string>
<string name="add_custom_header">افزودن هدر</string>
<string name="remove_custom_header">حذف %1$s</string>
<string name="run_onboarding_again">اجرای دوباره فرایند شروع به کار</string>
<string name="resolved_endpoint">نقطه پایانی حل‌شده</string>
<string name="gateway_trust_first_seen">پیش از اعتماد به این دروازه، اثر انگشت گواهی را تأیید کنید.\n\n%1$s</string>
<string name="gateway_trust_changed">گواهی دروازه تغییر کرده است. فقط در صورتی ادامه دهید که انتظار این تغییر را داشتید.\n\nSHA-256 قدیمی:\n%1$s\n\nSHA-256 جدید:\n%2$s</string>
<string name="switch_gateway">تغییر Gateway</string>
<string name="select_gateway">انتخاب Gateway</string>
<string name="active_gateway">Gateway فعال</string>
</resources>

View file

@ -1,37 +1,9 @@
<resources>
<string name="app_name">OpenClaw Node</string>
<string name="gateway_connection">Connexion à la passerelle</string>
<string name="connect_gateway">Connecter la passerelle</string>
<string name="disconnect">Déconnecter</string>
<string name="trust_this_gateway">Faire confiance à cette passerelle ?</string>
<string name="trust_and_continue">Faire confiance et continuer</string>
<string name="cancel">Annuler</string>
<string name="new_chat_in_worktree">Nouveau chat dans le worktree</string>
<string name="endpoint">Point de terminaison</string>
<string name="status">État</string>
<string name="connected_gateway_ready">Votre passerelle est active et prête.</string>
<string name="connect_gateway_get_started">Connectez-vous à votre passerelle pour commencer.</string>
<string name="copy_report_for_claw">Copier le rapport pour Claw</string>
<string name="advanced_controls">Contrôles avancés</string>
<string name="connection_method">Méthode de connexion</string>
<string name="setup_code">Code de configuration</string>
<string name="manual">Manuel</string>
<string name="paste_setup_code">Coller le code de configuration</string>
<string name="host">Hôte</string>
<string name="use_tls">Utiliser TLS</string>
<string name="token_optional">Jeton (facultatif)</string>
<string name="password">Mot de passe</string>
<string name="custom_headers_optional">En-têtes personnalisés (facultatif)</string>
<string name="custom_headers_explainer">Envoyés à chaque connexion à cette Gateway, par exemple les en-têtes de jeton de service Cloudflare Access. Stockés de manière chiffrée et appliqués lors de la prochaine reconnexion.</string>
<string name="custom_header_value">Valeur de len-tête</string>
<string name="custom_header_reserved">Cet en-tête est géré par la connexion et ne peut pas être remplacé.</string>
<string name="add_custom_header">Ajouter un en-tête</string>
<string name="remove_custom_header">Supprimer %1$s</string>
<string name="run_onboarding_again">Relancer lintégration</string>
<string name="resolved_endpoint">Point de terminaison résolu</string>
<string name="gateway_trust_first_seen">Vérifiez lempreinte du certificat avant daccorder votre confiance à cette passerelle.\n\n%1$s</string>
<string name="gateway_trust_changed">Le certificat de la passerelle a changé. Continuez uniquement si vous vous attendiez à ce changement.\n\nAncien SHA-256 :\n%1$s\n\nNouveau SHA-256 :\n%2$s</string>
<string name="switch_gateway">Changer de Gateway</string>
<string name="select_gateway">Sélectionner un Gateway</string>
<string name="active_gateway">Gateway actif</string>
</resources>

View file

@ -1,37 +1,9 @@
<resources>
<string name="app_name">OpenClaw Node</string>
<string name="gateway_connection">गेटवे कनेक्शन</string>
<string name="connect_gateway">गेटवे कनेक्ट करें</string>
<string name="disconnect">डिस्कनेक्ट करें</string>
<string name="trust_this_gateway">इस गेटवे पर भरोसा करें?</string>
<string name="trust_and_continue">भरोसा करें और जारी रखें</string>
<string name="cancel">रद्द करें</string>
<string name="new_chat_in_worktree">worktree में नया चैट</string>
<string name="endpoint">एंडपॉइंट</string>
<string name="status">स्थिति</string>
<string name="connected_gateway_ready">आपका गेटवे सक्रिय और तैयार है।</string>
<string name="connect_gateway_get_started">शुरू करने के लिए अपने गेटवे से कनेक्ट करें।</string>
<string name="copy_report_for_claw">Claw के लिए रिपोर्ट कॉपी करें</string>
<string name="advanced_controls">उन्नत नियंत्रण</string>
<string name="connection_method">कनेक्शन विधि</string>
<string name="setup_code">सेटअप कोड</string>
<string name="manual">मैन्युअल</string>
<string name="paste_setup_code">सेटअप कोड पेस्ट करें</string>
<string name="host">होस्ट</string>
<string name="use_tls">TLS का उपयोग करें</string>
<string name="token_optional">टोकन (वैकल्पिक)</string>
<string name="password">पासवर्ड</string>
<string name="custom_headers_optional">कस्टम हेडर (वैकल्पिक)</string>
<string name="custom_headers_explainer">इस gateway से हर कनेक्शन के साथ भेजा जाता है, उदाहरण के लिए Cloudflare Access service token headers. एन्क्रिप्ट करके संग्रहीत किया जाता है और अगले reconnect पर लागू किया जाता है।</string>
<string name="custom_header_value">हेडर मान</string>
<string name="custom_header_reserved">यह हेडर कनेक्शन द्वारा प्रबंधित है और इसे ओवरराइड नहीं किया जा सकता।</string>
<string name="add_custom_header">हेडर जोड़ें</string>
<string name="remove_custom_header">%1$s हटाएँ</string>
<string name="run_onboarding_again">ऑनबोर्डिंग फिर से चलाएँ</string>
<string name="resolved_endpoint">रिज़ॉल्व किया गया एंडपॉइंट</string>
<string name="gateway_trust_first_seen">इस गेटवे पर भरोसा करने से पहले प्रमाणपत्र फ़िंगरप्रिंट सत्यापित करें।\n\n%1$s</string>
<string name="gateway_trust_changed">गेटवे प्रमाणपत्र बदल गया है। केवल तभी जारी रखें जब आपको इस बदलाव की अपेक्षा थी।\n\nपुराना SHA-256:\n%1$s\n\nनया SHA-256:\n%2$s</string>
<string name="switch_gateway">Gateway बदलें</string>
<string name="select_gateway">Gateway चुनें</string>
<string name="active_gateway">सक्रिय gateway</string>
</resources>

View file

@ -1,37 +1,9 @@
<resources>
<string name="app_name">OpenClaw Node</string>
<string name="gateway_connection">Koneksi Gateway</string>
<string name="connect_gateway">Hubungkan Gateway</string>
<string name="disconnect">Putuskan koneksi</string>
<string name="trust_this_gateway">Percayai gateway ini?</string>
<string name="trust_and_continue">Percayai dan lanjutkan</string>
<string name="cancel">Batal</string>
<string name="new_chat_in_worktree">Chat baru di worktree</string>
<string name="endpoint">Endpoint</string>
<string name="status">Status</string>
<string name="connected_gateway_ready">Gateway Anda aktif dan siap.</string>
<string name="connect_gateway_get_started">Hubungkan ke gateway Anda untuk memulai.</string>
<string name="copy_report_for_claw">Salin Laporan untuk Claw</string>
<string name="advanced_controls">Kontrol lanjutan</string>
<string name="connection_method">Metode koneksi</string>
<string name="setup_code">Kode Penyiapan</string>
<string name="manual">Manual</string>
<string name="paste_setup_code">Tempel kode penyiapan</string>
<string name="host">Host</string>
<string name="use_tls">Gunakan TLS</string>
<string name="token_optional">Token (opsional)</string>
<string name="password">Kata sandi</string>
<string name="custom_headers_optional">Header kustom (opsional)</string>
<string name="custom_headers_explainer">Dikirim dengan setiap koneksi ke gateway ini, misalnya header token layanan Cloudflare Access. Disimpan secara terenkripsi dan diterapkan pada koneksi ulang berikutnya.</string>
<string name="custom_header_value">Nilai header</string>
<string name="custom_header_reserved">Header ini dikelola oleh koneksi dan tidak dapat ditimpa.</string>
<string name="add_custom_header">Tambahkan header</string>
<string name="remove_custom_header">Hapus %1$s</string>
<string name="run_onboarding_again">Jalankan onboarding lagi</string>
<string name="resolved_endpoint">Endpoint yang diselesaikan</string>
<string name="gateway_trust_first_seen">Verifikasi sidik jari sertifikat sebelum memercayai gateway ini.\n\n%1$s</string>
<string name="gateway_trust_changed">Sertifikat gateway berubah. Lanjutkan hanya jika Anda mengharapkan perubahan ini.\n\nSHA-256 lama:\n%1$s\n\nSHA-256 baru:\n%2$s</string>
<string name="switch_gateway">Ganti gateway</string>
<string name="select_gateway">Pilih gateway</string>
<string name="active_gateway">Gateway aktif</string>
</resources>

View file

@ -1,37 +1,9 @@
<resources>
<string name="app_name">OpenClaw Node</string>
<string name="gateway_connection">Connessione al gateway</string>
<string name="connect_gateway">Connetti gateway</string>
<string name="disconnect">Disconnetti</string>
<string name="trust_this_gateway">Considerare attendibile questo gateway?</string>
<string name="trust_and_continue">Considera attendibile e continua</string>
<string name="cancel">Annulla</string>
<string name="new_chat_in_worktree">Nuova chat nel worktree</string>
<string name="endpoint">Endpoint</string>
<string name="status">Stato</string>
<string name="connected_gateway_ready">Il tuo gateway è attivo e pronto.</string>
<string name="connect_gateway_get_started">Connettiti al tuo gateway per iniziare.</string>
<string name="copy_report_for_claw">Copia report per Claw</string>
<string name="advanced_controls">Controlli avanzati</string>
<string name="connection_method">Metodo di connessione</string>
<string name="setup_code">Codice di configurazione</string>
<string name="manual">Manuale</string>
<string name="paste_setup_code">Incolla codice di configurazione</string>
<string name="host">Host</string>
<string name="use_tls">Usa TLS</string>
<string name="token_optional">Token (opzionale)</string>
<string name="password">Password</string>
<string name="custom_headers_optional">Intestazioni personalizzate (facoltativo)</string>
<string name="custom_headers_explainer">Inviate con ogni connessione a questo gateway, ad esempio le intestazioni dei token del servizio Cloudflare Access. Archiviate crittografate e applicate alla prossima riconnessione.</string>
<string name="custom_header_value">Valore dell\'intestazione</string>
<string name="custom_header_reserved">Questa intestazione è gestita dalla connessione e non può essere sovrascritta.</string>
<string name="add_custom_header">Aggiungi intestazione</string>
<string name="remove_custom_header">Rimuovi %1$s</string>
<string name="run_onboarding_again">Esegui di nuovo lonboarding</string>
<string name="resolved_endpoint">Endpoint risolto</string>
<string name="gateway_trust_first_seen">Verifica limpronta digitale del certificato prima di considerare attendibile questo gateway.\n\n%1$s</string>
<string name="gateway_trust_changed">Il certificato del gateway è cambiato. Continua solo se ti aspettavi questa modifica.\n\nSHA-256 precedente:\n%1$s\n\nNuovo SHA-256:\n%2$s</string>
<string name="switch_gateway">Cambia gateway</string>
<string name="select_gateway">Seleziona gateway</string>
<string name="active_gateway">Gateway attivo</string>
</resources>

View file

@ -1,37 +1,9 @@
<resources>
<string name="app_name">OpenClaw Node</string>
<string name="gateway_connection">ゲートウェイ接続</string>
<string name="connect_gateway">ゲートウェイに接続</string>
<string name="disconnect">切断</string>
<string name="trust_this_gateway">このゲートウェイを信頼しますか?</string>
<string name="trust_and_continue">信頼して続行</string>
<string name="cancel">キャンセル</string>
<string name="new_chat_in_worktree">worktree で新規チャット</string>
<string name="endpoint">エンドポイント</string>
<string name="status">ステータス</string>
<string name="connected_gateway_ready">ゲートウェイはアクティブで準備完了です。</string>
<string name="connect_gateway_get_started">開始するにはゲートウェイに接続してください。</string>
<string name="copy_report_for_claw">Claw 用レポートをコピー</string>
<string name="advanced_controls">詳細コントロール</string>
<string name="connection_method">接続方法</string>
<string name="setup_code">セットアップコード</string>
<string name="manual">手動</string>
<string name="paste_setup_code">セットアップコードを貼り付け</string>
<string name="host">ホスト</string>
<string name="use_tls">TLS を使用</string>
<string name="token_optional">トークン(任意)</string>
<string name="password">パスワード</string>
<string name="custom_headers_optional">カスタムヘッダー(任意)</string>
<string name="custom_headers_explainer">このゲートウェイへのすべての接続で送信されます。たとえば、Cloudflare Access サービス トークン ヘッダーなどです。暗号化して保存され、次回の再接続時に適用されます。</string>
<string name="custom_header_value">ヘッダー値</string>
<string name="custom_header_reserved">このヘッダーは接続によって管理されており、上書きできません。</string>
<string name="add_custom_header">ヘッダーを追加</string>
<string name="remove_custom_header">%1$s を削除</string>
<string name="run_onboarding_again">オンボーディングを再実行</string>
<string name="resolved_endpoint">解決済みエンドポイント</string>
<string name="gateway_trust_first_seen">このゲートウェイを信頼する前に、証明書のフィンガープリントを確認してください。\n\n%1$s</string>
<string name="gateway_trust_changed">ゲートウェイの証明書が変更されました。想定した変更である場合のみ続行してください。\n\n以前の SHA-256:\n%1$s\n\n新しい SHA-256:\n%2$s</string>
<string name="switch_gateway">Gatewayを切り替え</string>
<string name="select_gateway">Gatewayを選択</string>
<string name="active_gateway">アクティブなGateway</string>
</resources>

View file

@ -1,37 +1,9 @@
<resources>
<string name="app_name">OpenClaw Node</string>
<string name="gateway_connection">게이트웨이 연결</string>
<string name="connect_gateway">게이트웨이 연결</string>
<string name="disconnect">연결 해제</string>
<string name="trust_this_gateway">이 게이트웨이를 신뢰하시겠습니까?</string>
<string name="trust_and_continue">신뢰하고 계속</string>
<string name="cancel">취소</string>
<string name="new_chat_in_worktree">worktree에서 새 채팅</string>
<string name="endpoint">엔드포인트</string>
<string name="status">상태</string>
<string name="connected_gateway_ready">게이트웨이가 활성화되어 준비되었습니다.</string>
<string name="connect_gateway_get_started">시작하려면 게이트웨이에 연결하세요.</string>
<string name="copy_report_for_claw">Claw용 보고서 복사</string>
<string name="advanced_controls">고급 제어</string>
<string name="connection_method">연결 방법</string>
<string name="setup_code">설정 코드</string>
<string name="manual">수동</string>
<string name="paste_setup_code">설정 코드 붙여넣기</string>
<string name="host">호스트</string>
<string name="use_tls">TLS 사용</string>
<string name="token_optional">토큰(선택 사항)</string>
<string name="password">비밀번호</string>
<string name="custom_headers_optional">사용자 지정 헤더(선택 사항)</string>
<string name="custom_headers_explainer">이 Gateway에 연결할 때마다 전송됩니다. 예: Cloudflare Access 서비스 토큰 헤더. 암호화되어 저장되며 다음 재연결 시 적용됩니다.</string>
<string name="custom_header_value">헤더 값</string>
<string name="custom_header_reserved">이 헤더는 연결에서 관리되며 재정의할 수 없습니다.</string>
<string name="add_custom_header">헤더 추가</string>
<string name="remove_custom_header">%1$s 제거</string>
<string name="run_onboarding_again">온보딩 다시 실행</string>
<string name="resolved_endpoint">확인된 엔드포인트</string>
<string name="gateway_trust_first_seen">이 게이트웨이를 신뢰하기 전에 인증서 지문을 확인하세요.\n\n%1$s</string>
<string name="gateway_trust_changed">게이트웨이 인증서가 변경되었습니다. 예상한 변경인 경우에만 계속하세요.\n\n이전 SHA-256:\n%1$s\n\n새 SHA-256:\n%2$s</string>
<string name="switch_gateway">Gateway 전환</string>
<string name="select_gateway">Gateway 선택</string>
<string name="active_gateway">활성 Gateway</string>
</resources>

View file

@ -1,37 +1,9 @@
<resources>
<string name="app_name">OpenClaw Node</string>
<string name="gateway_connection">Gatewayverbinding</string>
<string name="connect_gateway">Gateway verbinden</string>
<string name="disconnect">Verbinding verbreken</string>
<string name="trust_this_gateway">Deze gateway vertrouwen?</string>
<string name="trust_and_continue">Vertrouwen en doorgaan</string>
<string name="cancel">Annuleren</string>
<string name="new_chat_in_worktree">Nieuwe chat in worktree</string>
<string name="endpoint">Endpoint</string>
<string name="status">Status</string>
<string name="connected_gateway_ready">Je gateway is actief en klaar voor gebruik.</string>
<string name="connect_gateway_get_started">Verbind met je gateway om te beginnen.</string>
<string name="copy_report_for_claw">Rapport voor Claw kopiëren</string>
<string name="advanced_controls">Geavanceerde bediening</string>
<string name="connection_method">Verbindingsmethode</string>
<string name="setup_code">Setupcode</string>
<string name="manual">Handmatig</string>
<string name="paste_setup_code">Setupcode plakken</string>
<string name="host">Host</string>
<string name="use_tls">TLS gebruiken</string>
<string name="token_optional">Token (optioneel)</string>
<string name="password">Wachtwoord</string>
<string name="custom_headers_optional">Aangepaste headers (optioneel)</string>
<string name="custom_headers_explainer">Meegestuurd met elke verbinding met deze Gateway, bijvoorbeeld headers voor Cloudflare Access-servicetokens. Versleuteld opgeslagen en toegepast bij de volgende herverbinding.</string>
<string name="custom_header_value">Headerwaarde</string>
<string name="custom_header_reserved">Deze header wordt beheerd door de verbinding en kan niet worden overschreven.</string>
<string name="add_custom_header">Header toevoegen</string>
<string name="remove_custom_header">%1$s verwijderen</string>
<string name="run_onboarding_again">Onboarding opnieuw uitvoeren</string>
<string name="resolved_endpoint">Opgelost endpoint</string>
<string name="gateway_trust_first_seen">Controleer de certificaatvingerafdruk voordat je deze gateway vertrouwt.\n\n%1$s</string>
<string name="gateway_trust_changed">Het gatewaycertificaat is gewijzigd. Ga alleen door als je deze wijziging verwachtte.\n\nOude SHA-256:\n%1$s\n\nNieuwe SHA-256:\n%2$s</string>
<string name="switch_gateway">Van gateway wisselen</string>
<string name="select_gateway">Gateway selecteren</string>
<string name="active_gateway">Actieve gateway</string>
</resources>

View file

@ -1,37 +1,9 @@
<resources>
<string name="app_name">OpenClaw Node</string>
<string name="gateway_connection">Połączenie z bramą</string>
<string name="connect_gateway">Połącz z bramą</string>
<string name="disconnect">Rozłącz</string>
<string name="trust_this_gateway">Ufać tej bramie?</string>
<string name="trust_and_continue">Zaufaj i kontynuuj</string>
<string name="cancel">Anuluj</string>
<string name="new_chat_in_worktree">Nowy czat w worktree</string>
<string name="endpoint">Punkt końcowy</string>
<string name="status">Status</string>
<string name="connected_gateway_ready">Twoja brama jest aktywna i gotowa.</string>
<string name="connect_gateway_get_started">Połącz się ze swoją bramą, aby rozpocząć.</string>
<string name="copy_report_for_claw">Kopiuj raport dla Claw</string>
<string name="advanced_controls">Zaawansowane ustawienia</string>
<string name="connection_method">Metoda połączenia</string>
<string name="setup_code">Kod konfiguracji</string>
<string name="manual">Ręcznie</string>
<string name="paste_setup_code">Wklej kod konfiguracji</string>
<string name="host">Host</string>
<string name="use_tls">Użyj TLS</string>
<string name="token_optional">Token (opcjonalnie)</string>
<string name="password">Hasło</string>
<string name="custom_headers_optional">Niestandardowe nagłówki (opcjonalnie)</string>
<string name="custom_headers_explainer">Wysyłane przy każdym połączeniu z tym gateway, na przykład nagłówki tokena usługi Cloudflare Access. Przechowywane w postaci zaszyfrowanej i stosowane przy następnym ponownym połączeniu.</string>
<string name="custom_header_value">Wartość nagłówka</string>
<string name="custom_header_reserved">Ten nagłówek jest zarządzany przez połączenie i nie można go zastąpić.</string>
<string name="add_custom_header">Dodaj nagłówek</string>
<string name="remove_custom_header">Usuń %1$s</string>
<string name="run_onboarding_again">Uruchom ponownie wdrażanie</string>
<string name="resolved_endpoint">Rozpoznany punkt końcowy</string>
<string name="gateway_trust_first_seen">Sprawdź odcisk certyfikatu, zanim zaufasz tej bramie.\n\n%1$s</string>
<string name="gateway_trust_changed">Certyfikat bramy uległ zmianie. Kontynuuj tylko wtedy, gdy oczekujesz tej zmiany.\n\nStary SHA-256:\n%1$s\n\nNowy SHA-256:\n%2$s</string>
<string name="switch_gateway">Przełącz Gateway</string>
<string name="select_gateway">Wybierz Gateway</string>
<string name="active_gateway">Aktywne Gateway</string>
</resources>

View file

@ -1,37 +1,9 @@
<resources>
<string name="app_name">OpenClaw Node</string>
<string name="gateway_connection">Conexão do Gateway</string>
<string name="connect_gateway">Conectar Gateway</string>
<string name="disconnect">Desconectar</string>
<string name="trust_this_gateway">Confiar neste gateway?</string>
<string name="trust_and_continue">Confiar e continuar</string>
<string name="cancel">Cancelar</string>
<string name="new_chat_in_worktree">Novo chat no worktree</string>
<string name="endpoint">Endpoint</string>
<string name="status">Status</string>
<string name="connected_gateway_ready">Seu gateway está ativo e pronto.</string>
<string name="connect_gateway_get_started">Conecte-se ao seu gateway para começar.</string>
<string name="copy_report_for_claw">Copiar relatório para o Claw</string>
<string name="advanced_controls">Controles avançados</string>
<string name="connection_method">Método de conexão</string>
<string name="setup_code">Código de configuração</string>
<string name="manual">Manual</string>
<string name="paste_setup_code">Colar código de configuração</string>
<string name="host">Host</string>
<string name="use_tls">Usar TLS</string>
<string name="token_optional">Token (opcional)</string>
<string name="password">Senha</string>
<string name="custom_headers_optional">Cabeçalhos personalizados (opcional)</string>
<string name="custom_headers_explainer">Enviados com todas as conexões a este gateway, por exemplo, cabeçalhos de token de serviço do Cloudflare Access. Armazenados criptografados e aplicados na próxima reconexão.</string>
<string name="custom_header_value">Valor do cabeçalho</string>
<string name="custom_header_reserved">Este cabeçalho é gerenciado pela conexão e não pode ser substituído.</string>
<string name="add_custom_header">Adicionar cabeçalho</string>
<string name="remove_custom_header">Remover %1$s</string>
<string name="run_onboarding_again">Executar integração novamente</string>
<string name="resolved_endpoint">Endpoint resolvido</string>
<string name="gateway_trust_first_seen">Verifique a impressão digital do certificado antes de confiar neste gateway.\n\n%1$s</string>
<string name="gateway_trust_changed">O certificado do gateway foi alterado. Continue somente se você esperava essa alteração.\n\nSHA-256 antigo:\n%1$s\n\nSHA-256 novo:\n%2$s</string>
<string name="switch_gateway">Trocar Gateway</string>
<string name="select_gateway">Selecionar Gateway</string>
<string name="active_gateway">Gateway ativo</string>
</resources>

View file

@ -1,37 +1,9 @@
<resources>
<string name="app_name">OpenClaw Node</string>
<string name="gateway_connection">Подключение к шлюзу</string>
<string name="connect_gateway">Подключить шлюз</string>
<string name="disconnect">Отключить</string>
<string name="trust_this_gateway">Доверять этому шлюзу?</string>
<string name="trust_and_continue">Доверять и продолжить</string>
<string name="cancel">Отмена</string>
<string name="new_chat_in_worktree">Новый чат в worktree</string>
<string name="endpoint">Конечная точка</string>
<string name="status">Статус</string>
<string name="connected_gateway_ready">Ваш шлюз активен и готов.</string>
<string name="connect_gateway_get_started">Подключитесь к своему шлюзу, чтобы начать.</string>
<string name="copy_report_for_claw">Скопировать отчет для Claw</string>
<string name="advanced_controls">Расширенные настройки</string>
<string name="connection_method">Способ подключения</string>
<string name="setup_code">Код настройки</string>
<string name="manual">Вручную</string>
<string name="paste_setup_code">Вставьте код настройки</string>
<string name="host">Хост</string>
<string name="use_tls">Использовать TLS</string>
<string name="token_optional">Токен (необязательно)</string>
<string name="password">Пароль</string>
<string name="custom_headers_optional">Пользовательские заголовки (необязательно)</string>
<string name="custom_headers_explainer">Отправляются при каждом подключении к этому gateway, например заголовки токена службы Cloudflare Access. Хранятся в зашифрованном виде и применяются при следующем переподключении.</string>
<string name="custom_header_value">Значение заголовка</string>
<string name="custom_header_reserved">Этот заголовок управляется подключением и не может быть переопределен.</string>
<string name="add_custom_header">Добавить заголовок</string>
<string name="remove_custom_header">Удалить %1$s</string>
<string name="run_onboarding_again">Запустить настройку заново</string>
<string name="resolved_endpoint">Разрешенная конечная точка</string>
<string name="gateway_trust_first_seen">Проверьте отпечаток сертификата, прежде чем доверять этому шлюзу.\n\n%1$s</string>
<string name="gateway_trust_changed">Сертификат шлюза изменился. Продолжайте, только если вы ожидали это изменение.\n\nСтарый SHA-256:\n%1$s\n\nНовый SHA-256:\n%2$s</string>
<string name="switch_gateway">Переключить gateway</string>
<string name="select_gateway">Выбрать gateway</string>
<string name="active_gateway">Активный gateway</string>
</resources>

View file

@ -1,37 +1,9 @@
<resources>
<string name="app_name">OpenClaw-nod</string>
<string name="gateway_connection">Gatewayanslutning</string>
<string name="connect_gateway">Anslut gateway</string>
<string name="disconnect">Koppla från</string>
<string name="trust_this_gateway">Lita på denna gateway?</string>
<string name="trust_and_continue">Lita på och fortsätt</string>
<string name="cancel">Avbryt</string>
<string name="new_chat_in_worktree">Ny chatt i worktree</string>
<string name="endpoint">Slutpunkt</string>
<string name="status">Status</string>
<string name="connected_gateway_ready">Din gateway är aktiv och redo.</string>
<string name="connect_gateway_get_started">Anslut till din gateway för att komma igång.</string>
<string name="copy_report_for_claw">Kopiera rapport för Claw</string>
<string name="advanced_controls">Avancerade kontroller</string>
<string name="connection_method">Anslutningsmetod</string>
<string name="setup_code">Konfigurationskod</string>
<string name="manual">Manuell</string>
<string name="paste_setup_code">Klistra in konfigurationskod</string>
<string name="host">Värd</string>
<string name="use_tls">Använd TLS</string>
<string name="token_optional">Token (valfritt)</string>
<string name="password">Lösenord</string>
<string name="custom_headers_optional">Anpassade headers (valfritt)</string>
<string name="custom_headers_explainer">Skickas med varje anslutning till denna gateway, till exempel Cloudflare Access service token headers. Lagras krypterat och tillämpas vid nästa återanslutning.</string>
<string name="custom_header_value">Headervärde</string>
<string name="custom_header_reserved">Denna header hanteras av anslutningen och kan inte åsidosättas.</string>
<string name="add_custom_header">Lägg till header</string>
<string name="remove_custom_header">Ta bort %1$s</string>
<string name="run_onboarding_again">Kör introduktionen igen</string>
<string name="resolved_endpoint">Löst slutpunkt</string>
<string name="gateway_trust_first_seen">Verifiera certifikatets fingeravtryck innan du litar på denna gateway.\n\n%1$s</string>
<string name="gateway_trust_changed">Gateway-certifikatet har ändrats. Fortsätt bara om du förväntade dig denna ändring.\n\nTidigare SHA-256:\n%1$s\n\nNy SHA-256:\n%2$s</string>
<string name="switch_gateway">Byt Gateway</string>
<string name="select_gateway">Välj Gateway</string>
<string name="active_gateway">Aktiv Gateway</string>
</resources>

View file

@ -1,37 +1,9 @@
<resources>
<string name="app_name">OpenClaw Node</string>
<string name="gateway_connection">การเชื่อมต่อเกตเวย์</string>
<string name="connect_gateway">เชื่อมต่อเกตเวย์</string>
<string name="disconnect">ตัดการเชื่อมต่อ</string>
<string name="trust_this_gateway">เชื่อถือเกตเวย์นี้หรือไม่?</string>
<string name="trust_and_continue">เชื่อถือและดำเนินการต่อ</string>
<string name="cancel">ยกเลิก</string>
<string name="new_chat_in_worktree">แชทใหม่ใน worktree</string>
<string name="endpoint">เอนด์พอยต์</string>
<string name="status">สถานะ</string>
<string name="connected_gateway_ready">เกตเวย์ของคุณเปิดใช้งานและพร้อมใช้งานแล้ว</string>
<string name="connect_gateway_get_started">เชื่อมต่อกับเกตเวย์ของคุณเพื่อเริ่มต้นใช้งาน</string>
<string name="copy_report_for_claw">คัดลอกรายงานสำหรับ Claw</string>
<string name="advanced_controls">การควบคุมขั้นสูง</string>
<string name="connection_method">วิธีการเชื่อมต่อ</string>
<string name="setup_code">รหัสตั้งค่า</string>
<string name="manual">ด้วยตนเอง</string>
<string name="paste_setup_code">วางรหัสตั้งค่า</string>
<string name="host">โฮสต์</string>
<string name="use_tls">ใช้ TLS</string>
<string name="token_optional">โทเค็น (ไม่บังคับ)</string>
<string name="password">รหัสผ่าน</string>
<string name="custom_headers_optional">ส่วนหัวที่กำหนดเอง (ไม่บังคับ)</string>
<string name="custom_headers_explainer">ส่งไปพร้อมกับทุกการเชื่อมต่อไปยัง gateway นี้ เช่น ส่วนหัวโทเค็นบริการ Cloudflare Access จัดเก็บแบบเข้ารหัสและจะนำไปใช้ในการเชื่อมต่อใหม่ครั้งถัดไป</string>
<string name="custom_header_value">ค่าส่วนหัว</string>
<string name="custom_header_reserved">ส่วนหัวนี้ถูกจัดการโดยการเชื่อมต่อและไม่สามารถเขียนทับได้</string>
<string name="add_custom_header">เพิ่มส่วนหัว</string>
<string name="remove_custom_header">ลบ %1$s</string>
<string name="run_onboarding_again">เรียกใช้การเริ่มต้นใช้งานอีกครั้ง</string>
<string name="resolved_endpoint">เอนด์พอยต์ที่แก้ไขแล้ว</string>
<string name="gateway_trust_first_seen">ตรวจสอบลายนิ้วมือของใบรับรองก่อนเชื่อถือเกตเวย์นี้\n\n%1$s</string>
<string name="gateway_trust_changed">ใบรับรองของเกตเวย์มีการเปลี่ยนแปลง ดำเนินการต่อเมื่อคุณคาดว่าจะมีการเปลี่ยนแปลงนี้เท่านั้น\n\nSHA-256 เดิม:\n%1$s\n\nSHA-256 ใหม่:\n%2$s</string>
<string name="switch_gateway">สลับ Gateway</string>
<string name="select_gateway">เลือก Gateway</string>
<string name="active_gateway">Gateway ที่ใช้งานอยู่</string>
</resources>

View file

@ -1,37 +1,9 @@
<resources>
<string name="app_name">OpenClaw Node</string>
<string name="gateway_connection">Ağ Geçidi Bağlantısı</string>
<string name="connect_gateway">Ağ Geçidine Bağlan</string>
<string name="disconnect">Bağlantıyı Kes</string>
<string name="trust_this_gateway">Bu ağ geçidine güvenilsin mi?</string>
<string name="trust_and_continue">Güven ve devam et</string>
<string name="cancel">İptal</string>
<string name="new_chat_in_worktree">Worktree\'de yeni sohbet</string>
<string name="endpoint">Uç nokta</string>
<string name="status">Durum</string>
<string name="connected_gateway_ready">Ağ geçidiniz etkin ve hazır.</string>
<string name="connect_gateway_get_started">Başlamak için ağ geçidinize bağlanın.</string>
<string name="copy_report_for_claw">Claw için Raporu Kopyala</string>
<string name="advanced_controls">Gelişmiş kontroller</string>
<string name="connection_method">Bağlantı yöntemi</string>
<string name="setup_code">Kurulum Kodu</string>
<string name="manual">Manuel</string>
<string name="paste_setup_code">Kurulum kodunu yapıştır</string>
<string name="host">Ana makine</string>
<string name="use_tls">TLS kullan</string>
<string name="token_optional">Token (isteğe bağlı)</string>
<string name="password">Parola</string>
<string name="custom_headers_optional">Özel üst bilgiler (isteğe bağlı)</string>
<string name="custom_headers_explainer">Bu gateway\'e yapılan her bağlantıyla birlikte gönderilir; örneğin Cloudflare Access hizmet belirteci üst bilgileri. Şifrelenmiş olarak saklanır ve bir sonraki yeniden bağlantıda uygulanır.</string>
<string name="custom_header_value">Üst bilgi değeri</string>
<string name="custom_header_reserved">Bu üst bilgi bağlantı tarafından yönetilir ve geçersiz kılınamaz.</string>
<string name="add_custom_header">Üst bilgi ekle</string>
<string name="remove_custom_header">%1$s kaldır</string>
<string name="run_onboarding_again">Başlangıç sürecini tekrar çalıştır</string>
<string name="resolved_endpoint">Çözümlenen uç nokta</string>
<string name="gateway_trust_first_seen">Bu ağ geçidine güvenmeden önce sertifika parmak izini doğrulayın.\n\n%1$s</string>
<string name="gateway_trust_changed">Ağ geçidi sertifikası değişti. Yalnızca bu değişikliği bekliyorsanız devam edin.\n\nEski SHA-256:\n%1$s\n\nYeni SHA-256:\n%2$s</string>
<string name="switch_gateway">Gateway değiştir</string>
<string name="select_gateway">Gateway seç</string>
<string name="active_gateway">Etkin Gateway</string>
</resources>

View file

@ -1,37 +1,9 @@
<resources>
<string name="app_name">OpenClaw Node</string>
<string name="gateway_connection">Підключення до шлюзу</string>
<string name="connect_gateway">Підключити шлюз</string>
<string name="disconnect">Відключити</string>
<string name="trust_this_gateway">Довіряти цьому шлюзу?</string>
<string name="trust_and_continue">Довіряти й продовжити</string>
<string name="cancel">Скасувати</string>
<string name="new_chat_in_worktree">Новий чат у worktree</string>
<string name="endpoint">Кінцева точка</string>
<string name="status">Стан</string>
<string name="connected_gateway_ready">Ваш шлюз активний і готовий.</string>
<string name="connect_gateway_get_started">Підключіться до свого шлюзу, щоб почати.</string>
<string name="copy_report_for_claw">Скопіювати звіт для Claw</string>
<string name="advanced_controls">Розширені елементи керування</string>
<string name="connection_method">Спосіб підключення</string>
<string name="setup_code">Код налаштування</string>
<string name="manual">Вручну</string>
<string name="paste_setup_code">Вставте код налаштування</string>
<string name="host">Хост</string>
<string name="use_tls">Використовувати TLS</string>
<string name="token_optional">Токен (необов’язково)</string>
<string name="password">Пароль</string>
<string name="custom_headers_optional">Користувацькі заголовки (необов’язково)</string>
<string name="custom_headers_explainer">Надсилається з кожним підключенням до цього gateway, наприклад заголовки токена сервісу Cloudflare Access. Зберігається в зашифрованому вигляді та застосовується під час наступного повторного підключення.</string>
<string name="custom_header_value">Значення заголовка</string>
<string name="custom_header_reserved">Цей заголовок керується підключенням і не може бути перевизначений.</string>
<string name="add_custom_header">Додати заголовок</string>
<string name="remove_custom_header">Видалити %1$s</string>
<string name="run_onboarding_again">Запустити адаптацію знову</string>
<string name="resolved_endpoint">Визначена кінцева точка</string>
<string name="gateway_trust_first_seen">Перевірте відбиток сертифіката, перш ніж довіряти цьому шлюзу.\n\n%1$s</string>
<string name="gateway_trust_changed">Сертифікат шлюзу змінився. Продовжуйте, лише якщо ви очікували цю зміну.\n\nСтарий SHA-256:\n%1$s\n\nНовий SHA-256:\n%2$s</string>
<string name="switch_gateway">Перемкнути gateway</string>
<string name="select_gateway">Виберіть gateway</string>
<string name="active_gateway">Активний gateway</string>
</resources>

View file

@ -1,37 +1,9 @@
<resources>
<string name="app_name">OpenClaw Node</string>
<string name="gateway_connection">Kết nối cổng</string>
<string name="connect_gateway">Kết nối cổng</string>
<string name="disconnect">Ngắt kết nối</string>
<string name="trust_this_gateway">Tin cậy cổng này?</string>
<string name="trust_and_continue">Tin cậy và tiếp tục</string>
<string name="cancel">Hủy</string>
<string name="new_chat_in_worktree">Trò chuyện mới trong worktree</string>
<string name="endpoint">Điểm cuối</string>
<string name="status">Trạng thái</string>
<string name="connected_gateway_ready">Cổng của bạn đang hoạt động và sẵn sàng.</string>
<string name="connect_gateway_get_started">Kết nối với cổng của bạn để bắt đầu.</string>
<string name="copy_report_for_claw">Sao chép báo cáo cho Claw</string>
<string name="advanced_controls">Điều khiển nâng cao</string>
<string name="connection_method">Phương thức kết nối</string>
<string name="setup_code">Mã thiết lập</string>
<string name="manual">Thủ công</string>
<string name="paste_setup_code">Dán mã thiết lập</string>
<string name="host">Máy chủ</string>
<string name="use_tls">Sử dụng TLS</string>
<string name="token_optional">Token (tùy chọn)</string>
<string name="password">Mật khẩu</string>
<string name="custom_headers_optional">Tiêu đề tùy chỉnh (không bắt buộc)</string>
<string name="custom_headers_explainer">Được gửi cùng mọi kết nối đến gateway này, ví dụ như các tiêu đề mã thông báo dịch vụ Cloudflare Access. Được lưu trữ ở dạng mã hóa và áp dụng trong lần kết nối lại tiếp theo.</string>
<string name="custom_header_value">Giá trị tiêu đề</string>
<string name="custom_header_reserved">Tiêu đề này do kết nối quản lý và không thể bị ghi đè.</string>
<string name="add_custom_header">Thêm tiêu đề</string>
<string name="remove_custom_header">Xóa %1$s</string>
<string name="run_onboarding_again">Chạy hướng dẫn thiết lập lại</string>
<string name="resolved_endpoint">Điểm cuối đã phân giải</string>
<string name="gateway_trust_first_seen">Xác minh dấu vân tay chứng chỉ trước khi tin cậy cổng này.\n\n%1$s</string>
<string name="gateway_trust_changed">Chứng chỉ cổng đã thay đổi. Chỉ tiếp tục nếu bạn mong đợi thay đổi này.\n\nSHA-256 cũ:\n%1$s\n\nSHA-256 mới:\n%2$s</string>
<string name="switch_gateway">Chuyển Gateway</string>
<string name="select_gateway">Chọn Gateway</string>
<string name="active_gateway">Gateway đang hoạt động</string>
</resources>

View file

@ -1,37 +1,9 @@
<resources>
<string name="app_name">OpenClaw Node</string>
<string name="gateway_connection">网关连接</string>
<string name="connect_gateway">连接网关</string>
<string name="disconnect">断开连接</string>
<string name="trust_this_gateway">信任此网关?</string>
<string name="trust_and_continue">信任并继续</string>
<string name="cancel">取消</string>
<string name="new_chat_in_worktree">在 worktree 中新建聊天</string>
<string name="endpoint">端点</string>
<string name="status">状态</string>
<string name="connected_gateway_ready">你的网关已激活并准备就绪。</string>
<string name="connect_gateway_get_started">连接到你的网关以开始使用。</string>
<string name="copy_report_for_claw">复制 Claw 报告</string>
<string name="advanced_controls">高级控制</string>
<string name="connection_method">连接方式</string>
<string name="setup_code">设置代码</string>
<string name="manual">手动</string>
<string name="paste_setup_code">粘贴设置代码</string>
<string name="host">主机</string>
<string name="use_tls">使用 TLS</string>
<string name="token_optional">令牌(可选)</string>
<string name="password">密码</string>
<string name="custom_headers_optional">自定义标头(可选)</string>
<string name="custom_headers_explainer">随每次连接到此 Gateway 一起发送,例如 Cloudflare Access 服务令牌标头。将加密存储,并在下次重新连接时应用。</string>
<string name="custom_header_value">标头值</string>
<string name="custom_header_reserved">此标头由连接管理,无法覆盖。</string>
<string name="add_custom_header">添加标头</string>
<string name="remove_custom_header">移除 %1$s</string>
<string name="run_onboarding_again">再次运行引导流程</string>
<string name="resolved_endpoint">已解析的端点</string>
<string name="gateway_trust_first_seen">验证证书指纹后再信任此网关。\n\n%1$s</string>
<string name="gateway_trust_changed">网关证书已更改。仅当这是您预期的更改时才继续。\n\n旧 SHA-256\n%1$s\n\n新 SHA-256\n%2$s</string>
<string name="switch_gateway">切换 Gateway</string>
<string name="select_gateway">选择 Gateway</string>
<string name="active_gateway">当前 Gateway</string>
</resources>

View file

@ -1,37 +1,9 @@
<resources>
<string name="app_name">OpenClaw Node</string>
<string name="gateway_connection">閘道連線</string>
<string name="connect_gateway">連接閘道</string>
<string name="disconnect">中斷連線</string>
<string name="trust_this_gateway">信任此閘道?</string>
<string name="trust_and_continue">信任並繼續</string>
<string name="cancel">取消</string>
<string name="new_chat_in_worktree">在 worktree 中新增聊天</string>
<string name="endpoint">端點</string>
<string name="status">狀態</string>
<string name="connected_gateway_ready">您的閘道已啟用並準備就緒。</string>
<string name="connect_gateway_get_started">連接到您的閘道以開始使用。</string>
<string name="copy_report_for_claw">複製 Claw 報告</string>
<string name="advanced_controls">進階控制項</string>
<string name="connection_method">連線方式</string>
<string name="setup_code">設定碼</string>
<string name="manual">手動</string>
<string name="paste_setup_code">貼上設定碼</string>
<string name="host">主機</string>
<string name="use_tls">使用 TLS</string>
<string name="token_optional">權杖(選填)</string>
<string name="password">密碼</string>
<string name="custom_headers_optional">自訂標頭(選填)</string>
<string name="custom_headers_explainer">會隨每次連線傳送至此 gateway例如 Cloudflare Access 服務權杖標頭。已加密儲存,並會在下次重新連線時套用。</string>
<string name="custom_header_value">標頭值</string>
<string name="custom_header_reserved">此標頭由連線管理,無法覆寫。</string>
<string name="add_custom_header">新增標頭</string>
<string name="remove_custom_header">移除 %1$s</string>
<string name="run_onboarding_again">再次執行新手導覽</string>
<string name="resolved_endpoint">已解析的端點</string>
<string name="gateway_trust_first_seen">請先驗證憑證指紋,再信任此閘道。\n\n%1$s</string>
<string name="gateway_trust_changed">閘道憑證已變更。僅在這是您預期的變更時繼續。\n\n舊 SHA-256\n%1$s\n\n新 SHA-256\n%2$s</string>
<string name="switch_gateway">切換 gateway</string>
<string name="select_gateway">選取 gateway</string>
<string name="active_gateway">使用中的 gateway</string>
</resources>

View file

@ -1,37 +1,9 @@
<resources>
<string name="app_name">OpenClaw Node</string>
<string name="gateway_connection">Gateway Connection</string>
<string name="connect_gateway">Connect Gateway</string>
<string name="disconnect">Disconnect</string>
<string name="trust_this_gateway">Trust this gateway?</string>
<string name="trust_and_continue">Trust and continue</string>
<string name="cancel">Cancel</string>
<string name="new_chat_in_worktree">New chat in worktree</string>
<string name="endpoint">Endpoint</string>
<string name="status">Status</string>
<string name="connected_gateway_ready">Your gateway is active and ready.</string>
<string name="connect_gateway_get_started">Connect to your gateway to get started.</string>
<string name="copy_report_for_claw">Copy Report for Claw</string>
<string name="advanced_controls">Advanced controls</string>
<string name="connection_method">Connection method</string>
<string name="setup_code">Setup Code</string>
<string name="manual">Manual</string>
<string name="paste_setup_code">Paste setup code</string>
<string name="host">Host</string>
<string name="use_tls">Use TLS</string>
<string name="token_optional">Token (optional)</string>
<string name="password">Password</string>
<string name="custom_headers_optional">Custom headers (optional)</string>
<string name="custom_headers_explainer">Sent with every connection to this gateway, for example Cloudflare Access service token headers. Stored encrypted and applied on the next reconnect.</string>
<string name="custom_header_value">Header value</string>
<string name="custom_header_reserved">This header is managed by the connection and cannot be overridden.</string>
<string name="add_custom_header">Add header</string>
<string name="remove_custom_header">Remove %1$s</string>
<string name="run_onboarding_again">Run onboarding again</string>
<string name="resolved_endpoint">Resolved endpoint</string>
<string name="gateway_trust_first_seen">Verify the certificate fingerprint before trusting this gateway.\n\n%1$s</string>
<string name="gateway_trust_changed">The gateway certificate changed. Continue only if you expected this.\n\nOld SHA-256:\n%1$s\n\nNew SHA-256:\n%2$s</string>
<string name="switch_gateway">Switch gateway</string>
<string name="select_gateway">Select gateway</string>
<string name="active_gateway">Active gateway</string>
</resources>