fix: address WinUI3 code review findings (#203)
Some checks failed
Build and Test / test (push) Has been cancelled
Build and Test / build (win-arm64) (push) Has been cancelled
Build and Test / build (win-x64) (push) Has been cancelled
Build and Test / build-msix (ARM64, win-arm64) (push) Has been cancelled
Build and Test / build-msix (x64, win-x64) (push) Has been cancelled
Build and Test / build-extension (arm64) (push) Has been cancelled
Build and Test / build-extension (x64) (push) Has been cancelled
Build and Test / release (push) Has been cancelled

- Add AutomationProperties.AutomationId to all interactive controls
- Add AutomationProperties.Name to 4 icon-only WebChat toolbar buttons
- Localize all SetupWizard strings via LocalizationHelper + .resw
- Replace hardcoded colors with ThemeResource brushes
- Replace raw FontSize with typography styles where appropriate

Prepares codebase for automated UI testing (winapp ui) and
proper Dark/Light/HighContrast theme support.
This commit is contained in:
Scott Hanselman 2026-04-22 22:28:53 -07:00 committed by GitHub
parent 5ecaa8427b
commit 1960a436e0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 781 additions and 106 deletions

View File

@ -624,6 +624,138 @@ Use one of these options:
<value>Open Activity Stream</value>
</data>
<!-- ==================== SetupWizardWindow (C# code) ==================== -->
<data name="Setup_Title" xml:space="preserve">
<value>OpenClaw Setup</value>
</data>
<data name="Setup_StepConnect" xml:space="preserve">
<value>Step 1 of 3 — Connect</value>
</data>
<data name="Setup_ConnectTitle" xml:space="preserve">
<value>Connect to your gateway</value>
</data>
<data name="Setup_ConnectDescription" xml:space="preserve">
<value>On your gateway host (Mac/Linux), run this to get a setup code:</value>
</data>
<data name="Setup_SetupCodeHeader" xml:space="preserve">
<value>Setup Code</value>
</data>
<data name="Setup_SetupCodePlaceholder" xml:space="preserve">
<value>Paste the setup code from your gateway dashboard</value>
</data>
<data name="Setup_ManualEntryToggle" xml:space="preserve">
<value>Or enter URL and token manually ▾</value>
</data>
<data name="Setup_ManualEntryToggleHide" xml:space="preserve">
<value>Hide manual entry ▴</value>
</data>
<data name="Setup_GatewayUrlHeader" xml:space="preserve">
<value>Gateway URL</value>
</data>
<data name="Setup_GatewayUrlPlaceholder" xml:space="preserve">
<value>ws://192.168.1.x:18789</value>
</data>
<data name="Setup_GatewayUrlHint" xml:space="preserve">
<value>💡 Accepts ws://, wss://, http://, or https://</value>
</data>
<data name="Setup_TokenHeader" xml:space="preserve">
<value>Gateway Token</value>
</data>
<data name="Setup_TokenPlaceholder" xml:space="preserve">
<value>Paste your token here</value>
</data>
<data name="Setup_TestButton" xml:space="preserve">
<value>Test Connection</value>
</data>
<data name="Setup_Testing" xml:space="preserve">
<value>⏳ Connecting to gateway...</value>
</data>
<data name="Setup_Connected" xml:space="preserve">
<value>✅ Connected!</value>
</data>
<data name="Setup_PairingRequired" xml:space="preserve">
<value>✅ Gateway reached! Device needs pairing approval.
On your gateway host (Mac/Linux), run:
openclaw devices approve {0}</value>
</data>
<data name="Setup_TokenMismatch" xml:space="preserve">
<value>❌ Token doesn't match.
💡 Check gateway auth token:
cat ~/.openclaw/openclaw.json | grep token</value>
</data>
<data name="Setup_OriginNotAllowed" xml:space="preserve">
<value>❌ Origin not allowed.
💡 Add this machine to gateway.controlUi.allowedOrigins.</value>
</data>
<data name="Setup_RateLimited" xml:space="preserve">
<value>❌ Rate-limited. Wait a minute and try again.</value>
</data>
<data name="Setup_TimedOut" xml:space="preserve">
<value>❌ Timed out. Check the URL and gateway is running.</value>
</data>
<data name="Setup_TokenRequired" xml:space="preserve">
<value>❌ Please enter a token</value>
</data>
<data name="Setup_TestFirst" xml:space="preserve">
<value>⚠️ Please test the connection first</value>
</data>
<data name="Setup_CodeDecoded" xml:space="preserve">
<value>✅ Setup code decoded — press Test Connection</value>
</data>
<data name="Setup_NodeModeTitle" xml:space="preserve">
<value>Enable Node Mode (optional)</value>
</data>
<data name="Setup_NodeModeDescription" xml:space="preserve">
<value>Node Mode lets your Windows machine run tasks for OpenClaw — like screen capture, camera access, and canvas drawing.</value>
</data>
<data name="Setup_NodeModeToggle" xml:space="preserve">
<value>Enable Node Mode</value>
</data>
<data name="Setup_DeviceIdLoading" xml:space="preserve">
<value>Device ID: loading...</value>
</data>
<data name="Setup_DeviceIdFallback" xml:space="preserve">
<value>Device ID: (will be generated on first connect)</value>
</data>
<data name="Setup_CopyDeviceId" xml:space="preserve">
<value>📋 Copy Device ID</value>
</data>
<data name="Setup_DeviceIdCopied" xml:space="preserve">
<value>✅ Copied!</value>
</data>
<data name="Setup_ApproveInstructions" xml:space="preserve">
<value>To approve this node, run on your gateway host:</value>
</data>
<data name="Setup_ApproveHint" xml:space="preserve">
<value>💡 You can finish setup now — pairing will continue in the background. You'll get a notification when approved.</value>
</data>
<data name="Setup_DoneTitle" xml:space="preserve">
<value>🎉 You're all set!</value>
</data>
<data name="Setup_DoneDescription" xml:space="preserve">
<value>OpenClaw Tray will connect to your gateway and start monitoring.</value>
</data>
<data name="Setup_BackButton" xml:space="preserve">
<value>Back</value>
</data>
<data name="Setup_NextButton" xml:space="preserve">
<value>Next</value>
</data>
<data name="Setup_FinishButton" xml:space="preserve">
<value>Finish</value>
</data>
<data name="Setup_StepNodeMode" xml:space="preserve">
<value>Step 2 of 3 — Node Mode</value>
</data>
<data name="Setup_StepDone" xml:space="preserve">
<value>Step 3 of 3 — Done</value>
</data>
<!-- ==================== DownloadProgressDialog ==================== -->
<data name="WindowTitle_Downloading" xml:space="preserve">

View File

@ -624,6 +624,138 @@ Utilisez l'une de ces options :
<value>Créer un nouveau flux d'activités</value>
</data>
<!-- ==================== SetupWizardWindow (C# code) ==================== -->
<data name="Setup_Title" xml:space="preserve">
<value>Configuration d'OpenClaw</value>
</data>
<data name="Setup_StepConnect" xml:space="preserve">
<value>Étape 1 sur 3 — Connexion</value>
</data>
<data name="Setup_ConnectTitle" xml:space="preserve">
<value>Connectez-vous à votre passerelle</value>
</data>
<data name="Setup_ConnectDescription" xml:space="preserve">
<value>Sur votre hôte passerelle (Mac/Linux), exécutez cette commande pour obtenir un code de configuration :</value>
</data>
<data name="Setup_SetupCodeHeader" xml:space="preserve">
<value>Code de configuration</value>
</data>
<data name="Setup_SetupCodePlaceholder" xml:space="preserve">
<value>Collez le code de configuration depuis le tableau de bord de votre passerelle</value>
</data>
<data name="Setup_ManualEntryToggle" xml:space="preserve">
<value>Ou entrez l'URL et le jeton manuellement ▾</value>
</data>
<data name="Setup_ManualEntryToggleHide" xml:space="preserve">
<value>Masquer la saisie manuelle ▴</value>
</data>
<data name="Setup_GatewayUrlHeader" xml:space="preserve">
<value>URL de la passerelle</value>
</data>
<data name="Setup_GatewayUrlPlaceholder" xml:space="preserve">
<value>ws://192.168.1.x:18789</value>
</data>
<data name="Setup_GatewayUrlHint" xml:space="preserve">
<value>💡 Accepte ws://, wss://, http:// ou https://</value>
</data>
<data name="Setup_TokenHeader" xml:space="preserve">
<value>Jeton de la passerelle</value>
</data>
<data name="Setup_TokenPlaceholder" xml:space="preserve">
<value>Collez votre jeton ici</value>
</data>
<data name="Setup_TestButton" xml:space="preserve">
<value>Tester la connexion</value>
</data>
<data name="Setup_Testing" xml:space="preserve">
<value>⏳ Connexion à la passerelle...</value>
</data>
<data name="Setup_Connected" xml:space="preserve">
<value>✅ Connecté !</value>
</data>
<data name="Setup_PairingRequired" xml:space="preserve">
<value>✅ Passerelle atteinte ! L'appareil nécessite une approbation d'appariement.
Sur votre hôte passerelle (Mac/Linux), exécutez :
openclaw devices approve {0}</value>
</data>
<data name="Setup_TokenMismatch" xml:space="preserve">
<value>❌ Le jeton ne correspond pas.
💡 Vérifiez le jeton d'authentification de la passerelle :
cat ~/.openclaw/openclaw.json | grep token</value>
</data>
<data name="Setup_OriginNotAllowed" xml:space="preserve">
<value>❌ Origine non autorisée.
💡 Ajoutez cette machine à gateway.controlUi.allowedOrigins.</value>
</data>
<data name="Setup_RateLimited" xml:space="preserve">
<value>❌ Trop de tentatives. Attendez une minute et réessayez.</value>
</data>
<data name="Setup_TimedOut" xml:space="preserve">
<value>❌ Délai expiré. Vérifiez l'URL et que la passerelle fonctionne.</value>
</data>
<data name="Setup_TokenRequired" xml:space="preserve">
<value>❌ Veuillez entrer un jeton</value>
</data>
<data name="Setup_TestFirst" xml:space="preserve">
<value>⚠️ Veuillez d'abord tester la connexion</value>
</data>
<data name="Setup_CodeDecoded" xml:space="preserve">
<value>✅ Code de configuration décodé — appuyez sur Tester la connexion</value>
</data>
<data name="Setup_NodeModeTitle" xml:space="preserve">
<value>Activer le mode nœud (optionnel)</value>
</data>
<data name="Setup_NodeModeDescription" xml:space="preserve">
<value>Le mode nœud permet à votre machine Windows d'exécuter des tâches pour OpenClaw — comme la capture d'écran, l'accès caméra et le dessin sur canevas.</value>
</data>
<data name="Setup_NodeModeToggle" xml:space="preserve">
<value>Activer le mode nœud</value>
</data>
<data name="Setup_DeviceIdLoading" xml:space="preserve">
<value>ID de l'appareil : chargement...</value>
</data>
<data name="Setup_DeviceIdFallback" xml:space="preserve">
<value>ID de l'appareil : (sera généré lors de la première connexion)</value>
</data>
<data name="Setup_CopyDeviceId" xml:space="preserve">
<value>📋 Copier l'ID de l'appareil</value>
</data>
<data name="Setup_DeviceIdCopied" xml:space="preserve">
<value>✅ Copié !</value>
</data>
<data name="Setup_ApproveInstructions" xml:space="preserve">
<value>Pour approuver ce nœud, exécutez sur votre hôte passerelle :</value>
</data>
<data name="Setup_ApproveHint" xml:space="preserve">
<value>💡 Vous pouvez terminer la configuration maintenant — l'appariement continuera en arrière-plan. Vous recevrez une notification une fois approuvé.</value>
</data>
<data name="Setup_DoneTitle" xml:space="preserve">
<value>🎉 Tout est prêt !</value>
</data>
<data name="Setup_DoneDescription" xml:space="preserve">
<value>OpenClaw Tray se connectera à votre passerelle et commencera la surveillance.</value>
</data>
<data name="Setup_BackButton" xml:space="preserve">
<value>Retour</value>
</data>
<data name="Setup_NextButton" xml:space="preserve">
<value>Suivant</value>
</data>
<data name="Setup_FinishButton" xml:space="preserve">
<value>Terminer</value>
</data>
<data name="Setup_StepNodeMode" xml:space="preserve">
<value>Étape 2 sur 3 — Mode nœud</value>
</data>
<data name="Setup_StepDone" xml:space="preserve">
<value>Étape 3 sur 3 — Terminé</value>
</data>
<!-- ==================== DownloadProgressDialog ==================== -->
<data name="WindowTitle_Downloading" xml:space="preserve">

View File

@ -624,6 +624,138 @@ Gebruik een van deze opties:
<value>Activiteitenstroom openen</value>
</data>
<!-- ==================== SetupWizardWindow (C# code) ==================== -->
<data name="Setup_Title" xml:space="preserve">
<value>OpenClaw Instellen</value>
</data>
<data name="Setup_StepConnect" xml:space="preserve">
<value>Stap 1 van 3 — Verbinden</value>
</data>
<data name="Setup_ConnectTitle" xml:space="preserve">
<value>Verbind met uw gateway</value>
</data>
<data name="Setup_ConnectDescription" xml:space="preserve">
<value>Voer op uw gateway-host (Mac/Linux) deze opdracht uit om een installatiecode te krijgen:</value>
</data>
<data name="Setup_SetupCodeHeader" xml:space="preserve">
<value>Installatiecode</value>
</data>
<data name="Setup_SetupCodePlaceholder" xml:space="preserve">
<value>Plak de installatiecode van uw gateway-dashboard</value>
</data>
<data name="Setup_ManualEntryToggle" xml:space="preserve">
<value>Of voer URL en token handmatig in ▾</value>
</data>
<data name="Setup_ManualEntryToggleHide" xml:space="preserve">
<value>Handmatige invoer verbergen ▴</value>
</data>
<data name="Setup_GatewayUrlHeader" xml:space="preserve">
<value>Gateway-URL</value>
</data>
<data name="Setup_GatewayUrlPlaceholder" xml:space="preserve">
<value>ws://192.168.1.x:18789</value>
</data>
<data name="Setup_GatewayUrlHint" xml:space="preserve">
<value>💡 Accepteert ws://, wss://, http:// of https://</value>
</data>
<data name="Setup_TokenHeader" xml:space="preserve">
<value>Gateway-token</value>
</data>
<data name="Setup_TokenPlaceholder" xml:space="preserve">
<value>Plak hier uw token</value>
</data>
<data name="Setup_TestButton" xml:space="preserve">
<value>Verbinding testen</value>
</data>
<data name="Setup_Testing" xml:space="preserve">
<value>⏳ Verbinden met gateway...</value>
</data>
<data name="Setup_Connected" xml:space="preserve">
<value>✅ Verbonden!</value>
</data>
<data name="Setup_PairingRequired" xml:space="preserve">
<value>✅ Gateway bereikt! Apparaat moet worden goedgekeurd voor koppeling.
Voer op uw gateway-host (Mac/Linux) uit:
openclaw devices approve {0}</value>
</data>
<data name="Setup_TokenMismatch" xml:space="preserve">
<value>❌ Token komt niet overeen.
💡 Controleer het gateway-verificatietoken:
cat ~/.openclaw/openclaw.json | grep token</value>
</data>
<data name="Setup_OriginNotAllowed" xml:space="preserve">
<value>❌ Oorsprong niet toegestaan.
💡 Voeg deze machine toe aan gateway.controlUi.allowedOrigins.</value>
</data>
<data name="Setup_RateLimited" xml:space="preserve">
<value>❌ Te veel verzoeken. Wacht een minuut en probeer opnieuw.</value>
</data>
<data name="Setup_TimedOut" xml:space="preserve">
<value>❌ Time-out. Controleer de URL en of de gateway draait.</value>
</data>
<data name="Setup_TokenRequired" xml:space="preserve">
<value>❌ Voer een token in</value>
</data>
<data name="Setup_TestFirst" xml:space="preserve">
<value>⚠️ Test eerst de verbinding</value>
</data>
<data name="Setup_CodeDecoded" xml:space="preserve">
<value>✅ Installatiecode gedecodeerd — druk op Verbinding testen</value>
</data>
<data name="Setup_NodeModeTitle" xml:space="preserve">
<value>Knooppuntmodus inschakelen (optioneel)</value>
</data>
<data name="Setup_NodeModeDescription" xml:space="preserve">
<value>Knooppuntmodus laat uw Windows-machine taken uitvoeren voor OpenClaw — zoals schermopname, cameratoegang en canvas-tekenen.</value>
</data>
<data name="Setup_NodeModeToggle" xml:space="preserve">
<value>Knooppuntmodus inschakelen</value>
</data>
<data name="Setup_DeviceIdLoading" xml:space="preserve">
<value>Apparaat-ID: laden...</value>
</data>
<data name="Setup_DeviceIdFallback" xml:space="preserve">
<value>Apparaat-ID: (wordt gegenereerd bij eerste verbinding)</value>
</data>
<data name="Setup_CopyDeviceId" xml:space="preserve">
<value>📋 Apparaat-ID kopiëren</value>
</data>
<data name="Setup_DeviceIdCopied" xml:space="preserve">
<value>✅ Gekopieerd!</value>
</data>
<data name="Setup_ApproveInstructions" xml:space="preserve">
<value>Om dit knooppunt goed te keuren, voer uit op uw gateway-host:</value>
</data>
<data name="Setup_ApproveHint" xml:space="preserve">
<value>💡 U kunt de installatie nu voltooien — koppeling gaat op de achtergrond door. U ontvangt een melding wanneer goedgekeurd.</value>
</data>
<data name="Setup_DoneTitle" xml:space="preserve">
<value>🎉 Alles is klaar!</value>
</data>
<data name="Setup_DoneDescription" xml:space="preserve">
<value>OpenClaw Tray zal verbinding maken met uw gateway en beginnen met monitoren.</value>
</data>
<data name="Setup_BackButton" xml:space="preserve">
<value>Vorige</value>
</data>
<data name="Setup_NextButton" xml:space="preserve">
<value>Volgende</value>
</data>
<data name="Setup_FinishButton" xml:space="preserve">
<value>Voltooien</value>
</data>
<data name="Setup_StepNodeMode" xml:space="preserve">
<value>Stap 2 van 3 — Knooppuntmodus</value>
</data>
<data name="Setup_StepDone" xml:space="preserve">
<value>Stap 3 van 3 — Gereed</value>
</data>
<!-- ==================== DownloadProgressDialog ==================== -->
<data name="WindowTitle_Downloading" xml:space="preserve">

View File

@ -624,6 +624,138 @@
<value>打开活动流</value>
</data>
<!-- ==================== SetupWizardWindow (C# code) ==================== -->
<data name="Setup_Title" xml:space="preserve">
<value>OpenClaw 设置</value>
</data>
<data name="Setup_StepConnect" xml:space="preserve">
<value>第 1 步(共 3 步)— 连接</value>
</data>
<data name="Setup_ConnectTitle" xml:space="preserve">
<value>连接到您的网关</value>
</data>
<data name="Setup_ConnectDescription" xml:space="preserve">
<value>在您的网关主机Mac/Linux上运行以下命令获取设置码</value>
</data>
<data name="Setup_SetupCodeHeader" xml:space="preserve">
<value>设置码</value>
</data>
<data name="Setup_SetupCodePlaceholder" xml:space="preserve">
<value>粘贴来自网关仪表板的设置码</value>
</data>
<data name="Setup_ManualEntryToggle" xml:space="preserve">
<value>或手动输入 URL 和令牌 ▾</value>
</data>
<data name="Setup_ManualEntryToggleHide" xml:space="preserve">
<value>隐藏手动输入 ▴</value>
</data>
<data name="Setup_GatewayUrlHeader" xml:space="preserve">
<value>网关地址</value>
</data>
<data name="Setup_GatewayUrlPlaceholder" xml:space="preserve">
<value>ws://192.168.1.x:18789</value>
</data>
<data name="Setup_GatewayUrlHint" xml:space="preserve">
<value>💡 支持 ws://、wss://、http:// 或 https://</value>
</data>
<data name="Setup_TokenHeader" xml:space="preserve">
<value>网关令牌</value>
</data>
<data name="Setup_TokenPlaceholder" xml:space="preserve">
<value>在此粘贴您的令牌</value>
</data>
<data name="Setup_TestButton" xml:space="preserve">
<value>测试连接</value>
</data>
<data name="Setup_Testing" xml:space="preserve">
<value>⏳ 正在连接网关...</value>
</data>
<data name="Setup_Connected" xml:space="preserve">
<value>✅ 连接成功!</value>
</data>
<data name="Setup_PairingRequired" xml:space="preserve">
<value>✅ 已连接到网关!设备需要配对批准。
在您的网关主机Mac/Linux上运行
openclaw devices approve {0}</value>
</data>
<data name="Setup_TokenMismatch" xml:space="preserve">
<value>❌ 令牌不匹配。
💡 检查网关认证令牌:
cat ~/.openclaw/openclaw.json | grep token</value>
</data>
<data name="Setup_OriginNotAllowed" xml:space="preserve">
<value>❌ 来源不被允许。
💡 将此机器添加到 gateway.controlUi.allowedOrigins。</value>
</data>
<data name="Setup_RateLimited" xml:space="preserve">
<value>❌ 请求过于频繁。请等待一分钟后重试。</value>
</data>
<data name="Setup_TimedOut" xml:space="preserve">
<value>❌ 连接超时。请检查 URL 和网关是否正在运行。</value>
</data>
<data name="Setup_TokenRequired" xml:space="preserve">
<value>❌ 请输入令牌</value>
</data>
<data name="Setup_TestFirst" xml:space="preserve">
<value>⚠️ 请先测试连接</value>
</data>
<data name="Setup_CodeDecoded" xml:space="preserve">
<value>✅ 设置码已解码 — 请点击"测试连接"</value>
</data>
<data name="Setup_NodeModeTitle" xml:space="preserve">
<value>启用节点模式(可选)</value>
</data>
<data name="Setup_NodeModeDescription" xml:space="preserve">
<value>节点模式允许您的 Windows 电脑为 OpenClaw 执行任务 — 如屏幕截图、摄像头访问和画布绘制。</value>
</data>
<data name="Setup_NodeModeToggle" xml:space="preserve">
<value>启用节点模式</value>
</data>
<data name="Setup_DeviceIdLoading" xml:space="preserve">
<value>设备 ID加载中...</value>
</data>
<data name="Setup_DeviceIdFallback" xml:space="preserve">
<value>设备 ID将在首次连接时生成</value>
</data>
<data name="Setup_CopyDeviceId" xml:space="preserve">
<value>📋 复制设备 ID</value>
</data>
<data name="Setup_DeviceIdCopied" xml:space="preserve">
<value>✅ 已复制!</value>
</data>
<data name="Setup_ApproveInstructions" xml:space="preserve">
<value>要批准此节点,请在网关主机上运行:</value>
</data>
<data name="Setup_ApproveHint" xml:space="preserve">
<value>💡 您可以现在完成设置 — 配对将在后台继续。获得批准后会收到通知。</value>
</data>
<data name="Setup_DoneTitle" xml:space="preserve">
<value>🎉 一切就绪!</value>
</data>
<data name="Setup_DoneDescription" xml:space="preserve">
<value>OpenClaw 托盘将连接到您的网关并开始监控。</value>
</data>
<data name="Setup_BackButton" xml:space="preserve">
<value>上一步</value>
</data>
<data name="Setup_NextButton" xml:space="preserve">
<value>下一步</value>
</data>
<data name="Setup_FinishButton" xml:space="preserve">
<value>完成</value>
</data>
<data name="Setup_StepNodeMode" xml:space="preserve">
<value>第 2 步(共 3 步)— 节点模式</value>
</data>
<data name="Setup_StepDone" xml:space="preserve">
<value>第 3 步(共 3 步)— 完成</value>
</data>
<!-- ==================== DownloadProgressDialog ==================== -->
<data name="WindowTitle_Downloading" xml:space="preserve">

View File

@ -624,6 +624,138 @@
<value>打開串流活動</value>
</data>
<!-- ==================== SetupWizardWindow (C# code) ==================== -->
<data name="Setup_Title" xml:space="preserve">
<value>OpenClaw 設定</value>
</data>
<data name="Setup_StepConnect" xml:space="preserve">
<value>第 1 步(共 3 步)— 連線</value>
</data>
<data name="Setup_ConnectTitle" xml:space="preserve">
<value>連線到您的閘道器</value>
</data>
<data name="Setup_ConnectDescription" xml:space="preserve">
<value>在您的閘道器主機Mac/Linux上執行以下命令取得設定碼</value>
</data>
<data name="Setup_SetupCodeHeader" xml:space="preserve">
<value>設定碼</value>
</data>
<data name="Setup_SetupCodePlaceholder" xml:space="preserve">
<value>貼上來自閘道器儀表板的設定碼</value>
</data>
<data name="Setup_ManualEntryToggle" xml:space="preserve">
<value>或手動輸入 URL 和權杖 ▾</value>
</data>
<data name="Setup_ManualEntryToggleHide" xml:space="preserve">
<value>隱藏手動輸入 ▴</value>
</data>
<data name="Setup_GatewayUrlHeader" xml:space="preserve">
<value>閘道器網址</value>
</data>
<data name="Setup_GatewayUrlPlaceholder" xml:space="preserve">
<value>ws://192.168.1.x:18789</value>
</data>
<data name="Setup_GatewayUrlHint" xml:space="preserve">
<value>💡 支援 ws://、wss://、http:// 或 https://</value>
</data>
<data name="Setup_TokenHeader" xml:space="preserve">
<value>閘道器權杖</value>
</data>
<data name="Setup_TokenPlaceholder" xml:space="preserve">
<value>在此貼上您的權杖</value>
</data>
<data name="Setup_TestButton" xml:space="preserve">
<value>測試連線</value>
</data>
<data name="Setup_Testing" xml:space="preserve">
<value>⏳ 正在連線到閘道器...</value>
</data>
<data name="Setup_Connected" xml:space="preserve">
<value>✅ 已連線!</value>
</data>
<data name="Setup_PairingRequired" xml:space="preserve">
<value>✅ 已連線到閘道器!裝置需要配對核准。
在您的閘道器主機Mac/Linux上執行
openclaw devices approve {0}</value>
</data>
<data name="Setup_TokenMismatch" xml:space="preserve">
<value>❌ 權杖不符。
💡 檢查閘道器認證權杖:
cat ~/.openclaw/openclaw.json | grep token</value>
</data>
<data name="Setup_OriginNotAllowed" xml:space="preserve">
<value>❌ 來源不被允許。
💡 將此機器加入 gateway.controlUi.allowedOrigins。</value>
</data>
<data name="Setup_RateLimited" xml:space="preserve">
<value>❌ 請求過於頻繁。請等待一分鐘後重試。</value>
</data>
<data name="Setup_TimedOut" xml:space="preserve">
<value>❌ 連線逾時。請檢查網址和閘道器是否正在執行。</value>
</data>
<data name="Setup_TokenRequired" xml:space="preserve">
<value>❌ 請輸入權杖</value>
</data>
<data name="Setup_TestFirst" xml:space="preserve">
<value>⚠️ 請先測試連線</value>
</data>
<data name="Setup_CodeDecoded" xml:space="preserve">
<value>✅ 設定碼已解碼 — 請點擊「測試連線」</value>
</data>
<data name="Setup_NodeModeTitle" xml:space="preserve">
<value>啟用節點模式(選用)</value>
</data>
<data name="Setup_NodeModeDescription" xml:space="preserve">
<value>節點模式允許您的 Windows 電腦為 OpenClaw 執行任務 — 如螢幕擷取、攝影機存取和畫布繪製。</value>
</data>
<data name="Setup_NodeModeToggle" xml:space="preserve">
<value>啟用節點模式</value>
</data>
<data name="Setup_DeviceIdLoading" xml:space="preserve">
<value>裝置 ID載入中...</value>
</data>
<data name="Setup_DeviceIdFallback" xml:space="preserve">
<value>裝置 ID將在首次連線時產生</value>
</data>
<data name="Setup_CopyDeviceId" xml:space="preserve">
<value>📋 複製裝置 ID</value>
</data>
<data name="Setup_DeviceIdCopied" xml:space="preserve">
<value>✅ 已複製!</value>
</data>
<data name="Setup_ApproveInstructions" xml:space="preserve">
<value>要核准此節點,請在閘道器主機上執行:</value>
</data>
<data name="Setup_ApproveHint" xml:space="preserve">
<value>💡 您可以現在完成設定 — 配對將在背景繼續。獲得核准後會收到通知。</value>
</data>
<data name="Setup_DoneTitle" xml:space="preserve">
<value>🎉 一切就緒!</value>
</data>
<data name="Setup_DoneDescription" xml:space="preserve">
<value>OpenClaw 系統匣將連線到您的閘道器並開始監控。</value>
</data>
<data name="Setup_BackButton" xml:space="preserve">
<value>上一步</value>
</data>
<data name="Setup_NextButton" xml:space="preserve">
<value>下一步</value>
</data>
<data name="Setup_FinishButton" xml:space="preserve">
<value>完成</value>
</data>
<data name="Setup_StepNodeMode" xml:space="preserve">
<value>第 2 步(共 3 步)— 節點模式</value>
</data>
<data name="Setup_StepDone" xml:space="preserve">
<value>第 3 步(共 3 步)— 完成</value>
</data>
<!-- ==================== DownloadProgressDialog ==================== -->
<data name="WindowTitle_Downloading" xml:space="preserve">

View File

@ -25,7 +25,7 @@
Foreground="{ThemeResource TextFillColorSecondaryBrush}"/>
</StackPanel>
<ComboBox x:Name="FilterCombo" Width="180" SelectionChanged="OnFilterChanged">
<ComboBox x:Name="FilterCombo" AutomationProperties.AutomationId="FilterCombo" Width="180" SelectionChanged="OnFilterChanged">
<ComboBoxItem x:Uid="ActivityFilterAll" Content="All activity" Tag="all" />
<ComboBoxItem x:Uid="ActivityFilterSessions" Content="Sessions" Tag="session" />
<ComboBoxItem x:Uid="ActivityFilterUsage" Content="Usage" Tag="usage" />
@ -34,7 +34,7 @@
</ComboBox>
</StackPanel>
<ListView x:Name="ActivityList" Grid.Row="1"
<ListView x:Name="ActivityList" AutomationProperties.AutomationId="ActivityList" Grid.Row="1"
SelectionMode="Single"
IsItemClickEnabled="True"
ItemClick="OnItemClick">
@ -90,9 +90,9 @@
<StackPanel Grid.Row="2" Orientation="Horizontal" Spacing="8"
HorizontalAlignment="Right" Margin="0,12,0,0">
<Button x:Uid="ActivityOpenDashboardButton" Content="Open Dashboard" Click="OnOpenDashboard"/>
<Button x:Uid="ActivityClearAllButton" Content="Clear All" Click="OnClearAll"/>
<Button x:Uid="ActivityCloseButton" Content="Close" Click="OnClose"/>
<Button x:Name="OpenDashboardButton" x:Uid="ActivityOpenDashboardButton" AutomationProperties.AutomationId="OpenDashboardButton" Content="Open Dashboard" Click="OnOpenDashboard"/>
<Button x:Name="ClearButton" x:Uid="ActivityClearAllButton" AutomationProperties.AutomationId="ClearButton" Content="Clear All" Click="OnClearAll"/>
<Button x:Name="CloseButton" x:Uid="ActivityCloseButton" AutomationProperties.AutomationId="CloseButton" Content="Close" Click="OnClose"/>
</StackPanel>
</Grid>
</winex:WindowEx>

View File

@ -32,7 +32,7 @@
Spacing="16"
Padding="32">
<TextBlock x:Uid="CanvasErrorTitle" Text="❌ Canvas Error"
FontSize="18"
Style="{StaticResource SubtitleTextBlockStyle}"
FontWeight="SemiBold"
HorizontalAlignment="Center"/>
<TextBlock x:Name="ErrorText"
@ -40,7 +40,7 @@
MaxWidth="400"
HorizontalAlignment="Center"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"/>
<Button x:Uid="CanvasRetryButton" Content="Retry"
<Button x:Name="RetryButton" x:Uid="CanvasRetryButton" AutomationProperties.AutomationId="RetryButton" Content="Retry"
HorizontalAlignment="Center"
Click="OnRetryClick"/>
</StackPanel>

View File

@ -26,7 +26,7 @@
</StackPanel>
<!-- List -->
<ListView x:Name="NotificationList" Grid.Row="1"
<ListView x:Name="NotificationList" AutomationProperties.AutomationId="NotificationList" Grid.Row="1"
SelectionMode="Single"
ItemClick="OnItemClick"
IsItemClickEnabled="True">
@ -80,8 +80,8 @@
<!-- Footer -->
<StackPanel Grid.Row="2" Orientation="Horizontal" Spacing="8"
HorizontalAlignment="Right" Margin="0,16,0,0">
<Button x:Uid="NotificationClearAllButton" Content="Clear All" Click="OnClearAll"/>
<Button x:Uid="NotificationCloseButton" Content="Close" Click="OnClose"/>
<Button x:Name="ClearButton" x:Uid="NotificationClearAllButton" AutomationProperties.AutomationId="ClearButton" Content="Clear All" Click="OnClearAll"/>
<Button x:Name="CloseButton" x:Uid="NotificationCloseButton" AutomationProperties.AutomationId="CloseButton" Content="Close" Click="OnClose"/>
</StackPanel>
</Grid>
</winex:WindowEx>

View File

@ -24,9 +24,9 @@
<!-- Connection Section -->
<StackPanel Spacing="8">
<TextBlock x:Uid="SettingsConnectionHeader" Text="CONNECTION" Style="{StaticResource CaptionTextBlockStyle}"
Foreground="#E74C3C" FontWeight="Bold"/>
Foreground="{ThemeResource AccentTextFillColorPrimaryBrush}" FontWeight="Bold"/>
<ToggleSwitch x:Name="UseSshTunnelToggle" Header="Connect through SSH tunnel" Toggled="OnUseSshTunnelToggled"/>
<ToggleSwitch x:Name="UseSshTunnelToggle" AutomationProperties.AutomationId="UseSshTunnelToggle" Header="Connect through SSH tunnel" Toggled="OnUseSshTunnelToggled"/>
<StackPanel x:Name="SshTunnelDetailsPanel" Spacing="8" Visibility="Collapsed">
<TextBlock Text="When enabled, the app will connect through ws://127.0.0.1:&lt;local-port&gt; and create an SSH forward to the remote gateway host."
@ -40,8 +40,8 @@
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBox x:Name="SshTunnelUserTextBox" Grid.Column="0" Header="SSH User" PlaceholderText="user"/>
<TextBox x:Name="SshTunnelHostTextBox" Grid.Column="1" Header="SSH Host" PlaceholderText="machine-name or IP"/>
<TextBox x:Name="SshTunnelUserTextBox" AutomationProperties.AutomationId="SshTunnelUserTextBox" Grid.Column="0" Header="SSH User" PlaceholderText="user"/>
<TextBox x:Name="SshTunnelHostTextBox" AutomationProperties.AutomationId="SshTunnelHostTextBox" Grid.Column="1" Header="SSH Host" PlaceholderText="machine-name or IP"/>
</Grid>
<Grid ColumnSpacing="8">
@ -50,18 +50,18 @@
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBox x:Name="SshTunnelRemotePortTextBox" Grid.Column="0" Header="Remote Gateway Port" PlaceholderText="18789"/>
<TextBox x:Name="SshTunnelLocalPortTextBox" Grid.Column="1" Header="Local Forward Port" PlaceholderText="18789" TextChanged="OnSshTunnelLocalPortTextChanged"/>
<TextBox x:Name="SshTunnelRemotePortTextBox" AutomationProperties.AutomationId="SshTunnelRemotePortTextBox" Grid.Column="0" Header="Remote Gateway Port" PlaceholderText="18789"/>
<TextBox x:Name="SshTunnelLocalPortTextBox" AutomationProperties.AutomationId="SshTunnelLocalPortTextBox" Grid.Column="1" Header="Local Forward Port" PlaceholderText="18789" TextChanged="OnSshTunnelLocalPortTextChanged"/>
</Grid>
</StackPanel>
<TextBox x:Name="GatewayUrlTextBox" x:Uid="SettingsGatewayUrlTextBox" Header="Gateway URL"
<TextBox x:Name="GatewayUrlTextBox" x:Uid="SettingsGatewayUrlTextBox" AutomationProperties.AutomationId="GatewayUrlTextBox" Header="Gateway URL"
PlaceholderText="ws://localhost:18789 or https://host.tailnet.ts.net"/>
<StackPanel Orientation="Horizontal" Spacing="8">
<TextBox x:Name="TokenTextBox" x:Uid="SettingsTokenTextBox" Header="Token"
<TextBox x:Name="TokenTextBox" x:Uid="SettingsTokenTextBox" AutomationProperties.AutomationId="TokenTextBox" Header="Token"
PlaceholderText="Your API token" Width="300"/>
<Button x:Name="TestConnectionButton" x:Uid="SettingsTestConnectionButton" Content="Test"
<Button x:Name="TestConnectionButton" x:Uid="SettingsTestConnectionButton" AutomationProperties.AutomationId="TestConnectionButton" Content="Test"
VerticalAlignment="Bottom" Click="OnTestConnection"/>
</StackPanel>
@ -72,20 +72,20 @@
<!-- Startup Section -->
<StackPanel Spacing="8">
<TextBlock x:Uid="SettingsStartupHeader" Text="STARTUP" Style="{StaticResource CaptionTextBlockStyle}"
Foreground="#E74C3C" FontWeight="Bold"/>
Foreground="{ThemeResource AccentTextFillColorPrimaryBrush}" FontWeight="Bold"/>
<ToggleSwitch x:Name="AutoStartToggle" x:Uid="SettingsAutoStartToggle" Header="Start automatically with Windows"/>
<ToggleSwitch x:Name="GlobalHotkeyToggle" x:Uid="SettingsGlobalHotkeyToggle" Header="Global hotkey (Ctrl+Alt+Shift+C → Quick Send)"/>
<ToggleSwitch x:Name="AutoStartToggle" x:Uid="SettingsAutoStartToggle" AutomationProperties.AutomationId="AutoStartToggle" Header="Start automatically with Windows"/>
<ToggleSwitch x:Name="GlobalHotkeyToggle" x:Uid="SettingsGlobalHotkeyToggle" AutomationProperties.AutomationId="GlobalHotkeyToggle" Header="Global hotkey (Ctrl+Alt+Shift+C → Quick Send)"/>
</StackPanel>
<!-- Notifications Section -->
<StackPanel Spacing="8">
<TextBlock x:Uid="SettingsNotificationsHeader" Text="NOTIFICATIONS" Style="{StaticResource CaptionTextBlockStyle}"
Foreground="#E74C3C" FontWeight="Bold"/>
Foreground="{ThemeResource AccentTextFillColorPrimaryBrush}" FontWeight="Bold"/>
<ToggleSwitch x:Name="NotificationsToggle" x:Uid="SettingsNotificationsToggle" Header="Show notifications"/>
<ToggleSwitch x:Name="NotificationsToggle" x:Uid="SettingsNotificationsToggle" AutomationProperties.AutomationId="NotificationsToggle" Header="Show notifications"/>
<ComboBox x:Name="NotificationSoundComboBox" x:Uid="SettingsSoundComboBox" Header="Sound" Width="200">
<ComboBox x:Name="NotificationSoundComboBox" x:Uid="SettingsSoundComboBox" AutomationProperties.AutomationId="NotificationSoundComboBox" Header="Sound" Width="200">
<ComboBoxItem x:Uid="SettingsSoundDefault" Content="Default" Tag="Default"/>
<ComboBoxItem x:Uid="SettingsSoundNone" Content="None" Tag="None"/>
<ComboBoxItem x:Uid="SettingsSoundSubtle" Content="Subtle" Tag="Subtle"/>
@ -98,27 +98,27 @@
Margin="0,0,0,4"/>
<StackPanel Spacing="4">
<CheckBox x:Name="NotifyHealthCb" x:Uid="SettingsNotifyHealthCb" Content="Health alerts"/>
<CheckBox x:Name="NotifyUrgentCb" x:Uid="SettingsNotifyUrgentCb" Content="Urgent messages"/>
<CheckBox x:Name="NotifyReminderCb" x:Uid="SettingsNotifyReminderCb" Content="Reminders"/>
<CheckBox x:Name="NotifyEmailCb" x:Uid="SettingsNotifyEmailCb" Content="Email summaries"/>
<CheckBox x:Name="NotifyCalendarCb" x:Uid="SettingsNotifyCalendarCb" Content="Calendar events"/>
<CheckBox x:Name="NotifyBuildCb" x:Uid="SettingsNotifyBuildCb" Content="Build notifications"/>
<CheckBox x:Name="NotifyStockCb" x:Uid="SettingsNotifyStockCb" Content="Stock alerts"/>
<CheckBox x:Name="NotifyInfoCb" x:Uid="SettingsNotifyInfoCb" Content="Info messages"/>
<CheckBox x:Name="NotifyHealthCb" x:Uid="SettingsNotifyHealthCb" AutomationProperties.AutomationId="NotifyHealthCb" Content="Health alerts"/>
<CheckBox x:Name="NotifyUrgentCb" x:Uid="SettingsNotifyUrgentCb" AutomationProperties.AutomationId="NotifyUrgentCb" Content="Urgent messages"/>
<CheckBox x:Name="NotifyReminderCb" x:Uid="SettingsNotifyReminderCb" AutomationProperties.AutomationId="NotifyReminderCb" Content="Reminders"/>
<CheckBox x:Name="NotifyEmailCb" x:Uid="SettingsNotifyEmailCb" AutomationProperties.AutomationId="NotifyEmailCb" Content="Email summaries"/>
<CheckBox x:Name="NotifyCalendarCb" x:Uid="SettingsNotifyCalendarCb" AutomationProperties.AutomationId="NotifyCalendarCb" Content="Calendar events"/>
<CheckBox x:Name="NotifyBuildCb" x:Uid="SettingsNotifyBuildCb" AutomationProperties.AutomationId="NotifyBuildCb" Content="Build notifications"/>
<CheckBox x:Name="NotifyStockCb" x:Uid="SettingsNotifyStockCb" AutomationProperties.AutomationId="NotifyStockCb" Content="Stock alerts"/>
<CheckBox x:Name="NotifyInfoCb" x:Uid="SettingsNotifyInfoCb" AutomationProperties.AutomationId="NotifyInfoCb" Content="Info messages"/>
</StackPanel>
</StackPanel>
<!-- Test Notification -->
<Button x:Name="TestNotificationButton" x:Uid="SettingsTestNotificationButton" Content="Send Test Notification"
<Button x:Name="TestNotificationButton" x:Uid="SettingsTestNotificationButton" AutomationProperties.AutomationId="TestNotificationButton" Content="Send Test Notification"
Click="OnTestNotification"/>
<!-- Advanced Section -->
<StackPanel Spacing="8">
<TextBlock x:Uid="SettingsAdvancedHeader" Text="ADVANCED (EXPERIMENTAL)" Style="{StaticResource CaptionTextBlockStyle}"
Foreground="#E74C3C" FontWeight="Bold"/>
Foreground="{ThemeResource AccentTextFillColorPrimaryBrush}" FontWeight="Bold"/>
<ToggleSwitch x:Name="NodeModeToggle" x:Uid="SettingsNodeModeToggle" Header="Enable Node Mode"/>
<ToggleSwitch x:Name="NodeModeToggle" x:Uid="SettingsNodeModeToggle" AutomationProperties.AutomationId="NodeModeToggle" Header="Enable Node Mode"/>
<TextBlock x:Uid="SettingsNodeModeDescription" Text="When enabled, this PC can receive commands from the agent (canvas, screenshots, etc.)"
Style="{StaticResource CaptionTextBlockStyle}"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
@ -137,7 +137,7 @@
Padding="24,16">
<StackPanel Orientation="Horizontal" Spacing="8" HorizontalAlignment="Right">
<Button x:Uid="SettingsCancelButton" Content="Cancel" Click="OnCancel" Width="80"/>
<Button x:Name="SaveButton" x:Uid="SettingsSaveButton" Content="Save"
<Button x:Name="SaveButton" x:Uid="SettingsSaveButton" AutomationProperties.AutomationId="SaveButton" Content="Save"
Click="OnSave" Width="80" Style="{ThemeResource AccentButtonStyle}"/>
</StackPanel>
</Border>

View File

@ -1,4 +1,5 @@
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Automation;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Text;
@ -60,7 +61,7 @@ public sealed class SetupWizardWindow : WindowEx
_draftToken = settings.Token;
_draftEnableNodeMode = settings.EnableNodeMode;
Title = "OpenClaw Setup";
Title = LocalizationHelper.GetString("Setup_Title");
this.SetWindowSize(720, 700);
this.CenterOnScreen();
this.SetIcon("Assets\\openclaw.ico");
@ -77,7 +78,7 @@ public sealed class SetupWizardWindow : WindowEx
header.Children.Add(new TextBlock { Text = "🦞", FontSize = 36 });
header.Children.Add(new TextBlock
{
Text = "OpenClaw Setup",
Text = LocalizationHelper.GetString("Setup_Title"),
Style = (Style)Application.Current.Resources["TitleTextBlockStyle"],
VerticalAlignment = VerticalAlignment.Center
});
@ -87,8 +88,8 @@ public sealed class SetupWizardWindow : WindowEx
// Step indicator
_stepIndicator = new TextBlock
{
Text = "Step 1 of 3 — Connect",
Foreground = new SolidColorBrush(Microsoft.UI.Colors.Gray),
Text = LocalizationHelper.GetString("Setup_StepConnect"),
Foreground = (SolidColorBrush)Application.Current.Resources["TextFillColorSecondaryBrush"],
Margin = new Thickness(0, 0, 0, 16)
};
Grid.SetRow(_stepIndicator, 1);
@ -101,15 +102,15 @@ public sealed class SetupWizardWindow : WindowEx
_stepPanels[0] = new StackPanel { Spacing = 12 };
_stepPanels[0].Children.Add(new TextBlock
{
Text = "Connect to your gateway",
Text = LocalizationHelper.GetString("Setup_ConnectTitle"),
FontWeight = FontWeights.SemiBold,
FontSize = 16
Style = (Style)Application.Current.Resources["SubtitleTextBlockStyle"]
});
_stepPanels[0].Children.Add(new TextBlock
{
Text = "On your gateway host (Mac/Linux), run this to get a setup code:",
Text = LocalizationHelper.GetString("Setup_ConnectDescription"),
TextWrapping = TextWrapping.Wrap,
Foreground = new SolidColorBrush(Microsoft.UI.Colors.Gray)
Foreground = (SolidColorBrush)Application.Current.Resources["TextFillColorSecondaryBrush"]
});
var cmdHint = new TextBox
{
@ -117,58 +118,64 @@ public sealed class SetupWizardWindow : WindowEx
IsReadOnly = true,
FontFamily = new FontFamily("Cascadia Mono, Consolas"),
BorderThickness = new Thickness(1),
Background = new SolidColorBrush(Microsoft.UI.ColorHelper.FromArgb(255, 40, 40, 40)),
Foreground = new SolidColorBrush(Microsoft.UI.Colors.LightGreen),
Background = (SolidColorBrush)Application.Current.Resources["CardBackgroundFillColorDefaultBrush"],
Foreground = (SolidColorBrush)Application.Current.Resources["SystemFillColorSuccessBrush"],
Padding = new Thickness(12, 8, 12, 8)
};
_stepPanels[0].Children.Add(cmdHint);
_setupCodeBox = new TextBox
{
Header = "Setup Code",
PlaceholderText = "Paste the setup code from your gateway dashboard",
Header = LocalizationHelper.GetString("Setup_SetupCodeHeader"),
PlaceholderText = LocalizationHelper.GetString("Setup_SetupCodePlaceholder"),
TextWrapping = TextWrapping.Wrap,
AcceptsReturn = false
};
AutomationProperties.SetAutomationId(_setupCodeBox, "SetupCodeBox");
_setupCodeBox.TextChanged += OnSetupCodeChanged;
_stepPanels[0].Children.Add(_setupCodeBox);
// Manual entry toggle
var manualToggle = new HyperlinkButton { Content = "Or enter URL and token manually ▾" };
var manualToggle = new HyperlinkButton { Content = LocalizationHelper.GetString("Setup_ManualEntryToggle") };
AutomationProperties.SetAutomationId(manualToggle, "ManualEntryToggle");
_manualEntryPanel = new StackPanel { Spacing = 8, Visibility = Visibility.Collapsed };
manualToggle.Click += (s, e) =>
{
_manualEntryPanel.Visibility = _manualEntryPanel.Visibility == Visibility.Visible
? Visibility.Collapsed : Visibility.Visible;
manualToggle.Content = _manualEntryPanel.Visibility == Visibility.Visible
? "Hide manual entry ▴" : "Or enter URL and token manually ▾";
? LocalizationHelper.GetString("Setup_ManualEntryToggleHide") : LocalizationHelper.GetString("Setup_ManualEntryToggle");
};
_stepPanels[0].Children.Add(manualToggle);
_gatewayUrlBox = new TextBox
{
Header = "Gateway URL",
PlaceholderText = "ws://192.168.1.x:18789",
Header = LocalizationHelper.GetString("Setup_GatewayUrlHeader"),
PlaceholderText = LocalizationHelper.GetString("Setup_GatewayUrlPlaceholder"),
Text = _draftGatewayUrl
};
AutomationProperties.SetAutomationId(_gatewayUrlBox, "GatewayUrlBox");
_gatewayUrlBox.TextChanged += (s, e) => _connectionTested = false;
_manualEntryPanel.Children.Add(_gatewayUrlBox);
_manualEntryPanel.Children.Add(new TextBlock
{
Text = "💡 Accepts ws://, wss://, http://, or https://",
FontSize = 12, Foreground = new SolidColorBrush(Microsoft.UI.Colors.Gray)
Text = LocalizationHelper.GetString("Setup_GatewayUrlHint"),
Style = (Style)Application.Current.Resources["CaptionTextBlockStyle"],
Foreground = (SolidColorBrush)Application.Current.Resources["TextFillColorSecondaryBrush"]
});
_tokenBox = new PasswordBox
{
Header = "Gateway Token",
PlaceholderText = "Paste your token here",
Header = LocalizationHelper.GetString("Setup_TokenHeader"),
PlaceholderText = LocalizationHelper.GetString("Setup_TokenPlaceholder"),
Password = _draftToken
};
AutomationProperties.SetAutomationId(_tokenBox, "TokenBox");
_tokenBox.PasswordChanged += (s, e) => _connectionTested = false;
_manualEntryPanel.Children.Add(_tokenBox);
_stepPanels[0].Children.Add(_manualEntryPanel);
// Test connection
_testButton = new Button { Content = "Test Connection" };
_testButton = new Button { Content = LocalizationHelper.GetString("Setup_TestButton") };
AutomationProperties.SetAutomationId(_testButton, "TestConnectionButton");
_testButton.Click += OnTestConnection;
_stepPanels[0].Children.Add(_testButton);
_testStatusLabel = new TextBlock
@ -183,21 +190,22 @@ public sealed class SetupWizardWindow : WindowEx
_stepPanels[1] = new StackPanel { Spacing = 12, Visibility = Visibility.Collapsed };
_stepPanels[1].Children.Add(new TextBlock
{
Text = "Enable Node Mode (optional)",
Text = LocalizationHelper.GetString("Setup_NodeModeTitle"),
FontWeight = FontWeights.SemiBold,
FontSize = 16
Style = (Style)Application.Current.Resources["SubtitleTextBlockStyle"]
});
_stepPanels[1].Children.Add(new TextBlock
{
Text = "Node Mode lets your Windows machine run tasks for OpenClaw — like screen capture, camera access, and canvas drawing.",
Text = LocalizationHelper.GetString("Setup_NodeModeDescription"),
TextWrapping = TextWrapping.Wrap,
Foreground = new SolidColorBrush(Microsoft.UI.Colors.Gray)
Foreground = (SolidColorBrush)Application.Current.Resources["TextFillColorSecondaryBrush"]
});
_nodeModeToggle = new ToggleSwitch
{
Header = "Enable Node Mode",
Header = LocalizationHelper.GetString("Setup_NodeModeToggle"),
IsOn = _draftEnableNodeMode
};
AutomationProperties.SetAutomationId(_nodeModeToggle, "NodeModeToggle");
_nodeModeToggle.Toggled += (s, e) =>
{
var showPairing = _nodeModeToggle.IsOn;
@ -209,7 +217,7 @@ public sealed class SetupWizardWindow : WindowEx
_deviceIdText = new TextBlock
{
Text = "Device ID: loading...",
Text = LocalizationHelper.GetString("Setup_DeviceIdLoading"),
FontFamily = new FontFamily("Cascadia Mono, Consolas"),
IsTextSelectionEnabled = true,
TextWrapping = TextWrapping.Wrap,
@ -219,9 +227,10 @@ public sealed class SetupWizardWindow : WindowEx
_copyDeviceIdButton = new Button
{
Content = "📋 Copy Device ID",
Content = LocalizationHelper.GetString("Setup_CopyDeviceId"),
Visibility = _draftEnableNodeMode ? Visibility.Visible : Visibility.Collapsed
};
AutomationProperties.SetAutomationId(_copyDeviceIdButton, "CopyDeviceIdButton");
_copyDeviceIdButton.Click += OnCopyDeviceId;
_stepPanels[1].Children.Add(_copyDeviceIdButton);
@ -239,9 +248,9 @@ public sealed class SetupWizardWindow : WindowEx
};
pairingInstructions.Children.Add(new TextBlock
{
Text = "To approve this node, run on your gateway host:",
Text = LocalizationHelper.GetString("Setup_ApproveInstructions"),
TextWrapping = TextWrapping.Wrap,
Foreground = new SolidColorBrush(Microsoft.UI.Colors.Gray)
Foreground = (SolidColorBrush)Application.Current.Resources["TextFillColorSecondaryBrush"]
});
var approveCmd = new TextBox
{
@ -249,8 +258,8 @@ public sealed class SetupWizardWindow : WindowEx
IsReadOnly = true,
FontFamily = new FontFamily("Cascadia Mono, Consolas"),
BorderThickness = new Thickness(1),
Background = new SolidColorBrush(Microsoft.UI.ColorHelper.FromArgb(255, 40, 40, 40)),
Foreground = new SolidColorBrush(Microsoft.UI.Colors.LightGreen),
Background = (SolidColorBrush)Application.Current.Resources["CardBackgroundFillColorDefaultBrush"],
Foreground = (SolidColorBrush)Application.Current.Resources["SystemFillColorSuccessBrush"],
Padding = new Thickness(12, 8, 12, 8),
AcceptsReturn = true,
TextWrapping = TextWrapping.Wrap
@ -258,10 +267,10 @@ public sealed class SetupWizardWindow : WindowEx
pairingInstructions.Children.Add(approveCmd);
pairingInstructions.Children.Add(new TextBlock
{
Text = "💡 You can finish setup now — pairing will continue in the background. You'll get a notification when approved.",
Text = LocalizationHelper.GetString("Setup_ApproveHint"),
TextWrapping = TextWrapping.Wrap,
FontSize = 12,
Foreground = new SolidColorBrush(Microsoft.UI.Colors.Gray)
Style = (Style)Application.Current.Resources["CaptionTextBlockStyle"],
Foreground = (SolidColorBrush)Application.Current.Resources["TextFillColorSecondaryBrush"]
});
_stepPanels[1].Children.Add(pairingInstructions);
contentArea.Children.Add(_stepPanels[1]);
@ -270,15 +279,15 @@ public sealed class SetupWizardWindow : WindowEx
_stepPanels[2] = new StackPanel { Spacing = 12, Visibility = Visibility.Collapsed };
_stepPanels[2].Children.Add(new TextBlock
{
Text = "🎉 You're all set!",
Text = LocalizationHelper.GetString("Setup_DoneTitle"),
FontWeight = FontWeights.SemiBold,
FontSize = 16
Style = (Style)Application.Current.Resources["SubtitleTextBlockStyle"]
});
_stepPanels[2].Children.Add(new TextBlock
{
Text = "OpenClaw Tray will connect to your gateway and start monitoring.",
Text = LocalizationHelper.GetString("Setup_DoneDescription"),
TextWrapping = TextWrapping.Wrap,
Foreground = new SolidColorBrush(Microsoft.UI.Colors.Gray)
Foreground = (SolidColorBrush)Application.Current.Resources["TextFillColorSecondaryBrush"]
});
contentArea.Children.Add(_stepPanels[2]);
@ -298,15 +307,17 @@ public sealed class SetupWizardWindow : WindowEx
Spacing = 8,
Margin = new Thickness(0, 16, 0, 0)
};
_backButton = new Button { Content = "Back", Visibility = Visibility.Collapsed };
_backButton = new Button { Content = LocalizationHelper.GetString("Setup_BackButton"), Visibility = Visibility.Collapsed };
AutomationProperties.SetAutomationId(_backButton, "BackButton");
_backButton.Click += (s, e) => GoToStep(_currentStep - 1);
navPanel.Children.Add(_backButton);
_nextButton = new Button
{
Content = "Next",
Content = LocalizationHelper.GetString("Setup_NextButton"),
Style = (Style)Application.Current.Resources["AccentButtonStyle"]
};
AutomationProperties.SetAutomationId(_nextButton, "NextButton");
_nextButton.Click += OnNextClicked;
navPanel.Children.Add(_nextButton);
@ -330,16 +341,16 @@ public sealed class SetupWizardWindow : WindowEx
_backButton.Visibility = _currentStep > 0 ? Visibility.Visible : Visibility.Collapsed;
var stepNames = new[] { "Connect", "Node Mode", "Done" };
_stepIndicator.Text = $"Step {_currentStep + 1} of {TotalSteps} — {stepNames[_currentStep]}";
var stepKeys = new[] { "Setup_StepConnect", "Setup_StepNodeMode", "Setup_StepDone" };
_stepIndicator.Text = LocalizationHelper.GetString(stepKeys[_currentStep]);
if (_currentStep == TotalSteps - 1)
{
_nextButton.Content = "Finish";
_nextButton.Content = LocalizationHelper.GetString("Setup_FinishButton");
}
else
{
_nextButton.Content = "Next";
_nextButton.Content = LocalizationHelper.GetString("Setup_NextButton");
}
}
@ -350,7 +361,7 @@ public sealed class SetupWizardWindow : WindowEx
case 0: // Connection — must have tested successfully
if (!_connectionTested)
{
_testStatusLabel.Text = "⚠️ Please test the connection first";
_testStatusLabel.Text = LocalizationHelper.GetString("Setup_TestFirst");
return;
}
GoToStep(1);
@ -396,7 +407,7 @@ public sealed class SetupWizardWindow : WindowEx
// Show manual fields so user can see what was decoded
_manualEntryPanel.Visibility = Visibility.Visible;
_testStatusLabel.Text = "✅ Setup code decoded — press Test Connection";
_testStatusLabel.Text = LocalizationHelper.GetString("Setup_CodeDecoded");
Logger.Info($"[Setup] Setup code decoded: gateway={GatewayUrlHelper.SanitizeForDisplay(_draftGatewayUrl)}");
}
catch
@ -418,11 +429,11 @@ public sealed class SetupWizardWindow : WindowEx
if (string.IsNullOrWhiteSpace(_draftToken))
{
_testStatusLabel.Text = "❌ Please enter a token";
_testStatusLabel.Text = LocalizationHelper.GetString("Setup_TokenRequired");
return;
}
_testStatusLabel.Text = "⏳ Testing...";
_testStatusLabel.Text = LocalizationHelper.GetString("Setup_Testing");
_testButton.IsEnabled = false;
_connectionTested = false;
@ -465,7 +476,7 @@ public sealed class SetupWizardWindow : WindowEx
if (connected)
{
Logger.Info("[Setup] Test succeeded - fully connected");
_testStatusLabel.Text = "✅ Connected!";
_testStatusLabel.Text = LocalizationHelper.GetString("Setup_Connected");
_connectionTested = true;
}
else if (lastError.Contains("pairing required", StringComparison.OrdinalIgnoreCase) ||
@ -473,20 +484,20 @@ public sealed class SetupWizardWindow : WindowEx
{
Logger.Info("[Setup] Test succeeded - pairing approval needed");
var deviceId = _copyDeviceIdButton.Tag?.ToString() ?? "your-device-id";
_testStatusLabel.Text = $"✅ Gateway reached! Device needs pairing approval.\n\nOn your gateway host (Mac/Linux), run:\n\n openclaw devices approve {deviceId}";
_testStatusLabel.Text = string.Format(LocalizationHelper.GetString("Setup_PairingRequired"), deviceId);
_connectionTested = true;
}
else if (lastError.Contains("token mismatch", StringComparison.OrdinalIgnoreCase))
{
_testStatusLabel.Text = "❌ Token doesn't match.\n\n💡 Check gateway auth token:\n cat ~/.openclaw/openclaw.json | grep token";
_testStatusLabel.Text = LocalizationHelper.GetString("Setup_TokenMismatch");
}
else if (lastError.Contains("origin not allowed", StringComparison.OrdinalIgnoreCase))
{
_testStatusLabel.Text = "❌ Origin not allowed.\n\n💡 Add this machine to gateway.controlUi.allowedOrigins.";
_testStatusLabel.Text = LocalizationHelper.GetString("Setup_OriginNotAllowed");
}
else if (lastError.Contains("too many failed", StringComparison.OrdinalIgnoreCase))
{
_testStatusLabel.Text = "❌ Rate-limited. Wait a minute and try again.";
_testStatusLabel.Text = LocalizationHelper.GetString("Setup_RateLimited");
}
else if (!string.IsNullOrEmpty(lastError))
{
@ -494,7 +505,7 @@ public sealed class SetupWizardWindow : WindowEx
}
else
{
_testStatusLabel.Text = "❌ Timed out. Check the URL and gateway is running.";
_testStatusLabel.Text = LocalizationHelper.GetString("Setup_TimedOut");
}
}
catch (Exception ex)
@ -540,7 +551,7 @@ public sealed class SetupWizardWindow : WindowEx
catch (Exception ex)
{
Logger.Warn($"[Setup] Could not load device identity: {ex.Message}");
_deviceIdText.Text = "Device ID: (will be generated on first connect)";
_deviceIdText.Text = LocalizationHelper.GetString("Setup_DeviceIdFallback");
}
}
@ -554,13 +565,13 @@ public sealed class SetupWizardWindow : WindowEx
var dataPackage = new global::Windows.ApplicationModel.DataTransfer.DataPackage();
dataPackage.SetText(fullId);
global::Windows.ApplicationModel.DataTransfer.Clipboard.SetContent(dataPackage);
_copyDeviceIdButton.Content = "✅ Copied!";
_copyDeviceIdButton.Content = LocalizationHelper.GetString("Setup_DeviceIdCopied");
Logger.Info("[Setup] Device ID copied to clipboard");
// Reset button text after 2 seconds
_ = Task.Delay(2000).ContinueWith(_ =>
{
DispatcherQueue.TryEnqueue(() => _copyDeviceIdButton.Content = "📋 Copy Device ID");
DispatcherQueue.TryEnqueue(() => _copyDeviceIdButton.Content = LocalizationHelper.GetString("Setup_CopyDeviceId"));
});
}
catch (Exception ex)

View File

@ -16,7 +16,7 @@
<!-- Status Header -->
<StackPanel Orientation="Horizontal" Spacing="12">
<FontIcon x:Name="StatusIcon" Glyph="&#xE8FB;" FontSize="32"
Foreground="#E74C3C"/>
Foreground="{ThemeResource SystemFillColorCriticalBrush}"/>
<StackPanel>
<TextBlock x:Name="StatusText" x:Uid="StatusConnectedText" Text="Connected"
Style="{StaticResource SubtitleTextBlockStyle}"/>
@ -29,7 +29,7 @@
<!-- Usage Section -->
<StackPanel x:Name="UsageSection" Spacing="8">
<TextBlock x:Uid="StatusUsageHeader" Text="USAGE" Style="{StaticResource CaptionTextBlockStyle}"
Foreground="#E74C3C" FontWeight="Bold"/>
Foreground="{ThemeResource AccentTextFillColorPrimaryBrush}" FontWeight="Bold"/>
<Grid ColumnSpacing="16" RowSpacing="4">
<Grid.ColumnDefinitions>
@ -59,7 +59,7 @@
<!-- Sessions Section -->
<StackPanel x:Name="SessionsSection" Spacing="8">
<TextBlock x:Uid="StatusSessionsHeader" Text="ACTIVE SESSIONS" Style="{StaticResource CaptionTextBlockStyle}"
Foreground="#E74C3C" FontWeight="Bold"/>
Foreground="{ThemeResource AccentTextFillColorPrimaryBrush}" FontWeight="Bold"/>
<ItemsControl x:Name="SessionsList">
<ItemsControl.ItemTemplate>
@ -92,7 +92,7 @@
<!-- Channels Section -->
<StackPanel x:Name="ChannelsSection" Spacing="8">
<TextBlock x:Uid="StatusChannelsHeader" Text="CHANNELS" Style="{StaticResource CaptionTextBlockStyle}"
Foreground="#E74C3C" FontWeight="Bold"/>
Foreground="{ThemeResource AccentTextFillColorPrimaryBrush}" FontWeight="Bold"/>
<ItemsControl x:Name="ChannelsList">
<ItemsControl.ItemTemplate>
@ -114,7 +114,7 @@
</StackPanel>
<!-- Refresh Button -->
<Button x:Uid="StatusRefreshButton" Content="Refresh" Click="OnRefresh" HorizontalAlignment="Center"/>
<Button x:Name="RefreshButton" x:Uid="StatusRefreshButton" AutomationProperties.AutomationId="RefreshButton" Content="Refresh" Click="OnRefresh" HorizontalAlignment="Center"/>
</StackPanel>
</ScrollViewer>

View File

@ -20,22 +20,26 @@
<StackPanel Grid.Row="0" Orientation="Horizontal" Spacing="4" Padding="8,8,16,8"
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}">
<Button x:Name="HomeButton" ToolTipService.ToolTip="Home" Click="OnHome"
<Button x:Name="HomeButton" AutomationProperties.AutomationId="HomeButton" AutomationProperties.Name="Home"
ToolTipService.ToolTip="Home" Click="OnHome"
Width="40" Height="36" Padding="4">
<FontIcon Glyph="&#xE80F;" FontSize="16"/>
</Button>
<Button x:Name="RefreshButton" ToolTipService.ToolTip="Refresh" Click="OnRefresh"
<Button x:Name="RefreshButton" AutomationProperties.AutomationId="RefreshButton" AutomationProperties.Name="Refresh"
ToolTipService.ToolTip="Refresh" Click="OnRefresh"
Width="40" Height="36" Padding="4">
<FontIcon Glyph="&#xE72C;" FontSize="16"/>
</Button>
<Button x:Name="PopoutButton" ToolTipService.ToolTip="Open in Browser" Click="OnPopout"
<Button x:Name="PopoutButton" AutomationProperties.AutomationId="PopoutButton" AutomationProperties.Name="Open in Browser"
ToolTipService.ToolTip="Open in Browser" Click="OnPopout"
Width="40" Height="36" Padding="4">
<FontIcon Glyph="&#xE8A7;" FontSize="16"/>
</Button>
<Button x:Name="DevToolsButton" ToolTipService.ToolTip="Developer Tools" Click="OnDevTools"
<Button x:Name="DevToolsButton" AutomationProperties.AutomationId="DevToolsButton" AutomationProperties.Name="Developer Tools"
ToolTipService.ToolTip="Developer Tools" Click="OnDevTools"
Width="40" Height="36" Padding="4">
<FontIcon Glyph="&#xE90F;" FontSize="16"/>
</Button>
@ -52,10 +56,10 @@
<!-- Error display (hidden by default) -->
<ScrollViewer x:Name="ErrorPanel" Grid.Row="1" Visibility="Collapsed" Padding="20">
<StackPanel Spacing="12">
<TextBlock x:Uid="WebChatErrorTitle" Text="Web Chat Unavailable" FontSize="18" FontWeight="SemiBold"
<TextBlock x:Uid="WebChatErrorTitle" Text="Web Chat Unavailable" Style="{StaticResource SubtitleTextBlockStyle}" FontWeight="SemiBold"
Foreground="{ThemeResource SystemFillColorCriticalBrush}"/>
<TextBlock x:Name="ErrorText" TextWrapping="Wrap" IsTextSelectionEnabled="True"
FontFamily="Consolas" FontSize="12"/>
FontFamily="Consolas" Style="{StaticResource CaptionTextBlockStyle}"/>
<Button x:Uid="WebChatOpenBrowserButton" Content="Open in Browser Instead" Click="OnPopout" HorizontalAlignment="Left"/>
</StackPanel>
</ScrollViewer>